From 78ecb3206fddd6ac51eb570f4ca5e6e69f54a276 Mon Sep 17 00:00:00 2001 From: Andreas Woess Date: Wed, 8 May 2019 01:42:37 +0200 Subject: [PATCH 01/12] Speculate that TruffleBoundary methods do not throw (unless transferToInterpreterOnException=false). --- .../common/TruffleCompilerRuntime.java | 8 +- .../truffle/compiler/PartialEvaluator.java | 47 ++++++++++- .../nodes/SpeculativeExceptionGuardNode.java | 79 +++++++++++++++++++ .../truffle/runtime/GraalTruffleRuntime.java | 4 +- 4 files changed, 133 insertions(+), 5 deletions(-) create mode 100644 compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/nodes/SpeculativeExceptionGuardNode.java diff --git a/compiler/src/org.graalvm.compiler.truffle.common/src/org/graalvm/compiler/truffle/common/TruffleCompilerRuntime.java b/compiler/src/org.graalvm.compiler.truffle.common/src/org/graalvm/compiler/truffle/common/TruffleCompilerRuntime.java index 0498d27799cf..1bf11cd95177 100644 --- a/compiler/src/org.graalvm.compiler.truffle.common/src/org/graalvm/compiler/truffle/common/TruffleCompilerRuntime.java +++ b/compiler/src/org.graalvm.compiler.truffle.common/src/org/graalvm/compiler/truffle/common/TruffleCompilerRuntime.java @@ -194,7 +194,13 @@ enum InlineKind { * Denotes a call site must not be inlined and the execution should be transferred to * interpreter in case of an exception. */ - DO_NOT_INLINE_DEOPTIMIZE_ON_EXCEPTION(false); + DO_NOT_INLINE_DEOPTIMIZE_ON_EXCEPTION(false), + + /** + * Denotes a call site must not be inlined and the execution should be speculatively + * transferred to interpreter in case of an exception, unless the speculation has failed. + */ + DO_NOT_INLINE_WITH_SPECULATIVE_EXCEPTION(false); private final boolean allowsInlining; diff --git a/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/PartialEvaluator.java b/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/PartialEvaluator.java index 09cc0b6e796d..4006b1934006 100644 --- a/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/PartialEvaluator.java +++ b/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/PartialEvaluator.java @@ -59,8 +59,14 @@ import org.graalvm.compiler.graph.SourceLanguagePositionProvider; import org.graalvm.compiler.java.ComputeLoopFrequenciesClosure; import org.graalvm.compiler.loop.phases.ConvertDeoptimizeToGuardPhase; +import org.graalvm.compiler.nodes.AbstractBeginNode; import org.graalvm.compiler.nodes.Cancellable; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.FixedNode; +import org.graalvm.compiler.nodes.FixedWithNextNode; +import org.graalvm.compiler.nodes.Invoke; +import org.graalvm.compiler.nodes.InvokeWithExceptionNode; +import org.graalvm.compiler.nodes.LogicConstantNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; import org.graalvm.compiler.nodes.ValueNode; @@ -94,12 +100,15 @@ import org.graalvm.compiler.replacements.PEGraphDecoder; import org.graalvm.compiler.replacements.ReplacementsImpl; import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.SpeculationReasonGroup; import org.graalvm.compiler.truffle.common.CompilableTruffleAST; import org.graalvm.compiler.truffle.common.TruffleCompilerRuntime; +import org.graalvm.compiler.truffle.common.TruffleCompilerRuntime.InlineKind; import org.graalvm.compiler.truffle.common.TruffleInliningPlan; import org.graalvm.compiler.truffle.common.TruffleSourceLanguagePosition; import org.graalvm.compiler.truffle.compiler.debug.HistogramInlineInvokePlugin; import org.graalvm.compiler.truffle.compiler.nodes.TruffleAssumption; +import org.graalvm.compiler.truffle.compiler.nodes.SpeculativeExceptionGuardNode; import org.graalvm.compiler.truffle.compiler.nodes.asserts.NeverPartOfCompilationNode; import org.graalvm.compiler.truffle.compiler.nodes.frame.AllowMaterializeNode; import org.graalvm.compiler.truffle.compiler.phases.InstrumentBranchesPhase; @@ -113,12 +122,15 @@ import jdk.vm.ci.code.Architecture; import jdk.vm.ci.meta.ConstantReflectionProvider; +import jdk.vm.ci.meta.DeoptimizationAction; +import jdk.vm.ci.meta.DeoptimizationReason; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; import jdk.vm.ci.meta.SpeculationLog; +import jdk.vm.ci.meta.SpeculationLog.SpeculationReason; /** * Class performing the partial evaluation starting from the root node of an AST. @@ -301,7 +313,8 @@ private class PEInlineInvokePlugin implements InlineInvokePlugin { @Override public InlineInfo shouldInlineInvoke(GraphBuilderContext builder, ResolvedJavaMethod original, ValueNode[] arguments) { TruffleCompilerRuntime rt = TruffleCompilerRuntime.getRuntime(); - InlineInfo inlineInfo = asInlineInfo(rt.getInlineKind(original, true), original); + InlineKind inlineKind = rt.getInlineKind(original, true); + InlineInfo inlineInfo = asInlineInfo(inlineKind, original); if (!inlineInfo.allowsInlining()) { return inlineInfo; } @@ -354,6 +367,7 @@ private class ParsingInlineInvokePlugin implements InlineInvokePlugin { private final ReplacementsImpl replacements; private final InvocationPlugins invocationPlugins; private final LoopExplosionPlugin loopExplosionPlugin; + private boolean speculativeExceptionEdge; ParsingInlineInvokePlugin(ReplacementsImpl replacements, InvocationPlugins invocationPlugins, LoopExplosionPlugin loopExplosionPlugin) { this.replacements = replacements; @@ -388,8 +402,13 @@ public InlineInfo shouldInlineInvoke(GraphBuilderContext builder, ResolvedJavaMe } TruffleCompilerRuntime rt = TruffleCompilerRuntime.getRuntime(); - InlineInfo inlineInfo = asInlineInfo(rt.getInlineKind(original, true), original); + InlineKind inlineKind = rt.getInlineKind(original, true); + InlineInfo inlineInfo = asInlineInfo(inlineKind, original); if (!inlineInfo.allowsInlining()) { + if (inlineKind == InlineKind.DO_NOT_INLINE_WITH_SPECULATIVE_EXCEPTION) { + assert !speculativeExceptionEdge; + speculativeExceptionEdge = true; + } return inlineInfo; } if (original.equals(callIndirectMethod) || original.equals(callDirectMethod)) { @@ -404,6 +423,23 @@ public InlineInfo shouldInlineInvoke(GraphBuilderContext builder, ResolvedJavaMe } return null; } + + @Override + public void notifyNotInlined(GraphBuilderContext b, ResolvedJavaMethod method, Invoke invoke) { + if (speculativeExceptionEdge) { + InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke; + AbstractBeginNode exceptionEdge = invokeWithException.exceptionEdge(); + FixedNode next = exceptionEdge.next(); + assert next != null; + exceptionEdge.setNext(null); + // Note: Speculation is inserted during PE. + FixedWithNextNode guard = b.getGraph().add(new SpeculativeExceptionGuardNode(LogicConstantNode.tautology(b.getGraph()), + DeoptimizationReason.TransferToInterpreter, DeoptimizationAction.InvalidateRecompile, SpeculationLog.NO_SPECULATION, false, method)); + guard.setNext(next); + exceptionEdge.setNext(guard); + speculativeExceptionEdge = false; + } + } } private class PELoopExplosionPlugin implements LoopExplosionPlugin { @@ -741,6 +777,7 @@ private static InlineInfo asInlineInfo(final TruffleCompilerRuntime.InlineKind i case DO_NOT_INLINE_NO_EXCEPTION: return InlineInfo.DO_NOT_INLINE_NO_EXCEPTION; case DO_NOT_INLINE_WITH_EXCEPTION: + case DO_NOT_INLINE_WITH_SPECULATIVE_EXCEPTION: return InlineInfo.DO_NOT_INLINE_WITH_EXCEPTION; case INLINE: return InlineInfo.createStandardInlineInfo(method); @@ -749,6 +786,12 @@ private static InlineInfo asInlineInfo(final TruffleCompilerRuntime.InlineKind i } } + private static final SpeculationReasonGroup TRUFFLE_BOUNDARY_EXCEPTION_SPECULATIONS = new SpeculationReasonGroup("TruffleBoundaryWithoutException", ResolvedJavaMethod.class); + + public static SpeculationReason createTruffleBoundaryExceptionSpeculation(ResolvedJavaMethod targetMethod) { + return TRUFFLE_BOUNDARY_EXCEPTION_SPECULATIONS.createSpeculationReason(targetMethod); + } + private static final class SourceLanguagePositionImpl implements SourceLanguagePosition { private final TruffleSourceLanguagePosition delegate; diff --git a/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/nodes/SpeculativeExceptionGuardNode.java b/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/nodes/SpeculativeExceptionGuardNode.java new file mode 100644 index 000000000000..b7df7c7070db --- /dev/null +++ b/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/nodes/SpeculativeExceptionGuardNode.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.truffle.compiler.nodes; + +import static org.graalvm.compiler.nodeinfo.InputType.Guard; +import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_1; +import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_1; + +import org.graalvm.compiler.graph.Node; +import org.graalvm.compiler.graph.NodeClass; +import org.graalvm.compiler.graph.spi.Canonicalizable; +import org.graalvm.compiler.graph.spi.CanonicalizerTool; +import org.graalvm.compiler.graph.spi.SimplifierTool; +import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.AbstractFixedGuardNode; +import org.graalvm.compiler.nodes.DeoptimizeNode; +import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.truffle.compiler.PartialEvaluator; + +import jdk.vm.ci.meta.DeoptimizationAction; +import jdk.vm.ci.meta.DeoptimizationReason; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.SpeculationLog; +import jdk.vm.ci.meta.SpeculationLog.Speculation; +import jdk.vm.ci.meta.SpeculationLog.SpeculationReason; + +@NodeInfo(nameTemplate = "SpeculativeExceptionGuard {p#reason/s}", allowedUsageTypes = Guard, cycles = CYCLES_1, size = SIZE_1) +public final class SpeculativeExceptionGuardNode extends AbstractFixedGuardNode implements Canonicalizable { + + public static final NodeClass TYPE = NodeClass.create(SpeculativeExceptionGuardNode.class); + + protected ResolvedJavaMethod targetMethod; + + public SpeculativeExceptionGuardNode(LogicNode condition, DeoptimizationReason reason, DeoptimizationAction action, SpeculationLog.Speculation speculation, boolean negated, + ResolvedJavaMethod targetMethod) { + super(TYPE, condition, reason, action, speculation, negated); + this.targetMethod = targetMethod; + } + + @Override + public void simplify(SimplifierTool tool) { + } + + @Override + public Node canonical(CanonicalizerTool tool) { + SpeculationLog speculationLog = graph().getSpeculationLog(); + if (speculationLog != null) { + SpeculationReason speculationReason = PartialEvaluator.createTruffleBoundaryExceptionSpeculation(targetMethod); + if (speculationLog.maySpeculate(speculationReason)) { + Speculation exceptionSpeculation = speculationLog.speculate(speculationReason); + return new DeoptimizeNode(getAction(), getReason(), exceptionSpeculation); + } + return null; + } + return this; + } +} diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java index 8bf6671df1a0..5c44de8d115a 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java @@ -1003,8 +1003,8 @@ public InlineKind getInlineKind(ResolvedJavaMethod original, boolean duringParti // by the partial evaluator, we want to prevent inlining across the boundary during // partial evaluation, // even if the TruffleBoundary allows inlining after partial evaluation. - if (truffleBoundary.transferToInterpreterOnException()) { - return InlineKind.DO_NOT_INLINE_DEOPTIMIZE_ON_EXCEPTION; + if (duringPartialEvaluation && truffleBoundary.transferToInterpreterOnException()) { + return InlineKind.DO_NOT_INLINE_WITH_SPECULATIVE_EXCEPTION; } else { return InlineKind.DO_NOT_INLINE_WITH_EXCEPTION; } From 1c3eaba0c6ff73e0c1162ec7990a727314c45e2d Mon Sep 17 00:00:00 2001 From: Andreas Woess Date: Wed, 8 May 2019 15:45:19 +0200 Subject: [PATCH 02/12] Simplify GraalTruffleRuntime.getInlineKind. --- .../compiler/truffle/runtime/GraalTruffleRuntime.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java index 5c44de8d115a..c29960a735f3 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java @@ -998,16 +998,18 @@ public int getFrameSlotKindTagsCount() { public InlineKind getInlineKind(ResolvedJavaMethod original, boolean duringPartialEvaluation) { TruffleBoundary truffleBoundary = getAnnotation(TruffleBoundary.class, original); if (truffleBoundary != null) { - if (duringPartialEvaluation || !truffleBoundary.allowInlining()) { + if (duringPartialEvaluation) { // Since this method is invoked by the bytecode parser plugins, which can be invoked // by the partial evaluator, we want to prevent inlining across the boundary during // partial evaluation, // even if the TruffleBoundary allows inlining after partial evaluation. - if (duringPartialEvaluation && truffleBoundary.transferToInterpreterOnException()) { + if (truffleBoundary.transferToInterpreterOnException()) { return InlineKind.DO_NOT_INLINE_WITH_SPECULATIVE_EXCEPTION; } else { return InlineKind.DO_NOT_INLINE_WITH_EXCEPTION; } + } else if (!truffleBoundary.allowInlining()) { + return InlineKind.DO_NOT_INLINE_WITH_EXCEPTION; } } else if (getAnnotation(TruffleCallBoundary.class, original) != null) { return InlineKind.DO_NOT_INLINE_WITH_EXCEPTION; From 483eb235b80f65dc8cc6d254a55789acaee77c1f Mon Sep 17 00:00:00 2001 From: Andreas Woess Date: Wed, 8 May 2019 15:39:02 +0200 Subject: [PATCH 03/12] Fix TruffleBoundaryExceptionsTest. --- .../test/TruffleBoundaryExceptionsTest.java | 111 +++++++++++------- 1 file changed, 69 insertions(+), 42 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/TruffleBoundaryExceptionsTest.java b/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/TruffleBoundaryExceptionsTest.java index 54a4f40ebd88..d4e1748e2412 100644 --- a/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/TruffleBoundaryExceptionsTest.java +++ b/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/TruffleBoundaryExceptionsTest.java @@ -24,12 +24,14 @@ */ package org.graalvm.compiler.truffle.test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + import org.graalvm.compiler.truffle.runtime.GraalTruffleRuntime; import org.graalvm.compiler.truffle.runtime.GraalTruffleRuntimeListener; import org.graalvm.compiler.truffle.runtime.OptimizedCallTarget; -import org.graalvm.compiler.truffle.runtime.TruffleRuntimeOptions; import org.graalvm.compiler.truffle.runtime.SharedTruffleRuntimeOptions; -import org.junit.Assert; +import org.graalvm.compiler.truffle.runtime.TruffleRuntimeOptions; import org.junit.Test; import com.oracle.truffle.api.CompilerDirectives; @@ -42,7 +44,7 @@ public class TruffleBoundaryExceptionsTest extends TestWithSynchronousCompiling private static final GraalTruffleRuntime runtime = (GraalTruffleRuntime) Truffle.getRuntime(); @Test - public void testExceptionOnTruffleBoundaryDoesNotDeop() { + public void testExceptionOnTruffleBoundaryDeoptsOnce() { final int compilationThreshold = TruffleRuntimeOptions.getValue(SharedTruffleRuntimeOptions.TruffleCompilationThreshold); class DeoptCountingExceptionOverBoundaryRootNode extends RootNode { @@ -50,7 +52,7 @@ protected DeoptCountingExceptionOverBoundaryRootNode() { super(null); } - int deopCounter = 0; + int deoptCounter = 0; int catchCounter = 0; int interpretCount = 0; @@ -66,7 +68,7 @@ public Object execute(VirtualFrame frame) { catchCounter++; } if (startedCompiled && CompilerDirectives.inInterpreter()) { - deopCounter++; + deoptCounter++; } return null; } @@ -83,29 +85,36 @@ public void onCompilationStarted(OptimizedCallTarget target) { compilationCount[0]++; } }; - final OptimizedCallTarget outerTarget = (OptimizedCallTarget) runtime.createCallTarget(new DeoptCountingExceptionOverBoundaryRootNode()); + DeoptCountingExceptionOverBoundaryRootNode rootNode = new DeoptCountingExceptionOverBoundaryRootNode(); + final OptimizedCallTarget outerTarget = (OptimizedCallTarget) runtime.createCallTarget(rootNode); for (int i = 0; i < compilationThreshold; i++) { outerTarget.call(); } + // deoptimizes immediately due to the exception + assertEquals("Incorrect number of deopts detected!", 1, rootNode.deoptCounter); + assertNotCompiled(outerTarget); + // recompile with exception branch + outerTarget.call(); assertCompiled(outerTarget); runtime.addListener(listener); - final int execCount = 10; - for (int i = 0; i < execCount; i++) { - outerTarget.call(); - } + try { + final int execCount = 10; + for (int i = 0; i < execCount; i++) { + outerTarget.call(); + } - final int totalExecutions = compilationThreshold + execCount; - int catchCount = ((DeoptCountingExceptionOverBoundaryRootNode) outerTarget.getRootNode()).catchCounter; - Assert.assertEquals("Incorrect number of catch block executions", totalExecutions, catchCount); + final int totalExecutions = compilationThreshold + 1 + execCount; + assertEquals("Incorrect number of catch block executions", totalExecutions, rootNode.catchCounter); - int interpretCount = ((DeoptCountingExceptionOverBoundaryRootNode) outerTarget.getRootNode()).interpretCount; - int deopCount = ((DeoptCountingExceptionOverBoundaryRootNode) outerTarget.getRootNode()).deopCounter; - Assert.assertEquals("Incorrect number of deops detected!", totalExecutions - interpretCount, deopCount); + assertEquals("Incorrect number of interpreted executions", compilationThreshold - 1, rootNode.interpretCount); + assertEquals("Incorrect number of deopts detected!", 1, rootNode.deoptCounter); - Assert.assertEquals("Compilation happened!", 0, compilationCount[0]); - runtime.removeListener(listener); + assertEquals("Compilation happened!", 0, compilationCount[0]); + } finally { + runtime.removeListener(listener); + } } @Test @@ -117,7 +126,7 @@ protected DeoptCountingExceptionOverBoundaryRootNode() { super(null); } - int deopCounter = 0; + int deoptCounter = 0; int catchCounter = 0; @Override @@ -129,7 +138,7 @@ public Object execute(VirtualFrame frame) { catchCounter++; } if (startedCompiled && CompilerDirectives.inInterpreter()) { - deopCounter++; + deoptCounter++; } return null; } @@ -140,7 +149,8 @@ public void throwExceptionBoundary() { } } - final OptimizedCallTarget outerTarget = (OptimizedCallTarget) runtime.createCallTarget(new DeoptCountingExceptionOverBoundaryRootNode()); + DeoptCountingExceptionOverBoundaryRootNode rootNode = new DeoptCountingExceptionOverBoundaryRootNode(); + final OptimizedCallTarget outerTarget = (OptimizedCallTarget) runtime.createCallTarget(rootNode); for (int i = 0; i < compilationThreshold; i++) { outerTarget.call(); @@ -153,12 +163,9 @@ public void throwExceptionBoundary() { } final int totalExecutions = compilationThreshold + execCount; - int catchCount = ((DeoptCountingExceptionOverBoundaryRootNode) outerTarget.getRootNode()).catchCounter; - Assert.assertEquals("Incorrect number of catch block executions", totalExecutions, catchCount); - - int deopCount = ((DeoptCountingExceptionOverBoundaryRootNode) outerTarget.getRootNode()).deopCounter; - Assert.assertEquals("Incorrect number of deops detected!", 0, deopCount); + assertEquals("Incorrect number of catch block executions", totalExecutions, rootNode.catchCounter); + assertEquals("Incorrect number of deopts detected!", 0, rootNode.deoptCounter); } @Test @@ -170,14 +177,18 @@ protected DeoptCountingExceptionOverBoundaryRootNode() { super(null); } - int deopCounter = 0; + int deoptCounter = 0; @Override public Object execute(VirtualFrame frame) { boolean startedCompiled = CompilerDirectives.inCompiledCode(); - throwExceptionBoundary(); - if (startedCompiled && CompilerDirectives.inInterpreter()) { - deopCounter++; + try { + throwExceptionBoundary(); + } catch (Exception e) { + if (startedCompiled && CompilerDirectives.inInterpreter()) { + deoptCounter++; + } + throw e; } return null; } @@ -188,29 +199,40 @@ public void throwExceptionBoundary() { } } - final OptimizedCallTarget outerTarget = (OptimizedCallTarget) runtime.createCallTarget(new DeoptCountingExceptionOverBoundaryRootNode()); + DeoptCountingExceptionOverBoundaryRootNode rootNode = new DeoptCountingExceptionOverBoundaryRootNode(); + final OptimizedCallTarget outerTarget = (OptimizedCallTarget) runtime.createCallTarget(rootNode); for (int i = 0; i < compilationThreshold; i++) { try { outerTarget.call(); + fail(); } catch (RuntimeException e) { // do nothing } } + // deoptimizes immediately due to the exception + assertNotCompiled(outerTarget); + assertEquals("Incorrect number of deopts detected!", 1, rootNode.deoptCounter); + // recompile with exception branch + try { + outerTarget.call(); + fail(); + } catch (RuntimeException e) { + // do nothing + } assertCompiled(outerTarget); final int execCount = 10; for (int i = 0; i < execCount; i++) { try { outerTarget.call(); + fail(); } catch (RuntimeException e) { // do nothing } } - int deopCount = ((DeoptCountingExceptionOverBoundaryRootNode) outerTarget.getRootNode()).deopCounter; - Assert.assertEquals("Incorrect number of deops detected!", 0, deopCount); - + assertEquals("Incorrect number of deopts detected!", 1, rootNode.deoptCounter); } @Test @@ -222,14 +244,18 @@ protected DeoptCountingExceptionOverBoundaryRootNode() { super(null); } - int deopCounter = 0; + int deoptCounter = 0; @Override public Object execute(VirtualFrame frame) { boolean startedCompiled = CompilerDirectives.inCompiledCode(); - throwExceptionBoundary(); - if (startedCompiled && CompilerDirectives.inInterpreter()) { - deopCounter++; + try { + throwExceptionBoundary(); + } catch (Exception e) { + if (startedCompiled && CompilerDirectives.inInterpreter()) { + deoptCounter++; + } + throw e; } return null; } @@ -240,11 +266,13 @@ public void throwExceptionBoundary() { } } - final OptimizedCallTarget outerTarget = (OptimizedCallTarget) runtime.createCallTarget(new DeoptCountingExceptionOverBoundaryRootNode()); + DeoptCountingExceptionOverBoundaryRootNode rootNode = new DeoptCountingExceptionOverBoundaryRootNode(); + final OptimizedCallTarget outerTarget = (OptimizedCallTarget) runtime.createCallTarget(rootNode); for (int i = 0; i < compilationThreshold; i++) { try { outerTarget.call(); + fail(); } catch (RuntimeException e) { // do nothing } @@ -255,13 +283,12 @@ public void throwExceptionBoundary() { for (int i = 0; i < execCount; i++) { try { outerTarget.call(); + fail(); } catch (RuntimeException e) { // do nothing } } - int deopCount = ((DeoptCountingExceptionOverBoundaryRootNode) outerTarget.getRootNode()).deopCounter; - Assert.assertEquals("Incorrect number of deops detected!", 0, deopCount); - + assertEquals("Incorrect number of deopts detected!", 0, rootNode.deoptCounter); } } From d983b48afa708350c6994da1d1e850a3b9bb2ad5 Mon Sep 17 00:00:00 2001 From: Andreas Woess Date: Thu, 9 May 2019 16:44:59 +0200 Subject: [PATCH 04/12] Update javadoc for TruffleBoundary.transferToInterpreterOnException. --- .../src/com/oracle/truffle/api/CompilerDirectives.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/CompilerDirectives.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/CompilerDirectives.java index 11b0d7fd97de..9fe6926950f5 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/CompilerDirectives.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/CompilerDirectives.java @@ -265,8 +265,9 @@ public static void bailout(String reason) { public @interface TruffleBoundary { /** - * Determines whether execution should be transferred to the interpreter in the case that an - * exception is thrown across this boundary. + * Determines whether execution should be transferred to the interpreter if an exception is + * thrown across this boundary, in which case the caller's compiled code is invalidated and + * will not transfer to the interpreter on exceptions for this method again. * * @since 0.28 */ From 70064ecf5621eca6da94622caf766419ca476bf1 Mon Sep 17 00:00:00 2001 From: Andreas Woess Date: Fri, 10 May 2019 00:49:59 +0200 Subject: [PATCH 05/12] Add javadoc to SpeculativeExceptionGuardNode. --- .../compiler/nodes/SpeculativeExceptionGuardNode.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/nodes/SpeculativeExceptionGuardNode.java b/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/nodes/SpeculativeExceptionGuardNode.java index b7df7c7070db..a7475b0a349f 100644 --- a/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/nodes/SpeculativeExceptionGuardNode.java +++ b/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/nodes/SpeculativeExceptionGuardNode.java @@ -46,6 +46,13 @@ import jdk.vm.ci.meta.SpeculationLog.Speculation; import jdk.vm.ci.meta.SpeculationLog.SpeculationReason; +/** + * A speculation-less guard node that is inserted into the exception branch of TruffleBoundary calls + * during parsing (graph encoding). During partial evaluation (graph decoding) when a speculation + * log is available, it will speculate that TruffleBoundary method will not throw and either becomes + * a control-flow sink {@link DeoptimizeNode} with the {@link Speculation} in order to off the + * branch, or if the speculation has already failed for this compilation root, disappears. + */ @NodeInfo(nameTemplate = "SpeculativeExceptionGuard {p#reason/s}", allowedUsageTypes = Guard, cycles = CYCLES_1, size = SIZE_1) public final class SpeculativeExceptionGuardNode extends AbstractFixedGuardNode implements Canonicalizable { From 7446742b4ebcfb2759513abc5ff05671154d802b Mon Sep 17 00:00:00 2001 From: Andreas Woess Date: Tue, 14 May 2019 19:10:22 +0200 Subject: [PATCH 06/12] SVM support for TruffleBoundary exception speculation. --- .../phases/SubstrateGraphBuilderPhase.java | 22 +++++++++++----- .../svm/truffle/api/TruffleBoundaryPhase.java | 25 ++++++++++++++----- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SubstrateGraphBuilderPhase.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SubstrateGraphBuilderPhase.java index e4491e3867ac..713cdaca0e96 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SubstrateGraphBuilderPhase.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SubstrateGraphBuilderPhase.java @@ -34,10 +34,10 @@ import org.graalvm.compiler.java.GraphBuilderPhase; import org.graalvm.compiler.nodes.AbstractBeginNode; import org.graalvm.compiler.nodes.CallTargetNode; -import org.graalvm.compiler.nodes.DeoptimizeNode; import org.graalvm.compiler.nodes.FixedWithNextNode; import org.graalvm.compiler.nodes.InvokeWithExceptionNode; import org.graalvm.compiler.nodes.KillingBeginNode; +import org.graalvm.compiler.nodes.LogicConstantNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.graphbuilderconf.GeneratedInvocationPlugin; @@ -48,6 +48,7 @@ import org.graalvm.compiler.phases.OptimisticOptimizations; import org.graalvm.compiler.phases.util.Providers; import org.graalvm.compiler.replacements.SnippetTemplate; +import org.graalvm.compiler.truffle.compiler.nodes.SpeculativeExceptionGuardNode; import org.graalvm.compiler.word.WordTypes; import org.graalvm.word.LocationIdentity; @@ -61,6 +62,7 @@ import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; +import jdk.vm.ci.meta.SpeculationLog; public class SubstrateGraphBuilderPhase extends SharedGraphBuilderPhase { @@ -105,22 +107,28 @@ protected NewArrayNode createNewArray(ResolvedJavaType elementType, ValueNode le } /** - * We do not have access to the inovked method i {@link #createHandleExceptionTarget}. + * We do not have access to the invoked method in {@link #createHandleExceptionTarget}. * Therefore, we need to make the decision whether to deoptimize in * {@link #createInvokeWithException} and propagate the result via this field. */ private boolean curDeoptimizeOnException; + private ResolvedJavaMethod curInvokedMethod; @Override protected void createHandleExceptionTarget(FixedWithNextNode afterExceptionLoaded, int bci, FrameStateBuilder dispatchState) { + FixedWithNextNode instrumentedExceptionLoaded; if (curDeoptimizeOnException) { - DeoptimizeNode deoptimize = graph.add(new DeoptimizeNode(DeoptimizationAction.None, DeoptimizationReason.NotCompiledExceptionHandler)); VMError.guarantee(afterExceptionLoaded.next() == null); - afterExceptionLoaded.setNext(deoptimize); - + VMError.guarantee(curInvokedMethod != null); + // Note: Speculation is inserted during PE. + FixedWithNextNode guard = graph.add(new SpeculativeExceptionGuardNode(LogicConstantNode.tautology(graph), + DeoptimizationReason.TransferToInterpreter, DeoptimizationAction.InvalidateRecompile, SpeculationLog.NO_SPECULATION, false, curInvokedMethod)); + afterExceptionLoaded.setNext(guard); + instrumentedExceptionLoaded = guard; } else { - super.createHandleExceptionTarget(afterExceptionLoaded, bci, dispatchState); + instrumentedExceptionLoaded = afterExceptionLoaded; } + super.createHandleExceptionTarget(instrumentedExceptionLoaded, bci, dispatchState); } @Override @@ -128,8 +136,10 @@ protected InvokeWithExceptionNode createInvokeWithException(int invokeBci, CallT try { assert curDeoptimizeOnException == false; curDeoptimizeOnException = getGraphBuilderInstance().deoptimizeOnExceptionPredicate.test(callTarget.targetMethod()); + curInvokedMethod = callTarget.targetMethod(); return super.createInvokeWithException(invokeBci, callTarget, resultType, exceptionEdgeAction); } finally { + curInvokedMethod = null; curDeoptimizeOnException = false; } } diff --git a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/api/TruffleBoundaryPhase.java b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/api/TruffleBoundaryPhase.java index 7062f5a3525a..7c9cb2ea6673 100644 --- a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/api/TruffleBoundaryPhase.java +++ b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/api/TruffleBoundaryPhase.java @@ -32,12 +32,17 @@ import org.graalvm.compiler.nodes.java.ExceptionObjectNode; import org.graalvm.compiler.nodes.util.GraphUtil; import org.graalvm.compiler.phases.Phase; +import org.graalvm.compiler.truffle.compiler.PartialEvaluator; import com.oracle.svm.hosted.phases.SubstrateGraphBuilderPhase.SubstrateBytecodeParser; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import jdk.vm.ci.meta.DeoptimizationAction; import jdk.vm.ci.meta.DeoptimizationReason; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.SpeculationLog; +import jdk.vm.ci.meta.SpeculationLog.Speculation; +import jdk.vm.ci.meta.SpeculationLog.SpeculationReason; /** * Deoptimize for {@link TruffleBoundary} calls when {@link TruffleBoundary#transferToInterpreter()} @@ -58,10 +63,11 @@ protected void run(StructuredGraph graph) { FixedNode originalNext = exceptionObject.next(); if (!(originalNext instanceof DeoptimizeNode) && invoke.callTarget().targetMethod() != null) { - TruffleBoundary truffleBoundary = invoke.callTarget().targetMethod().getAnnotation(TruffleBoundary.class); + ResolvedJavaMethod targetMethod = invoke.callTarget().targetMethod(); + TruffleBoundary truffleBoundary = targetMethod.getAnnotation(TruffleBoundary.class); if (truffleBoundary != null) { if (truffleBoundary.transferToInterpreterOnException()) { - addDeoptimizeNode(graph, originalNext); + addDeoptimizeNode(graph, originalNext, targetMethod); } } } @@ -69,9 +75,16 @@ protected void run(StructuredGraph graph) { } } - private static void addDeoptimizeNode(StructuredGraph graph, FixedNode originalNext) { - DeoptimizeNode deoptimize = graph.add(new DeoptimizeNode(DeoptimizationAction.None, DeoptimizationReason.NotCompiledExceptionHandler)); - originalNext.replaceAtPredecessor(deoptimize); - GraphUtil.killCFG(originalNext); + private static void addDeoptimizeNode(StructuredGraph graph, FixedNode originalNext, ResolvedJavaMethod targetMethod) { + SpeculationLog speculationLog = graph.getSpeculationLog(); + if (speculationLog != null) { + SpeculationReason speculationReason = PartialEvaluator.createTruffleBoundaryExceptionSpeculation(targetMethod); + if (speculationLog.maySpeculate(speculationReason)) { + Speculation exceptionSpeculation = speculationLog.speculate(speculationReason); + DeoptimizeNode deoptimize = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.TransferToInterpreter, exceptionSpeculation)); + originalNext.replaceAtPredecessor(deoptimize); + GraphUtil.killCFG(originalNext); + } + } } } From e3099a6a90b79b01473b37f9b63f5239b8f906da Mon Sep 17 00:00:00 2001 From: Andreas Woess Date: Thu, 16 May 2019 18:52:45 +0200 Subject: [PATCH 07/12] Move TruffleBoundary exception instrumention to a separate phase. --- .../replacements/test/PEGraphDecoderTest.java | 2 +- .../replacements/CachingPEGraphDecoder.java | 10 ++- .../truffle/compiler/PartialEvaluator.java | 46 ++--------- .../phases/DeoptimizeOnExceptionPhase.java | 77 +++++++++++++++++++ .../oracle/svm/graal/hosted/GraalFeature.java | 11 ++- .../svm/hosted/code/SubstrateGraphMaker.java | 2 +- .../phases/HostedGraphBuilderPhase.java | 2 +- .../phases/SubstrateGraphBuilderPhase.java | 54 +------------ 8 files changed, 105 insertions(+), 99 deletions(-) create mode 100644 compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/phases/DeoptimizeOnExceptionPhase.java diff --git a/compiler/src/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/PEGraphDecoderTest.java b/compiler/src/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/PEGraphDecoderTest.java index e17b2bc42b6b..8ab6ae8b15e6 100644 --- a/compiler/src/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/PEGraphDecoderTest.java +++ b/compiler/src/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/PEGraphDecoderTest.java @@ -138,7 +138,7 @@ public void test() { registerPlugins(graphBuilderConfig.getPlugins().getInvocationPlugins()); targetGraph = new StructuredGraph.Builder(getInitialOptions(), debug, AllowAssumptions.YES).method(testMethod).build(); CachingPEGraphDecoder decoder = new CachingPEGraphDecoder(getTarget().arch, targetGraph, getProviders(), graphBuilderConfig, OptimisticOptimizations.NONE, AllowAssumptions.YES, - null, null, new InlineInvokePlugin[]{new InlineAll()}, null, null, null, null); + null, null, new InlineInvokePlugin[]{new InlineAll()}, null, null, null, null, null); decoder.decode(testMethod, false, false); debug.dump(DebugContext.BASIC_LEVEL, targetGraph, "Target Graph"); diff --git a/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/CachingPEGraphDecoder.java b/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/CachingPEGraphDecoder.java index edefeab8fee2..70ded5d154c6 100644 --- a/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/CachingPEGraphDecoder.java +++ b/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/CachingPEGraphDecoder.java @@ -46,6 +46,8 @@ import org.graalvm.compiler.nodes.graphbuilderconf.MethodSubstitutionPlugin; import org.graalvm.compiler.nodes.graphbuilderconf.NodePlugin; import org.graalvm.compiler.nodes.graphbuilderconf.ParameterPlugin; +import org.graalvm.compiler.nodes.spi.CoreProviders; +import org.graalvm.compiler.phases.BasePhase; import org.graalvm.compiler.phases.OptimisticOptimizations; import org.graalvm.compiler.phases.common.CanonicalizerPhase; import org.graalvm.compiler.phases.util.Providers; @@ -64,11 +66,13 @@ public class CachingPEGraphDecoder extends PEGraphDecoder { protected final OptimisticOptimizations optimisticOpts; private final AllowAssumptions allowAssumptions; private final EconomicMap graphCache; + private final BasePhase postParsingPhase; public CachingPEGraphDecoder(Architecture architecture, StructuredGraph graph, Providers providers, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, AllowAssumptions allowAssumptions, LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, InlineInvokePlugin[] inlineInvokePlugins, ParameterPlugin parameterPlugin, - NodePlugin[] nodePlugins, ResolvedJavaMethod callInlinedMethod, SourceLanguagePositionProvider sourceLanguagePositionProvider) { + NodePlugin[] nodePlugins, ResolvedJavaMethod callInlinedMethod, SourceLanguagePositionProvider sourceLanguagePositionProvider, + BasePhase postParsingPhase) { super(architecture, graph, providers, loopExplosionPlugin, invocationPlugins, inlineInvokePlugins, parameterPlugin, nodePlugins, callInlinedMethod, sourceLanguagePositionProvider); @@ -76,6 +80,7 @@ public CachingPEGraphDecoder(Architecture architecture, StructuredGraph graph, P this.graphBuilderConfig = graphBuilderConfig; this.optimisticOpts = optimisticOpts; this.allowAssumptions = allowAssumptions; + this.postParsingPhase = postParsingPhase; this.graphCache = EconomicMap.create(); } @@ -128,6 +133,9 @@ private StructuredGraph buildGraph(ResolvedJavaMethod method, MethodSubstitution GraphBuilderPhase.Instance graphBuilderPhaseInstance = createGraphBuilderPhaseInstance(initialIntrinsicContext); graphBuilderPhaseInstance.apply(graphToEncode); new CanonicalizerPhase().apply(graphToEncode, providers); + if (postParsingPhase != null) { + postParsingPhase.apply(graphToEncode, providers); + } } catch (Throwable ex) { throw debug.handle(ex); } diff --git a/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/PartialEvaluator.java b/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/PartialEvaluator.java index 4006b1934006..a0796d22edbf 100644 --- a/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/PartialEvaluator.java +++ b/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/PartialEvaluator.java @@ -59,14 +59,8 @@ import org.graalvm.compiler.graph.SourceLanguagePositionProvider; import org.graalvm.compiler.java.ComputeLoopFrequenciesClosure; import org.graalvm.compiler.loop.phases.ConvertDeoptimizeToGuardPhase; -import org.graalvm.compiler.nodes.AbstractBeginNode; import org.graalvm.compiler.nodes.Cancellable; import org.graalvm.compiler.nodes.ConstantNode; -import org.graalvm.compiler.nodes.FixedNode; -import org.graalvm.compiler.nodes.FixedWithNextNode; -import org.graalvm.compiler.nodes.Invoke; -import org.graalvm.compiler.nodes.InvokeWithExceptionNode; -import org.graalvm.compiler.nodes.LogicConstantNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; import org.graalvm.compiler.nodes.ValueNode; @@ -108,12 +102,12 @@ import org.graalvm.compiler.truffle.common.TruffleSourceLanguagePosition; import org.graalvm.compiler.truffle.compiler.debug.HistogramInlineInvokePlugin; import org.graalvm.compiler.truffle.compiler.nodes.TruffleAssumption; -import org.graalvm.compiler.truffle.compiler.nodes.SpeculativeExceptionGuardNode; import org.graalvm.compiler.truffle.compiler.nodes.asserts.NeverPartOfCompilationNode; import org.graalvm.compiler.truffle.compiler.nodes.frame.AllowMaterializeNode; import org.graalvm.compiler.truffle.compiler.phases.InstrumentBranchesPhase; import org.graalvm.compiler.truffle.compiler.phases.InstrumentPhase; import org.graalvm.compiler.truffle.compiler.phases.InstrumentTruffleBoundariesPhase; +import org.graalvm.compiler.truffle.compiler.phases.DeoptimizeOnExceptionPhase; import org.graalvm.compiler.truffle.compiler.phases.VerifyFrameDoesNotEscapePhase; import org.graalvm.compiler.truffle.compiler.substitutions.KnownTruffleTypes; import org.graalvm.compiler.truffle.compiler.substitutions.TruffleGraphBuilderPlugins; @@ -122,8 +116,6 @@ import jdk.vm.ci.code.Architecture; import jdk.vm.ci.meta.ConstantReflectionProvider; -import jdk.vm.ci.meta.DeoptimizationAction; -import jdk.vm.ci.meta.DeoptimizationReason; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.MetaAccessProvider; @@ -313,8 +305,7 @@ private class PEInlineInvokePlugin implements InlineInvokePlugin { @Override public InlineInfo shouldInlineInvoke(GraphBuilderContext builder, ResolvedJavaMethod original, ValueNode[] arguments) { TruffleCompilerRuntime rt = TruffleCompilerRuntime.getRuntime(); - InlineKind inlineKind = rt.getInlineKind(original, true); - InlineInfo inlineInfo = asInlineInfo(inlineKind, original); + InlineInfo inlineInfo = asInlineInfo(rt.getInlineKind(original, true), original); if (!inlineInfo.allowsInlining()) { return inlineInfo; } @@ -367,7 +358,6 @@ private class ParsingInlineInvokePlugin implements InlineInvokePlugin { private final ReplacementsImpl replacements; private final InvocationPlugins invocationPlugins; private final LoopExplosionPlugin loopExplosionPlugin; - private boolean speculativeExceptionEdge; ParsingInlineInvokePlugin(ReplacementsImpl replacements, InvocationPlugins invocationPlugins, LoopExplosionPlugin loopExplosionPlugin) { this.replacements = replacements; @@ -402,13 +392,8 @@ public InlineInfo shouldInlineInvoke(GraphBuilderContext builder, ResolvedJavaMe } TruffleCompilerRuntime rt = TruffleCompilerRuntime.getRuntime(); - InlineKind inlineKind = rt.getInlineKind(original, true); - InlineInfo inlineInfo = asInlineInfo(inlineKind, original); + InlineInfo inlineInfo = asInlineInfo(rt.getInlineKind(original, true), original); if (!inlineInfo.allowsInlining()) { - if (inlineKind == InlineKind.DO_NOT_INLINE_WITH_SPECULATIVE_EXCEPTION) { - assert !speculativeExceptionEdge; - speculativeExceptionEdge = true; - } return inlineInfo; } if (original.equals(callIndirectMethod) || original.equals(callDirectMethod)) { @@ -423,23 +408,6 @@ public InlineInfo shouldInlineInvoke(GraphBuilderContext builder, ResolvedJavaMe } return null; } - - @Override - public void notifyNotInlined(GraphBuilderContext b, ResolvedJavaMethod method, Invoke invoke) { - if (speculativeExceptionEdge) { - InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke; - AbstractBeginNode exceptionEdge = invokeWithException.exceptionEdge(); - FixedNode next = exceptionEdge.next(); - assert next != null; - exceptionEdge.setNext(null); - // Note: Speculation is inserted during PE. - FixedWithNextNode guard = b.getGraph().add(new SpeculativeExceptionGuardNode(LogicConstantNode.tautology(b.getGraph()), - DeoptimizationReason.TransferToInterpreter, DeoptimizationAction.InvalidateRecompile, SpeculationLog.NO_SPECULATION, false, method)); - guard.setNext(next); - exceptionEdge.setNext(guard); - speculativeExceptionEdge = false; - } - } } private class PELoopExplosionPlugin implements LoopExplosionPlugin { @@ -481,10 +449,12 @@ protected PEGraphDecoder createGraphDecoder(StructuredGraph graph, final HighTie plugins.appendInlineInvokePlugin(new InlineDuringParsingPlugin()); } + DeoptimizeOnExceptionPhase postParsingPhase = new DeoptimizeOnExceptionPhase( + method -> TruffleCompilerRuntime.getRuntime().getInlineKind(method, true) == InlineKind.DO_NOT_INLINE_WITH_SPECULATIVE_EXCEPTION); + Providers compilationUnitProviders = providers.copyWith(new TruffleConstantFieldProvider(providers.getConstantFieldProvider(), providers.getMetaAccess())); - return new CachingPEGraphDecoder(architecture, graph, compilationUnitProviders, newConfig, TruffleCompilerImpl.Optimizations, - AllowAssumptions.ifNonNull(graph.getAssumptions()), - loopExplosionPlugin, decodingInvocationPlugins, inlineInvokePlugins, parameterPlugin, nodePluginList, callInlined, sourceLanguagePositionProvider); + return new CachingPEGraphDecoder(architecture, graph, compilationUnitProviders, newConfig, TruffleCompilerImpl.Optimizations, AllowAssumptions.ifNonNull(graph.getAssumptions()), + loopExplosionPlugin, decodingInvocationPlugins, inlineInvokePlugins, parameterPlugin, nodePluginList, callInlined, sourceLanguagePositionProvider, postParsingPhase); } protected void doGraphPE(CompilableTruffleAST compilable, StructuredGraph graph, HighTierContext tierContext, TruffleInliningPlan inliningDecision) { diff --git a/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/phases/DeoptimizeOnExceptionPhase.java b/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/phases/DeoptimizeOnExceptionPhase.java new file mode 100644 index 000000000000..14ac394e9f5e --- /dev/null +++ b/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/phases/DeoptimizeOnExceptionPhase.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.truffle.compiler.phases; + +import java.util.function.Predicate; + +import org.graalvm.compiler.nodes.AbstractBeginNode; +import org.graalvm.compiler.nodes.FixedNode; +import org.graalvm.compiler.nodes.FixedWithNextNode; +import org.graalvm.compiler.nodes.Invoke; +import org.graalvm.compiler.nodes.InvokeWithExceptionNode; +import org.graalvm.compiler.nodes.LogicConstantNode; +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.phases.Phase; +import org.graalvm.compiler.truffle.compiler.nodes.SpeculativeExceptionGuardNode; + +import jdk.vm.ci.meta.DeoptimizationAction; +import jdk.vm.ci.meta.DeoptimizationReason; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.SpeculationLog; + +/** + * Instruments the exception edge of TruffleBoundary method calls with a speculative transfer to + * interpreter. + */ +public class DeoptimizeOnExceptionPhase extends Phase { + private final Predicate deoptimizeOnExceptionPredicate; + + public DeoptimizeOnExceptionPhase(Predicate deoptimizeOnExceptionPredicate) { + this.deoptimizeOnExceptionPredicate = deoptimizeOnExceptionPredicate; + } + + @Override + protected void run(StructuredGraph graph) { + for (Invoke invoke : graph.getInvokes()) { + if (invoke instanceof InvokeWithExceptionNode) { + InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke; + ResolvedJavaMethod targetMethod = invokeWithException.callTarget().targetMethod(); + if (deoptimizeOnExceptionPredicate.test(targetMethod)) { + // Method has @TruffleBoundary(transferToInterpreterOnException=true) + AbstractBeginNode exceptionEdge = invokeWithException.exceptionEdge(); + FixedNode next = exceptionEdge.next(); + assert next != null; + exceptionEdge.setNext(null); + // Note: Speculation is inserted during PE. + FixedWithNextNode guard = graph.add(new SpeculativeExceptionGuardNode(LogicConstantNode.tautology(graph), + DeoptimizationReason.TransferToInterpreter, DeoptimizationAction.InvalidateRecompile, SpeculationLog.NO_SPECULATION, false, + targetMethod)); + guard.setNext(next); + exceptionEdge.setNext(guard); + } + } + } + } +} diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java index d5db92bf6a05..027cc2a84564 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java @@ -71,6 +71,7 @@ import org.graalvm.compiler.phases.common.inlining.InliningUtil; import org.graalvm.compiler.phases.tiers.Suites; import org.graalvm.compiler.phases.util.Providers; +import org.graalvm.compiler.truffle.compiler.phases.DeoptimizeOnExceptionPhase; import org.graalvm.compiler.word.WordTypes; import org.graalvm.nativeimage.hosted.Feature; import org.graalvm.nativeimage.ImageSingletons; @@ -238,8 +239,8 @@ public static class RuntimeGraphBuilderPhase extends SubstrateGraphBuilderPhase RuntimeGraphBuilderPhase(Providers providers, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext, WordTypes wordTypes, - Predicate deoptimizeOnExceptionPredicate, CallTreeNode node) { - super(providers, graphBuilderConfig, optimisticOpts, initialIntrinsicContext, wordTypes, deoptimizeOnExceptionPredicate); + CallTreeNode node) { + super(providers, graphBuilderConfig, optimisticOpts, initialIntrinsicContext, wordTypes); this.node = node; } @@ -501,8 +502,7 @@ private void processMethod(CallTreeNode node, Deque worklist, BigB try (DebugContext.Scope scope = debug.scope("RuntimeCompile", graph)) { if (parse) { - RuntimeGraphBuilderPhase builderPhase = new RuntimeGraphBuilderPhase(hostedProviders, graphBuilderConfig, optimisticOpts, null, hostedProviders.getWordTypes(), - deoptimizeOnExceptionPredicate, node); + RuntimeGraphBuilderPhase builderPhase = new RuntimeGraphBuilderPhase(hostedProviders, graphBuilderConfig, optimisticOpts, null, hostedProviders.getWordTypes(), node); builderPhase.apply(graph); } @@ -520,6 +520,9 @@ private void processMethod(CallTreeNode node, Deque worklist, BigB } new CanonicalizerPhase().apply(graph, hostedProviders); + if (deoptimizeOnExceptionPredicate != null) { + new DeoptimizeOnExceptionPhase(deoptimizeOnExceptionPredicate).apply(graph); + } new ConvertDeoptimizeToGuardPhase().apply(graph, hostedProviders); graphEncoder.prepare(graph); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/SubstrateGraphMaker.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/SubstrateGraphMaker.java index f63e2313c01d..a938db5e47bd 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/SubstrateGraphMaker.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/SubstrateGraphMaker.java @@ -53,7 +53,7 @@ protected SubstrateGraphMaker(ReplacementsImpl replacements, ResolvedJavaMethod @Override protected Instance createGraphBuilder(Providers providers, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext) { - return new SubstrateGraphBuilderPhase(providers, graphBuilderConfig, optimisticOpts, initialIntrinsicContext, wordTypes, null); + return new SubstrateGraphBuilderPhase(providers, graphBuilderConfig, optimisticOpts, initialIntrinsicContext, wordTypes); } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/HostedGraphBuilderPhase.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/HostedGraphBuilderPhase.java index dcc349b8d97d..e17eabf8720d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/HostedGraphBuilderPhase.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/HostedGraphBuilderPhase.java @@ -67,7 +67,7 @@ public class HostedGraphBuilderPhase extends SubstrateGraphBuilderPhase { public HostedGraphBuilderPhase(Providers providers, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext, WordTypes wordTypes) { - super(providers, graphBuilderConfig, optimisticOpts, initialIntrinsicContext, wordTypes, null); + super(providers, graphBuilderConfig, optimisticOpts, initialIntrinsicContext, wordTypes); } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SubstrateGraphBuilderPhase.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SubstrateGraphBuilderPhase.java index 713cdaca0e96..c1ed0a2f0974 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SubstrateGraphBuilderPhase.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SubstrateGraphBuilderPhase.java @@ -24,20 +24,15 @@ */ package com.oracle.svm.hosted.phases; -import java.util.function.Predicate; - import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.Node.NodeIntrinsic; import org.graalvm.compiler.java.BytecodeParser; -import org.graalvm.compiler.java.FrameStateBuilder; import org.graalvm.compiler.java.GraphBuilderPhase; import org.graalvm.compiler.nodes.AbstractBeginNode; import org.graalvm.compiler.nodes.CallTargetNode; -import org.graalvm.compiler.nodes.FixedWithNextNode; import org.graalvm.compiler.nodes.InvokeWithExceptionNode; import org.graalvm.compiler.nodes.KillingBeginNode; -import org.graalvm.compiler.nodes.LogicConstantNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.graphbuilderconf.GeneratedInvocationPlugin; @@ -48,31 +43,22 @@ import org.graalvm.compiler.phases.OptimisticOptimizations; import org.graalvm.compiler.phases.util.Providers; import org.graalvm.compiler.replacements.SnippetTemplate; -import org.graalvm.compiler.truffle.compiler.nodes.SpeculativeExceptionGuardNode; import org.graalvm.compiler.word.WordTypes; import org.graalvm.word.LocationIdentity; import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.graal.nodes.SubstrateNewArrayNode; import com.oracle.svm.core.graal.nodes.SubstrateNewInstanceNode; -import com.oracle.svm.core.util.VMError; -import jdk.vm.ci.meta.DeoptimizationAction; -import jdk.vm.ci.meta.DeoptimizationReason; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; -import jdk.vm.ci.meta.SpeculationLog; public class SubstrateGraphBuilderPhase extends SharedGraphBuilderPhase { - private final Predicate deoptimizeOnExceptionPredicate; - public SubstrateGraphBuilderPhase(Providers providers, - GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext, WordTypes wordTypes, - Predicate deoptimizeOnExceptionPredicate) { + GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext, WordTypes wordTypes) { super(providers, graphBuilderConfig, optimisticOpts, initialIntrinsicContext, wordTypes); - this.deoptimizeOnExceptionPredicate = deoptimizeOnExceptionPredicate != null ? deoptimizeOnExceptionPredicate : (method -> false); } @Override @@ -106,44 +92,6 @@ protected NewArrayNode createNewArray(ResolvedJavaType elementType, ValueNode le return new SubstrateNewArrayNode(elementType, length, fillContents, null); } - /** - * We do not have access to the invoked method in {@link #createHandleExceptionTarget}. - * Therefore, we need to make the decision whether to deoptimize in - * {@link #createInvokeWithException} and propagate the result via this field. - */ - private boolean curDeoptimizeOnException; - private ResolvedJavaMethod curInvokedMethod; - - @Override - protected void createHandleExceptionTarget(FixedWithNextNode afterExceptionLoaded, int bci, FrameStateBuilder dispatchState) { - FixedWithNextNode instrumentedExceptionLoaded; - if (curDeoptimizeOnException) { - VMError.guarantee(afterExceptionLoaded.next() == null); - VMError.guarantee(curInvokedMethod != null); - // Note: Speculation is inserted during PE. - FixedWithNextNode guard = graph.add(new SpeculativeExceptionGuardNode(LogicConstantNode.tautology(graph), - DeoptimizationReason.TransferToInterpreter, DeoptimizationAction.InvalidateRecompile, SpeculationLog.NO_SPECULATION, false, curInvokedMethod)); - afterExceptionLoaded.setNext(guard); - instrumentedExceptionLoaded = guard; - } else { - instrumentedExceptionLoaded = afterExceptionLoaded; - } - super.createHandleExceptionTarget(instrumentedExceptionLoaded, bci, dispatchState); - } - - @Override - protected InvokeWithExceptionNode createInvokeWithException(int invokeBci, CallTargetNode callTarget, JavaKind resultType, ExceptionEdgeAction exceptionEdgeAction) { - try { - assert curDeoptimizeOnException == false; - curDeoptimizeOnException = getGraphBuilderInstance().deoptimizeOnExceptionPredicate.test(callTarget.targetMethod()); - curInvokedMethod = callTarget.targetMethod(); - return super.createInvokeWithException(invokeBci, callTarget, resultType, exceptionEdgeAction); - } finally { - curInvokedMethod = null; - curDeoptimizeOnException = false; - } - } - /** * {@link Fold} and {@link NodeIntrinsic} can be deferred during parsing/decoding. Only by * the end of {@linkplain SnippetTemplate#instantiate Snippet instantiation} do they need to From d7b735c92eea838e70cc6f4b337306857c20da27 Mon Sep 17 00:00:00 2001 From: Andreas Woess Date: Thu, 16 May 2019 19:14:29 +0200 Subject: [PATCH 08/12] Simplify speculative exception anchor node. --- ...va => SpeculativeExceptionAnchorNode.java} | 35 +++++++++---------- .../phases/DeoptimizeOnExceptionPhase.java | 12 +++---- 2 files changed, 20 insertions(+), 27 deletions(-) rename compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/nodes/{SpeculativeExceptionGuardNode.java => SpeculativeExceptionAnchorNode.java} (68%) diff --git a/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/nodes/SpeculativeExceptionGuardNode.java b/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/nodes/SpeculativeExceptionAnchorNode.java similarity index 68% rename from compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/nodes/SpeculativeExceptionGuardNode.java rename to compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/nodes/SpeculativeExceptionAnchorNode.java index a7475b0a349f..09f45507bf83 100644 --- a/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/nodes/SpeculativeExceptionGuardNode.java +++ b/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/nodes/SpeculativeExceptionAnchorNode.java @@ -24,19 +24,17 @@ */ package org.graalvm.compiler.truffle.compiler.nodes; -import static org.graalvm.compiler.nodeinfo.InputType.Guard; -import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_1; -import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_1; +import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_0; +import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_0; +import org.graalvm.compiler.core.common.type.StampFactory; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.spi.Canonicalizable; import org.graalvm.compiler.graph.spi.CanonicalizerTool; -import org.graalvm.compiler.graph.spi.SimplifierTool; import org.graalvm.compiler.nodeinfo.NodeInfo; -import org.graalvm.compiler.nodes.AbstractFixedGuardNode; import org.graalvm.compiler.nodes.DeoptimizeNode; -import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.FixedWithNextNode; import org.graalvm.compiler.truffle.compiler.PartialEvaluator; import jdk.vm.ci.meta.DeoptimizationAction; @@ -47,29 +45,28 @@ import jdk.vm.ci.meta.SpeculationLog.SpeculationReason; /** - * A speculation-less guard node that is inserted into the exception branch of TruffleBoundary calls + * A speculation-less node that is inserted into the exception branch of TruffleBoundary calls * during parsing (graph encoding). During partial evaluation (graph decoding) when a speculation * log is available, it will speculate that TruffleBoundary method will not throw and either becomes * a control-flow sink {@link DeoptimizeNode} with the {@link Speculation} in order to off the * branch, or if the speculation has already failed for this compilation root, disappears. */ -@NodeInfo(nameTemplate = "SpeculativeExceptionGuard {p#reason/s}", allowedUsageTypes = Guard, cycles = CYCLES_1, size = SIZE_1) -public final class SpeculativeExceptionGuardNode extends AbstractFixedGuardNode implements Canonicalizable { +@NodeInfo(cycles = CYCLES_0, size = SIZE_0) +public final class SpeculativeExceptionAnchorNode extends FixedWithNextNode implements Canonicalizable { - public static final NodeClass TYPE = NodeClass.create(SpeculativeExceptionGuardNode.class); + public static final NodeClass TYPE = NodeClass.create(SpeculativeExceptionAnchorNode.class); - protected ResolvedJavaMethod targetMethod; + private final DeoptimizationReason reason; + private final DeoptimizationAction action; + private final ResolvedJavaMethod targetMethod; - public SpeculativeExceptionGuardNode(LogicNode condition, DeoptimizationReason reason, DeoptimizationAction action, SpeculationLog.Speculation speculation, boolean negated, - ResolvedJavaMethod targetMethod) { - super(TYPE, condition, reason, action, speculation, negated); + public SpeculativeExceptionAnchorNode(DeoptimizationReason reason, DeoptimizationAction action, ResolvedJavaMethod targetMethod) { + super(TYPE, StampFactory.forVoid()); + this.reason = reason; + this.action = action; this.targetMethod = targetMethod; } - @Override - public void simplify(SimplifierTool tool) { - } - @Override public Node canonical(CanonicalizerTool tool) { SpeculationLog speculationLog = graph().getSpeculationLog(); @@ -77,7 +74,7 @@ public Node canonical(CanonicalizerTool tool) { SpeculationReason speculationReason = PartialEvaluator.createTruffleBoundaryExceptionSpeculation(targetMethod); if (speculationLog.maySpeculate(speculationReason)) { Speculation exceptionSpeculation = speculationLog.speculate(speculationReason); - return new DeoptimizeNode(getAction(), getReason(), exceptionSpeculation); + return new DeoptimizeNode(action, reason, exceptionSpeculation); } return null; } diff --git a/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/phases/DeoptimizeOnExceptionPhase.java b/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/phases/DeoptimizeOnExceptionPhase.java index 14ac394e9f5e..6a8578d15430 100644 --- a/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/phases/DeoptimizeOnExceptionPhase.java +++ b/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/phases/DeoptimizeOnExceptionPhase.java @@ -31,15 +31,13 @@ import org.graalvm.compiler.nodes.FixedWithNextNode; import org.graalvm.compiler.nodes.Invoke; import org.graalvm.compiler.nodes.InvokeWithExceptionNode; -import org.graalvm.compiler.nodes.LogicConstantNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.phases.Phase; -import org.graalvm.compiler.truffle.compiler.nodes.SpeculativeExceptionGuardNode; +import org.graalvm.compiler.truffle.compiler.nodes.SpeculativeExceptionAnchorNode; import jdk.vm.ci.meta.DeoptimizationAction; import jdk.vm.ci.meta.DeoptimizationReason; import jdk.vm.ci.meta.ResolvedJavaMethod; -import jdk.vm.ci.meta.SpeculationLog; /** * Instruments the exception edge of TruffleBoundary method calls with a speculative transfer to @@ -65,11 +63,9 @@ protected void run(StructuredGraph graph) { assert next != null; exceptionEdge.setNext(null); // Note: Speculation is inserted during PE. - FixedWithNextNode guard = graph.add(new SpeculativeExceptionGuardNode(LogicConstantNode.tautology(graph), - DeoptimizationReason.TransferToInterpreter, DeoptimizationAction.InvalidateRecompile, SpeculationLog.NO_SPECULATION, false, - targetMethod)); - guard.setNext(next); - exceptionEdge.setNext(guard); + FixedWithNextNode newNode = graph.add(new SpeculativeExceptionAnchorNode(DeoptimizationReason.TransferToInterpreter, DeoptimizationAction.InvalidateRecompile, targetMethod)); + newNode.setNext(next); + exceptionEdge.setNext(newNode); } } } From f3e8944adc78b706d4fd08a0d1ff6ddc0d225939 Mon Sep 17 00:00:00 2001 From: Andreas Woess Date: Fri, 17 May 2019 16:21:25 +0200 Subject: [PATCH 09/12] Simplify insertion of the speculative exception node. --- .../compiler/phases/DeoptimizeOnExceptionPhase.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/phases/DeoptimizeOnExceptionPhase.java b/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/phases/DeoptimizeOnExceptionPhase.java index 6a8578d15430..6867365929bf 100644 --- a/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/phases/DeoptimizeOnExceptionPhase.java +++ b/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/phases/DeoptimizeOnExceptionPhase.java @@ -27,7 +27,6 @@ import java.util.function.Predicate; import org.graalvm.compiler.nodes.AbstractBeginNode; -import org.graalvm.compiler.nodes.FixedNode; import org.graalvm.compiler.nodes.FixedWithNextNode; import org.graalvm.compiler.nodes.Invoke; import org.graalvm.compiler.nodes.InvokeWithExceptionNode; @@ -58,14 +57,10 @@ protected void run(StructuredGraph graph) { ResolvedJavaMethod targetMethod = invokeWithException.callTarget().targetMethod(); if (deoptimizeOnExceptionPredicate.test(targetMethod)) { // Method has @TruffleBoundary(transferToInterpreterOnException=true) - AbstractBeginNode exceptionEdge = invokeWithException.exceptionEdge(); - FixedNode next = exceptionEdge.next(); - assert next != null; - exceptionEdge.setNext(null); // Note: Speculation is inserted during PE. + AbstractBeginNode exceptionEdge = invokeWithException.exceptionEdge(); FixedWithNextNode newNode = graph.add(new SpeculativeExceptionAnchorNode(DeoptimizationReason.TransferToInterpreter, DeoptimizationAction.InvalidateRecompile, targetMethod)); - newNode.setNext(next); - exceptionEdge.setNext(newNode); + graph.addAfterFixed(exceptionEdge, newNode); } } } From 690c89d036991a1a992997264e3b92be4e094801 Mon Sep 17 00:00:00 2001 From: Andreas Woess Date: Thu, 16 May 2019 19:51:22 +0200 Subject: [PATCH 10/12] Use getInvokes() in TruffleBoundaryPhase. --- .../src/com/oracle/svm/truffle/api/TruffleBoundaryPhase.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/api/TruffleBoundaryPhase.java b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/api/TruffleBoundaryPhase.java index 7c9cb2ea6673..4564f2d88633 100644 --- a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/api/TruffleBoundaryPhase.java +++ b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/api/TruffleBoundaryPhase.java @@ -24,9 +24,9 @@ */ package com.oracle.svm.truffle.api; -import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.nodes.DeoptimizeNode; import org.graalvm.compiler.nodes.FixedNode; +import org.graalvm.compiler.nodes.Invoke; import org.graalvm.compiler.nodes.InvokeWithExceptionNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.java.ExceptionObjectNode; @@ -56,7 +56,7 @@ public class TruffleBoundaryPhase extends Phase { @Override @SuppressWarnings("deprecation") protected void run(StructuredGraph graph) { - for (Node n : graph.getNodes()) { + for (Invoke n : graph.getInvokes()) { if (n instanceof InvokeWithExceptionNode) { InvokeWithExceptionNode invoke = (InvokeWithExceptionNode) n; ExceptionObjectNode exceptionObject = (ExceptionObjectNode) invoke.exceptionEdge(); From 7adae213ad79b24f0aa64df8e015f08d967c2361 Mon Sep 17 00:00:00 2001 From: Andreas Woess Date: Thu, 9 May 2019 16:22:02 +0200 Subject: [PATCH 11/12] Update truffle changelog. --- truffle/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/truffle/CHANGELOG.md b/truffle/CHANGELOG.md index 7b90913884d8..a449a0421011 100644 --- a/truffle/CHANGELOG.md +++ b/truffle/CHANGELOG.md @@ -8,6 +8,7 @@ This changelog summarizes major changes between Truffle versions relevant to lan * Removed deprecated methods`TruffleStackTraceElement#getStackTrace` and `TruffleStackTraceElement#fillIn` (use methods of `TruffleStackTrace` instead). * `SlowPathException#fillInStackTrace` is now `final`. * Added an ability to read a [path separator](https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/TruffleLanguage.Env.html#getPathSeparator--) used to separate filenames in a path list. +* `@TruffleBoundary` methods that throw but are not annotated with `@TruffleBoundary(transferToInterpreterOnException=false)` will now transfer to the interpreter only once per `CallTarget` (compilation root). ## Version 19.0.0 * Renamed version 1.0.0 to 19.0.0 From c88b1df9b1bab20afef09d74e0642d381b971e01 Mon Sep 17 00:00:00 2001 From: Andreas Woess Date: Wed, 22 May 2019 15:21:13 +0200 Subject: [PATCH 12/12] Update fastr import. --- vm/mx.vm/suite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/mx.vm/suite.py b/vm/mx.vm/suite.py index 3ad4865706fc..d9862163c167 100644 --- a/vm/mx.vm/suite.py +++ b/vm/mx.vm/suite.py @@ -61,7 +61,7 @@ }, { "name": "fastr", - "version": "b6a319e06982464d5280b8d0691dc861b9e7192b", + "version": "0d5c6ae9127282147e401c8b3698787aedd55e41", "dynamic": True, "urls": [ {"url": "https://github.com/oracle/fastr.git", "kind": "git"},