GraphKit.java revision 13083:b9a173f12fe6
1/* 2 * Copyright (c) 2014, 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 */ 23package org.graalvm.compiler.replacements; 24 25import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING; 26 27import java.lang.reflect.Method; 28import java.lang.reflect.Modifier; 29import java.util.ArrayList; 30import java.util.List; 31 32import org.graalvm.compiler.core.common.spi.ConstantFieldProvider; 33import org.graalvm.compiler.core.common.type.StampFactory; 34import org.graalvm.compiler.core.common.type.StampPair; 35import org.graalvm.compiler.graph.Graph; 36import org.graalvm.compiler.graph.Node.ValueNumberable; 37import org.graalvm.compiler.java.FrameStateBuilder; 38import org.graalvm.compiler.java.GraphBuilderPhase; 39import org.graalvm.compiler.nodes.AbstractBeginNode; 40import org.graalvm.compiler.nodes.AbstractMergeNode; 41import org.graalvm.compiler.nodes.BeginNode; 42import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind; 43import org.graalvm.compiler.nodes.EndNode; 44import org.graalvm.compiler.nodes.FixedNode; 45import org.graalvm.compiler.nodes.FixedWithNextNode; 46import org.graalvm.compiler.nodes.IfNode; 47import org.graalvm.compiler.nodes.InvokeNode; 48import org.graalvm.compiler.nodes.LogicNode; 49import org.graalvm.compiler.nodes.MergeNode; 50import org.graalvm.compiler.nodes.StructuredGraph; 51import org.graalvm.compiler.nodes.ValueNode; 52import org.graalvm.compiler.nodes.calc.FloatingNode; 53import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; 54import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; 55import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderTool; 56import org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext; 57import org.graalvm.compiler.nodes.java.MethodCallTargetNode; 58import org.graalvm.compiler.nodes.spi.StampProvider; 59import org.graalvm.compiler.nodes.type.StampTool; 60import org.graalvm.compiler.phases.OptimisticOptimizations; 61import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase; 62import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase.Optionality; 63import org.graalvm.compiler.phases.common.inlining.InliningUtil; 64import org.graalvm.compiler.phases.util.Providers; 65import org.graalvm.compiler.word.WordTypes; 66 67import jdk.vm.ci.code.BytecodeFrame; 68import jdk.vm.ci.meta.ConstantReflectionProvider; 69import jdk.vm.ci.meta.JavaKind; 70import jdk.vm.ci.meta.JavaType; 71import jdk.vm.ci.meta.MetaAccessProvider; 72import jdk.vm.ci.meta.ResolvedJavaMethod; 73import jdk.vm.ci.meta.ResolvedJavaType; 74import jdk.vm.ci.meta.Signature; 75 76/** 77 * A utility for manually creating a graph. This will be expanded as necessary to support all 78 * subsystems that employ manual graph creation (as opposed to {@linkplain GraphBuilderPhase 79 * bytecode parsing} based graph creation). 80 */ 81public class GraphKit implements GraphBuilderTool { 82 83 protected final Providers providers; 84 protected final StructuredGraph graph; 85 protected final WordTypes wordTypes; 86 protected final GraphBuilderConfiguration.Plugins graphBuilderPlugins; 87 protected FixedWithNextNode lastFixedNode; 88 89 private final List<Structure> structures; 90 91 protected abstract static class Structure { 92 } 93 94 public GraphKit(StructuredGraph graph, Providers providers, WordTypes wordTypes, GraphBuilderConfiguration.Plugins graphBuilderPlugins) { 95 this.providers = providers; 96 this.graph = graph; 97 this.wordTypes = wordTypes; 98 this.graphBuilderPlugins = graphBuilderPlugins; 99 this.lastFixedNode = graph.start(); 100 101 structures = new ArrayList<>(); 102 /* 103 * Add a dummy element, so that the access of the last element never leads to an exception. 104 */ 105 structures.add(new Structure() { 106 }); 107 } 108 109 @Override 110 public StructuredGraph getGraph() { 111 return graph; 112 } 113 114 @Override 115 public ConstantReflectionProvider getConstantReflection() { 116 return providers.getConstantReflection(); 117 } 118 119 @Override 120 public ConstantFieldProvider getConstantFieldProvider() { 121 return providers.getConstantFieldProvider(); 122 } 123 124 @Override 125 public MetaAccessProvider getMetaAccess() { 126 return providers.getMetaAccess(); 127 } 128 129 @Override 130 public StampProvider getStampProvider() { 131 return providers.getStampProvider(); 132 } 133 134 @Override 135 public boolean parsingIntrinsic() { 136 return true; 137 } 138 139 /** 140 * Ensures a floating node is added to or already present in the graph via {@link Graph#unique}. 141 * 142 * @return a node similar to {@code node} if one exists, otherwise {@code node} 143 */ 144 public <T extends FloatingNode & ValueNumberable> T unique(T node) { 145 return graph.unique(changeToWord(node)); 146 } 147 148 public <T extends ValueNode> T add(T node) { 149 return graph.add(changeToWord(node)); 150 } 151 152 public <T extends ValueNode> T changeToWord(T node) { 153 if (wordTypes != null && wordTypes.isWord(node)) { 154 node.setStamp(wordTypes.getWordStamp(StampTool.typeOrNull(node))); 155 } 156 return node; 157 } 158 159 @Override 160 public <T extends ValueNode> T append(T node) { 161 T result = graph.addOrUniqueWithInputs(changeToWord(node)); 162 if (result instanceof FixedNode) { 163 updateLastFixed((FixedNode) result); 164 } 165 return result; 166 } 167 168 private void updateLastFixed(FixedNode result) { 169 assert lastFixedNode != null; 170 assert result.predecessor() == null; 171 graph.addAfterFixed(lastFixedNode, result); 172 if (result instanceof FixedWithNextNode) { 173 lastFixedNode = (FixedWithNextNode) result; 174 } else { 175 lastFixedNode = null; 176 } 177 } 178 179 public InvokeNode createInvoke(Class<?> declaringClass, String name, ValueNode... args) { 180 return createInvoke(declaringClass, name, InvokeKind.Static, null, BytecodeFrame.UNKNOWN_BCI, args); 181 } 182 183 /** 184 * Creates and appends an {@link InvokeNode} for a call to a given method with a given set of 185 * arguments. The method is looked up via reflection based on the declaring class and name. 186 * 187 * @param declaringClass the class declaring the invoked method 188 * @param name the name of the invoked method 189 * @param args the arguments to the invocation 190 */ 191 public InvokeNode createInvoke(Class<?> declaringClass, String name, InvokeKind invokeKind, FrameStateBuilder frameStateBuilder, int bci, ValueNode... args) { 192 boolean isStatic = invokeKind == InvokeKind.Static; 193 ResolvedJavaMethod method = findMethod(declaringClass, name, isStatic); 194 return createInvoke(method, invokeKind, frameStateBuilder, bci, args); 195 } 196 197 public ResolvedJavaMethod findMethod(Class<?> declaringClass, String name, boolean isStatic) { 198 ResolvedJavaMethod method = null; 199 for (Method m : declaringClass.getDeclaredMethods()) { 200 if (Modifier.isStatic(m.getModifiers()) == isStatic && m.getName().equals(name)) { 201 assert method == null : "found more than one method in " + declaringClass + " named " + name; 202 method = providers.getMetaAccess().lookupJavaMethod(m); 203 } 204 } 205 assert method != null : "did not find method in " + declaringClass + " named " + name; 206 return method; 207 } 208 209 public ResolvedJavaMethod findMethod(Class<?> declaringClass, String name, Class<?>... parameterTypes) { 210 try { 211 Method m = declaringClass.getDeclaredMethod(name, parameterTypes); 212 return providers.getMetaAccess().lookupJavaMethod(m); 213 } catch (NoSuchMethodException | SecurityException e) { 214 throw new AssertionError(e); 215 } 216 } 217 218 /** 219 * Creates and appends an {@link InvokeNode} for a call to a given method with a given set of 220 * arguments. 221 */ 222 public InvokeNode createInvoke(ResolvedJavaMethod method, InvokeKind invokeKind, FrameStateBuilder frameStateBuilder, int bci, ValueNode... args) { 223 assert method.isStatic() == (invokeKind == InvokeKind.Static); 224 Signature signature = method.getSignature(); 225 JavaType returnType = signature.getReturnType(null); 226 assert checkArgs(method, args); 227 StampPair returnStamp = graphBuilderPlugins.getOverridingStamp(this, returnType, false); 228 if (returnStamp == null) { 229 returnStamp = StampFactory.forDeclaredType(graph.getAssumptions(), returnType, false); 230 } 231 MethodCallTargetNode callTarget = graph.add(createMethodCallTarget(invokeKind, method, args, returnStamp, bci)); 232 InvokeNode invoke = append(new InvokeNode(callTarget, bci)); 233 234 if (frameStateBuilder != null) { 235 if (invoke.getStackKind() != JavaKind.Void) { 236 frameStateBuilder.push(returnType.getJavaKind(), invoke); 237 } 238 invoke.setStateAfter(frameStateBuilder.create(bci, invoke)); 239 if (invoke.getStackKind() != JavaKind.Void) { 240 frameStateBuilder.pop(returnType.getJavaKind()); 241 } 242 } 243 return invoke; 244 } 245 246 protected MethodCallTargetNode createMethodCallTarget(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] args, StampPair returnStamp, @SuppressWarnings("unused") int bci) { 247 return new MethodCallTargetNode(invokeKind, targetMethod, args, returnStamp, null); 248 } 249 250 /** 251 * Determines if a given set of arguments is compatible with the signature of a given method. 252 * 253 * @return true if {@code args} are compatible with the signature if {@code method} 254 * @throws AssertionError if {@code args} are not compatible with the signature if 255 * {@code method} 256 */ 257 public boolean checkArgs(ResolvedJavaMethod method, ValueNode... args) { 258 Signature signature = method.getSignature(); 259 boolean isStatic = method.isStatic(); 260 if (signature.getParameterCount(!isStatic) != args.length) { 261 throw new AssertionError(graph + ": wrong number of arguments to " + method); 262 } 263 int argIndex = 0; 264 if (!isStatic) { 265 ResolvedJavaType expectedType = method.getDeclaringClass(); 266 JavaKind expected = wordTypes == null ? expectedType.getJavaKind() : wordTypes.asKind(expectedType); 267 JavaKind actual = args[argIndex++].stamp().getStackKind(); 268 assert expected == actual : graph + ": wrong kind of value for receiver argument of call to " + method + " [" + actual + " != " + expected + "]"; 269 } 270 for (int i = 0; i != signature.getParameterCount(false); i++) { 271 JavaType expectedType = signature.getParameterType(i, method.getDeclaringClass()); 272 JavaKind expected = wordTypes == null ? expectedType.getJavaKind().getStackKind() : wordTypes.asKind(expectedType).getStackKind(); 273 JavaKind actual = args[argIndex++].stamp().getStackKind(); 274 if (expected != actual) { 275 throw new AssertionError(graph + ": wrong kind of value for argument " + i + " of call to " + method + " [" + actual + " != " + expected + "]"); 276 } 277 } 278 return true; 279 } 280 281 /** 282 * Recursively {@linkplain #inline inlines} all invocations currently in the graph. 283 */ 284 public void inlineInvokes() { 285 while (!graph.getNodes().filter(InvokeNode.class).isEmpty()) { 286 for (InvokeNode invoke : graph.getNodes().filter(InvokeNode.class).snapshot()) { 287 inline(invoke); 288 } 289 } 290 291 // Clean up all code that is now dead after inlining. 292 new DeadCodeEliminationPhase().apply(graph); 293 } 294 295 /** 296 * Inlines a given invocation to a method. The graph of the inlined method is processed in the 297 * same manner as for snippets and method substitutions. 298 */ 299 public void inline(InvokeNode invoke) { 300 ResolvedJavaMethod method = ((MethodCallTargetNode) invoke.callTarget()).targetMethod(); 301 302 MetaAccessProvider metaAccess = providers.getMetaAccess(); 303 Plugins plugins = new Plugins(graphBuilderPlugins); 304 GraphBuilderConfiguration config = GraphBuilderConfiguration.getSnippetDefault(plugins); 305 306 StructuredGraph calleeGraph = new StructuredGraph.Builder(invoke.getOptions()).method(method).build(); 307 IntrinsicContext initialReplacementContext = new IntrinsicContext(method, method, providers.getReplacements().getDefaultReplacementBytecodeProvider(), INLINE_AFTER_PARSING); 308 GraphBuilderPhase.Instance instance = new GraphBuilderPhase.Instance(metaAccess, providers.getStampProvider(), providers.getConstantReflection(), providers.getConstantFieldProvider(), config, 309 OptimisticOptimizations.NONE, 310 initialReplacementContext); 311 instance.apply(calleeGraph); 312 313 // Remove all frame states from inlinee 314 calleeGraph.clearAllStateAfter(); 315 new DeadCodeEliminationPhase(Optionality.Required).apply(calleeGraph); 316 317 InliningUtil.inline(invoke, calleeGraph, false, method); 318 } 319 320 protected void pushStructure(Structure structure) { 321 structures.add(structure); 322 } 323 324 protected <T extends Structure> T getTopStructure(Class<T> expectedClass) { 325 return expectedClass.cast(structures.get(structures.size() - 1)); 326 } 327 328 protected void popStructure() { 329 structures.remove(structures.size() - 1); 330 } 331 332 protected enum IfState { 333 CONDITION, 334 THEN_PART, 335 ELSE_PART, 336 FINISHED 337 } 338 339 static class IfStructure extends Structure { 340 protected IfState state; 341 protected FixedNode thenPart; 342 protected FixedNode elsePart; 343 } 344 345 /** 346 * Starts an if-block. This call can be followed by a call to {@link #thenPart} to start 347 * emitting the code executed when the condition hold; and a call to {@link #elsePart} to start 348 * emititng the code when the condition does not hold. It must be followed by a call to 349 * {@link #endIf} to close the if-block. 350 * 351 * @param condition The condition for the if-block 352 * @param trueProbability The estimated probability the condition is true 353 */ 354 public void startIf(LogicNode condition, double trueProbability) { 355 AbstractBeginNode thenSuccessor = graph.add(new BeginNode()); 356 AbstractBeginNode elseSuccessor = graph.add(new BeginNode()); 357 append(new IfNode(condition, thenSuccessor, elseSuccessor, trueProbability)); 358 lastFixedNode = null; 359 360 IfStructure s = new IfStructure(); 361 s.state = IfState.CONDITION; 362 s.thenPart = thenSuccessor; 363 s.elsePart = elseSuccessor; 364 pushStructure(s); 365 } 366 367 private IfStructure saveLastNode() { 368 IfStructure s = getTopStructure(IfStructure.class); 369 switch (s.state) { 370 case CONDITION: 371 assert lastFixedNode == null; 372 break; 373 case THEN_PART: 374 s.thenPart = lastFixedNode; 375 break; 376 case ELSE_PART: 377 s.elsePart = lastFixedNode; 378 break; 379 case FINISHED: 380 assert false; 381 break; 382 } 383 lastFixedNode = null; 384 return s; 385 } 386 387 public void thenPart() { 388 IfStructure s = saveLastNode(); 389 lastFixedNode = (FixedWithNextNode) s.thenPart; 390 s.state = IfState.THEN_PART; 391 } 392 393 public void elsePart() { 394 IfStructure s = saveLastNode(); 395 lastFixedNode = (FixedWithNextNode) s.elsePart; 396 s.state = IfState.ELSE_PART; 397 } 398 399 public void endIf() { 400 IfStructure s = saveLastNode(); 401 402 FixedWithNextNode thenPart = s.thenPart instanceof FixedWithNextNode ? (FixedWithNextNode) s.thenPart : null; 403 FixedWithNextNode elsePart = s.elsePart instanceof FixedWithNextNode ? (FixedWithNextNode) s.elsePart : null; 404 405 if (thenPart != null && elsePart != null) { 406 /* Both parts are alive, we need a real merge. */ 407 EndNode thenEnd = graph.add(new EndNode()); 408 graph.addAfterFixed(thenPart, thenEnd); 409 EndNode elseEnd = graph.add(new EndNode()); 410 graph.addAfterFixed(elsePart, elseEnd); 411 412 AbstractMergeNode merge = graph.add(new MergeNode()); 413 merge.addForwardEnd(thenEnd); 414 merge.addForwardEnd(elseEnd); 415 416 lastFixedNode = merge; 417 418 } else if (thenPart != null) { 419 /* elsePart ended with a control sink, so we can continue with thenPart. */ 420 lastFixedNode = thenPart; 421 422 } else if (elsePart != null) { 423 /* thenPart ended with a control sink, so we can continue with elsePart. */ 424 lastFixedNode = elsePart; 425 426 } else { 427 /* Both parts ended with a control sink, so no nodes can be added after the if. */ 428 assert lastFixedNode == null; 429 } 430 s.state = IfState.FINISHED; 431 popStructure(); 432 } 433} 434