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}