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