1/*
2 * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23package org.graalvm.compiler.replacements.nodes;
24
25import static jdk.vm.ci.code.BytecodeFrame.isPlaceholderBci;
26import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_UNKNOWN;
27import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_UNKNOWN;
28
29import org.graalvm.compiler.api.replacements.MethodSubstitution;
30import org.graalvm.compiler.api.replacements.Snippet;
31import org.graalvm.compiler.core.common.type.StampPair;
32import org.graalvm.compiler.debug.DebugContext;
33import org.graalvm.compiler.debug.GraalError;
34import org.graalvm.compiler.graph.NodeClass;
35import org.graalvm.compiler.graph.NodeInputList;
36import org.graalvm.compiler.nodeinfo.NodeInfo;
37import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind;
38import org.graalvm.compiler.nodes.FixedWithNextNode;
39import org.graalvm.compiler.nodes.FrameState;
40import org.graalvm.compiler.nodes.InvokeNode;
41import org.graalvm.compiler.nodes.StructuredGraph;
42import org.graalvm.compiler.nodes.StructuredGraph.GuardsStage;
43import org.graalvm.compiler.nodes.ValueNode;
44import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
45import org.graalvm.compiler.nodes.spi.Lowerable;
46import org.graalvm.compiler.nodes.spi.LoweringTool;
47import org.graalvm.compiler.phases.common.CanonicalizerPhase;
48import org.graalvm.compiler.phases.common.FrameStateAssignmentPhase;
49import org.graalvm.compiler.phases.common.GuardLoweringPhase;
50import org.graalvm.compiler.phases.common.LoweringPhase;
51import org.graalvm.compiler.phases.common.RemoveValueProxyPhase;
52import org.graalvm.compiler.phases.common.inlining.InliningUtil;
53import org.graalvm.compiler.phases.tiers.PhaseContext;
54
55import jdk.vm.ci.meta.JavaKind;
56import jdk.vm.ci.meta.ResolvedJavaMethod;
57
58/**
59 * Macro nodes can be used to temporarily replace an invoke. They can, for example, be used to
60 * implement constant folding for known JDK functions like {@link Class#isInterface()}.<br/>
61 * <br/>
62 * During lowering, multiple sources are queried in order to look for a replacement:
63 * <ul>
64 * <li>If {@link #getLoweredSnippetGraph(LoweringTool)} returns a non-null result, this graph is
65 * used as a replacement.</li>
66 * <li>If a {@link MethodSubstitution} for the target method is found, this substitution is used as
67 * a replacement.</li>
68 * <li>Otherwise, the macro node is replaced with an {@link InvokeNode}. Note that this is only
69 * possible if the macro node is a {@link MacroStateSplitNode}.</li>
70 * </ul>
71 */
72//@formatter:off
73@NodeInfo(cycles = CYCLES_UNKNOWN,
74          cyclesRationale = "If this node is not optimized away it will be lowered to a call, which we cannot estimate",
75          size = SIZE_UNKNOWN,
76          sizeRationale = "If this node is not optimized away it will be lowered to a call, which we cannot estimate")
77//@formatter:on
78public abstract class MacroNode extends FixedWithNextNode implements Lowerable {
79
80    public static final NodeClass<MacroNode> TYPE = NodeClass.create(MacroNode.class);
81    @Input protected NodeInputList<ValueNode> arguments;
82
83    protected final int bci;
84    protected final ResolvedJavaMethod targetMethod;
85    protected final StampPair returnStamp;
86    protected final InvokeKind invokeKind;
87
88    protected MacroNode(NodeClass<? extends MacroNode> c, InvokeKind invokeKind, ResolvedJavaMethod targetMethod, int bci, StampPair returnStamp, ValueNode... arguments) {
89        super(c, returnStamp.getTrustedStamp());
90        assert targetMethod.getSignature().getParameterCount(!targetMethod.isStatic()) == arguments.length;
91        this.arguments = new NodeInputList<>(this, arguments);
92        this.bci = bci;
93        this.targetMethod = targetMethod;
94        this.returnStamp = returnStamp;
95        this.invokeKind = invokeKind;
96        assert !isPlaceholderBci(bci);
97    }
98
99    public ValueNode getArgument(int i) {
100        return arguments.get(i);
101    }
102
103    public int getArgumentCount() {
104        return arguments.size();
105    }
106
107    public ValueNode[] toArgumentArray() {
108        return arguments.toArray(new ValueNode[0]);
109    }
110
111    public int getBci() {
112        return bci;
113    }
114
115    public ResolvedJavaMethod getTargetMethod() {
116        return targetMethod;
117    }
118
119    protected FrameState stateAfter() {
120        return null;
121    }
122
123    /**
124     * Gets a snippet to be used for lowering this macro node. The returned graph (if non-null) must
125     * have been {@linkplain #lowerReplacement(StructuredGraph, LoweringTool) lowered}.
126     */
127    @SuppressWarnings("unused")
128    protected StructuredGraph getLoweredSnippetGraph(LoweringTool tool) {
129        return null;
130    }
131
132    /**
133     * Applies {@linkplain LoweringPhase lowering} to a replacement graph.
134     *
135     * @param replacementGraph a replacement (i.e., snippet or method substitution) graph
136     */
137    @SuppressWarnings("try")
138    protected StructuredGraph lowerReplacement(final StructuredGraph replacementGraph, LoweringTool tool) {
139        final PhaseContext c = new PhaseContext(tool.getMetaAccess(), tool.getConstantReflection(), tool.getConstantFieldProvider(), tool.getLowerer(), tool.getReplacements(),
140                        tool.getStampProvider());
141        if (!graph().hasValueProxies()) {
142            new RemoveValueProxyPhase().apply(replacementGraph);
143        }
144        GuardsStage guardsStage = graph().getGuardsStage();
145        if (!guardsStage.allowsFloatingGuards()) {
146            new GuardLoweringPhase().apply(replacementGraph, null);
147            if (guardsStage.areFrameStatesAtDeopts()) {
148                new FrameStateAssignmentPhase().apply(replacementGraph);
149            }
150        }
151        DebugContext debug = replacementGraph.getDebug();
152        try (DebugContext.Scope s = debug.scope("LoweringSnippetTemplate", replacementGraph)) {
153            new LoweringPhase(new CanonicalizerPhase(), tool.getLoweringStage()).apply(replacementGraph, c);
154        } catch (Throwable e) {
155            throw debug.handle(e);
156        }
157        return replacementGraph;
158    }
159
160    @Override
161    public void lower(LoweringTool tool) {
162        StructuredGraph replacementGraph = getLoweredSnippetGraph(tool);
163
164        InvokeNode invoke = replaceWithInvoke();
165        assert invoke.verify();
166
167        if (replacementGraph != null) {
168            // Pull out the receiver null check so that a replaced
169            // receiver can be lowered if necessary
170            if (!targetMethod.isStatic()) {
171                ValueNode nonNullReceiver = InliningUtil.nonNullReceiver(invoke);
172                if (nonNullReceiver instanceof Lowerable) {
173                    ((Lowerable) nonNullReceiver).lower(tool);
174                }
175            }
176            InliningUtil.inline(invoke, replacementGraph, false, targetMethod);
177            replacementGraph.getDebug().dump(DebugContext.DETAILED_LEVEL, graph(), "After inlining replacement %s", replacementGraph);
178        } else {
179            if (isPlaceholderBci(invoke.bci())) {
180                throw new GraalError("%s: cannot lower to invoke with placeholder BCI: %s", graph(), this);
181            }
182
183            if (invoke.stateAfter() == null) {
184                ResolvedJavaMethod method = graph().method();
185                if (method.getAnnotation(MethodSubstitution.class) != null || method.getAnnotation(Snippet.class) != null) {
186                    // One cause for this is that a MacroNode is created for a method that
187                    // no longer needs a MacroNode. For example, Class.getComponentType()
188                    // only needs a MacroNode prior to JDK9 as it was given a non-native
189                    // implementation in JDK9.
190                    throw new GraalError("%s macro created for call to %s in %s must be lowerable to a snippet or intrinsic graph. " +
191                                    "Maybe a macro node is not needed for this method in the current JDK?", getClass().getSimpleName(), targetMethod.format("%h.%n(%p)"), graph());
192                }
193                throw new GraalError("%s: cannot lower to invoke without state: %s", graph(), this);
194            }
195            invoke.lower(tool);
196        }
197    }
198
199    public InvokeNode replaceWithInvoke() {
200        InvokeNode invoke = createInvoke();
201        graph().replaceFixedWithFixed(this, invoke);
202        return invoke;
203    }
204
205    protected InvokeNode createInvoke() {
206        MethodCallTargetNode callTarget = graph().add(new MethodCallTargetNode(invokeKind, targetMethod, arguments.toArray(new ValueNode[arguments.size()]), returnStamp, null));
207        InvokeNode invoke = graph().add(new InvokeNode(callTarget, bci));
208        if (stateAfter() != null) {
209            invoke.setStateAfter(stateAfter().duplicate());
210            if (getStackKind() != JavaKind.Void) {
211                invoke.stateAfter().replaceFirstInput(this, invoke);
212            }
213        }
214        return invoke;
215    }
216}
217