Skip to content

Commit

Permalink
[Truffle] Replace transferToInterpreter to transferToInterpreterAndIn…
Browse files Browse the repository at this point in the history
…validate.

* Since Truffle only cares about the first one and
* there is no valid use case for transferToInterpreter.
eregon committed Jun 8, 2016
1 parent c7e57bf commit 1fdc645
Showing 122 changed files with 344 additions and 344 deletions.
Original file line number Diff line number Diff line change
@@ -38,7 +38,7 @@ public Object execute(VirtualFrame frame) {

if (noBlockProfile.profile(block == null)) {
if (toEnumNode == null) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
toEnumNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext()));
}

Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@ public YieldingCoreMethodNode(RubyContext context, SourceSection sourceSection)

private boolean booleanCast(VirtualFrame frame, Object value) {
if (booleanCastNode == null) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
booleanCastNode = insert(BooleanCastNodeGen.create(getContext(), getSourceSection(), null));
}
return booleanCastNode.executeBoolean(frame, value);
Original file line number Diff line number Diff line change
@@ -1090,7 +1090,7 @@ public static double toDouble(Object value, DynamicObject nil) {
return (double) value;
}

CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new UnsupportedOperationException();
}

36 changes: 18 additions & 18 deletions truffle/src/main/java/org/jruby/truffle/core/MathNodes.java
Original file line number Diff line number Diff line change
@@ -39,7 +39,7 @@ public abstract static class ACosNode extends SimpleMonadicMathNode {
@Override
protected double doFunction(double a) {
if (a < -1.0 || a > 1.0) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new RaiseException(coreExceptions().mathDomainError("acos", this));
}

@@ -58,7 +58,7 @@ protected double doFunction(double a) {
if (Double.isNaN(a)) {
return Double.NaN;
} else if (a < 1) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new RaiseException(coreExceptions().mathDomainError("acosh", this));
} else if (a < 94906265.62) {
return Math.log(a + Math.sqrt(a * a - 1.0));
@@ -75,7 +75,7 @@ public abstract static class ASinNode extends SimpleMonadicMathNode {
@Override
protected double doFunction(double a) {
if (a < -1.0 || a > 1.0) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new RaiseException(coreExceptions().mathDomainError("asin", this));
}

@@ -138,7 +138,7 @@ protected double doFunction(double a) {
// Copied from RubyMath - see copyright notices there

if (a < -1.0 || a > 1.0) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new RaiseException(coreExceptions().mathDomainError("atanh", this));
}

@@ -336,7 +336,7 @@ protected double doFunction(double a) {
// Copied from RubyMath - see copyright notices there

if (a == -1) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new RaiseException(coreExceptions().mathDomainError("gamma", this));
}

@@ -348,7 +348,7 @@ protected double doFunction(double a) {
if (a > 0) {
return Double.POSITIVE_INFINITY;
} else {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new RaiseException(coreExceptions().mathDomainError("gamma", this));
}
}
@@ -368,7 +368,7 @@ protected double doFunction(double a) {
}

if (Double.isNaN(a)) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new RaiseException(coreExceptions().mathDomainError("gamma", this));
}

@@ -459,7 +459,7 @@ public double function(double a, long b) {
@Specialization
public double function(double a, double b) {
if (Double.isNaN(b)) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new RaiseException(coreExceptions().rangeError("float", Double.toString(b), "integer", this));
}

@@ -473,7 +473,7 @@ public double function(VirtualFrame frame, Object a, Object b) {
floatANode.callFloat(frame, a, "to_f", null),
integerBNode.callLongFixnum(frame, b, "to_int", null));
} else {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new RaiseException(coreExceptions().typeErrorCantConvertInto(a, "Float", this));
}
}
@@ -514,7 +514,7 @@ public DynamicObject lgamma(double a) {
// Copied from RubyMath - see copyright notices there

if (a < 0 && Double.isInfinite(a)) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new RaiseException(coreExceptions().mathDomainError("log2", this));
}

@@ -528,7 +528,7 @@ public DynamicObject lgamma(VirtualFrame frame, Object a) {
if (isANode.executeIsA(a, coreLibrary().getNumericClass())) {
return lgamma(floatNode.callFloat(frame, a, "to_f", null));
} else {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new RaiseException(coreExceptions().typeErrorCantConvertInto(a, "Float", this));
}
}
@@ -563,14 +563,14 @@ public double function(VirtualFrame frame, Object a, NotProvided b) {
if (isANode.executeIsA(a, coreLibrary().getNumericClass())) {
return doFunction(floatANode.callFloat(frame, a, "to_f", null));
} else {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new RaiseException(coreExceptions().typeErrorCantConvertInto(a, "Float", this));
}
}

private double doFunction(double a) {
if (a < 0) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new RaiseException(coreExceptions().mathDomainError("log", this));
}

@@ -580,7 +580,7 @@ private double doFunction(double a) {
@Override
protected double doFunction(double a, double b) {
if (a < 0) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new RaiseException(coreExceptions().mathDomainError("log", this));
}

@@ -595,7 +595,7 @@ public abstract static class Log10Node extends SimpleMonadicMathNode {
@Override
protected double doFunction(double a) {
if (a < 0) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new RaiseException(coreExceptions().mathDomainError("log10", this));
}

@@ -612,7 +612,7 @@ public abstract static class Log2Node extends SimpleMonadicMathNode {
@Override
protected double doFunction(double a) {
if (a < 0) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new RaiseException(coreExceptions().mathDomainError("log2", this));
}

@@ -717,7 +717,7 @@ public double function(VirtualFrame frame, Object a) {
if (isANode.executeIsA(a, coreLibrary().getNumericClass())) {
return doFunction(floatNode.callFloat(frame, a, "to_f", null));
} else {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new RaiseException(coreExceptions().typeErrorCantConvertInto(a, "Float", this));
}
}
@@ -835,7 +835,7 @@ public double function(VirtualFrame frame, Object a, Object b) {
floatANode.callFloat(frame, a, "to_f", null),
floatBNode.callFloat(frame, b, "to_f", null));
} else {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();

throw new RaiseException(coreExceptions().typeErrorCantConvertInto(a, "Float", this));
}
4 changes: 2 additions & 2 deletions truffle/src/main/java/org/jruby/truffle/core/ObjectNodes.java
Original file line number Diff line number Diff line change
@@ -122,15 +122,15 @@ public ObjectInfectPrimitiveNode(RubyContext context, SourceSection sourceSectio
@Specialization
public Object objectInfect(Object host, Object source) {
if (isTaintedNode == null) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
isTaintedNode = insert(IsTaintedNodeGen.create(getContext(), getSourceSection(), null));
}

if (isTaintedNode.executeIsTainted(source)) {
// This lazy node allocation effectively gives us a branch profile

if (taintNode == null) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
taintNode = insert(TaintNodeGen.create(getContext(), getSourceSection(), null));
}

Original file line number Diff line number Diff line change
@@ -105,7 +105,7 @@ public CatchNode(RubyContext context, SourceSection sourceSection) {

private boolean areSame(VirtualFrame frame, Object left, Object right) {
if (referenceEqualNode == null) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
referenceEqualNode = insert(BasicObjectNodesFactory.ReferenceEqualNodeFactory.create(null));
}
return referenceEqualNode.executeReferenceEqual(frame, left, right);
Original file line number Diff line number Diff line change
@@ -85,13 +85,13 @@ public void resume(Object[] store) {

@Override
public Object start() {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
return new Object[getContext().getOptions().ARRAY_UNINITIALIZED_SIZE];
}

@Override
public Object start(int length) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
return new Object[length];
}

@@ -103,7 +103,7 @@ public Object ensure(Object store, int length) {

@Override
public Object appendArray(Object store, int index, DynamicObject array) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();

for (Object value : ArrayOperations.toIterable(array)) {
store = appendValue(store, index, value);
@@ -115,7 +115,7 @@ public Object appendArray(Object store, int index, DynamicObject array) {

@Override
public Object appendValue(Object store, int index, Object value) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();

screen(value);

@@ -183,7 +183,7 @@ public Object start() {
@Override
public Object start(int length) {
if (length > expectedLength) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
return restart(length);
}

@@ -193,7 +193,7 @@ public Object start(int length) {
@Override
public Object ensure(Object store, int length) {
if (length > ((int[]) store).length) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();

final Object[] newStore = ArrayUtils.box((int[]) store);

@@ -219,7 +219,7 @@ public Object appendArray(Object store, int index, DynamicObject array) {
return store;
}

CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();

return replace(new ObjectArrayBuilderNode(getContext(), expectedLength)).
appendArray(ArrayUtils.box((int[]) store), index, array);
@@ -232,7 +232,7 @@ public Object appendValue(Object store, int index, Object value) {
((int[]) store)[index] = (int) value;
return store;
} else {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
return appendValueFallback(store, index, value, expectedLength);
}
}
@@ -261,7 +261,7 @@ public Object start() {
@Override
public Object start(int length) {
if (length > expectedLength) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
return restart(length);
}

@@ -270,7 +270,7 @@ public Object start(int length) {

@Override
public Object ensure(Object store, int length) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new UnsupportedOperationException();
}

@@ -287,7 +287,7 @@ public Object appendArray(Object store, int index, DynamicObject array) {
return store;
}

CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();

return replace(new ObjectArrayBuilderNode(getContext(), expectedLength)).
appendArray(ArrayUtils.box((long[]) store), index, array);
@@ -305,7 +305,7 @@ public Object appendValue(Object store, int index, Object value) {
return store;
}
}
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
return appendValueFallback(store, index, value, expectedLength);
}

@@ -333,7 +333,7 @@ public Object start() {
@Override
public Object start(int length) {
if (length > expectedLength) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
return restart(length);
}

@@ -343,7 +343,7 @@ public Object start(int length) {
@Override
public Object ensure(Object store, int length) {
if (length > ((double[]) store).length) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
final Object[] newStore = ArrayUtils.box((double[]) store);
final UninitializedArrayBuilderNode newNode = new UninitializedArrayBuilderNode(getContext());
replace(newNode);
@@ -366,7 +366,7 @@ public Object appendArray(Object store, int index, DynamicObject array) {
return store;
}

CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();

return replace(new ObjectArrayBuilderNode(getContext(), expectedLength)).
appendArray(ArrayUtils.box((double[]) store), index, array);
@@ -379,7 +379,7 @@ public Object appendValue(Object store, int index, Object value) {
((double[]) store)[index] = (double) value;
return store;
} else {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
return appendValueFallback(store, index, value, expectedLength);
}
}
@@ -410,7 +410,7 @@ public Object start() {
@Override
public Object start(int length) {
if (length > expectedLength) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();
return restart(length);
}

@@ -420,7 +420,7 @@ public Object start(int length) {
@Override
public Object ensure(Object store, int length) {
if (length > ((Object[]) store).length) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();

final UninitializedArrayBuilderNode newNode = new UninitializedArrayBuilderNode(getContext());
replace(newNode);
Original file line number Diff line number Diff line change
@@ -237,7 +237,7 @@ public UninitialisedArrayLiteralNode(RubyContext context, SourceSection sourceSe

@Override
public Object execute(VirtualFrame frame) {
CompilerDirectives.transferToInterpreter();
CompilerDirectives.transferToInterpreterAndInvalidate();

final Object[] executedValues = new Object[values.length];

Loading

6 comments on commit 1fdc645

@thedarkone
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't this result in unnecessary repeated recompiles?

For example in a code like this:

arr = (0..10_000).to_a # a dataset

def compute(x) # work fun, on some inputs it might throw an error
  1000 / x
end

100.times do
  arr.shuffle.each do |v|
    begin
      compute(x)
    rescue
      # log an error, we ignore invalid inputs
    end
  end
end

Isn't it the case, that previously foo (or the containing each block) would compile, run at full speed, on exception transfer to interpreter, throw an error, and then go back into full speed JIT-ed code? Whereas now on transfer it additionally discards the JIT-ed code, recompiles foo again (into an identical machine code) and then go back to full speed?

Before ~999,900 compiled calls, 100 transfers; after: ~999,900 compiled calls, 100 transfers, 100 identical recompiles of compute? What am I missing?

@eregon
Copy link
Member Author

@eregon eregon commented on 1fdc645 Jun 9, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thedarkone It could, but in practice transferToInterpreter is almost never the right tool.
In a dynamic compiler, one needs to make sure the code does not continuously deoptimize, so once we do we want to take action so it does not happen a second time for the same reason (or a very small bounded number of times).

An error like a division by 0 should first deopt, then be checked actively (raise if b == 0) so it does not deopt anymore. So we really want only 1 transfer there.
BTW, rescue nor throwing a Ruby exception needs a transfer.

But, currently the division node is indeed bugged because it never checks actively for b == 0 and relies wrongly on the ArithmeticException which is always a deopt. Fix coming...

@eregon
Copy link
Member Author

@eregon eregon commented on 1fdc645 Jun 9, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thedarkone The division by zero thing is fixed as of fd17285.
Now it compiles into a nice mostly-empty loop with no deopts.

@thedarkone
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, I get it, you like to invalidate things 😝.

Trans&invalidates look fine to me in cases of unreachable defaults and right before insert(), but I think you should audit all other cases (similarly to that div by Fixnum 0).

I think there are other zeroDivisionError, also check out:

I went scrolling upwards and gave up after about 20% of the commit, I'll leave the rest of it up to you guys!

@eregon
Copy link
Member Author

@eregon eregon commented on 1fdc645 Jun 9, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thedarkone Good catch, indeed that one should be profiled instead.
The reason I did it for all usages is even if transferToInterpreter would be more appropriate, is it does not produce good results either as it constantly deoptimizes.
Thank you for your watchful eye 😃

@thedarkone
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, indeed that one should be profiled instead.

Damn, I see that I failed at copy-pasting. The above links were not meant to be identical ;). The second example is this: 1fdc645#diff-0631793139d5299a8e27ab21a2b2880aR93

Please sign in to comment.