001/* 002 * Copyright (c) 2014, 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; 024 025import static com.oracle.graal.graphbuilderconf.IntrinsicContext.CompilationContext.*; 026 027import java.lang.reflect.*; 028import java.util.*; 029 030import jdk.internal.jvmci.code.*; 031import jdk.internal.jvmci.meta.*; 032 033import com.oracle.graal.graph.*; 034import com.oracle.graal.graphbuilderconf.*; 035import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; 036import com.oracle.graal.java.*; 037import com.oracle.graal.nodes.*; 038import com.oracle.graal.nodes.CallTargetNode.InvokeKind; 039import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; 040import com.oracle.graal.nodes.calc.*; 041import com.oracle.graal.nodes.java.*; 042import com.oracle.graal.nodes.type.*; 043import com.oracle.graal.phases.*; 044import com.oracle.graal.phases.common.*; 045import com.oracle.graal.phases.common.DeadCodeEliminationPhase.Optionality; 046import com.oracle.graal.phases.common.inlining.*; 047import com.oracle.graal.phases.util.*; 048import com.oracle.graal.word.*; 049 050/** 051 * A utility for manually creating a graph. This will be expanded as necessary to support all 052 * subsystems that employ manual graph creation (as opposed to {@linkplain GraphBuilderPhase 053 * bytecode parsing} based graph creation). 054 */ 055public class GraphKit { 056 057 protected final Providers providers; 058 protected final StructuredGraph graph; 059 protected final WordTypes wordTypes; 060 protected final GraphBuilderConfiguration.Plugins graphBuilderPlugins; 061 protected FixedWithNextNode lastFixedNode; 062 063 private final List<Structure> structures; 064 065 abstract static class Structure { 066 } 067 068 public GraphKit(StructuredGraph graph, Providers providers, WordTypes wordTypes, GraphBuilderConfiguration.Plugins graphBuilderPlugins) { 069 this.providers = providers; 070 this.graph = graph; 071 this.wordTypes = wordTypes; 072 this.graphBuilderPlugins = graphBuilderPlugins; 073 this.lastFixedNode = graph.start(); 074 075 structures = new ArrayList<>(); 076 /* Add a dummy element, so that the access of the last element never leads to an exception. */ 077 structures.add(new Structure() { 078 }); 079 } 080 081 public StructuredGraph getGraph() { 082 return graph; 083 } 084 085 /** 086 * Ensures a floating node is added to or already present in the graph via {@link Graph#unique}. 087 * 088 * @return a node similar to {@code node} if one exists, otherwise {@code node} 089 */ 090 public <T extends FloatingNode> T unique(T node) { 091 return graph.unique(changeToWord(node)); 092 } 093 094 public <T extends ValueNode> T changeToWord(T node) { 095 if (wordTypes != null && wordTypes.isWord(node)) { 096 node.setStamp(wordTypes.getWordStamp(StampTool.typeOrNull(node))); 097 } 098 return node; 099 } 100 101 /** 102 * Appends a fixed node to the graph. 103 */ 104 public <T extends FixedNode> T append(T node) { 105 T result = graph.add(changeToWord(node)); 106 assert lastFixedNode != null; 107 assert result.predecessor() == null; 108 graph.addAfterFixed(lastFixedNode, result); 109 if (result instanceof FixedWithNextNode) { 110 lastFixedNode = (FixedWithNextNode) result; 111 } else { 112 lastFixedNode = null; 113 } 114 return result; 115 } 116 117 public InvokeNode createInvoke(Class<?> declaringClass, String name, ValueNode... args) { 118 return createInvoke(declaringClass, name, InvokeKind.Static, null, BytecodeFrame.UNKNOWN_BCI, args); 119 } 120 121 /** 122 * Creates and appends an {@link InvokeNode} for a call to a given method with a given set of 123 * arguments. The method is looked up via reflection based on the declaring class and name. 124 * 125 * @param declaringClass the class declaring the invoked method 126 * @param name the name of the invoked method 127 * @param args the arguments to the invocation 128 */ 129 public InvokeNode createInvoke(Class<?> declaringClass, String name, InvokeKind invokeKind, FrameStateBuilder frameStateBuilder, int bci, ValueNode... args) { 130 boolean isStatic = invokeKind == InvokeKind.Static; 131 ResolvedJavaMethod method = findMethod(declaringClass, name, isStatic); 132 return createInvoke(method, invokeKind, frameStateBuilder, bci, args); 133 } 134 135 public ResolvedJavaMethod findMethod(Class<?> declaringClass, String name, boolean isStatic) { 136 ResolvedJavaMethod method = null; 137 for (Method m : declaringClass.getDeclaredMethods()) { 138 if (Modifier.isStatic(m.getModifiers()) == isStatic && m.getName().equals(name)) { 139 assert method == null : "found more than one method in " + declaringClass + " named " + name; 140 method = providers.getMetaAccess().lookupJavaMethod(m); 141 } 142 } 143 assert method != null : "did not find method in " + declaringClass + " named " + name; 144 return method; 145 } 146 147 /** 148 * Creates and appends an {@link InvokeNode} for a call to a given method with a given set of 149 * arguments. 150 */ 151 public InvokeNode createInvoke(ResolvedJavaMethod method, InvokeKind invokeKind, FrameStateBuilder frameStateBuilder, int bci, ValueNode... args) { 152 assert method.isStatic() == (invokeKind == InvokeKind.Static); 153 Signature signature = method.getSignature(); 154 JavaType returnType = signature.getReturnType(null); 155 assert checkArgs(method, args); 156 MethodCallTargetNode callTarget = graph.add(createMethodCallTarget(invokeKind, method, args, returnType, bci)); 157 InvokeNode invoke = append(new InvokeNode(callTarget, bci)); 158 159 if (frameStateBuilder != null) { 160 if (invoke.getKind() != Kind.Void) { 161 frameStateBuilder.push(returnType.getKind(), invoke); 162 } 163 invoke.setStateAfter(frameStateBuilder.create(bci, invoke)); 164 if (invoke.getKind() != Kind.Void) { 165 frameStateBuilder.pop(returnType.getKind()); 166 } 167 } 168 return invoke; 169 } 170 171 protected MethodCallTargetNode createMethodCallTarget(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] args, JavaType returnType, @SuppressWarnings("unused") int bci) { 172 return new MethodCallTargetNode(invokeKind, targetMethod, args, returnType, null); 173 } 174 175 /** 176 * Determines if a given set of arguments is compatible with the signature of a given method. 177 * 178 * @return true if {@code args} are compatible with the signature if {@code method} 179 * @throws AssertionError if {@code args} are not compatible with the signature if 180 * {@code method} 181 */ 182 public boolean checkArgs(ResolvedJavaMethod method, ValueNode... args) { 183 Signature signature = method.getSignature(); 184 boolean isStatic = method.isStatic(); 185 if (signature.getParameterCount(!isStatic) != args.length) { 186 throw new AssertionError(graph + ": wrong number of arguments to " + method); 187 } 188 int argIndex = 0; 189 if (!isStatic) { 190 ResolvedJavaType expectedType = method.getDeclaringClass(); 191 Kind expected = wordTypes == null ? expectedType.getKind() : wordTypes.asKind(expectedType); 192 Kind actual = args[argIndex++].stamp().getStackKind(); 193 assert expected == actual : graph + ": wrong kind of value for receiver argument of call to " + method + " [" + actual + " != " + expected + "]"; 194 } 195 for (int i = 0; i != signature.getParameterCount(false); i++) { 196 JavaType expectedType = signature.getParameterType(i, method.getDeclaringClass()); 197 Kind expected = wordTypes == null ? expectedType.getKind().getStackKind() : wordTypes.asKind(expectedType).getStackKind(); 198 Kind actual = args[argIndex++].stamp().getStackKind(); 199 if (expected != actual) { 200 throw new AssertionError(graph + ": wrong kind of value for argument " + i + " of call to " + method + " [" + actual + " != " + expected + "]"); 201 } 202 } 203 return true; 204 } 205 206 /** 207 * Recursively {@linkplain #inline inlines} all invocations currently in the graph. 208 */ 209 public void inlineInvokes() { 210 while (!graph.getNodes().filter(InvokeNode.class).isEmpty()) { 211 for (InvokeNode invoke : graph.getNodes().filter(InvokeNode.class).snapshot()) { 212 inline(invoke); 213 } 214 } 215 216 // Clean up all code that is now dead after inlining. 217 new DeadCodeEliminationPhase().apply(graph); 218 } 219 220 /** 221 * Inlines a given invocation to a method. The graph of the inlined method is processed in the 222 * same manner as for snippets and method substitutions. 223 */ 224 public void inline(InvokeNode invoke) { 225 ResolvedJavaMethod method = ((MethodCallTargetNode) invoke.callTarget()).targetMethod(); 226 227 MetaAccessProvider metaAccess = providers.getMetaAccess(); 228 Plugins plugins = new Plugins(graphBuilderPlugins); 229 GraphBuilderConfiguration config = GraphBuilderConfiguration.getSnippetDefault(plugins); 230 231 StructuredGraph calleeGraph = new StructuredGraph(method, AllowAssumptions.NO); 232 IntrinsicContext initialReplacementContext = new IntrinsicContext(method, method, INLINE_AFTER_PARSING); 233 new GraphBuilderPhase.Instance(metaAccess, providers.getStampProvider(), providers.getConstantReflection(), config, OptimisticOptimizations.NONE, initialReplacementContext).apply(calleeGraph); 234 235 // Remove all frame states from inlinee 236 for (Node node : calleeGraph.getNodes()) { 237 if (node instanceof StateSplit) { 238 ((StateSplit) node).setStateAfter(null); 239 } 240 } 241 new DeadCodeEliminationPhase(Optionality.Required).apply(calleeGraph); 242 243 InliningUtil.inline(invoke, calleeGraph, false, null); 244 } 245 246 protected void pushStructure(Structure structure) { 247 structures.add(structure); 248 } 249 250 protected <T extends Structure> T getTopStructure(Class<T> expectedClass) { 251 return expectedClass.cast(structures.get(structures.size() - 1)); 252 } 253 254 protected void popStructure() { 255 structures.remove(structures.size() - 1); 256 } 257 258 protected enum IfState { 259 CONDITION, 260 THEN_PART, 261 ELSE_PART, 262 FINISHED 263 } 264 265 static class IfStructure extends Structure { 266 protected IfState state; 267 protected FixedNode thenPart; 268 protected FixedNode elsePart; 269 } 270 271 /** 272 * Starts an if-block. This call can be followed by a call to {@link #thenPart} to start 273 * emitting the code executed when the condition hold; and a call to {@link #elsePart} to start 274 * emititng the code when the condition does not hold. It must be followed by a call to 275 * {@link #endIf} to close the if-block. 276 * 277 * @param condition The condition for the if-block 278 * @param trueProbability The estimated probability the the condition is true 279 */ 280 public void startIf(LogicNode condition, double trueProbability) { 281 AbstractBeginNode thenSuccessor = graph.add(new BeginNode()); 282 AbstractBeginNode elseSuccessor = graph.add(new BeginNode()); 283 append(new IfNode(condition, thenSuccessor, elseSuccessor, trueProbability)); 284 lastFixedNode = null; 285 286 IfStructure s = new IfStructure(); 287 s.state = IfState.CONDITION; 288 s.thenPart = thenSuccessor; 289 s.elsePart = elseSuccessor; 290 pushStructure(s); 291 } 292 293 private IfStructure saveLastNode() { 294 IfStructure s = getTopStructure(IfStructure.class); 295 switch (s.state) { 296 case CONDITION: 297 assert lastFixedNode == null; 298 break; 299 case THEN_PART: 300 s.thenPart = lastFixedNode; 301 break; 302 case ELSE_PART: 303 s.elsePart = lastFixedNode; 304 break; 305 case FINISHED: 306 assert false; 307 break; 308 } 309 lastFixedNode = null; 310 return s; 311 } 312 313 public void thenPart() { 314 IfStructure s = saveLastNode(); 315 lastFixedNode = (FixedWithNextNode) s.thenPart; 316 s.state = IfState.THEN_PART; 317 } 318 319 public void elsePart() { 320 IfStructure s = saveLastNode(); 321 lastFixedNode = (FixedWithNextNode) s.elsePart; 322 s.state = IfState.ELSE_PART; 323 } 324 325 public void endIf() { 326 IfStructure s = saveLastNode(); 327 328 FixedWithNextNode thenPart = s.thenPart instanceof FixedWithNextNode ? (FixedWithNextNode) s.thenPart : null; 329 FixedWithNextNode elsePart = s.elsePart instanceof FixedWithNextNode ? (FixedWithNextNode) s.elsePart : null; 330 331 if (thenPart != null && elsePart != null) { 332 /* Both parts are alive, we need a real merge. */ 333 EndNode thenEnd = graph.add(new EndNode()); 334 graph.addAfterFixed(thenPart, thenEnd); 335 EndNode elseEnd = graph.add(new EndNode()); 336 graph.addAfterFixed(elsePart, elseEnd); 337 338 AbstractMergeNode merge = graph.add(new MergeNode()); 339 merge.addForwardEnd(thenEnd); 340 merge.addForwardEnd(elseEnd); 341 342 lastFixedNode = merge; 343 344 } else if (thenPart != null) { 345 /* elsePart ended with a control sink, so we can continue with thenPart. */ 346 lastFixedNode = thenPart; 347 348 } else if (elsePart != null) { 349 /* thenPart ended with a control sink, so we can continue with elsePart. */ 350 lastFixedNode = elsePart; 351 352 } else { 353 /* Both parts ended with a control sink, so no nodes can be added after the if. */ 354 assert lastFixedNode == null; 355 } 356 s.state = IfState.FINISHED; 357 popStructure(); 358 } 359}