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  */
  23 package org.graalvm.compiler.replacements.nodes;
  24 
  25 import static jdk.vm.ci.code.BytecodeFrame.isPlaceholderBci;
  26 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_UNKNOWN;
  27 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_UNKNOWN;
  28 
  29 import org.graalvm.compiler.api.replacements.MethodSubstitution;
  30 import org.graalvm.compiler.api.replacements.Snippet;
  31 import org.graalvm.compiler.core.common.type.StampPair;
  32 import org.graalvm.compiler.debug.DebugContext;
  33 import org.graalvm.compiler.debug.GraalError;
  34 import org.graalvm.compiler.graph.NodeClass;
  35 import org.graalvm.compiler.graph.NodeInputList;
  36 import org.graalvm.compiler.nodeinfo.NodeInfo;
  37 import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind;
  38 import org.graalvm.compiler.nodes.FixedWithNextNode;
  39 import org.graalvm.compiler.nodes.FrameState;
  40 import org.graalvm.compiler.nodes.InvokeNode;
  41 import org.graalvm.compiler.nodes.StructuredGraph;
  42 import org.graalvm.compiler.nodes.StructuredGraph.GuardsStage;
  43 import org.graalvm.compiler.nodes.ValueNode;
  44 import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
  45 import org.graalvm.compiler.nodes.spi.Lowerable;
  46 import org.graalvm.compiler.nodes.spi.LoweringTool;
  47 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
  48 import org.graalvm.compiler.phases.common.FrameStateAssignmentPhase;
  49 import org.graalvm.compiler.phases.common.GuardLoweringPhase;
  50 import org.graalvm.compiler.phases.common.LoweringPhase;
  51 import org.graalvm.compiler.phases.common.RemoveValueProxyPhase;
  52 import org.graalvm.compiler.phases.common.inlining.InliningUtil;
  53 import org.graalvm.compiler.phases.tiers.PhaseContext;
  54 
  55 import jdk.vm.ci.meta.JavaKind;
  56 import 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
  78 public 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 }