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