1 /*
   2  * Copyright (c) 2015, 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.nodes.graphbuilderconf;
  26 
  27 import static jdk.vm.ci.code.BytecodeFrame.AFTER_BCI;
  28 import static jdk.vm.ci.code.BytecodeFrame.AFTER_EXCEPTION_BCI;
  29 import static jdk.vm.ci.code.BytecodeFrame.BEFORE_BCI;
  30 import static jdk.vm.ci.code.BytecodeFrame.INVALID_FRAMESTATE_BCI;
  31 import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING;
  32 import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.ROOT_COMPILATION;
  33 
  34 import org.graalvm.compiler.api.replacements.MethodSubstitution;
  35 import org.graalvm.compiler.bytecode.BytecodeProvider;
  36 import org.graalvm.compiler.graph.NodeSourcePosition;
  37 import org.graalvm.compiler.nodes.AbstractMergeNode;
  38 import org.graalvm.compiler.nodes.FrameState;
  39 import org.graalvm.compiler.nodes.Invoke;
  40 import org.graalvm.compiler.nodes.StateSplit;
  41 import org.graalvm.compiler.nodes.StructuredGraph;
  42 import org.graalvm.compiler.nodes.java.ExceptionObjectNode;
  43 
  44 import jdk.vm.ci.meta.ResolvedJavaMethod;
  45 
  46 /**
  47  * An intrinsic is a substitute implementation of a Java method (or a bytecode in the case of
  48  * snippets) that is itself implemented in Java. This interface provides information about the
  49  * intrinsic currently being processed by the graph builder.
  50  *
  51  * When in the scope of an intrinsic, the graph builder does not check the value kinds flowing
  52  * through the JVM state since intrinsics can employ non-Java kinds to represent values such as raw
  53  * machine words and pointers.
  54  */
  55 public class IntrinsicContext {
  56 
  57     /**
  58      * Method being intrinsified.
  59      */
  60     final ResolvedJavaMethod originalMethod;
  61 
  62     /**
  63      * Method providing the intrinsic implementation.
  64      */
  65     final ResolvedJavaMethod intrinsicMethod;
  66 
  67     /**
  68      * Provider of bytecode to be parsed for a method that is part of an intrinsic.
  69      */
  70     final BytecodeProvider bytecodeProvider;
  71 
  72     final CompilationContext compilationContext;
  73 
  74     final boolean allowPartialIntrinsicArgumentMismatch;
  75 
  76     public IntrinsicContext(ResolvedJavaMethod method, ResolvedJavaMethod intrinsic, BytecodeProvider bytecodeProvider, CompilationContext compilationContext) {
  77         this(method, intrinsic, bytecodeProvider, compilationContext, false);
  78     }
  79 
  80     public IntrinsicContext(ResolvedJavaMethod method, ResolvedJavaMethod intrinsic, BytecodeProvider bytecodeProvider, CompilationContext compilationContext,
  81                     boolean allowPartialIntrinsicArgumentMismatch) {
  82         this.originalMethod = method;
  83         this.intrinsicMethod = intrinsic;
  84         this.bytecodeProvider = bytecodeProvider;
  85         assert bytecodeProvider != null;
  86         this.compilationContext = compilationContext;
  87         this.allowPartialIntrinsicArgumentMismatch = allowPartialIntrinsicArgumentMismatch;
  88         assert !isCompilationRoot() || method.hasBytecodes() : "Cannot root compile intrinsic for native or abstract method " + method.format("%H.%n(%p)");
  89         assert !method.equals(intrinsic) || method.getAnnotation(MethodSubstitution.class) == null : "method and intrinsic must be different: " + method + " " + intrinsic;
  90     }
  91 
  92     /**
  93      * A partial intrinsic exits by (effectively) calling the intrinsified method. Normally, this
  94      * call must use exactly the same arguments as the call that is being intrinsified. This allows
  95      * to override this behavior.
  96      */
  97     public boolean allowPartialIntrinsicArgumentMismatch() {
  98         return allowPartialIntrinsicArgumentMismatch;
  99     }
 100 
 101     /**
 102      * Gets the method being intrinsified.
 103      */
 104     public ResolvedJavaMethod getOriginalMethod() {
 105         return originalMethod;
 106     }
 107 
 108     /**
 109      * Gets the method providing the intrinsic implementation.
 110      */
 111     public ResolvedJavaMethod getIntrinsicMethod() {
 112         return intrinsicMethod;
 113     }
 114 
 115     /**
 116      * Gets provider of bytecode to be parsed for a method that is part of an intrinsic.
 117      */
 118     public BytecodeProvider getBytecodeProvider() {
 119         return bytecodeProvider;
 120     }
 121 
 122     /**
 123      * Determines if a call within the compilation scope of this intrinsic represents a call to the
 124      * {@linkplain #getOriginalMethod() original} method. This denotes the path where a partial
 125      * intrinsification falls back to the original method.
 126      */
 127     public boolean isCallToOriginal(ResolvedJavaMethod targetMethod) {
 128         return originalMethod.equals(targetMethod) || intrinsicMethod.equals(targetMethod);
 129     }
 130 
 131     private NodeSourcePosition nodeSourcePosition;
 132 
 133     public boolean isPostParseInlined() {
 134         return compilationContext.equals(INLINE_AFTER_PARSING);
 135     }
 136 
 137     public boolean isCompilationRoot() {
 138         return compilationContext.equals(ROOT_COMPILATION);
 139     }
 140 
 141     public NodeSourcePosition getNodeSourcePosition() {
 142         return nodeSourcePosition;
 143     }
 144 
 145     public void setNodeSourcePosition(NodeSourcePosition position) {
 146         assert nodeSourcePosition == null : "can only be set once";
 147         this.nodeSourcePosition = position;
 148     }
 149 
 150     /**
 151      * Denotes the compilation context in which an intrinsic is being parsed.
 152      */
 153     public enum CompilationContext {
 154         /**
 155          * An intrinsic is being processed when parsing an invoke bytecode that calls the
 156          * intrinsified method.
 157          */
 158         INLINE_DURING_PARSING,
 159 
 160         /**
 161          * An intrinsic is being processed when inlining an {@link Invoke} in an existing graph.
 162          */
 163         INLINE_AFTER_PARSING,
 164 
 165         /**
 166          * An intrinsic is the root of compilation.
 167          */
 168         ROOT_COMPILATION
 169     }
 170 
 171     /**
 172      * Models the state of a graph in terms of {@link StateSplit#hasSideEffect() side effects} that
 173      * are control flow predecessors of the current point in a graph.
 174      */
 175     public interface SideEffectsState {
 176 
 177         /**
 178          * Determines if the current program point is preceded by one or more side effects.
 179          */
 180         boolean isAfterSideEffect();
 181 
 182         /**
 183          * Gets the side effects preceding the current program point.
 184          */
 185         Iterable<StateSplit> sideEffects();
 186 
 187         /**
 188          * Records a side effect for the current program point.
 189          */
 190         void addSideEffect(StateSplit sideEffect);
 191     }
 192 
 193     public FrameState createFrameState(StructuredGraph graph, SideEffectsState sideEffects, StateSplit forStateSplit, NodeSourcePosition sourcePosition) {
 194         assert forStateSplit != graph.start();
 195         if (forStateSplit.hasSideEffect()) {
 196             if (sideEffects.isAfterSideEffect()) {
 197                 // Only the last side effect on any execution path in a replacement
 198                 // can inherit the stateAfter of the replaced node
 199                 FrameState invalid = graph.add(new FrameState(INVALID_FRAMESTATE_BCI));
 200                 if (graph.trackNodeSourcePosition()) {
 201                     invalid.setNodeSourcePosition(sourcePosition);
 202                 }
 203                 for (StateSplit lastSideEffect : sideEffects.sideEffects()) {
 204                     lastSideEffect.setStateAfter(invalid);
 205                 }
 206             }
 207             sideEffects.addSideEffect(forStateSplit);
 208             FrameState frameState;
 209             if (forStateSplit instanceof ExceptionObjectNode) {
 210                 frameState = graph.add(new FrameState(AFTER_EXCEPTION_BCI, (ExceptionObjectNode) forStateSplit));
 211             } else {
 212                 frameState = graph.add(new FrameState(AFTER_BCI));
 213             }
 214             if (graph.trackNodeSourcePosition()) {
 215                 frameState.setNodeSourcePosition(sourcePosition);
 216             }
 217             return frameState;
 218         } else {
 219             if (forStateSplit instanceof AbstractMergeNode) {
 220                 // Merge nodes always need a frame state
 221                 if (sideEffects.isAfterSideEffect()) {
 222                     // A merge after one or more side effects
 223                     FrameState frameState = graph.add(new FrameState(AFTER_BCI));
 224                     if (graph.trackNodeSourcePosition()) {
 225                         frameState.setNodeSourcePosition(sourcePosition);
 226                     }
 227                     return frameState;
 228                 } else {
 229                     // A merge before any side effects
 230                     FrameState frameState = graph.add(new FrameState(BEFORE_BCI));
 231                     if (graph.trackNodeSourcePosition()) {
 232                         frameState.setNodeSourcePosition(sourcePosition);
 233                     }
 234                     return frameState;
 235                 }
 236             } else {
 237                 // Other non-side-effects do not need a state
 238                 return null;
 239             }
 240         }
 241     }
 242 
 243     @Override
 244     public String toString() {
 245         return "Intrinsic{original: " + originalMethod.format("%H.%n(%p)") + ", intrinsic: " + intrinsicMethod.format("%H.%n(%p)") + ", context: " + compilationContext + "}";
 246     }
 247 }