1 /* 2 * Copyright (c) 2015, 2019, 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 @SuppressWarnings("unused") 195 public boolean isDeferredInvoke(StateSplit stateSplit) { 196 return false; 197 } 198 199 public FrameState createFrameState(StructuredGraph graph, SideEffectsState sideEffects, StateSplit forStateSplit, NodeSourcePosition sourcePosition) { 200 assert forStateSplit != graph.start(); 201 if (forStateSplit.hasSideEffect()) { 202 if (sideEffects.isAfterSideEffect()) { 203 // Only the last side effect on any execution path in a replacement 204 // can inherit the stateAfter of the replaced node 205 FrameState invalid = graph.add(new FrameState(INVALID_FRAMESTATE_BCI)); 206 if (graph.trackNodeSourcePosition()) { 207 invalid.setNodeSourcePosition(sourcePosition); 208 } 209 for (StateSplit lastSideEffect : sideEffects.sideEffects()) { 210 lastSideEffect.setStateAfter(invalid); 211 } 212 } 213 FrameState frameState; 214 if (isDeferredInvoke(forStateSplit)) { 215 frameState = graph.add(new FrameState(INVALID_FRAMESTATE_BCI)); 216 } else { 217 sideEffects.addSideEffect(forStateSplit); 218 if (forStateSplit instanceof ExceptionObjectNode) { 219 frameState = graph.add(new FrameState(AFTER_EXCEPTION_BCI, (ExceptionObjectNode) forStateSplit)); 220 } else { 221 frameState = graph.add(new FrameState(AFTER_BCI)); 222 } 223 } 224 if (graph.trackNodeSourcePosition()) { 225 frameState.setNodeSourcePosition(sourcePosition); 226 } 227 return frameState; 228 } else { 229 if (forStateSplit instanceof AbstractMergeNode || forStateSplit instanceof LoopExitNode) { 230 // Merge nodes always need a frame state 231 if (sideEffects.isAfterSideEffect()) { 232 // A merge after one or more side effects 233 FrameState frameState = graph.add(new FrameState(AFTER_BCI)); 234 if (graph.trackNodeSourcePosition()) { 235 frameState.setNodeSourcePosition(sourcePosition); 236 } 237 return frameState; 238 } else { 239 // A merge before any side effects 240 FrameState frameState = graph.add(new FrameState(BEFORE_BCI)); 241 if (graph.trackNodeSourcePosition()) { 242 frameState.setNodeSourcePosition(sourcePosition); 243 } 244 return frameState; 245 } 246 } else { 247 // Other non-side-effects do not need a state 248 return null; 249 } 250 } 251 } 252 253 @Override 254 public String toString() { 255 return "Intrinsic{original: " + originalMethod.format("%H.%n(%p)") + ", intrinsic: " + (intrinsicMethod != null ? intrinsicMethod.format("%H.%n(%p)") : "null") + ", context: " + 256 compilationContext + "}"; 257 } 258 }