/*
 * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package org.graalvm.compiler.nodes.graphbuilderconf;

import static jdk.vm.ci.code.BytecodeFrame.AFTER_BCI;
import static jdk.vm.ci.code.BytecodeFrame.AFTER_EXCEPTION_BCI;
import static jdk.vm.ci.code.BytecodeFrame.BEFORE_BCI;
import static jdk.vm.ci.code.BytecodeFrame.INVALID_FRAMESTATE_BCI;
import static jdk.vm.ci.services.Services.IS_IN_NATIVE_IMAGE;
import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING;
import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.ROOT_COMPILATION;
import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.ROOT_COMPILATION_ENCODING;

import org.graalvm.compiler.api.replacements.MethodSubstitution;
import org.graalvm.compiler.bytecode.BytecodeProvider;
import org.graalvm.compiler.graph.NodeSourcePosition;
import org.graalvm.compiler.nodes.AbstractMergeNode;
import org.graalvm.compiler.nodes.FrameState;
import org.graalvm.compiler.nodes.Invoke;
import org.graalvm.compiler.nodes.LoopExitNode;
import org.graalvm.compiler.nodes.StateSplit;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.java.ExceptionObjectNode;

import jdk.vm.ci.meta.ResolvedJavaMethod;

An intrinsic is a substitute implementation of a Java method (or a bytecode in the case of snippets) that is itself implemented in Java. This interface provides information about the intrinsic currently being processed by the graph builder. When in the scope of an intrinsic, the graph builder does not check the value kinds flowing through the JVM state since intrinsics can employ non-Java kinds to represent values such as raw machine words and pointers.
/** * An intrinsic is a substitute implementation of a Java method (or a bytecode in the case of * snippets) that is itself implemented in Java. This interface provides information about the * intrinsic currently being processed by the graph builder. * * When in the scope of an intrinsic, the graph builder does not check the value kinds flowing * through the JVM state since intrinsics can employ non-Java kinds to represent values such as raw * machine words and pointers. */
public class IntrinsicContext {
Method being intrinsified.
/** * Method being intrinsified. */
final ResolvedJavaMethod originalMethod;
Method providing the intrinsic implementation.
/** * Method providing the intrinsic implementation. */
final ResolvedJavaMethod intrinsicMethod;
Provider of bytecode to be parsed for a method that is part of an intrinsic.
/** * Provider of bytecode to be parsed for a method that is part of an intrinsic. */
final BytecodeProvider bytecodeProvider; final CompilationContext compilationContext; final boolean allowPartialIntrinsicArgumentMismatch; public IntrinsicContext(ResolvedJavaMethod method, ResolvedJavaMethod intrinsic, BytecodeProvider bytecodeProvider, CompilationContext compilationContext) { this(method, intrinsic, bytecodeProvider, compilationContext, false); } public IntrinsicContext(ResolvedJavaMethod method, ResolvedJavaMethod intrinsic, BytecodeProvider bytecodeProvider, CompilationContext compilationContext, boolean allowPartialIntrinsicArgumentMismatch) { this.originalMethod = method; this.intrinsicMethod = intrinsic; this.bytecodeProvider = bytecodeProvider; assert bytecodeProvider != null; this.compilationContext = compilationContext; this.allowPartialIntrinsicArgumentMismatch = allowPartialIntrinsicArgumentMismatch; assert !isCompilationRoot() || method.hasBytecodes() : "Cannot root compile intrinsic for native or abstract method " + method.format("%H.%n(%p)"); assert IS_IN_NATIVE_IMAGE || !method.equals(intrinsic) || method.getAnnotation(MethodSubstitution.class) == null : "method and intrinsic must be different: " + method + " " + intrinsic; }
A partial intrinsic exits by (effectively) calling the intrinsified method. Normally, this call must use exactly the same arguments as the call that is being intrinsified. This allows to override this behavior.
/** * A partial intrinsic exits by (effectively) calling the intrinsified method. Normally, this * call must use exactly the same arguments as the call that is being intrinsified. This allows * to override this behavior. */
public boolean allowPartialIntrinsicArgumentMismatch() { return allowPartialIntrinsicArgumentMismatch; }
Gets the method being intrinsified.
/** * Gets the method being intrinsified. */
public ResolvedJavaMethod getOriginalMethod() { return originalMethod; }
Gets the method providing the intrinsic implementation.
/** * Gets the method providing the intrinsic implementation. */
public ResolvedJavaMethod getIntrinsicMethod() { return intrinsicMethod; }
Gets provider of bytecode to be parsed for a method that is part of an intrinsic.
/** * Gets provider of bytecode to be parsed for a method that is part of an intrinsic. */
public BytecodeProvider getBytecodeProvider() { return bytecodeProvider; }
Determines if a call within the compilation scope of this intrinsic represents a call to the original method. This denotes the path where a partial intrinsification falls back to the original method.
/** * Determines if a call within the compilation scope of this intrinsic represents a call to the * {@linkplain #getOriginalMethod() original} method. This denotes the path where a partial * intrinsification falls back to the original method. */
public boolean isCallToOriginal(ResolvedJavaMethod targetMethod) { return originalMethod.equals(targetMethod) || intrinsicMethod.equals(targetMethod); } private NodeSourcePosition nodeSourcePosition; public boolean isPostParseInlined() { return compilationContext.equals(INLINE_AFTER_PARSING); } public boolean isCompilationRoot() { return compilationContext.equals(ROOT_COMPILATION) || compilationContext.equals(ROOT_COMPILATION_ENCODING); } public boolean isIntrinsicEncoding() { return compilationContext.equals(ROOT_COMPILATION_ENCODING); } public NodeSourcePosition getNodeSourcePosition() { return nodeSourcePosition; } public void setNodeSourcePosition(NodeSourcePosition position) { assert nodeSourcePosition == null : "can only be set once"; this.nodeSourcePosition = position; }
Denotes the compilation context in which an intrinsic is being parsed.
/** * Denotes the compilation context in which an intrinsic is being parsed. */
public enum CompilationContext {
An intrinsic is being processed when parsing an invoke bytecode that calls the intrinsified method.
/** * An intrinsic is being processed when parsing an invoke bytecode that calls the * intrinsified method. */
INLINE_DURING_PARSING,
An intrinsic is being processed when inlining an Invoke in an existing graph.
/** * An intrinsic is being processed when inlining an {@link Invoke} in an existing graph. */
INLINE_AFTER_PARSING,
An intrinsic is the root of compilation.
/** * An intrinsic is the root of compilation. */
ROOT_COMPILATION,
An intrinsic is the root of a compilation done for graph encoding.
/** * An intrinsic is the root of a compilation done for graph encoding. */
ROOT_COMPILATION_ENCODING }
Models the state of a graph in terms of side effects that are control flow predecessors of the current point in a graph.
/** * Models the state of a graph in terms of {@link StateSplit#hasSideEffect() side effects} that * are control flow predecessors of the current point in a graph. */
public interface SideEffectsState {
Determines if the current program point is preceded by one or more side effects.
/** * Determines if the current program point is preceded by one or more side effects. */
boolean isAfterSideEffect();
Gets the side effects preceding the current program point.
/** * Gets the side effects preceding the current program point. */
Iterable<StateSplit> sideEffects();
Records a side effect for the current program point.
/** * Records a side effect for the current program point. */
void addSideEffect(StateSplit sideEffect); } @SuppressWarnings("unused") public boolean isDeferredInvoke(StateSplit stateSplit) { return false; } public FrameState createFrameState(StructuredGraph graph, SideEffectsState sideEffects, StateSplit forStateSplit, NodeSourcePosition sourcePosition) { assert forStateSplit != graph.start(); if (forStateSplit.hasSideEffect()) { if (sideEffects.isAfterSideEffect()) { // Only the last side effect on any execution path in a replacement // can inherit the stateAfter of the replaced node FrameState invalid = graph.add(new FrameState(INVALID_FRAMESTATE_BCI)); if (graph.trackNodeSourcePosition()) { invalid.setNodeSourcePosition(sourcePosition); } for (StateSplit lastSideEffect : sideEffects.sideEffects()) { lastSideEffect.setStateAfter(invalid); } } FrameState frameState; if (isDeferredInvoke(forStateSplit)) { frameState = graph.add(new FrameState(INVALID_FRAMESTATE_BCI)); } else { sideEffects.addSideEffect(forStateSplit); if (forStateSplit instanceof ExceptionObjectNode) { frameState = graph.add(new FrameState(AFTER_EXCEPTION_BCI, (ExceptionObjectNode) forStateSplit)); } else { frameState = graph.add(new FrameState(AFTER_BCI)); } } if (graph.trackNodeSourcePosition()) { frameState.setNodeSourcePosition(sourcePosition); } return frameState; } else { if (forStateSplit instanceof AbstractMergeNode || forStateSplit instanceof LoopExitNode) { // Merge nodes always need a frame state if (sideEffects.isAfterSideEffect()) { // A merge after one or more side effects FrameState frameState = graph.add(new FrameState(AFTER_BCI)); if (graph.trackNodeSourcePosition()) { frameState.setNodeSourcePosition(sourcePosition); } return frameState; } else { // A merge before any side effects FrameState frameState = graph.add(new FrameState(BEFORE_BCI)); if (graph.trackNodeSourcePosition()) { frameState.setNodeSourcePosition(sourcePosition); } return frameState; } } else { // Other non-side-effects do not need a state return null; } } } @Override public String toString() { return "Intrinsic{original: " + originalMethod.format("%H.%n(%p)") + ", intrinsic: " + (intrinsicMethod != null ? intrinsicMethod.format("%H.%n(%p)") : "null") + ", context: " + compilationContext + "}"; } }