Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: a683daa690eb
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 3df96ddf4f65
Choose a head ref
  • 7 commits
  • 11 files changed
  • 1 contributor

Commits on Mar 6, 2015

  1. Copy the full SHA
    a4d2067 View commit details
  2. [Truffle] Allow DISPATCH_POLYMORPHIC_MAX CachedYieldDispatchNode.

    * And not DISPATCH_POLYMORPHIC_MAX - 1.
    eregon committed Mar 6, 2015
    Copy the full SHA
    94f406f View commit details
  3. [Truffle] Make doUnboxedObject/doRubyBasicObject return a DispatchNode.

    * Less duplication of the call to executeDispatch().
    eregon committed Mar 6, 2015
    Copy the full SHA
    93e30e9 View commit details
  4. Copy the full SHA
    92aa918 View commit details
  5. Copy the full SHA
    43c075a View commit details
  6. Copy the full SHA
    b8385e5 View commit details
  7. [Truffle] Make DispatchNode rewriting thread-safe.

    * By re-reading under the lock the chain and checking if some other thread added
      a matching specilization. If that fails, we can rewrite safely rewrite.
    eregon committed Mar 6, 2015
    Copy the full SHA
    3df96dd View commit details
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@
import com.oracle.truffle.api.nodes.IndirectCallNode;
import com.oracle.truffle.api.nodes.InvalidAssumptionException;
import com.oracle.truffle.api.utilities.BranchProfile;

import org.jruby.truffle.runtime.RubyArguments;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.RubyProc;
@@ -90,14 +91,19 @@ public CachedBooleanDispatchNode(
}
}

@Override
protected boolean guard(Object methodName, Object receiver) {
return guardName(methodName) && (receiver instanceof Boolean);
}

@Override
public Object executeDispatch(
VirtualFrame frame,
Object receiverObject,
Object methodName,
Object blockObject,
Object argumentsObjects) {
if (!guardName(methodName) || !(receiverObject instanceof Boolean)) {
if (!guard(methodName, receiverObject)) {
return next.executeDispatch(
frame,
receiverObject,
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@
import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.IndirectCallNode;
import com.oracle.truffle.api.nodes.InvalidAssumptionException;

import org.jruby.truffle.runtime.DebugOperations;
import org.jruby.truffle.runtime.RubyArguments;
import org.jruby.truffle.runtime.RubyContext;
@@ -92,14 +93,21 @@ public CachedBoxedDispatchNode(
}
}

@Override
protected boolean guard(Object methodName, Object receiver) {
return guardName(methodName) &&
(receiver instanceof RubyBasicObject) &&
((RubyBasicObject) receiver).getMetaClass() == expectedClass;
}

@Override
public Object executeDispatch(
VirtualFrame frame,
Object receiverObject,
Object methodName,
Object blockObject,
Object argumentsObjects) {
if (!guardName(methodName) || !(receiverObject instanceof RubyBasicObject) || ((RubyBasicObject) receiverObject).getMetaClass() != expectedClass) {
if (!guard(methodName, receiverObject)) {
return next.executeDispatch(
frame,
receiverObject,
Original file line number Diff line number Diff line change
@@ -75,14 +75,21 @@ public CachedBoxedMethodMissingDispatchNode(
}
}

@Override
protected boolean guard(Object methodName, Object receiver) {
return guardName(methodName) &&
(receiver instanceof RubyBasicObject) &&
((RubyBasicObject) receiver).getMetaClass() == expectedClass;
}

@Override
public Object executeDispatch(
VirtualFrame frame,
Object receiverObject,
Object methodName,
Object blockObject,
Object argumentsObjects) {
if (!guardName(methodName) || !(receiverObject instanceof RubyBasicObject) || ((RubyBasicObject) receiverObject).getMetaClass() != expectedClass) {
if (!guard(methodName, receiverObject)) {
return next.executeDispatch(
frame,
receiverObject,
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.InvalidAssumptionException;

import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.RubyBasicObject;
import org.jruby.truffle.runtime.core.RubyClass;
@@ -39,14 +40,21 @@ public CachedBoxedReturnMissingDispatchNode(
this.next = next;
}

@Override
protected boolean guard(Object methodName, Object receiver) {
return guardName(methodName) &&
(receiver instanceof RubyBasicObject) &&
((RubyBasicObject) receiver).getMetaClass() == expectedClass;
}

@Override
public Object executeDispatch(
VirtualFrame frame,
Object receiverObject,
Object methodName,
Object blockObject,
Object argumentsObjects) {
if (!guardName(methodName) || !(receiverObject instanceof RubyBasicObject) || ((RubyBasicObject) receiverObject).getMetaClass() != expectedClass) {
if (!guard(methodName, receiverObject)) {
return next.executeDispatch(
frame,
receiverObject,
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@
import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.IndirectCallNode;
import com.oracle.truffle.api.nodes.InvalidAssumptionException;

import org.jruby.truffle.runtime.RubyArguments;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.RubyProc;
@@ -61,14 +62,19 @@ public CachedBoxedSymbolDispatchNode(
}
}

@Override
protected boolean guard(Object methodName, Object receiver) {
return guardName(methodName) && (receiver instanceof RubySymbol);
}

@Override
public Object executeDispatch(
VirtualFrame frame,
Object receiverObject,
Object methodName,
Object blockObject,
Object argumentsObjects) {
if (!guardName(methodName) || !(receiverObject instanceof RubySymbol)) {
if (!guard(methodName, receiverObject)) {
return next.executeDispatch(
frame,
receiverObject,
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@
package org.jruby.truffle.nodes.dispatch;

import com.oracle.truffle.api.utilities.BranchProfile;

import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.RubyString;
import org.jruby.truffle.runtime.core.RubySymbol;
@@ -58,6 +59,11 @@ public CachedDispatchNode(CachedDispatchNode prev) {
indirect = prev.indirect;
}

@Override
protected DispatchNode getNext() {
return next;
}

protected final boolean guardName(Object methodName) {
if (cachedName == methodName) {
return true;
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@
import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.IndirectCallNode;
import com.oracle.truffle.api.nodes.InvalidAssumptionException;

import org.jruby.truffle.runtime.RubyArguments;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.RubyBasicObject;
@@ -59,14 +60,21 @@ public CachedUnboxedDispatchNode(
}
}

@Override
protected boolean guard(Object methodName, Object receiver) {
return guardName(methodName) &&
!(receiver instanceof RubyBasicObject) &&
(receiver.getClass() == expectedClass);
}

@Override
public Object executeDispatch(
VirtualFrame frame,
Object receiverObject,
Object methodName,
Object blockObject,
Object argumentsObjects) {
if (!guardName(methodName) || receiverObject instanceof RubyBasicObject || receiverObject.getClass() != expectedClass) {
if (!guard(methodName, receiverObject)) {
return next.executeDispatch(
frame,
receiverObject,
Original file line number Diff line number Diff line change
@@ -52,6 +52,12 @@ public DispatchNode(DispatchNode prev) {
dispatchAction = prev.dispatchAction;
}

protected abstract boolean guard(Object methodName, Object receiver);

protected DispatchNode getNext() {
return null;
}

public abstract Object executeDispatch(
VirtualFrame frame,
Object receiverObject,
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.IndirectCallNode;
import com.oracle.truffle.api.utilities.BranchProfile;

import org.jruby.truffle.nodes.conversion.ToJavaStringNode;
import org.jruby.truffle.nodes.conversion.ToJavaStringNodeFactory;
import org.jruby.truffle.nodes.conversion.ToSymbolNode;
@@ -49,6 +50,11 @@ public UncachedDispatchNode(RubyContext context, boolean ignoreVisibility, Dispa
toJavaStringNode = ToJavaStringNodeFactory.create(context, null, null);
}

@Override
protected boolean guard(Object methodName, Object receiver) {
return true;
}

@Override
public Object executeDispatch(
VirtualFrame frame,
Original file line number Diff line number Diff line change
@@ -9,6 +9,8 @@
*/
package org.jruby.truffle.nodes.dispatch;

import java.util.concurrent.Callable;

import com.oracle.truffle.api.Assumption;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
@@ -50,52 +52,64 @@ public UnresolvedDispatchNode(
this.missingBehavior = missingBehavior;
}

@Override
protected boolean guard(Object methodName, Object receiver) {
return false;
}

@Override
public Object executeDispatch(
VirtualFrame frame,
Object receiverObject,
Object methodName,
final VirtualFrame frame,
final Object receiverObject,
final Object methodName,
Object blockObject,
Object argumentsObjects) {
CompilerDirectives.transferToInterpreterAndInvalidate();

if (depth == DISPATCH_POLYMORPHIC_MAX) {
return getHeadNode().getFirstDispatchNode()
.replace(new UncachedDispatchNode(getContext(), ignoreVisibility, getDispatchAction(), missingBehavior))
.executeDispatch(frame, receiverObject,
methodName, blockObject, argumentsObjects);
}
final DispatchNode dispatch = atomic(new Callable<DispatchNode>() {
@Override
public DispatchNode call() throws Exception {
final DispatchNode first = getHeadNode().getFirstDispatchNode();

depth++;
// First try to see if we did not a miss a specialization added by another thread.

final DispatchNode first = getHeadNode().getFirstDispatchNode();
DispatchNode lookupDispatch = first;
while (lookupDispatch != null) {
if (lookupDispatch.guard(methodName, receiverObject)) {
// This one worked, no need to rewrite anything.
return lookupDispatch;
}
lookupDispatch = lookupDispatch.getNext();
}

if (isRubyBasicObject(receiverObject)) {
return doRubyBasicObject(
frame,
first,
receiverObject,
methodName,
blockObject,
argumentsObjects);
} else {
return doUnboxedObject(
frame,
first,
receiverObject,
methodName,
blockObject,
argumentsObjects);
}
// We need a new node to handle this case.

final DispatchNode newDispathNode;

if (depth == DISPATCH_POLYMORPHIC_MAX) {
newDispathNode = new UncachedDispatchNode(getContext(), ignoreVisibility, getDispatchAction(), missingBehavior);
} else {
depth++;
if (isRubyBasicObject(receiverObject)) {
newDispathNode = doRubyBasicObject(frame, first, receiverObject, methodName);
} else {
newDispathNode = doUnboxedObject(frame, first, receiverObject, methodName);
}
}

first.replace(newDispathNode);
return newDispathNode;
}
});

return dispatch.executeDispatch(frame, receiverObject, methodName, blockObject, argumentsObjects);
}

private Object doUnboxedObject(
private DispatchNode doUnboxedObject(
VirtualFrame frame,
DispatchNode first,
Object receiverObject,
Object methodName,
Object blockObject,
Object argumentsObjects) {
Object methodName) {
final DispatchAction dispatchAction = getDispatchAction();

final RubyClass callerClass;
@@ -110,9 +124,7 @@ private Object doUnboxedObject(
final InternalMethod method = lookup(callerClass, receiverObject, methodName.toString(), ignoreVisibility);

if (method == null) {
final DispatchNode newDispatch = createMethodMissingNode(methodName, receiverObject);
return newDispatch.executeDispatch(frame, receiverObject,
methodName, blockObject, argumentsObjects);
return createMethodMissingNode(first, methodName, receiverObject);
}

if (receiverObject instanceof Boolean) {
@@ -134,37 +146,25 @@ private Object doUnboxedObject(
throw new UnsupportedOperationException();
}

final CachedBooleanDispatchNode newDispatch = new CachedBooleanDispatchNode(getContext(),
return new CachedBooleanDispatchNode(getContext(),
methodName, first,
falseUnmodifiedAssumption, null, falseMethod,
trueUnmodifiedAssumption, null, trueMethod, indirect, getDispatchAction());

first.replace(newDispatch);

return newDispatch.executeDispatch(frame, receiverObject,
methodName, blockObject, argumentsObjects);
} else {
final CachedUnboxedDispatchNode newDispatch = new CachedUnboxedDispatchNode(getContext(),
return new CachedUnboxedDispatchNode(getContext(),
methodName, first, receiverObject.getClass(),
getContext().getCoreLibrary().getLogicalClass(receiverObject).getUnmodifiedAssumption(), null, method, indirect, getDispatchAction());

first.replace(newDispatch);

return newDispatch.executeDispatch(frame, receiverObject, methodName,
blockObject, argumentsObjects);
}
} else {
throw new UnsupportedOperationException();
}
}

private Object doRubyBasicObject(
private DispatchNode doRubyBasicObject(
VirtualFrame frame,
DispatchNode first,
Object receiverObject,
Object methodName,
Object blockObject,
Object argumentsObjects) {
Object methodName) {
final DispatchAction dispatchAction = getDispatchAction();

final RubyClass callerClass = ignoreVisibility ? null : getContext().getCoreLibrary().getMetaClass(RubyArguments.getSelf(frame.getArguments()));
@@ -173,33 +173,23 @@ private Object doRubyBasicObject(
final InternalMethod method = lookup(callerClass, receiverObject, methodName.toString(), ignoreVisibility);

if (method == null) {
final DispatchNode newDispatch = createMethodMissingNode(methodName, receiverObject);
return newDispatch.executeDispatch(frame, receiverObject,
methodName, blockObject, argumentsObjects);
return createMethodMissingNode(first, methodName, receiverObject);
}

final DispatchNode newDispatch;

if (receiverObject instanceof RubySymbol) {
newDispatch = new CachedBoxedSymbolDispatchNode(getContext(), methodName, first, null, method, indirect, getDispatchAction());
return new CachedBoxedSymbolDispatchNode(getContext(), methodName, first, null, method, indirect, getDispatchAction());
} else {
newDispatch = new CachedBoxedDispatchNode(getContext(), methodName, first,
return new CachedBoxedDispatchNode(getContext(), methodName, first,
getContext().getCoreLibrary().getMetaClass(receiverObject), null, method, indirect, getDispatchAction());
}

first.replace(newDispatch);
return newDispatch.executeDispatch(frame, receiverObject,
methodName, blockObject, argumentsObjects);

} else if (dispatchAction == DispatchAction.READ_CONSTANT) {
final RubyModule module = (RubyModule) receiverObject;
final RubyConstant constant = lookupConstant(module, methodName.toString(),
ignoreVisibility);

if (constant == null) {
final DispatchNode newDispatch = createConstantMissingNode(methodName, callerClass, module);
return newDispatch.executeDispatch(frame, module,
methodName, blockObject, argumentsObjects);
return createConstantMissingNode(first, methodName, callerClass, module);
}

if (constant.isAutoload()) {
@@ -210,33 +200,28 @@ private Object doRubyBasicObject(

requireNode.require((RubyString) constant.getValue());

return doRubyBasicObject(frame, first, receiverObject, methodName, blockObject, argumentsObjects);
return doRubyBasicObject(frame, first, receiverObject, methodName);
}

// The module, the "receiver" is an instance of its singleton class.
// But we want to check the module assumption, not its singleton class assumption.
final DispatchNode newDispatch = new CachedBoxedDispatchNode(getContext(), methodName, first,
return new CachedBoxedDispatchNode(getContext(), methodName, first,
module.getSingletonClass(null), module.getUnmodifiedAssumption(), constant.getValue(),
null, indirect, getDispatchAction());

first.replace(newDispatch);
return newDispatch.executeDispatch(frame, receiverObject,
methodName, blockObject, argumentsObjects);
} else {
throw new UnsupportedOperationException();
}
}

private DispatchNode createConstantMissingNode(
DispatchNode first,
Object methodName,
RubyClass callerClass,
RubyBasicObject receiverObject) {
final DispatchNode first = getHeadNode().getFirstDispatchNode();

switch (missingBehavior) {
case RETURN_MISSING: {
return first.replace(new CachedBoxedReturnMissingDispatchNode(getContext(), methodName, first,
receiverObject.getMetaClass(), indirect, getDispatchAction()));
return new CachedBoxedReturnMissingDispatchNode(getContext(), methodName, first,
receiverObject.getMetaClass(), indirect, getDispatchAction());
}

case CALL_CONST_MISSING: {
@@ -248,11 +233,11 @@ private DispatchNode createConstantMissingNode(
}

if (DISPATCH_METAPROGRAMMING_ALWAYS_UNCACHED) {
return first.replace(new UncachedDispatchNode(getContext(), ignoreVisibility, getDispatchAction(), missingBehavior));
return new UncachedDispatchNode(getContext(), ignoreVisibility, getDispatchAction(), missingBehavior);
}

return first.replace(new CachedBoxedMethodMissingDispatchNode(getContext(), methodName, first,
receiverObject.getMetaClass(), method, DISPATCH_METAPROGRAMMING_ALWAYS_INDIRECT, getDispatchAction()));
return new CachedBoxedMethodMissingDispatchNode(getContext(), methodName, first,
receiverObject.getMetaClass(), method, DISPATCH_METAPROGRAMMING_ALWAYS_INDIRECT, getDispatchAction());
}

default: {
@@ -262,14 +247,13 @@ private DispatchNode createConstantMissingNode(
}

private DispatchNode createMethodMissingNode(
DispatchNode first,
Object methodName,
Object receiverObject) {
final DispatchNode first = getHeadNode().getFirstDispatchNode();

switch (missingBehavior) {
case RETURN_MISSING: {
return first.replace(new CachedBoxedReturnMissingDispatchNode(getContext(), methodName, first,
getContext().getCoreLibrary().getMetaClass(receiverObject), indirect, getDispatchAction()));
return new CachedBoxedReturnMissingDispatchNode(getContext(), methodName, first,
getContext().getCoreLibrary().getMetaClass(receiverObject), indirect, getDispatchAction());
}

case CALL_METHOD_MISSING: {
@@ -281,11 +265,11 @@ private DispatchNode createMethodMissingNode(
}

if (DISPATCH_METAPROGRAMMING_ALWAYS_UNCACHED) {
return first.replace(new UncachedDispatchNode(getContext(), ignoreVisibility, getDispatchAction(), missingBehavior));
return new UncachedDispatchNode(getContext(), ignoreVisibility, getDispatchAction(), missingBehavior);
}

return first.replace(new CachedBoxedMethodMissingDispatchNode(getContext(), methodName, first,
getContext().getCoreLibrary().getMetaClass(receiverObject), method, DISPATCH_METAPROGRAMMING_ALWAYS_INDIRECT, getDispatchAction()));
return new CachedBoxedMethodMissingDispatchNode(getContext(), methodName, first,
getContext().getCoreLibrary().getMetaClass(receiverObject), method, DISPATCH_METAPROGRAMMING_ALWAYS_INDIRECT, getDispatchAction());
}

default: {
Original file line number Diff line number Diff line change
@@ -55,8 +55,8 @@ public YieldDispatchNode call() {
lookupDispatch = lookupDispatch.getNext();
}

depth++;
if (depth < DispatchNode.DISPATCH_POLYMORPHIC_MAX) {
depth++;
CachedYieldDispatchNode cachedDispatch = new CachedYieldDispatchNode(getContext(), block, currentNode);
replace(cachedDispatch);
return cachedDispatch;