001/*
002 * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.replacements.nodes;
024
025import static jdk.internal.jvmci.code.BytecodeFrame.*;
026import jdk.internal.jvmci.common.*;
027import com.oracle.graal.debug.*;
028import com.oracle.graal.debug.Debug.*;
029import jdk.internal.jvmci.meta.*;
030
031import com.oracle.graal.api.replacements.*;
032import com.oracle.graal.compiler.common.type.*;
033import com.oracle.graal.graph.*;
034import com.oracle.graal.nodeinfo.*;
035import com.oracle.graal.nodes.CallTargetNode.InvokeKind;
036import com.oracle.graal.nodes.*;
037import com.oracle.graal.nodes.StructuredGraph.GuardsStage;
038import com.oracle.graal.nodes.java.*;
039import com.oracle.graal.nodes.spi.*;
040import com.oracle.graal.phases.common.*;
041import com.oracle.graal.phases.common.inlining.*;
042import com.oracle.graal.phases.tiers.*;
043import com.oracle.graal.replacements.*;
044
045/**
046 * Macro nodes can be used to temporarily replace an invoke. They can, for example, be used to
047 * implement constant folding for known JDK functions like {@link Class#isInterface()}.<br/>
048 * <br/>
049 * During lowering, multiple sources are queried in order to look for a replacement:
050 * <ul>
051 * <li>If {@link #getLoweredSnippetGraph(LoweringTool)} returns a non-null result, this graph is
052 * used as a replacement.</li>
053 * <li>If a {@link MethodSubstitution} for the target method is found, this substitution is used as
054 * a replacement.</li>
055 * <li>Otherwise, the macro node is replaced with an {@link InvokeNode}. Note that this is only
056 * possible if the macro node is a {@link MacroStateSplitNode}.</li>
057 * </ul>
058 */
059@NodeInfo
060public abstract class MacroNode extends FixedWithNextNode implements Lowerable {
061
062    public static final NodeClass<MacroNode> TYPE = NodeClass.create(MacroNode.class);
063    @Input protected NodeInputList<ValueNode> arguments;
064
065    protected final int bci;
066    protected final ResolvedJavaMethod targetMethod;
067    protected final JavaType returnType;
068    protected final InvokeKind invokeKind;
069
070    protected MacroNode(NodeClass<? extends MacroNode> c, InvokeKind invokeKind, ResolvedJavaMethod targetMethod, int bci, JavaType returnType, ValueNode... arguments) {
071        super(c, returnStamp(returnType));
072        assert targetMethod.getSignature().getParameterCount(!targetMethod.isStatic()) == arguments.length;
073        this.arguments = new NodeInputList<>(this, arguments);
074        this.bci = bci;
075        this.targetMethod = targetMethod;
076        this.returnType = returnType;
077        this.invokeKind = invokeKind;
078        assert !isPlaceholderBci(bci);
079    }
080
081    private static Stamp returnStamp(JavaType returnType) {
082        Kind kind = returnType.getKind();
083        if (kind == Kind.Object) {
084            return StampFactory.declared((ResolvedJavaType) returnType);
085        } else {
086            return StampFactory.forKind(kind);
087        }
088    }
089
090    public int getBci() {
091        return bci;
092    }
093
094    public ResolvedJavaMethod getTargetMethod() {
095        return targetMethod;
096    }
097
098    public JavaType getReturnType() {
099        return returnType;
100    }
101
102    protected FrameState stateAfter() {
103        return null;
104    }
105
106    /**
107     * Gets a snippet to be used for lowering this macro node. The returned graph (if non-null) must
108     * have been {@linkplain #lowerReplacement(StructuredGraph, LoweringTool) lowered}.
109     */
110    @SuppressWarnings("unused")
111    protected StructuredGraph getLoweredSnippetGraph(LoweringTool tool) {
112        return null;
113    }
114
115    /**
116     * Gets a normal method substitution to be used for lowering this macro node. This is only
117     * called if {@link #getLoweredSnippetGraph(LoweringTool)} returns null. The returned graph (if
118     * non-null) must have been {@linkplain #lowerReplacement(StructuredGraph, LoweringTool)
119     * lowered}.
120     */
121    protected StructuredGraph getLoweredSubstitutionGraph(LoweringTool tool) {
122        StructuredGraph methodSubstitution = tool.getReplacements().getSubstitution(getTargetMethod(), true, bci);
123        if (methodSubstitution != null) {
124            methodSubstitution = (StructuredGraph) methodSubstitution.copy();
125            return lowerReplacement(methodSubstitution, tool);
126        }
127        return null;
128    }
129
130    /**
131     * Applies {@linkplain LoweringPhase lowering} to a replacement graph.
132     *
133     * @param replacementGraph a replacement (i.e., snippet or method substitution) graph
134     */
135    protected StructuredGraph lowerReplacement(final StructuredGraph replacementGraph, LoweringTool tool) {
136        final PhaseContext c = new PhaseContext(tool.getMetaAccess(), tool.getConstantReflection(), tool.getLowerer(), tool.getReplacements(), tool.getStampProvider());
137        if (!graph().hasValueProxies()) {
138            new RemoveValueProxyPhase().apply(replacementGraph);
139        }
140        GuardsStage guardsStage = graph().getGuardsStage();
141        if (!guardsStage.allowsFloatingGuards()) {
142            new GuardLoweringPhase().apply(replacementGraph, null);
143            if (guardsStage.areFrameStatesAtDeopts()) {
144                new FrameStateAssignmentPhase().apply(replacementGraph);
145            }
146        }
147        try (Scope s = Debug.scope("LoweringSnippetTemplate", replacementGraph)) {
148            new LoweringPhase(new CanonicalizerPhase(), tool.getLoweringStage()).apply(replacementGraph, c);
149        } catch (Throwable e) {
150            throw Debug.handle(e);
151        }
152        return replacementGraph;
153    }
154
155    @Override
156    public void lower(LoweringTool tool) {
157        StructuredGraph replacementGraph = getLoweredSnippetGraph(tool);
158        if (replacementGraph == null) {
159            replacementGraph = getLoweredSubstitutionGraph(tool);
160        }
161
162        InvokeNode invoke = replaceWithInvoke();
163        assert invoke.verify();
164
165        if (replacementGraph != null) {
166            // Pull out the receiver null check so that a replaced
167            // receiver can be lowered if necessary
168            if (!targetMethod.isStatic()) {
169                ValueNode nonNullReceiver = InliningUtil.nonNullReceiver(invoke);
170                if (nonNullReceiver instanceof Lowerable) {
171                    ((Lowerable) nonNullReceiver).lower(tool);
172                }
173            }
174            InliningUtil.inline(invoke, replacementGraph, false, null);
175            Debug.dump(graph(), "After inlining replacement %s", replacementGraph);
176        } else {
177            if (isPlaceholderBci(invoke.bci())) {
178                throw new JVMCIError("%s: cannot lower to invoke with placeholder BCI: %s", graph(), this);
179            }
180
181            if (invoke.stateAfter() == null) {
182                ResolvedJavaMethod method = graph().method();
183                if (method.getAnnotation(MethodSubstitution.class) != null || method.getAnnotation(Snippet.class) != null) {
184                    // One cause for this is that a MacroNode is created for a method that
185                    // no longer needs a MacroNode. For example, Class.getComponentType()
186                    // only needs a MacroNode prior to JDK9 as it was given a non-native
187                    // implementation in JDK9.
188                    throw new JVMCIError("%s macro created for call to %s in %s must be lowerable to a snippet or intrinsic graph. "
189                                    + "Maybe a macro node is not needed for this method in the current JDK?", getClass().getSimpleName(), targetMethod.format("%h.%n(%p)"), graph());
190                }
191                throw new JVMCIError("%s: cannot lower to invoke without state: %s", graph(), this);
192            }
193            invoke.lower(tool);
194        }
195    }
196
197    protected InvokeNode replaceWithInvoke() {
198        InvokeNode invoke = createInvoke();
199        graph().replaceFixedWithFixed(this, invoke);
200        return invoke;
201    }
202
203    protected InvokeNode createInvoke() {
204        MethodCallTargetNode callTarget = graph().add(new MethodCallTargetNode(invokeKind, targetMethod, arguments.toArray(new ValueNode[arguments.size()]), returnType, null));
205        InvokeNode invoke = graph().add(new InvokeNode(callTarget, bci));
206        if (stateAfter() != null) {
207            invoke.setStateAfter(stateAfter().duplicate());
208            if (getKind() != Kind.Void) {
209                invoke.stateAfter().replaceFirstInput(this, invoke);
210            }
211        }
212        return invoke;
213    }
214}