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