package org.graalvm.compiler.replacements.nodes;
import static jdk.vm.ci.code.BytecodeFrame.isPlaceholderBci;
import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_UNKNOWN;
import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_UNKNOWN;
import org.graalvm.compiler.api.replacements.MethodSubstitution;
import org.graalvm.compiler.api.replacements.Snippet;
import org.graalvm.compiler.core.common.type.StampPair;
import org.graalvm.compiler.debug.DebugCloseable;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.graph.NodeClass;
import org.graalvm.compiler.graph.NodeInputList;
import org.graalvm.compiler.nodeinfo.NodeInfo;
import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind;
import org.graalvm.compiler.nodes.FixedNode;
import org.graalvm.compiler.nodes.Invokable;
import org.graalvm.compiler.nodes.FixedWithNextNode;
import org.graalvm.compiler.nodes.FrameState;
import org.graalvm.compiler.nodes.InvokeNode;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.StructuredGraph.GuardsStage;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
import org.graalvm.compiler.nodes.spi.Lowerable;
import org.graalvm.compiler.nodes.spi.LoweringTool;
import org.graalvm.compiler.phases.common.CanonicalizerPhase;
import org.graalvm.compiler.phases.common.FrameStateAssignmentPhase;
import org.graalvm.compiler.phases.common.GuardLoweringPhase;
import org.graalvm.compiler.phases.common.LoweringPhase;
import org.graalvm.compiler.phases.common.RemoveValueProxyPhase;
import org.graalvm.compiler.phases.common.inlining.InliningUtil;
import org.graalvm.compiler.phases.tiers.PhaseContext;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.ResolvedJavaMethod;
@NodeInfo(cycles = CYCLES_UNKNOWN,
cyclesRationale = "If this node is not optimized away it will be lowered to a call, which we cannot estimate",
size = SIZE_UNKNOWN,
sizeRationale = "If this node is not optimized away it will be lowered to a call, which we cannot estimate")
public abstract class MacroNode extends FixedWithNextNode implements Lowerable, Invokable {
public static final NodeClass<MacroNode> TYPE = NodeClass.create(MacroNode.class);
@Input protected NodeInputList<ValueNode> arguments;
protected final int bci;
protected final ResolvedJavaMethod targetMethod;
protected final StampPair returnStamp;
protected final InvokeKind invokeKind;
protected MacroNode(NodeClass<? extends MacroNode> c, InvokeKind invokeKind, ResolvedJavaMethod targetMethod, int bci, StampPair returnStamp, ValueNode... arguments) {
super(c, returnStamp.getTrustedStamp());
assert targetMethod.getSignature().getParameterCount(!targetMethod.isStatic()) == arguments.length;
this.arguments = new NodeInputList<>(this, arguments);
this.bci = bci;
this.targetMethod = targetMethod;
this.returnStamp = returnStamp;
this.invokeKind = invokeKind;
assert !isPlaceholderBci(bci);
}
public ValueNode getArgument(int i) {
return arguments.get(i);
}
public int getArgumentCount() {
return arguments.size();
}
public ValueNode[] toArgumentArray() {
return arguments.toArray(new ValueNode[0]);
}
@Override
public int bci() {
return bci;
}
@Override
public ResolvedJavaMethod getTargetMethod() {
return targetMethod;
}
protected FrameState stateAfter() {
return null;
}
@Override
protected void afterClone(Node other) {
updateInliningLogAfterClone(other);
}
@Override
public FixedNode asFixedNode() {
return this;
}
@SuppressWarnings("unused")
protected StructuredGraph getLoweredSnippetGraph(LoweringTool tool) {
return null;
}
@SuppressWarnings("try")
protected StructuredGraph lowerReplacement(final StructuredGraph replacementGraph, LoweringTool tool) {
final PhaseContext c = new PhaseContext(tool.getMetaAccess(), tool.getConstantReflection(), tool.getConstantFieldProvider(), tool.getLowerer(), tool.getReplacements(),
tool.getStampProvider());
if (!graph().hasValueProxies()) {
new RemoveValueProxyPhase().apply(replacementGraph);
}
GuardsStage guardsStage = graph().getGuardsStage();
if (!guardsStage.allowsFloatingGuards()) {
new GuardLoweringPhase().apply(replacementGraph, null);
if (guardsStage.areFrameStatesAtDeopts()) {
new FrameStateAssignmentPhase().apply(replacementGraph);
}
}
DebugContext debug = replacementGraph.getDebug();
try (DebugContext.Scope s = debug.scope("LoweringSnippetTemplate", replacementGraph)) {
new LoweringPhase(new CanonicalizerPhase(), tool.getLoweringStage()).apply(replacementGraph, c);
} catch (Throwable e) {
throw debug.handle(e);
}
return replacementGraph;
}
@Override
public void lower(LoweringTool tool) {
StructuredGraph replacementGraph = getLoweredSnippetGraph(tool);
InvokeNode invoke = replaceWithInvoke();
assert invoke.verify();
if (replacementGraph != null) {
if (!targetMethod.isStatic()) {
ValueNode nonNullReceiver = InliningUtil.nonNullReceiver(invoke);
if (nonNullReceiver instanceof Lowerable) {
((Lowerable) nonNullReceiver).lower(tool);
}
}
InliningUtil.inline(invoke, replacementGraph, false, targetMethod, "Replace with graph.", "LoweringPhase");
replacementGraph.getDebug().dump(DebugContext.DETAILED_LEVEL, graph(), "After inlining replacement %s", replacementGraph);
} else {
if (isPlaceholderBci(invoke.bci())) {
throw new GraalError("%s: cannot lower to invoke with placeholder BCI: %s", graph(), this);
}
if (invoke.stateAfter() == null) {
ResolvedJavaMethod method = graph().method();
if (method.getAnnotation(MethodSubstitution.class) != null || method.getAnnotation(Snippet.class) != null) {
throw new GraalError("%s macro created for call to %s in %s must be lowerable to a snippet or intrinsic graph. " +
"Maybe a macro node is not needed for this method in the current JDK?", getClass().getSimpleName(), targetMethod.format("%h.%n(%p)"), graph());
}
throw new GraalError("%s: cannot lower to invoke without state: %s", graph(), this);
}
invoke.lower(tool);
}
}
@SuppressWarnings("try")
public InvokeNode replaceWithInvoke() {
try (DebugCloseable context = withNodeSourcePosition()) {
InvokeNode invoke = createInvoke();
graph().replaceFixedWithFixed(this, invoke);
return invoke;
}
}
protected InvokeNode createInvoke() {
MethodCallTargetNode callTarget = graph().add(new MethodCallTargetNode(invokeKind, targetMethod, arguments.toArray(new ValueNode[arguments.size()]), returnStamp, null));
InvokeNode invoke = graph().add(new InvokeNode(callTarget, bci));
if (stateAfter() != null) {
invoke.setStateAfter(stateAfter().duplicate());
if (getStackKind() != JavaKind.Void) {
invoke.stateAfter().replaceFirstInput(this, invoke);
}
}
return invoke;
}
}