com.oracle.svm.hosted.phases.HostedGraphBuilderPhase Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of svm Show documentation
Show all versions of svm Show documentation
SubstrateVM image builder components
The newest version!
/*
* Copyright (c) 2013, 2017, 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 com.oracle.svm.hosted.phases;
import java.util.HashMap;
import java.util.Map;
import org.graalvm.compiler.core.common.type.StampPair;
import org.graalvm.compiler.graph.Node;
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.InvokeKind;
import org.graalvm.compiler.nodes.FixedNode;
import org.graalvm.compiler.nodes.FixedWithNextNode;
import org.graalvm.compiler.nodes.FrameState;
import org.graalvm.compiler.nodes.Invoke;
import org.graalvm.compiler.nodes.KillingBeginNode;
import org.graalvm.compiler.nodes.LoopBeginNode;
import org.graalvm.compiler.nodes.StateSplit;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.StructuredGraph.GuardsStage;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
import org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext;
import org.graalvm.compiler.nodes.java.ExceptionObjectNode;
import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
import org.graalvm.compiler.phases.OptimisticOptimizations;
import org.graalvm.compiler.phases.util.Providers;
import org.graalvm.compiler.word.WordTypes;
import com.oracle.svm.core.code.FrameInfoEncoder;
import com.oracle.svm.core.graal.nodes.DeoptEntryNode;
import com.oracle.svm.core.graal.nodes.DeoptProxyAnchorNode;
import com.oracle.svm.hosted.meta.HostedMethod;
import com.oracle.svm.hosted.nodes.DeoptProxyNode;
import com.oracle.svm.hosted.nodes.SubstrateMethodCallTargetNode;
import com.oracle.svm.hosted.phases.SubstrateGraphBuilderPhase.SubstrateBytecodeParser;
import jdk.vm.ci.meta.JavaTypeProfile;
import jdk.vm.ci.meta.ResolvedJavaMethod;
public class HostedGraphBuilderPhase extends SubstrateGraphBuilderPhase {
public HostedGraphBuilderPhase(Providers providers, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext,
WordTypes wordTypes) {
super(providers, graphBuilderConfig, optimisticOpts, initialIntrinsicContext, wordTypes);
}
@Override
protected BytecodeParser createBytecodeParser(StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI, IntrinsicContext intrinsicContext) {
return new HostedBytecodeParser(this, graph, parent, method, entryBCI, intrinsicContext);
}
}
class HostedBytecodeParser extends SubstrateBytecodeParser {
private static final DeoptEntryNode STICKY_DEOPT_ENTRY = new DeoptEntryNode();
private int currentDeoptIndex;
private Map deoptEntries = new HashMap<>();
HostedBytecodeParser(GraphBuilderPhase.Instance graphBuilderInstance, StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI, IntrinsicContext intrinsicContext) {
super(graphBuilderInstance, graph, parent, method, entryBCI, intrinsicContext, true);
}
@Override
public HostedMethod getMethod() {
return (HostedMethod) super.getMethod();
}
@Override
protected boolean forceLoopPhis() {
return getMethod().compilationInfo.isDeoptTarget() || super.forceLoopPhis();
}
@Override
protected boolean stampFromValueForForcedPhis() {
return true;
}
@Override
protected void build(FixedWithNextNode startInstruction, FrameStateBuilder startFrameState) {
super.build(startInstruction, startFrameState);
/* We never have floating guards in AOT compiled code. */
getGraph().setGuardsStage(GuardsStage.FIXED_DEOPTS);
assert !getMethod().isEntryPoint() : "Cannot directly use as entry point, create a call stub";
if (getMethod().compilationInfo.isDeoptTarget()) {
/*
* Remove dangling DeoptProxyNodes which remained after deletion of the corresponding
* DeoptEntryNodes.
*/
for (DeoptProxyNode deoptProxy : graph.getNodes(DeoptProxyNode.TYPE)) {
if (!deoptProxy.hasProxyPoint()) {
ValueNode originalValue = deoptProxy.getOriginalNode();
deoptProxy.replaceAtUsagesAndDelete(originalValue);
}
}
}
}
@Override
public MethodCallTargetNode createMethodCallTarget(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] args, StampPair returnStamp, JavaTypeProfile profile) {
return new SubstrateMethodCallTargetNode(invokeKind, targetMethod, args, returnStamp, getMethod().getProfilingInfo(), bci());
}
private void insertProxies(FixedNode deoptTarget, FrameStateBuilder state) {
/*
* At a deoptimization point we wrap non-constant locals (and java stack elements) with
* proxy nodes. This is to avoid global value numbering on locals (or derived expressions).
* The effect is that when a local is accessed after a deoptimization point it is really
* loaded from its location. This is similar to what happens in the GraphBuilderPhase if
* entryBCI is set for OSR.
*/
state.insertProxies(value -> createProxyNode(value, deoptTarget));
currentDeoptIndex++;
}
private ValueNode createProxyNode(ValueNode value, FixedNode deoptTarget) {
ValueNode v = DeoptProxyNode.create(value, deoptTarget, currentDeoptIndex);
if (v.graph() != null) {
return v;
}
return graph.addOrUniqueWithInputs(v);
}
/**
* Insert a deopt entry for the graph's start node.
*/
@Override
protected void finishPrepare(FixedWithNextNode startInstr, int bci, FrameStateBuilder state) {
super.finishPrepare(startInstr, bci, state);
if (getMethod().compilationInfo.isDeoptEntry(bci, false, false)) {
DeoptEntryNode deoptEntry = append(new DeoptEntryNode());
deoptEntry.setStateAfter(frameState.create(bci, deoptEntry));
deoptEntries.put(Long.valueOf(bci), deoptEntry);
insertProxies(deoptEntry, state);
}
}
@Override
protected void parseAndInlineCallee(ResolvedJavaMethod targetMethod, ValueNode[] args, IntrinsicContext calleeIntrinsicContext) {
assert calleeIntrinsicContext != null : "only inlining replacements";
if (getMethod().compilationInfo.isDeoptEntry(bci(), false, false)) {
/*
* Replacements use the frame state before the invoke for all nodes that need a state,
* i.e., we want to re-execute the whole replacement in case of deoptimization.
* Therefore, we need to register a DeoptEntryNode before inlining the replacement. The
* frame state used for the DeoptEntryNode needs to be the same state that will be used
* later on in the intrinsic.
*/
FrameState stateAfter = frameState.create(bci(), getNonIntrinsicAncestor(), false, targetMethod.getSignature().toParameterKinds(!targetMethod.isStatic()), args);
long encodedBci = FrameInfoEncoder.encodeBci(stateAfter.bci, stateAfter.duringCall(), stateAfter.rethrowException());
if (!deoptEntries.containsKey(encodedBci)) {
DeoptEntryNode deoptEntry = graph.add(new DeoptEntryNode());
deoptEntry.setStateAfter(stateAfter);
insertProxies(deoptEntry, frameState);
lastInstr.setNext(deoptEntry);
lastInstr = deoptEntry;
for (int i = 0; i < args.length; i++) {
args[i] = createProxyNode(args[i], deoptEntry);
}
}
/*
* Ensure that no one registers a later state (after the replacement) with the same
* frame state.
*/
deoptEntries.put(encodedBci, STICKY_DEOPT_ENTRY);
}
super.parseAndInlineCallee(targetMethod, args, calleeIntrinsicContext);
}
/**
* Insert deopt entries after all state splits.
*/
@Override
protected FixedWithNextNode finishInstruction(FixedWithNextNode instr, FrameStateBuilder stateBuilder) {
if (getMethod().compilationInfo.isDeoptTarget() && !parsingIntrinsic()) {
FrameState stateAfter = null;
if (instr instanceof StateSplit && !(instr instanceof DeoptEntryNode)) {
/*
* The regular case: the instruction is a state split and we insert a DeoptEntryNode
* right after it.
*/
StateSplit stateSplit = (StateSplit) instr;
stateAfter = stateSplit.stateAfter();
} else if (instr instanceof AbstractBeginNode) {
/*
* We are at a block begin. If the block predecessor is a LoopExitNode or an
* InvokeWithException (both are state splits), we didn't inserted a deopt entry
* yet. So we do it at the begin of a block.
*
* Note that this only happens if the LoopExitNode/InvokeWithException is the
* _single_ predcessor of this block. In case of multiple predecessors, the block
* starts with a MergeNode and this is handled like a regular case.
*/
Node predecessor = instr.predecessor();
if (predecessor instanceof KillingBeginNode) {
/*
* This is between an InvokeWithException and the BlockPlaceholderNode.
*/
predecessor = predecessor.predecessor();
}
if (predecessor instanceof StateSplit && !(predecessor instanceof DeoptEntryNode)) {
stateAfter = ((StateSplit) predecessor).stateAfter();
}
}
boolean needsDeoptEntry = false;
boolean needsProxies = false;
if (stateAfter != null) {
if (getMethod().compilationInfo.isDeoptEntry(stateAfter.bci, stateAfter.duringCall(), stateAfter.rethrowException())) {
needsDeoptEntry = true;
needsProxies = true;
} else if (instr.predecessor() instanceof Invoke && getMethod().compilationInfo.isDeoptEntry(((Invoke) instr.predecessor()).bci(), true, false)) {
/*
* Invoke nodes can be implicit deoptimization entry points. But we cannot
* anchor proxy nodes on invocations: The invoke has two successors (normal and
* exception handler), and we need to proxy values at the beginning of both.
*/
needsProxies = true;
} else if (instr instanceof ExceptionObjectNode && getMethod().compilationInfo.isDeoptEntry(((ExceptionObjectNode) instr).stateAfter().bci, true, false)) {
/*
* The predecessor of the ExceptionObjectNode will be an Invoke, but the Invoke
* has not been created yet. So the check above for the predecessor does not
* trigger.
*/
needsProxies = true;
}
}
if (needsProxies) {
long encodedBci = FrameInfoEncoder.encodeBci(stateAfter.bci, stateAfter.duringCall(), stateAfter.rethrowException());
DeoptProxyAnchorNode existingDeoptEntry = deoptEntries.get(encodedBci);
if (existingDeoptEntry != STICKY_DEOPT_ENTRY) {
if (existingDeoptEntry != null) {
/*
* Some state splits (i.e. MergeNode and DispatchBeginNode) do not have a
* correspondent byte code. Therefore there can be a previously added deopt
* entry with the same BCI. For MergeNodes we replace the previous entry
* because the new frame state has less live locals.
*/
existingDeoptEntry.replaceAtUsages(null);
graph.removeFixed(existingDeoptEntry);
deoptEntries.remove(encodedBci);
if (existingDeoptEntry instanceof DeoptEntryNode) {
/*
* We already had a DeoptEntryNode registered earlier for some reason,
* so be conservative and create one again (and not just a
* DeoptProxyAnchorNode).
*/
needsDeoptEntry = true;
}
}
assert !deoptEntries.containsKey(encodedBci) : "duplicate deopt entry for encoded BCI " + encodedBci;
DeoptProxyAnchorNode deoptEntry = createDeoptEntry(stateBuilder, stateAfter, !needsDeoptEntry);
if (instr instanceof LoopBeginNode) {
/*
* Loop headers to not have their own bci. Never move a deopt entry for the
* loop header down, e.g., into a loop end (that might then end up to be
* dead code).
*/
deoptEntries.put(encodedBci, STICKY_DEOPT_ENTRY);
} else {
deoptEntries.put(encodedBci, deoptEntry);
}
assert instr.next() == null : "cannot append instruction to instruction which isn't end (" + instr + "->" + instr.next() + ")";
instr.setNext(deoptEntry);
return deoptEntry;
}
}
}
return super.finishInstruction(instr, stateBuilder);
}
private DeoptProxyAnchorNode createDeoptEntry(FrameStateBuilder stateBuilder, FrameState stateAfter, boolean anchorOnly) {
DeoptProxyAnchorNode deoptEntry = graph.add(anchorOnly ? new DeoptProxyAnchorNode() : new DeoptEntryNode());
deoptEntry.setStateAfter(stateAfter);
insertProxies(deoptEntry, stateBuilder);
return deoptEntry;
}
}