/*
* Copyright (c) 2015, 2016, 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.
*
* 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.truffle.substitutions;
import static java.lang.Character.toUpperCase;
import static org.graalvm.compiler.truffle.TruffleCompilerOptions.TruffleUseFrameWithoutBoxing;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.EnumMap;
import java.util.Map;
import java.util.concurrent.Callable;
import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
import org.graalvm.compiler.core.common.LocationIdentity;
import org.graalvm.compiler.core.common.calc.Condition;
import org.graalvm.compiler.core.common.type.Stamp;
import org.graalvm.compiler.core.common.type.StampFactory;
import org.graalvm.compiler.core.common.type.StampPair;
import org.graalvm.compiler.core.common.type.TypeReference;
import org.graalvm.compiler.debug.Debug;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.nodes.CallTargetNode;
import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind;
import org.graalvm.compiler.nodes.ConditionAnchorNode;
import org.graalvm.compiler.nodes.ConstantNode;
import org.graalvm.compiler.nodes.DeoptimizeNode;
import org.graalvm.compiler.nodes.FrameState;
import org.graalvm.compiler.nodes.InvokeNode;
import org.graalvm.compiler.nodes.LogicConstantNode;
import org.graalvm.compiler.nodes.LogicNode;
import org.graalvm.compiler.nodes.PiArrayNode;
import org.graalvm.compiler.nodes.PiNode;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.ValuePhiNode;
import org.graalvm.compiler.nodes.calc.CompareNode;
import org.graalvm.compiler.nodes.extended.BoxNode;
import org.graalvm.compiler.nodes.extended.BranchProbabilityNode;
import org.graalvm.compiler.nodes.extended.GuardedUnsafeLoadNode;
import org.graalvm.compiler.nodes.extended.RawStoreNode;
import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;
import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin.Receiver;
import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration;
import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
import org.graalvm.compiler.nodes.type.StampTool;
import org.graalvm.compiler.nodes.virtual.EnsureVirtualizedNode;
import org.graalvm.compiler.options.Option;
import org.graalvm.compiler.options.OptionKey;
import org.graalvm.compiler.options.OptionType;
import org.graalvm.compiler.replacements.nodes.arithmetic.IntegerAddExactNode;
import org.graalvm.compiler.replacements.nodes.arithmetic.IntegerMulExactNode;
import org.graalvm.compiler.replacements.nodes.arithmetic.IntegerMulHighNode;
import org.graalvm.compiler.replacements.nodes.arithmetic.IntegerSubExactNode;
import org.graalvm.compiler.replacements.nodes.arithmetic.UnsignedMulHighNode;
import org.graalvm.compiler.truffle.FrameWithBoxing;
import org.graalvm.compiler.truffle.FrameWithoutBoxing;
import org.graalvm.compiler.truffle.OptimizedAssumption;
import org.graalvm.compiler.truffle.OptimizedCallTarget;
import org.graalvm.compiler.truffle.TruffleCompilerOptions;
import org.graalvm.compiler.truffle.nodes.AssumptionValidAssumption;
import org.graalvm.compiler.truffle.nodes.IsCompilationConstantNode;
import org.graalvm.compiler.truffle.nodes.ObjectLocationIdentity;
import org.graalvm.compiler.truffle.nodes.asserts.NeverPartOfCompilationNode;
import org.graalvm.compiler.truffle.nodes.frame.AllowMaterializeNode;
import org.graalvm.compiler.truffle.nodes.frame.ForceMaterializeNode;
import org.graalvm.compiler.truffle.nodes.frame.NewFrameNode;
import org.graalvm.compiler.truffle.nodes.frame.VirtualFrameGetNode;
import org.graalvm.compiler.truffle.nodes.frame.VirtualFrameIsNode;
import org.graalvm.compiler.truffle.nodes.frame.VirtualFrameSetNode;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.ExactMath;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.FrameSlot;
import jdk.vm.ci.meta.Constant;
import jdk.vm.ci.meta.ConstantReflectionProvider;
import jdk.vm.ci.meta.DeoptimizationAction;
import jdk.vm.ci.meta.DeoptimizationReason;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;
/**
* Provides {@link InvocationPlugin}s for Truffle classes.
*/
public class TruffleGraphBuilderPlugins {
public static class Options {
@Option(help = "Intrinsify get/set/is methods of FrameWithoutBoxing to improve Truffle compilation time", type = OptionType.Debug)//
public static final OptionKey<Boolean> TruffleIntrinsifyFrameAccess = new OptionKey<>(true);
}
public static void registerInvocationPlugins(InvocationPlugins plugins, boolean canDelayIntrinsification, SnippetReflectionProvider snippetReflection) {
registerOptimizedAssumptionPlugins(plugins, snippetReflection);
registerExactMathPlugins(plugins);
registerCompilerDirectivesPlugins(plugins, canDelayIntrinsification);
registerCompilerAssertsPlugins(plugins, canDelayIntrinsification);
registerOptimizedCallTargetPlugins(plugins, snippetReflection, canDelayIntrinsification);
registerCompilationFinalReferencePlugins(plugins, snippetReflection, canDelayIntrinsification);
if (TruffleCompilerOptions.getValue(TruffleUseFrameWithoutBoxing)) {
registerFrameWithoutBoxingPlugins(plugins, canDelayIntrinsification, snippetReflection);
} else {
registerFrameWithBoxingPlugins(plugins, canDelayIntrinsification);
}
}
public static void registerOptimizedAssumptionPlugins(InvocationPlugins plugins, SnippetReflectionProvider snippetReflection) {
Registration r = new Registration(plugins, OptimizedAssumption.class);
InvocationPlugin plugin = new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
if (receiver.isConstant() && b.getAssumptions() != null) {
Constant constant = receiver.get().asConstant();
OptimizedAssumption assumption = snippetReflection.asObject(OptimizedAssumption.class, (JavaConstant) constant);
if (assumption.isValid()) {
if (targetMethod.getName().equals("isValid")) {
b.addPush(JavaKind.Boolean, ConstantNode.forBoolean(true));
} else {
assert targetMethod.getName().equals("check") : targetMethod;
}
b.getAssumptions().record(new AssumptionValidAssumption(assumption));
} else {
if (targetMethod.getName().equals("isValid")) {
b.addPush(JavaKind.Boolean, ConstantNode.forBoolean(false));
} else {
assert targetMethod.getName().equals("check") : targetMethod;
b.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.None));
}
}
return true;
} else {
return false;
}
}
};
r.register1("isValid", Receiver.class, plugin);
r.register1("check", Receiver.class, plugin);
}
public static void registerExactMathPlugins(InvocationPlugins plugins) {
Registration r = new Registration(plugins, ExactMath.class);
for (JavaKind kind : new JavaKind[]{JavaKind.Int, JavaKind.Long}) {
Class<?> type = kind.toJavaClass();
r.register2("addExact", type, type, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
b.addPush(kind, new IntegerAddExactNode(x, y));
return true;
}
});
r.register2("subtractExact", type, type, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
b.addPush(kind, new IntegerSubExactNode(x, y));
return true;
}
});
r.register2("multiplyExact", type, type, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
b.addPush(kind, new IntegerMulExactNode(x, y));
return true;
}
});
r.register2("multiplyHigh", type, type, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
b.addPush(kind, new IntegerMulHighNode(x, y));
return true;
}
});
r.register2("multiplyHighUnsigned", type, type, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
b.addPush(kind, new UnsignedMulHighNode(x, y));
return true;
}
});
}
}
public static void registerCompilerDirectivesPlugins(InvocationPlugins plugins, boolean canDelayIntrinsification) {
Registration r = new Registration(plugins, CompilerDirectives.class);
r.register0("inInterpreter", new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
b.addPush(JavaKind.Boolean, ConstantNode.forBoolean(false));
return true;
}
});
r.register0("inCompiledCode", new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
b.addPush(JavaKind.Boolean, ConstantNode.forBoolean(true));
return true;
}
});
r.register0("transferToInterpreter", new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
b.add(new DeoptimizeNode(DeoptimizationAction.None, DeoptimizationReason.TransferToInterpreter));
return true;
}
});
r.register0("transferToInterpreterAndInvalidate", new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
b.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TransferToInterpreter));
return true;
}
});
r.register1("interpreterOnly", Runnable.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) {
return true;
}
});
r.register1("interpreterOnly", Callable.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) {
return true;
}
});
r.register2("injectBranchProbability", double.class, boolean.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode probability, ValueNode condition) {
b.addPush(JavaKind.Boolean, new BranchProbabilityNode(probability, condition));
return true;
}
});
r.register1("bailout", String.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode message) {
if (canDelayIntrinsification) {
/*
* We do not want to bailout yet, since we are still parsing individual methods
* and constant folding could still eliminate the call to bailout(). However, we
* also want to stop parsing, since we are sure that we will never need the
* graph beyond the bailout point.
*
* Therefore, we manually emit the call to bailout, which will be intrinsified
* later when intrinsifications can no longer be delayed. The call is followed
* by a NeverPartOfCompilationNode, which is a control sink and therefore stops
* any further parsing.
*/
StampPair returnStamp = b.getInvokeReturnStamp(b.getAssumptions());
CallTargetNode callTarget = b.add(new MethodCallTargetNode(InvokeKind.Static, targetMethod, new ValueNode[]{message}, returnStamp, null));
b.add(new InvokeNode(callTarget, b.bci()));
b.add(new NeverPartOfCompilationNode("intrinsification of call to bailout() will abort entire compilation"));
return true;
}
if (message.isConstant()) {
throw b.bailout(message.asConstant().toValueString());
}
throw b.bailout("bailout (message is not compile-time constant, so no additional information is available)");
}
});
r.register1("isCompilationConstant", Object.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
if ((value instanceof BoxNode ? ((BoxNode) value).getValue() : value).isConstant()) {
b.addPush(JavaKind.Boolean, ConstantNode.forBoolean(true));
} else {
b.addPush(JavaKind.Boolean, new IsCompilationConstantNode(value));
}
return true;
}
});
r.register1("isPartialEvaluationConstant", Object.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
if ((value instanceof BoxNode ? ((BoxNode) value).getValue() : value).isConstant()) {
b.addPush(JavaKind.Boolean, ConstantNode.forBoolean(true));
} else if (canDelayIntrinsification) {
return false;
} else {
b.addPush(JavaKind.Boolean, ConstantNode.forBoolean(false));
}
return true;
}
});
r.register1("materialize", Object.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
AllowMaterializeNode materializedValue = b.append(new AllowMaterializeNode(value));
b.add(new ForceMaterializeNode(materializedValue));
return true;
}
});
r.register1("ensureVirtualized", Object.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode object) {
b.add(new EnsureVirtualizedNode(object, false));
return true;
}
});
r.register1("ensureVirtualizedHere", Object.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode object) {
b.add(new EnsureVirtualizedNode(object, true));
return true;
}
});
}
public static void registerCompilerAssertsPlugins(InvocationPlugins plugins, boolean canDelayIntrinsification) {
Registration r = new Registration(plugins, CompilerAsserts.class);
r.register1("partialEvaluationConstant", Object.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
ValueNode curValue = value;
if (curValue instanceof BoxNode) {
BoxNode boxNode = (BoxNode) curValue;
curValue = boxNode.getValue();
}
if (curValue.isConstant()) {
return true;
} else if (canDelayIntrinsification) {
return false;
} else {
StringBuilder sb = new StringBuilder();
sb.append(curValue);
if (curValue instanceof ValuePhiNode) {
ValuePhiNode valuePhi = (ValuePhiNode) curValue;
sb.append(" (");
for (Node n : valuePhi.inputs()) {
sb.append(n);
sb.append("; ");
}
sb.append(")");
}
Debug.dump(Debug.VERBOSE_LEVEL, value.graph(), "Graph before bailout at node %s", sb);
throw b.bailout("Partial evaluation did not reduce value to a constant, is a regular compiler node: " + sb);
}
}
});
r.register0("neverPartOfCompilation", new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
b.add(new NeverPartOfCompilationNode("CompilerAsserts.neverPartOfCompilation()"));
return true;
}
});
r.register1("neverPartOfCompilation", String.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode message) {
if (message.isConstant()) {
String messageString = message.asConstant().toValueString();
b.add(new NeverPartOfCompilationNode(messageString));
return true;
} else {
throw b.bailout("message for never part of compilation is non-constant");
}
}
});
}
public static void registerOptimizedCallTargetPlugins(InvocationPlugins plugins, SnippetReflectionProvider snippetReflection, boolean canDelayIntrinsification) {
Registration r = new Registration(plugins, OptimizedCallTarget.class);
r.register2("createFrame", FrameDescriptor.class, Object[].class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode descriptor, ValueNode args) {
if (canDelayIntrinsification) {
return false;
}
if (!descriptor.isConstant()) {
throw b.bailout("Parameter 'descriptor' is not a compile-time constant");
}
FrameDescriptor constantDescriptor = snippetReflection.asObject(FrameDescriptor.class, descriptor.asJavaConstant());
ValueNode nonNullArguments = b.add(PiNode.create(args, StampFactory.objectNonNull(StampTool.typeReferenceOrNull(args))));
Class<?> frameClass = TruffleCompilerOptions.getValue(TruffleUseFrameWithoutBoxing) ? FrameWithoutBoxing.class : FrameWithBoxing.class;
NewFrameNode newFrame = new NewFrameNode(b.getMetaAccess(), snippetReflection, b.getGraph(), b.getMetaAccess().lookupJavaType(frameClass), constantDescriptor, descriptor,
nonNullArguments);
b.addPush(JavaKind.Object, newFrame);
return true;
}
});
r.register2("castArrayFixedLength", Object[].class, int.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode args, ValueNode length) {
b.addPush(JavaKind.Object, new PiArrayNode(args, length, args.stamp()));
return true;
}
});
registerUnsafeCast(r, canDelayIntrinsification);
}
public static void registerCompilationFinalReferencePlugins(InvocationPlugins plugins, SnippetReflectionProvider snippetReflection, boolean canDelayIntrinsification) {
Registration r = new Registration(plugins, Reference.class);
r.register1("get", Receiver.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
if (canDelayIntrinsification) {
return false;
}
if (receiver.isConstant()) {
JavaConstant constant = (JavaConstant) receiver.get().asConstant();
if (constant.isNonNull()) {
Reference<?> reference = snippetReflection.asObject(Reference.class, constant);
if (reference instanceof WeakReference<?> || reference instanceof SoftReference<?>) {
b.addPush(JavaKind.Object, ConstantNode.forConstant(snippetReflection.forObject(reference.get()), b.getMetaAccess()));
return true;
}
}
}
return false;
}
});
}
private static final EnumMap<JavaKind, Integer> accessorKindToTag;
static {
accessorKindToTag = new EnumMap<>(JavaKind.class);
accessorKindToTag.put(JavaKind.Object, (int) FrameWithoutBoxing.OBJECT_TAG);
accessorKindToTag.put(JavaKind.Long, (int) FrameWithoutBoxing.LONG_TAG);
accessorKindToTag.put(JavaKind.Int, (int) FrameWithoutBoxing.INT_TAG);
accessorKindToTag.put(JavaKind.Double, (int) FrameWithoutBoxing.DOUBLE_TAG);
accessorKindToTag.put(JavaKind.Float, (int) FrameWithoutBoxing.FLOAT_TAG);
accessorKindToTag.put(JavaKind.Boolean, (int) FrameWithoutBoxing.BOOLEAN_TAG);
accessorKindToTag.put(JavaKind.Byte, (int) FrameWithoutBoxing.BYTE_TAG);
}
public static void registerFrameWithoutBoxingPlugins(InvocationPlugins plugins, boolean canDelayIntrinsification, SnippetReflectionProvider snippetReflection) {
Registration r = new Registration(plugins, FrameWithoutBoxing.class);
registerFrameMethods(r);
registerUnsafeCast(r, canDelayIntrinsification);
registerUnsafeLoadStorePlugins(r, null, JavaKind.Int, JavaKind.Long, JavaKind.Float, JavaKind.Double, JavaKind.Object);
if (TruffleCompilerOptions.getValue(Options.TruffleIntrinsifyFrameAccess)) {
for (Map.Entry<JavaKind, Integer> kindAndTag : accessorKindToTag.entrySet()) {
registerFrameAccessors(r, kindAndTag.getKey(), kindAndTag.getValue(), snippetReflection);
}
}
}
public static void registerFrameWithBoxingPlugins(InvocationPlugins plugins, boolean canDelayIntrinsification) {
Registration r = new Registration(plugins, FrameWithBoxing.class);
registerFrameMethods(r);
registerUnsafeCast(r, canDelayIntrinsification);
}
/**
* We intrinisify the getXxx, setXxx, and isXxx methods for all type tags. The intrinsic nodes
* are lightweight fixed nodes without a {@link FrameState}. No {@link FrameState} is important
* for partial evaluation performance, because creating and later on discarding FrameStates for
* the setXxx methods have a high compile time cost.
*
* Intrinsification requires the following conditions: (1) the accessed frame is directly the
* {@link NewFrameNode}, (2) the accessed FrameSlot is a constant, and (3) the FrameDescriptor
* was never materialized before. All three conditions together guarantee that the escape
* analysis can virtualize the access. The condition (3) is necessary because a possible
* materialization of the frame can prevent escape analysis - so in that case a FrameState for
* setXxx methods is actually necessary since they stores can be state-changing memory
* operations.
*
* Note that we do not register an intrinsification for {@link FrameWithoutBoxing#getValue}. It
* is a complicated method to intrinsify, and it is not used frequently enough to justify the
* complexity of an intrinisification.
*/
private static void registerFrameAccessors(Registration r, JavaKind accessKind, int accessTag, SnippetReflectionProvider snippetReflection) {
String nameSuffix = accessKind.name();
r.register2("get" + nameSuffix, Receiver.class, FrameSlot.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver frameNode, ValueNode frameSlotNode) {
FrameSlot frameSlot = maybeGetConstantFrameSlot(snippetReflection, frameNode.get(false), frameSlotNode);
if (frameSlot != null) {
b.addPush(accessKind, new VirtualFrameGetNode((NewFrameNode) frameNode.get(), frameSlot, accessKind, accessTag));
return true;
}
return false;
}
});
r.register3("set" + nameSuffix, Receiver.class, FrameSlot.class, accessKind == JavaKind.Object ? Object.class : accessKind.toJavaClass(), new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver frameNode, ValueNode frameSlotNode, ValueNode value) {
FrameSlot frameSlot = maybeGetConstantFrameSlot(snippetReflection, frameNode.get(false), frameSlotNode);
if (frameSlot != null) {
b.add(new VirtualFrameSetNode((NewFrameNode) frameNode.get(), frameSlot, accessTag, value));
return true;
}
return false;
}
});
r.register2("is" + nameSuffix, Receiver.class, FrameSlot.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver frameNode, ValueNode frameSlotNode) {
FrameSlot frameSlot = maybeGetConstantFrameSlot(snippetReflection, frameNode.get(false), frameSlotNode);
if (frameSlot != null) {
b.addPush(JavaKind.Boolean, new VirtualFrameIsNode((NewFrameNode) frameNode.get(), frameSlot, accessTag));
return true;
}
return false;
}
});
}
static FrameSlot maybeGetConstantFrameSlot(SnippetReflectionProvider snippetReflection, ValueNode frameNode, ValueNode frameSlotNode) {
if (frameSlotNode.isConstant() && frameNode instanceof NewFrameNode && ((NewFrameNode) frameNode).getIntrinsifyAccessors()) {
return snippetReflection.asObject(FrameSlot.class, frameSlotNode.asJavaConstant());
}
return null;
}
private static void registerFrameMethods(Registration r) {
r.register1("getArguments", Receiver.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver frame) {
if (frame.get(false) instanceof NewFrameNode) {
b.push(JavaKind.Object, ((NewFrameNode) frame.get()).getArguments());
return true;
}
return false;
}
});
r.register1("getFrameDescriptor", Receiver.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver frame) {
if (frame.get(false) instanceof NewFrameNode) {
b.push(JavaKind.Object, ((NewFrameNode) frame.get()).getDescriptor());
return true;
}
return false;
}
});
r.register1("materialize", Receiver.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
ValueNode frame = receiver.get();
if (TruffleCompilerOptions.getValue(Options.TruffleIntrinsifyFrameAccess) && frame instanceof NewFrameNode && ((NewFrameNode) frame).getIntrinsifyAccessors()) {
JavaConstant speculation = b.getGraph().getSpeculationLog().speculate(((NewFrameNode) frame).getIntrinsifyAccessorsSpeculation());
b.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.RuntimeConstraint, speculation));
return true;
}
b.addPush(JavaKind.Object, new AllowMaterializeNode(frame));
return true;
}
});
}
public static void registerUnsafeCast(Registration r, boolean canDelayIntrinsification) {
r.register4("unsafeCast", Object.class, Class.class, boolean.class, boolean.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode object, ValueNode clazz, ValueNode condition, ValueNode nonNull) {
if (clazz.isConstant() && nonNull.isConstant()) {
ConstantReflectionProvider constantReflection = b.getConstantReflection();
ResolvedJavaType javaType = constantReflection.asJavaType(clazz.asConstant());
if (javaType == null) {
b.push(JavaKind.Object, object);
} else {
TypeReference type = TypeReference.createTrusted(b.getAssumptions(), javaType);
if (javaType.isArray()) {
type = type.asExactReference();
}
Stamp piStamp = StampFactory.object(type, nonNull.asJavaConstant().asInt() != 0);
ConditionAnchorNode valueAnchorNode = null;
if (condition.isConstant() && condition.asJavaConstant().asInt() == 1) {
// Nothing to do.
} else {
boolean skipAnchor = false;
LogicNode compareNode = CompareNode.createCompareNode(object.graph(), Condition.EQ, condition, ConstantNode.forBoolean(true, object.graph()), constantReflection);
if (compareNode instanceof LogicConstantNode) {
LogicConstantNode logicConstantNode = (LogicConstantNode) compareNode;
if (logicConstantNode.getValue()) {
skipAnchor = true;
}
}
if (!skipAnchor) {
valueAnchorNode = b.add(new ConditionAnchorNode(compareNode));
}
}
b.addPush(JavaKind.Object, PiNode.create(object, piStamp, valueAnchorNode));
}
return true;
} else if (canDelayIntrinsification) {
return false;
} else {
throw b.bailout("unsafeCast arguments could not reduce to a constant: " + clazz + ", " + nonNull);
}
}
});
}
public static void registerUnsafeLoadStorePlugins(Registration r, JavaConstant anyConstant, JavaKind... kinds) {
for (JavaKind kind : kinds) {
String kindName = kind.getJavaName();
kindName = toUpperCase(kindName.charAt(0)) + kindName.substring(1);
String getName = "unsafeGet" + kindName;
String putName = "unsafePut" + kindName;
r.register4(getName, Object.class, long.class, boolean.class, Object.class, new CustomizedUnsafeLoadPlugin(kind));
r.register4(putName, Object.class, long.class, kind == JavaKind.Object ? Object.class : kind.toJavaClass(), Object.class, new CustomizedUnsafeStorePlugin(kind, anyConstant));
}
}
static class CustomizedUnsafeLoadPlugin implements InvocationPlugin {
private final JavaKind returnKind;
CustomizedUnsafeLoadPlugin(JavaKind returnKind) {
this.returnKind = returnKind;
}
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode object, ValueNode offset, ValueNode condition, ValueNode location) {
if (location.isConstant()) {
LocationIdentity locationIdentity;
if (location.isNullConstant()) {
locationIdentity = LocationIdentity.any();
} else {
locationIdentity = ObjectLocationIdentity.create(location.asJavaConstant());
}
LogicNode compare = b.addWithInputs(CompareNode.createCompareNode(b.getConstantReflection(), b.getMetaAccess(), b.getOptions(), null, Condition.EQ, condition,
ConstantNode.forBoolean(true, object.graph())));
ConditionAnchorNode anchor = b.add(new ConditionAnchorNode(compare));
b.addPush(returnKind, b.add(new GuardedUnsafeLoadNode(b.addNonNullCast(object), offset, returnKind, locationIdentity, anchor)));
return true;
}
// TODO: should we throw b.bailout() here?
return false;
}
}
static class CustomizedUnsafeStorePlugin implements InvocationPlugin {
private final JavaKind kind;
private final JavaConstant anyConstant;
CustomizedUnsafeStorePlugin(JavaKind kind, JavaConstant anyConstant) {
this.kind = kind;
this.anyConstant = anyConstant;
}
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode object, ValueNode offset, ValueNode value, ValueNode location) {
ValueNode locationArgument = location;
if (locationArgument.isConstant()) {
LocationIdentity locationIdentity;
boolean forceAnyLocation = false;
if (locationArgument.isNullConstant()) {
locationIdentity = LocationIdentity.any();
} else if (locationArgument.asJavaConstant().equals(anyConstant)) {
locationIdentity = LocationIdentity.any();
forceAnyLocation = true;
} else {
locationIdentity = ObjectLocationIdentity.create(locationArgument.asJavaConstant());
}
b.add(new RawStoreNode(object, offset, value, kind, locationIdentity, true, null, forceAnyLocation));
return true;
}
// TODO: should we throw b.bailout() here?
return false;
}
}
}