1 /* 2 * Copyright (c) 2013, 2019, 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 24 25 package org.graalvm.compiler.replacements.nodes; 26 27 import static jdk.vm.ci.code.BytecodeFrame.isPlaceholderBci; 28 import static jdk.vm.ci.services.Services.IS_IN_NATIVE_IMAGE; 29 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_UNKNOWN; 30 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_UNKNOWN; 31 32 import org.graalvm.compiler.api.replacements.MethodSubstitution; 33 import org.graalvm.compiler.api.replacements.Snippet; 34 import org.graalvm.compiler.core.common.type.StampPair; 35 import org.graalvm.compiler.debug.DebugCloseable; 36 import org.graalvm.compiler.debug.DebugContext; 37 import org.graalvm.compiler.debug.GraalError; 38 import org.graalvm.compiler.graph.Node; 39 import org.graalvm.compiler.graph.NodeClass; 40 import org.graalvm.compiler.graph.NodeInputList; 41 import org.graalvm.compiler.nodeinfo.NodeInfo; 42 import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind; 43 import org.graalvm.compiler.nodes.FixedNode; 44 import org.graalvm.compiler.nodes.FixedWithNextNode; 45 import org.graalvm.compiler.nodes.FrameState; 46 import org.graalvm.compiler.nodes.Invokable; 47 import org.graalvm.compiler.nodes.InvokeNode; 48 import org.graalvm.compiler.nodes.StructuredGraph; 49 import org.graalvm.compiler.nodes.StructuredGraph.GuardsStage; 50 import org.graalvm.compiler.nodes.ValueNode; 51 import org.graalvm.compiler.nodes.java.MethodCallTargetNode; 52 import org.graalvm.compiler.nodes.spi.CoreProviders; 53 import org.graalvm.compiler.nodes.spi.Lowerable; 54 import org.graalvm.compiler.nodes.spi.LoweringTool; 55 import org.graalvm.compiler.phases.common.CanonicalizerPhase; 56 import org.graalvm.compiler.phases.common.FrameStateAssignmentPhase; 57 import org.graalvm.compiler.phases.common.GuardLoweringPhase; 58 import org.graalvm.compiler.phases.common.LoweringPhase; 59 import org.graalvm.compiler.phases.common.RemoveValueProxyPhase; 60 import org.graalvm.compiler.phases.common.inlining.InliningUtil; 61 import jdk.internal.vm.compiler.word.LocationIdentity; 62 63 import jdk.vm.ci.meta.JavaKind; 64 import jdk.vm.ci.meta.ResolvedJavaMethod; 65 66 /** 67 * Macro nodes can be used to temporarily replace an invoke. They can, for example, be used to 68 * implement constant folding for known JDK functions like {@link Class#isInterface()}.<br/> 69 * <br/> 70 * During lowering, multiple sources are queried in order to look for a replacement: 71 * <ul> 72 * <li>If {@link #getLoweredSnippetGraph(LoweringTool)} returns a non-null result, this graph is 73 * used as a replacement.</li> 74 * <li>If a {@link MethodSubstitution} for the target method is found, this substitution is used as 75 * a replacement.</li> 76 * <li>Otherwise, the macro node is replaced with an {@link InvokeNode}. Note that this is only 77 * possible if the macro node is a {@link MacroStateSplitNode}.</li> 78 * </ul> 79 */ 80 //@formatter:off 81 @NodeInfo(cycles = CYCLES_UNKNOWN, 82 cyclesRationale = "If this node is not optimized away it will be lowered to a call, which we cannot estimate", 83 size = SIZE_UNKNOWN, 84 sizeRationale = "If this node is not optimized away it will be lowered to a call, which we cannot estimate") 85 //@formatter:on 86 public abstract class MacroNode extends FixedWithNextNode implements Lowerable, Invokable { 87 88 public static final NodeClass<MacroNode> TYPE = NodeClass.create(MacroNode.class); 89 @Input protected NodeInputList<ValueNode> arguments; 90 91 protected final int bci; 92 protected final ResolvedJavaMethod targetMethod; 93 protected final InvokeKind invokeKind; 94 protected final StampPair returnStamp; 95 96 protected MacroNode(NodeClass<? extends MacroNode> c, InvokeKind invokeKind, ResolvedJavaMethod targetMethod, int bci, StampPair returnStamp, ValueNode... arguments) { 97 super(c, returnStamp != null ? returnStamp.getTrustedStamp() : null); 98 assertArgumentCount(targetMethod, arguments); 99 this.arguments = new NodeInputList<>(this, arguments); 100 this.bci = bci; 101 this.targetMethod = targetMethod; 102 this.returnStamp = returnStamp; 103 this.invokeKind = invokeKind; 104 assert !isPlaceholderBci(bci); 105 } 106 107 protected void assertArgumentCount(ResolvedJavaMethod method, ValueNode... args) { 108 assert method.getSignature().getParameterCount(!method.isStatic()) == args.length; 109 } 110 111 public ValueNode getArgument(int i) { 112 return arguments.get(i); 113 } 114 115 public int getArgumentCount() { 116 return arguments.size(); 117 } 118 119 public ValueNode[] toArgumentArray() { 120 return arguments.toArray(new ValueNode[0]); 121 } 122 123 @Override 124 public int bci() { 125 return bci; 126 } 127 128 @Override 129 public ResolvedJavaMethod getTargetMethod() { 130 return targetMethod; 131 } 132 133 protected FrameState stateAfter() { 134 return null; 135 } 136 137 @Override 138 protected void afterClone(Node other) { 139 updateInliningLogAfterClone(other); 140 } 141 142 @Override 143 public FixedNode asFixedNode() { 144 return this; 145 } 146 147 /** 148 * Gets a snippet to be used for lowering this macro node. The returned graph (if non-null) must 149 * have been {@linkplain #lowerReplacement(StructuredGraph, LoweringTool) lowered}. 150 */ 151 @SuppressWarnings("unused") 152 protected StructuredGraph getLoweredSnippetGraph(LoweringTool tool) { 153 return null; 154 } 155 156 /** 157 * Applies {@linkplain LoweringPhase lowering} to a replacement graph. 158 * 159 * @param replacementGraph a replacement (i.e., snippet or method substitution) graph 160 */ 161 @SuppressWarnings("try") 162 protected StructuredGraph lowerReplacement(final StructuredGraph replacementGraph, LoweringTool tool) { 163 final CoreProviders c = tool.getProviders(); 164 if (!graph().hasValueProxies()) { 165 new RemoveValueProxyPhase().apply(replacementGraph); 166 } 167 GuardsStage guardsStage = graph().getGuardsStage(); 168 if (!guardsStage.allowsFloatingGuards()) { 169 new GuardLoweringPhase().apply(replacementGraph, null); 170 if (guardsStage.areFrameStatesAtDeopts()) { 171 new FrameStateAssignmentPhase().apply(replacementGraph); 172 } 173 } 174 DebugContext debug = replacementGraph.getDebug(); 175 try (DebugContext.Scope s = debug.scope("LoweringSnippetTemplate", replacementGraph)) { 176 new LoweringPhase(new CanonicalizerPhase(), tool.getLoweringStage()).apply(replacementGraph, c); 177 } catch (Throwable e) { 178 throw debug.handle(e); 179 } 180 return replacementGraph; 181 } 182 183 @Override 184 public void lower(LoweringTool tool) { 185 StructuredGraph replacementGraph = getLoweredSnippetGraph(tool); 186 187 InvokeNode invoke = replaceWithInvoke(); 188 assert invoke.verify(); 189 190 if (replacementGraph != null) { 191 // Pull out the receiver null check so that a replaced 192 // receiver can be lowered if necessary 193 if (!targetMethod.isStatic()) { 194 ValueNode nonNullReceiver = InliningUtil.nonNullReceiver(invoke); 195 if (nonNullReceiver instanceof Lowerable) { 196 ((Lowerable) nonNullReceiver).lower(tool); 197 } 198 } 199 InliningUtil.inline(invoke, replacementGraph, false, targetMethod, "Replace with graph.", "LoweringPhase"); 200 replacementGraph.getDebug().dump(DebugContext.DETAILED_LEVEL, graph(), "After inlining replacement %s", replacementGraph); 201 } else { 202 if (isPlaceholderBci(invoke.bci())) { 203 throw new GraalError("%s: cannot lower to invoke with placeholder BCI: %s", graph(), this); 204 } 205 206 if (invoke.stateAfter() == null) { 207 ResolvedJavaMethod method = graph().method(); 208 if (!IS_IN_NATIVE_IMAGE) { 209 if (method.getAnnotation(MethodSubstitution.class) != null || method.getAnnotation(Snippet.class) != null) { 210 // One cause for this is that a MacroNode is created for a method that 211 // no longer needs a MacroNode. For example, Class.getComponentType() 212 // only needs a MacroNode prior to JDK9 as it was given a non-native 213 // implementation in JDK9. 214 throw new GraalError("%s macro created for call to %s in %s must be lowerable to a snippet or intrinsic graph. " + 215 "Maybe a macro node is not needed for this method in the current JDK?", getClass().getSimpleName(), targetMethod.format("%h.%n(%p)"), graph()); 216 } 217 } 218 throw new GraalError("%s: cannot lower to invoke without state: %s", graph(), this); 219 } 220 invoke.lower(tool); 221 } 222 } 223 224 @SuppressWarnings("try") 225 public InvokeNode replaceWithInvoke() { 226 try (DebugCloseable context = withNodeSourcePosition()) { 227 InvokeNode invoke = createInvoke(); 228 graph().replaceFixedWithFixed(this, invoke); 229 return invoke; 230 } 231 } 232 233 public LocationIdentity getLocationIdentity() { 234 return LocationIdentity.any(); 235 } 236 237 protected InvokeNode createInvoke() { 238 MethodCallTargetNode callTarget = graph().add(new MethodCallTargetNode(invokeKind, targetMethod, arguments.toArray(new ValueNode[arguments.size()]), returnStamp, null)); 239 InvokeNode invoke = graph().add(new InvokeNode(callTarget, bci, getLocationIdentity())); 240 if (stateAfter() != null) { 241 invoke.setStateAfter(stateAfter().duplicate()); 242 if (getStackKind() != JavaKind.Void) { 243 invoke.stateAfter().replaceFirstInput(this, invoke); 244 } 245 } 246 return invoke; 247 } 248 }