Skip to content

Commit

Permalink
Track extra metadata about DeoptEntry/DeoptProxyAnchor nodes and more…
Browse files Browse the repository at this point in the history
… aggressively remove them.
  • Loading branch information
teshull committed Aug 8, 2023
1 parent 9f016aa commit d7db489
Show file tree
Hide file tree
Showing 9 changed files with 266 additions and 116 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,12 @@ public boolean isOutOfBounds() {
}
}

/**
* Represents an entry into an exception handler routine. This block is needed to ensure there
* is a place to put checks that determine whether the handler should be entered (i.e., the
* exception kind is compatible) or execution should continue to check the next handler (or
* unwind).
*/
public static class ExceptionDispatchBlock extends BciBlock {
public final ExceptionHandler handler;
public final int deoptBci;
Expand Down Expand Up @@ -1351,7 +1357,7 @@ private void addSuccessor(int predBci, BciBlock sux) {
}

/**
* Logic for adding an the "normal" invoke successor link.
* Logic for adding the "normal" invoke successor link.
*/
protected void addInvokeNormalSuccessor(int invokeBci, BciBlock sux) {
addSuccessor(invokeBci, sux);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@

import com.oracle.svm.core.graal.lir.DeoptEntryOp;

import jdk.vm.ci.code.BytecodeFrame;

/**
* A landing-pad for deoptimization. This node is generated in deoptimization target methods for all
* deoptimization entry points.
Expand All @@ -53,8 +55,20 @@ public final class DeoptEntryNode extends WithExceptionNode implements DeoptEntr

@OptionalInput(InputType.State) protected FrameState stateAfter;

public DeoptEntryNode() {
private final int proxifiedInvokeBci;

protected DeoptEntryNode(int proxifiedInvokeBci) {
super(TYPE, StampFactory.forVoid());
this.proxifiedInvokeBci = proxifiedInvokeBci;
}

public static DeoptEntryNode create(int proxifiedInvokeBci) {
assert proxifiedInvokeBci != BytecodeFrame.UNKNOWN_BCI;
return new DeoptEntryNode(proxifiedInvokeBci);
}

public static DeoptEntryNode create() {
return new DeoptEntryNode(BytecodeFrame.UNKNOWN_BCI);
}

@Override
Expand Down Expand Up @@ -99,4 +113,9 @@ public void setStateAfter(FrameState x) {
public boolean hasSideEffect() {
return true;
}

@Override
public int getProxifiedInvokeBci() {
return proxifiedInvokeBci;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,12 @@
import org.graalvm.compiler.nodes.spi.LIRLowerable;

public interface DeoptEntrySupport extends LIRLowerable, ControlFlowAnchored, FixedNodeInterface, StateSplit {

/**
* Returns the invoke bci this deopt is serving as a proxy anchor for or
* {@link jdk.vm.ci.code.BytecodeFrame#UNKNOWN_BCI} if it is not serving as a proxy anchor for
* an invoke. Note this will be different than the entry's stateafter bci since this node will
* be after the invoke it is "proxifying".
*/
int getProxifiedInvokeBci();
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,22 @@
public class DeoptProxyAnchorNode extends AbstractStateSplit implements DeoptEntrySupport {
public static final NodeClass<DeoptProxyAnchorNode> TYPE = NodeClass.create(DeoptProxyAnchorNode.class);

public DeoptProxyAnchorNode() {
this(TYPE);
}
private final int proxifiedInvokeBci;

public DeoptProxyAnchorNode(int proxifiedInvokeBci) {
super(TYPE, StampFactory.forVoid());
assert proxifiedInvokeBci >= 0 : "DeoptProxyAnchorNode should be proxing an invoke";

protected DeoptProxyAnchorNode(NodeClass<? extends DeoptProxyAnchorNode> c) {
super(c, StampFactory.forVoid());
this.proxifiedInvokeBci = proxifiedInvokeBci;
}

@Override
public void generate(NodeLIRBuilderTool gen) {
/* No-op */
}

@Override
public int getProxifiedInvokeBci() {
return proxifiedInvokeBci;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ public ValueNode createCFunctionCall(ValueNode targetAddress, List<ValueNode> ar
* exception handlers and directly unwind.
*/
int bci = invoke.stateAfter().bci;
appendWithUnwind(new DeoptEntryNode(), bci);
appendWithUnwind(DeoptEntryNode.create(invoke.bci()), bci);
}

ValueNode result = invoke;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin;
import org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext;
import org.graalvm.compiler.nodes.graphbuilderconf.NodePlugin;
import org.graalvm.compiler.nodes.java.ExceptionObjectNode;
import org.graalvm.compiler.options.Option;
import org.graalvm.compiler.options.OptionValues;
import org.graalvm.compiler.phases.OptimisticOptimizations;
Expand Down Expand Up @@ -101,6 +102,8 @@
import com.oracle.svm.core.ParsingReason;
import com.oracle.svm.core.config.ConfigurationValues;
import com.oracle.svm.core.graal.nodes.DeoptEntryNode;
import com.oracle.svm.core.graal.nodes.DeoptEntrySupport;
import com.oracle.svm.core.graal.nodes.DeoptProxyAnchorNode;
import com.oracle.svm.core.graal.nodes.InlinedInvokeArgumentsNode;
import com.oracle.svm.core.graal.stackvalue.StackValueNode;
import com.oracle.svm.core.option.HostedOptionKey;
Expand All @@ -126,6 +129,7 @@
import com.oracle.svm.hosted.phases.InlineBeforeAnalysisPolicyUtils.AlwaysInlineScope;
import com.oracle.svm.hosted.phases.StrengthenStampsPhase;

import jdk.vm.ci.code.BytecodeFrame;
import jdk.vm.ci.code.BytecodePosition;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.ResolvedJavaMethod;
Expand All @@ -138,12 +142,8 @@
public class ParseOnceRuntimeCompilationFeature extends RuntimeCompilationFeature implements Feature, RuntimeCompilationSupport {

public static class Options {
/*
* Note this phase is currently overly aggressive and can illegally remove proxies. This
* will be fixed in GR-44459.
*/
@Option(help = "Remove Deopt(Entries,Anchors,Proxies) determined to be unneeded after the runtime compiled graphs have been finalized.")//
public static final HostedOptionKey<Boolean> RemoveUnneededDeoptSupport = new HostedOptionKey<>(false);
public static final HostedOptionKey<Boolean> RemoveUnneededDeoptSupport = new HostedOptionKey<>(true);

@Option(help = "Perform InlineBeforeAnalysis on runtime compiled methods")//
public static final HostedOptionKey<Boolean> RuntimeCompilationInlineBeforeAnalysis = new HostedOptionKey<>(true);
Expand Down Expand Up @@ -1216,58 +1216,103 @@ public boolean insertPlaceholderParamAndReturnFlows(MultiMethod.MultiMethodKey m
}

/**
* Removes Deoptimizations Entrypoints which are deemed to be unnecessary after the runtime
* compilation methods are optimized.
* Removes {@link DeoptEntryNode}s, {@link DeoptProxyAnchorNode}s, and {@link DeoptProxyNode}s
* which are determined to be unnecessary after the runtime compilation methods are optimized.
*/
static class RemoveUnneededDeoptSupport extends Phase {
enum RemovalDecision {
KEEP,
PROXIFY,
REMOVE
}

@Override
protected void run(StructuredGraph graph) {
EconomicMap<StateSplit, Boolean> decisionCache = EconomicMap.create();
EconomicMap<StateSplit, RemovalDecision> decisionCache = EconomicMap.create();

// First go through and delete all unneeded proxies
for (DeoptProxyNode proxyNode : graph.getNodes(DeoptProxyNode.TYPE).snapshot()) {
ValueNode proxyPoint = proxyNode.getProxyPoint();
if (proxyPoint instanceof StateSplit) {
if (proxyPoint instanceof DeoptEntryNode && shouldRemove((StateSplit) proxyPoint, decisionCache)) {
if (getDecision((StateSplit) proxyPoint, decisionCache) == RemovalDecision.REMOVE) {
proxyNode.replaceAtAllUsages(proxyNode.getOriginalNode(), true);
proxyNode.safeDelete();
}
}
}

// Next remove all unneeded DeoptEntryNodes
// Next, remove all unneeded DeoptEntryNodes
for (DeoptEntryNode deoptEntry : graph.getNodes().filter(DeoptEntryNode.class).snapshot()) {
if (shouldRemove(deoptEntry, decisionCache)) {
deoptEntry.killExceptionEdge();
graph.removeSplit(deoptEntry, deoptEntry.getPrimarySuccessor());
switch (getDecision(deoptEntry, decisionCache)) {
case REMOVE -> {
deoptEntry.killExceptionEdge();
graph.removeSplit(deoptEntry, deoptEntry.getPrimarySuccessor());
}
case PROXIFY -> {
deoptEntry.killExceptionEdge();
DeoptProxyAnchorNode newAnchor = graph.add(new DeoptProxyAnchorNode(deoptEntry.getProxifiedInvokeBci()));
newAnchor.setStateAfter(deoptEntry.stateAfter());
graph.replaceSplitWithFixed(deoptEntry, newAnchor, deoptEntry.getPrimarySuccessor());
}
}
}

// Finally, remove all unneeded DeoptProxyAnchorNodes
for (DeoptProxyAnchorNode proxyAnchor : graph.getNodes().filter(DeoptProxyAnchorNode.class).snapshot()) {
if (getDecision(proxyAnchor, decisionCache) == RemovalDecision.REMOVE) {
graph.removeFixed(proxyAnchor);
}
}
}

boolean shouldRemove(StateSplit node, EconomicMap<StateSplit, Boolean> decisionCache) {
Boolean cached = decisionCache.get(node);
RemovalDecision getDecision(StateSplit node, EconomicMap<StateSplit, RemovalDecision> decisionCache) {
RemovalDecision cached = decisionCache.get(node);
if (cached != null) {
return cached;
}

DeoptEntrySupport proxyNode;
if (node instanceof ExceptionObjectNode exceptionObject) {
/*
* For the exception edge of a DeoptEntryNode, we insert the proxies on the
* exception object.
*/
proxyNode = (DeoptEntrySupport) exceptionObject.predecessor();
} else {
proxyNode = (DeoptEntrySupport) node;
}

RemovalDecision decision = RemovalDecision.REMOVE;
var directive = SubstrateCompilationDirectives.singleton();
FrameState state = node.stateAfter();
FrameState state = proxyNode.stateAfter();
HostedMethod method = (HostedMethod) state.getMethod();
if (proxyNode instanceof DeoptEntryNode) {
if (directive.isDeoptEntry(method, state.bci, state.duringCall(), state.rethrowException())) {
// must keep all deopt entries which are still guarding nodes
decision = RemovalDecision.KEEP;
}
}

boolean result = true;
if (directive.isRegisteredDeoptTarget(method)) {
result = !directive.isDeoptEntry(method, state.bci, state.duringCall(), state.rethrowException());
if (decision == RemovalDecision.REMOVE) {
// now check for any implicit deopt entry being protected against
int proxifiedInvokeBci = proxyNode.getProxifiedInvokeBci();
if (proxifiedInvokeBci != BytecodeFrame.UNKNOWN_BCI && directive.isDeoptEntry(method, proxifiedInvokeBci, true, false)) {
// must keep still keep a proxy for nodes which are "proxifying" an invoke
decision = proxyNode instanceof DeoptEntryNode ? RemovalDecision.PROXIFY : RemovalDecision.KEEP;
}
}

// cache the decision
decisionCache.put(node, result);
return result;
decisionCache.put(node, decision);
if (proxyNode != node) {
decisionCache.put(proxyNode, decision);
}
return decision;
}

@Override
public CharSequence getName() {
return "RemoveDeoptEntries";
return "RemoveUnneededDeoptSupport";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ private static int startMethod(HostedGraphKit kit, DeoptInfoProvider deoptInfo,
if (deoptInfo != null) {
FrameState initialState = kit.getGraph().start().stateAfter();
if (shouldInsertDeoptEntry(deoptInfo, initialState.bci, false, false)) {
return appendDeoptWithExceptionUnwind(kit, initialState, initialState.bci, nextDeoptIndex);
return appendDeoptWithExceptionUnwind(kit, initialState, initialState.bci, nextDeoptIndex, DeoptEntryNode.create());
}
}
return nextDeoptIndex;
Expand Down Expand Up @@ -223,7 +223,7 @@ private static int invokeWithDeoptAndExceptionUnwind(HostedGraphKit kit, DeoptIn
if (shouldInsertDeoptEntry(deoptInfo, bci, false, true)) {
// Exception during invoke

var exceptionDeopt = kit.add(new DeoptEntryNode());
var exceptionDeopt = kit.add(DeoptEntryNode.create(invoke.bci()));
exceptionDeopt.setStateAfter(exception.stateAfter().duplicate());
var exceptionDeoptBegin = kit.add(new DeoptEntryBeginNode());
int exceptionDeoptIndex = nextDeoptIndex++;
Expand All @@ -246,10 +246,10 @@ private static int invokeWithDeoptAndExceptionUnwind(HostedGraphKit kit, DeoptIn
kit.noExceptionPart();
if (needDeoptEntry) {
// Deopt entry after invoke without exception
nextDeoptIndex = appendDeoptWithExceptionUnwind(kit, invoke.stateAfter(), invoke.stateAfter().bci, nextDeoptIndex);
nextDeoptIndex = appendDeoptWithExceptionUnwind(kit, invoke.stateAfter(), invoke.stateAfter().bci, nextDeoptIndex, DeoptEntryNode.create(invoke.bci()));
} else {
// Only a proxy is needed
nextDeoptIndex = appendDeoptProxyAnchorNode(kit, invoke.stateAfter(), nextDeoptIndex);
nextDeoptIndex = appendDeoptProxyAnchorNode(kit, invoke.stateAfter(), nextDeoptIndex, invoke.bci());
}
}
kit.endInvokeWithException();
Expand All @@ -258,8 +258,8 @@ private static int invokeWithDeoptAndExceptionUnwind(HostedGraphKit kit, DeoptIn
}

/** @see com.oracle.svm.hosted.phases.SharedGraphBuilderPhase */
private static int appendDeoptWithExceptionUnwind(HostedGraphKit kit, FrameState state, int exceptionBci, int nextDeoptIndex) {
var entry = kit.add(new DeoptEntryNode());
private static int appendDeoptWithExceptionUnwind(HostedGraphKit kit, FrameState state, int exceptionBci, int nextDeoptIndex, DeoptEntryNode deoptEntryNode) {
var entry = kit.add(deoptEntryNode);
entry.setStateAfter(state.duplicate());
var begin = kit.append(new DeoptEntryBeginNode());
((FixedWithNextNode) begin.predecessor()).setNext(entry);
Expand All @@ -280,8 +280,8 @@ private static int appendDeoptWithExceptionUnwind(HostedGraphKit kit, FrameState
}

/** @see com.oracle.svm.hosted.phases.SharedGraphBuilderPhase */
private static int appendDeoptProxyAnchorNode(HostedGraphKit kit, FrameState state, int nextDeoptIndex) {
var anchor = kit.append(new DeoptProxyAnchorNode());
private static int appendDeoptProxyAnchorNode(HostedGraphKit kit, FrameState state, int nextDeoptIndex, int invokeBci) {
var anchor = kit.append(new DeoptProxyAnchorNode(invokeBci));
anchor.setStateAfter(state.duplicate());

// Ensure later nodes see values from potential deoptimization
Expand Down
Loading

0 comments on commit d7db489

Please sign in to comment.