1 /*
   2  * Copyright (c) 2015, 2018, 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.replacements;
  26 
  27 import static jdk.vm.ci.code.MemoryBarriers.JMM_POST_VOLATILE_READ;
  28 import static jdk.vm.ci.code.MemoryBarriers.JMM_POST_VOLATILE_WRITE;
  29 import static jdk.vm.ci.code.MemoryBarriers.JMM_PRE_VOLATILE_READ;
  30 import static jdk.vm.ci.code.MemoryBarriers.JMM_PRE_VOLATILE_WRITE;
  31 import static jdk.vm.ci.code.MemoryBarriers.LOAD_LOAD;
  32 import static jdk.vm.ci.code.MemoryBarriers.LOAD_STORE;
  33 import static jdk.vm.ci.code.MemoryBarriers.STORE_LOAD;
  34 import static jdk.vm.ci.code.MemoryBarriers.STORE_STORE;
  35 import static org.graalvm.compiler.nodes.NamedLocationIdentity.OFF_HEAP_LOCATION;
  36 
  37 import java.lang.reflect.Array;
  38 import java.lang.reflect.Field;
  39 import java.util.Arrays;
  40 
  41 import org.graalvm.compiler.api.directives.GraalDirectives;
  42 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
  43 import org.graalvm.compiler.bytecode.BytecodeProvider;
  44 import org.graalvm.compiler.core.common.calc.Condition;
  45 import org.graalvm.compiler.core.common.calc.Condition.CanonicalizedCondition;
  46 import org.graalvm.compiler.core.common.calc.UnsignedMath;
  47 import org.graalvm.compiler.core.common.type.ObjectStamp;
  48 import org.graalvm.compiler.core.common.type.Stamp;
  49 import org.graalvm.compiler.core.common.type.StampFactory;
  50 import org.graalvm.compiler.core.common.type.TypeReference;
  51 import org.graalvm.compiler.debug.GraalError;
  52 import org.graalvm.compiler.graph.Edges;
  53 import org.graalvm.compiler.graph.Node;
  54 import org.graalvm.compiler.graph.NodeList;
  55 import org.graalvm.compiler.nodes.AbstractBeginNode;
  56 import org.graalvm.compiler.nodes.BeginNode;
  57 import org.graalvm.compiler.nodes.ConstantNode;
  58 import org.graalvm.compiler.nodes.DeoptimizeNode;
  59 import org.graalvm.compiler.nodes.EndNode;
  60 import org.graalvm.compiler.nodes.FixedGuardNode;
  61 import org.graalvm.compiler.nodes.FixedWithNextNode;
  62 import org.graalvm.compiler.nodes.IfNode;
  63 import org.graalvm.compiler.nodes.LogicNode;
  64 import org.graalvm.compiler.nodes.MergeNode;
  65 import org.graalvm.compiler.nodes.NamedLocationIdentity;
  66 import org.graalvm.compiler.nodes.NodeView;
  67 import org.graalvm.compiler.nodes.StateSplit;
  68 import org.graalvm.compiler.nodes.StructuredGraph;
  69 import org.graalvm.compiler.nodes.ValueNode;
  70 import org.graalvm.compiler.nodes.ValuePhiNode;
  71 import org.graalvm.compiler.nodes.calc.AbsNode;
  72 import org.graalvm.compiler.nodes.calc.CompareNode;
  73 import org.graalvm.compiler.nodes.calc.ConditionalNode;
  74 import org.graalvm.compiler.nodes.calc.FloatEqualsNode;
  75 import org.graalvm.compiler.nodes.calc.IntegerEqualsNode;
  76 import org.graalvm.compiler.nodes.calc.IsNullNode;
  77 import org.graalvm.compiler.nodes.calc.NarrowNode;
  78 import org.graalvm.compiler.nodes.calc.ReinterpretNode;
  79 import org.graalvm.compiler.nodes.calc.RightShiftNode;
  80 import org.graalvm.compiler.nodes.calc.SignExtendNode;
  81 import org.graalvm.compiler.nodes.calc.SqrtNode;
  82 import org.graalvm.compiler.nodes.calc.UnsignedDivNode;
  83 import org.graalvm.compiler.nodes.calc.UnsignedRemNode;
  84 import org.graalvm.compiler.nodes.calc.ZeroExtendNode;
  85 import org.graalvm.compiler.nodes.debug.BindToRegisterNode;
  86 import org.graalvm.compiler.nodes.debug.BlackholeNode;
  87 import org.graalvm.compiler.nodes.debug.ControlFlowAnchorNode;
  88 import org.graalvm.compiler.nodes.debug.SpillRegistersNode;
  89 import org.graalvm.compiler.nodes.extended.BoxNode;
  90 import org.graalvm.compiler.nodes.extended.BranchProbabilityNode;
  91 import org.graalvm.compiler.nodes.extended.BytecodeExceptionNode.BytecodeExceptionKind;
  92 import org.graalvm.compiler.nodes.extended.GetClassNode;
  93 import org.graalvm.compiler.nodes.extended.GuardingNode;
  94 import org.graalvm.compiler.nodes.extended.JavaReadNode;
  95 import org.graalvm.compiler.nodes.extended.JavaWriteNode;
  96 import org.graalvm.compiler.nodes.extended.MembarNode;
  97 import org.graalvm.compiler.nodes.extended.OpaqueNode;
  98 import org.graalvm.compiler.nodes.extended.RawLoadNode;
  99 import org.graalvm.compiler.nodes.extended.RawStoreNode;
 100 import org.graalvm.compiler.nodes.extended.UnboxNode;
 101 import org.graalvm.compiler.nodes.extended.UnsafeMemoryLoadNode;
 102 import org.graalvm.compiler.nodes.extended.UnsafeMemoryStoreNode;
 103 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
 104 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;
 105 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin.Receiver;
 106 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
 107 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration;
 108 import org.graalvm.compiler.nodes.java.ClassIsAssignableFromNode;
 109 import org.graalvm.compiler.nodes.java.DynamicNewArrayNode;
 110 import org.graalvm.compiler.nodes.java.DynamicNewInstanceNode;
 111 import org.graalvm.compiler.nodes.java.InstanceOfDynamicNode;
 112 import org.graalvm.compiler.nodes.java.LoadFieldNode;
 113 import org.graalvm.compiler.nodes.java.RegisterFinalizerNode;
 114 import org.graalvm.compiler.nodes.java.UnsafeCompareAndExchangeNode;
 115 import org.graalvm.compiler.nodes.java.UnsafeCompareAndSwapNode;
 116 import org.graalvm.compiler.nodes.memory.HeapAccess;
 117 import org.graalvm.compiler.nodes.memory.address.IndexAddressNode;
 118 import org.graalvm.compiler.nodes.type.StampTool;
 119 import org.graalvm.compiler.nodes.util.GraphUtil;
 120 import org.graalvm.compiler.nodes.virtual.EnsureVirtualizedNode;
 121 import org.graalvm.compiler.replacements.nodes.ProfileBooleanNode;
 122 import org.graalvm.compiler.replacements.nodes.ReverseBytesNode;
 123 import org.graalvm.compiler.replacements.nodes.VirtualizableInvokeMacroNode;
 124 import org.graalvm.compiler.replacements.nodes.arithmetic.IntegerAddExactNode;
 125 import org.graalvm.compiler.replacements.nodes.arithmetic.IntegerAddExactOverflowNode;
 126 import org.graalvm.compiler.replacements.nodes.arithmetic.IntegerAddExactSplitNode;
 127 import org.graalvm.compiler.replacements.nodes.arithmetic.IntegerExactArithmeticSplitNode;
 128 import org.graalvm.compiler.replacements.nodes.arithmetic.IntegerMulExactNode;
 129 import org.graalvm.compiler.replacements.nodes.arithmetic.IntegerMulExactOverflowNode;
 130 import org.graalvm.compiler.replacements.nodes.arithmetic.IntegerMulExactSplitNode;
 131 import org.graalvm.compiler.replacements.nodes.arithmetic.IntegerSubExactNode;
 132 import org.graalvm.compiler.replacements.nodes.arithmetic.IntegerSubExactOverflowNode;
 133 import org.graalvm.compiler.replacements.nodes.arithmetic.IntegerSubExactSplitNode;
 134 import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
 135 import org.graalvm.compiler.serviceprovider.SpeculationReasonGroup;
 136 import jdk.internal.vm.compiler.word.LocationIdentity;
 137 
 138 import jdk.vm.ci.code.BytecodePosition;
 139 import jdk.vm.ci.meta.DeoptimizationAction;
 140 import jdk.vm.ci.meta.DeoptimizationReason;
 141 import jdk.vm.ci.meta.JavaConstant;
 142 import jdk.vm.ci.meta.JavaKind;
 143 import jdk.vm.ci.meta.MetaAccessProvider;
 144 import jdk.vm.ci.meta.ResolvedJavaField;
 145 import jdk.vm.ci.meta.ResolvedJavaMethod;
 146 import jdk.vm.ci.meta.ResolvedJavaType;
 147 import jdk.vm.ci.meta.SpeculationLog;
 148 import jdk.vm.ci.meta.SpeculationLog.Speculation;
 149 import jdk.vm.ci.meta.SpeculationLog.SpeculationReason;
 150 import sun.misc.Unsafe;
 151 
 152 /**
 153  * Provides non-runtime specific {@link InvocationPlugin}s.
 154  */
 155 public class StandardGraphBuilderPlugins {
 156 
 157     public static void registerInvocationPlugins(MetaAccessProvider metaAccess, SnippetReflectionProvider snippetReflection, InvocationPlugins plugins, BytecodeProvider bytecodeProvider,
 158                     boolean allowDeoptimization, boolean explicitUnsafeNullChecks) {
 159         registerObjectPlugins(plugins);
 160         registerClassPlugins(plugins);
 161         registerMathPlugins(plugins, allowDeoptimization);
 162         registerStrictMathPlugins(plugins);
 163         registerUnsignedMathPlugins(plugins);
 164         registerStringPlugins(plugins, bytecodeProvider, snippetReflection);
 165         registerCharacterPlugins(plugins);
 166         registerShortPlugins(plugins);
 167         registerIntegerLongPlugins(plugins, JavaKind.Int);
 168         registerIntegerLongPlugins(plugins, JavaKind.Long);
 169         registerFloatPlugins(plugins);
 170         registerDoublePlugins(plugins);
 171         registerArraysPlugins(plugins, bytecodeProvider);
 172         registerArrayPlugins(plugins, bytecodeProvider);
 173         registerUnsafePlugins(plugins, bytecodeProvider, explicitUnsafeNullChecks);
 174         registerEdgesPlugins(metaAccess, plugins);
 175         registerGraalDirectivesPlugins(plugins);
 176         registerBoxingPlugins(plugins);
 177         registerJMHBlackholePlugins(plugins, bytecodeProvider);
 178         registerJFRThrowablePlugins(plugins, bytecodeProvider);
 179         registerMethodHandleImplPlugins(plugins, snippetReflection, bytecodeProvider);
 180         registerJcovCollectPlugins(plugins, bytecodeProvider);
 181     }
 182 
 183     private static final Field STRING_VALUE_FIELD;
 184     private static final Field STRING_CODER_FIELD;
 185 
 186     static {
 187         Field coder = null;
 188         try {
 189             STRING_VALUE_FIELD = String.class.getDeclaredField("value");
 190             if (JavaVersionUtil.JAVA_SPEC > 8) {
 191                 coder = String.class.getDeclaredField("coder");
 192             }
 193         } catch (NoSuchFieldException e) {
 194             throw new GraalError(e);
 195         }
 196         STRING_CODER_FIELD = coder;
 197     }
 198 
 199     private static void registerStringPlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider, SnippetReflectionProvider snippetReflection) {
 200         final Registration r = new Registration(plugins, String.class, bytecodeProvider);
 201         r.register1("hashCode", Receiver.class, new InvocationPlugin() {
 202             @Override
 203             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
 204                 if (receiver.isConstant()) {
 205                     String s = snippetReflection.asObject(String.class, (JavaConstant) receiver.get().asConstant());
 206                     if (s != null) {
 207                         b.addPush(JavaKind.Int, b.add(ConstantNode.forInt(s.hashCode())));
 208                         return true;
 209                     }
 210                 }
 211                 return false;
 212             }
 213         });
 214         r.register1("intern", Receiver.class, new InvocationPlugin() {
 215             @Override
 216             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
 217                 if (receiver.isConstant()) {
 218                     String s = snippetReflection.asObject(String.class, (JavaConstant) receiver.get().asConstant());
 219                     if (s != null) {
 220                         JavaConstant interned = snippetReflection.forObject(s.intern());
 221                         b.addPush(JavaKind.Object, b.add(ConstantNode.forConstant(interned, b.getMetaAccess(), b.getGraph())));
 222                         return true;
 223                     }
 224                 }
 225                 return false;
 226             }
 227         });
 228 
 229         if (JavaVersionUtil.JAVA_SPEC <= 8) {
 230             r.registerMethodSubstitution(StringSubstitutions.class, "equals", Receiver.class, Object.class);
 231 
 232             r.register7("indexOf", char[].class, int.class, int.class, char[].class, int.class, int.class, int.class, new StringIndexOfConstantPlugin());
 233 
 234             Registration sr = new Registration(plugins, StringSubstitutions.class);
 235             sr.register1("getValue", String.class, new InvocationPlugin() {
 236                 @Override
 237                 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 238                     ResolvedJavaField field = b.getMetaAccess().lookupJavaField(STRING_VALUE_FIELD);
 239                     b.addPush(JavaKind.Object, LoadFieldNode.create(b.getConstantFieldProvider(), b.getConstantReflection(), b.getMetaAccess(),
 240                                     b.getOptions(), b.getAssumptions(), value, field, false, false));
 241                     return true;
 242                 }
 243             });
 244         } else {
 245             r.registerMethodSubstitution(JDK9StringSubstitutions.class, "equals", Receiver.class, Object.class);
 246             Registration utf16sub = new Registration(plugins, StringUTF16Substitutions.class, bytecodeProvider);
 247             utf16sub.register2("getCharDirect", byte[].class, int.class, new InvocationPlugin() {
 248                 @Override
 249                 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg1, ValueNode arg2) {
 250                     b.addPush(JavaKind.Char, new JavaReadNode(JavaKind.Char, new IndexAddressNode(arg1, arg2, JavaKind.Byte), NamedLocationIdentity.getArrayLocation(JavaKind.Byte),
 251                                     HeapAccess.BarrierType.NONE, false));
 252                     return true;
 253                 }
 254             });
 255             utf16sub.register3("putCharDirect", byte[].class, int.class, int.class, new InvocationPlugin() {
 256                 @Override
 257                 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg1, ValueNode arg2, ValueNode arg3) {
 258                     b.add(new JavaWriteNode(JavaKind.Char, new IndexAddressNode(arg1, arg2, JavaKind.Byte), NamedLocationIdentity.getArrayLocation(JavaKind.Byte), arg3,
 259                                     HeapAccess.BarrierType.NONE, false));
 260                     return true;
 261                 }
 262             });
 263 
 264             final Registration latin1r = new Registration(plugins, "java.lang.StringLatin1", bytecodeProvider);
 265             latin1r.register5("indexOf", byte[].class, int.class, byte[].class, int.class, int.class, new StringLatin1IndexOfConstantPlugin());
 266 
 267             final Registration utf16r = new Registration(plugins, "java.lang.StringUTF16", bytecodeProvider);
 268             utf16r.register5("indexOfUnsafe", byte[].class, int.class, byte[].class, int.class, int.class, new StringUTF16IndexOfConstantPlugin());
 269             utf16r.setAllowOverwrite(true);
 270             utf16r.registerMethodSubstitution(StringUTF16Substitutions.class, "getChar", byte[].class, int.class);
 271             utf16r.registerMethodSubstitution(StringUTF16Substitutions.class, "putChar", byte[].class, int.class, int.class);
 272 
 273             Registration sr = new Registration(plugins, JDK9StringSubstitutions.class);
 274             sr.register1("getValue", String.class, new InvocationPlugin() {
 275                 @Override
 276                 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 277                     ResolvedJavaField field = b.getMetaAccess().lookupJavaField(STRING_VALUE_FIELD);
 278                     b.addPush(JavaKind.Object, LoadFieldNode.create(b.getConstantFieldProvider(), b.getConstantReflection(), b.getMetaAccess(),
 279                                     b.getOptions(), b.getAssumptions(), value, field, false, false));
 280                     return true;
 281                 }
 282             });
 283             sr.register1("getCoder", String.class, new InvocationPlugin() {
 284                 @Override
 285                 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 286                     ResolvedJavaField field = b.getMetaAccess().lookupJavaField(STRING_CODER_FIELD);
 287                     b.addPush(JavaKind.Int, LoadFieldNode.create(b.getConstantFieldProvider(), b.getConstantReflection(), b.getMetaAccess(),
 288                                     b.getOptions(), b.getAssumptions(), value, field, false, false));
 289                     return true;
 290                 }
 291             });
 292         }
 293     }
 294 
 295     private static void registerArraysPlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider) {
 296         Registration r = new Registration(plugins, Arrays.class, bytecodeProvider);
 297         r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", boolean[].class, boolean[].class);
 298         r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", byte[].class, byte[].class);
 299         r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", short[].class, short[].class);
 300         r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", char[].class, char[].class);
 301         r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", int[].class, int[].class);
 302         r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", long[].class, long[].class);
 303     }
 304 
 305     private static void registerArrayPlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider) {
 306         Registration r = new Registration(plugins, Array.class, bytecodeProvider);
 307         r.register2("newInstance", Class.class, int.class, new InvocationPlugin() {
 308             @Override
 309             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unused, ValueNode componentType, ValueNode length) {
 310                 b.addPush(JavaKind.Object, new DynamicNewArrayNode(componentType, length, true));
 311                 return true;
 312             }
 313         });
 314         r.registerMethodSubstitution(ArraySubstitutions.class, "getLength", Object.class);
 315     }
 316 
 317     /**
 318      * The intrinsic for {@link Math#sqrt(double)} is shared with {@link StrictMath#sqrt(double)}.
 319      *
 320      * @see "http://hg.openjdk.java.net/jdk/jdk/file/621efe32eb0b/src/hotspot/share/oops/method.cpp#l1504"
 321      */
 322     static final class MathSqrtPlugin implements InvocationPlugin {
 323         @Override
 324         public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 325             b.push(JavaKind.Double, b.append(SqrtNode.create(value, NodeView.DEFAULT)));
 326             return true;
 327         }
 328     }
 329 
 330     private abstract static class UnsafeCompareAndUpdatePluginsRegistrar {
 331         public void register(Registration r, String casPrefix, boolean explicitUnsafeNullChecks, JavaKind[] compareAndSwapTypes, boolean java11OrEarlier) {
 332             for (JavaKind kind : compareAndSwapTypes) {
 333                 Class<?> javaClass = kind == JavaKind.Object ? Object.class : kind.toJavaClass();
 334                 String kindName = (kind == JavaKind.Object && !java11OrEarlier) ? "Reference" : kind.name();
 335                 r.register5(casPrefix + kindName, Receiver.class, Object.class, long.class, javaClass, javaClass, new UnsafeAccessPlugin(returnKind(kind), explicitUnsafeNullChecks) {
 336                     @Override
 337                     public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unsafe, ValueNode object, ValueNode offset, ValueNode expected, ValueNode x) {
 338                         // Emits a null-check for the otherwise unused receiver
 339                         unsafe.get();
 340                         createUnsafeAccess(object, b, (obj, loc) -> UnsafeCompareAndUpdatePluginsRegistrar.this.createNode(obj, offset, expected, x, kind, loc));
 341                         return true;
 342                     }
 343                 });
 344             }
 345         }
 346 
 347         public abstract FixedWithNextNode createNode(ValueNode object, ValueNode offset, ValueNode expected, ValueNode newValue, JavaKind kind, LocationIdentity identity);
 348 
 349         public abstract JavaKind returnKind(JavaKind accessKind);
 350     }
 351 
 352     private static class UnsafeCompareAndSwapPluginsRegistrar extends UnsafeCompareAndUpdatePluginsRegistrar {
 353         @Override
 354         public FixedWithNextNode createNode(ValueNode object, ValueNode offset, ValueNode expected, ValueNode newValue, JavaKind kind, LocationIdentity identity) {
 355             return new UnsafeCompareAndSwapNode(object, offset, expected, newValue, kind, identity);
 356         }
 357 
 358         @Override
 359         public JavaKind returnKind(JavaKind accessKind) {
 360             return JavaKind.Boolean.getStackKind();
 361         }
 362     }
 363 
 364     private static UnsafeCompareAndSwapPluginsRegistrar unsafeCompareAndSwapPluginsRegistrar = new UnsafeCompareAndSwapPluginsRegistrar();
 365 
 366     private static class UnsafeCompareAndExchangePluginsRegistrar extends UnsafeCompareAndUpdatePluginsRegistrar {
 367         @Override
 368         public FixedWithNextNode createNode(ValueNode object, ValueNode offset, ValueNode expected, ValueNode newValue, JavaKind kind, LocationIdentity identity) {
 369             return new UnsafeCompareAndExchangeNode(object, offset, expected, newValue, kind, identity);
 370         }
 371 
 372         @Override
 373         public JavaKind returnKind(JavaKind accessKind) {
 374             if (accessKind.isNumericInteger()) {
 375                 return accessKind.getStackKind();
 376             } else {
 377                 return accessKind;
 378             }
 379         }
 380     }
 381 
 382     private static UnsafeCompareAndExchangePluginsRegistrar unsafeCompareAndExchangePluginsRegistrar = new UnsafeCompareAndExchangePluginsRegistrar();
 383 
 384     public static void registerPlatformSpecificUnsafePlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider, boolean explicitUnsafeNullChecks, JavaKind[] supportedCasKinds) {
 385         registerPlatformSpecificUnsafePlugins(supportedCasKinds, new Registration(plugins, Unsafe.class), true, explicitUnsafeNullChecks);
 386         if (JavaVersionUtil.JAVA_SPEC > 8) {
 387             registerPlatformSpecificUnsafePlugins(supportedCasKinds, new Registration(plugins, "jdk.internal.misc.Unsafe", bytecodeProvider), false, explicitUnsafeNullChecks);
 388         }
 389 
 390     }
 391 
 392     private static void registerPlatformSpecificUnsafePlugins(JavaKind[] supportedCasKinds, Registration r, boolean java8OrEarlier, boolean explicitUnsafeNullChecks) {
 393         if (java8OrEarlier) {
 394             unsafeCompareAndSwapPluginsRegistrar.register(r, "compareAndSwap", explicitUnsafeNullChecks, new JavaKind[]{JavaKind.Int, JavaKind.Long, JavaKind.Object}, true);
 395         } else {
 396             unsafeCompareAndSwapPluginsRegistrar.register(r, "compareAndSet", explicitUnsafeNullChecks, supportedCasKinds, JavaVersionUtil.JAVA_SPEC <= 11);
 397             unsafeCompareAndExchangePluginsRegistrar.register(r, "compareAndExchange", explicitUnsafeNullChecks, supportedCasKinds, JavaVersionUtil.JAVA_SPEC <= 11);
 398         }
 399     }
 400 
 401     private static void registerUnsafePlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider, boolean explicitUnsafeNullChecks) {
 402         registerUnsafePlugins(new Registration(plugins, Unsafe.class), true, explicitUnsafeNullChecks);
 403         if (JavaVersionUtil.JAVA_SPEC > 8) {
 404             registerUnsafePlugins(new Registration(plugins, "jdk.internal.misc.Unsafe", bytecodeProvider), false, explicitUnsafeNullChecks);
 405         }
 406     }
 407 
 408     private static void registerUnsafePlugins(Registration r, boolean sunMiscUnsafe, boolean explicitUnsafeNullChecks) {
 409         for (JavaKind kind : JavaKind.values()) {
 410             if ((kind.isPrimitive() && kind != JavaKind.Void) || kind == JavaKind.Object) {
 411                 Class<?> javaClass = kind == JavaKind.Object ? Object.class : kind.toJavaClass();
 412                 String kindName = (kind == JavaKind.Object && !sunMiscUnsafe && !(JavaVersionUtil.JAVA_SPEC <= 11)) ? "Reference" : kind.name();
 413                 String getName = "get" + kindName;
 414                 String putName = "put" + kindName;
 415                 // Object-based accesses
 416                 r.register3(getName, Receiver.class, Object.class, long.class, new UnsafeGetPlugin(kind, explicitUnsafeNullChecks));
 417                 r.register4(putName, Receiver.class, Object.class, long.class, javaClass, new UnsafePutPlugin(kind, explicitUnsafeNullChecks));
 418                 // Volatile object-based accesses
 419                 r.register3(getName + "Volatile", Receiver.class, Object.class, long.class, new UnsafeGetPlugin(kind, AccessKind.VOLATILE, explicitUnsafeNullChecks));
 420                 r.register4(putName + "Volatile", Receiver.class, Object.class, long.class, javaClass, new UnsafePutPlugin(kind, AccessKind.VOLATILE, explicitUnsafeNullChecks));
 421                 // Ordered object-based accesses
 422                 if (sunMiscUnsafe) {
 423                     if (kind == JavaKind.Int || kind == JavaKind.Long || kind == JavaKind.Object) {
 424                         r.register4("putOrdered" + kindName, Receiver.class, Object.class, long.class, javaClass, new UnsafePutPlugin(kind, AccessKind.RELEASE_ACQUIRE, explicitUnsafeNullChecks));
 425                     }
 426                 } else {
 427                     r.register4("put" + kindName + "Release", Receiver.class, Object.class, long.class, javaClass, new UnsafePutPlugin(kind, AccessKind.RELEASE_ACQUIRE, explicitUnsafeNullChecks));
 428                     r.register3("get" + kindName + "Acquire", Receiver.class, Object.class, long.class, new UnsafeGetPlugin(kind, AccessKind.RELEASE_ACQUIRE, explicitUnsafeNullChecks));
 429                     r.register4("put" + kindName + "Opaque", Receiver.class, Object.class, long.class, javaClass, new UnsafePutPlugin(kind, AccessKind.OPAQUE, explicitUnsafeNullChecks));
 430                     r.register3("get" + kindName + "Opaque", Receiver.class, Object.class, long.class, new UnsafeGetPlugin(kind, AccessKind.OPAQUE, explicitUnsafeNullChecks));
 431                 }
 432                 if (kind != JavaKind.Boolean && kind != JavaKind.Object) {
 433                     // Raw accesses to memory addresses
 434                     r.register2(getName, Receiver.class, long.class, new UnsafeGetPlugin(kind, explicitUnsafeNullChecks));
 435                     r.register3(putName, Receiver.class, long.class, kind.toJavaClass(), new UnsafePutPlugin(kind, explicitUnsafeNullChecks));
 436                 }
 437             }
 438         }
 439 
 440         // Accesses to native memory addresses.
 441         r.register2("getAddress", Receiver.class, long.class, new UnsafeGetPlugin(JavaKind.Long, explicitUnsafeNullChecks));
 442         r.register3("putAddress", Receiver.class, long.class, long.class, new UnsafePutPlugin(JavaKind.Long, explicitUnsafeNullChecks));
 443 
 444         r.register2("allocateInstance", Receiver.class, Class.class, new InvocationPlugin() {
 445 
 446             @Override
 447             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unsafe, ValueNode clazz) {
 448                 // Emits a null-check for the otherwise unused receiver
 449                 unsafe.get();
 450                 b.addPush(JavaKind.Object, new DynamicNewInstanceNode(b.nullCheckedValue(clazz, DeoptimizationAction.None), true));
 451                 return true;
 452             }
 453 
 454         });
 455 
 456         r.register1("loadFence", Receiver.class, new UnsafeFencePlugin(LOAD_LOAD | LOAD_STORE));
 457         r.register1("storeFence", Receiver.class, new UnsafeFencePlugin(STORE_STORE | LOAD_STORE));
 458         r.register1("fullFence", Receiver.class, new UnsafeFencePlugin(LOAD_LOAD | STORE_STORE | LOAD_STORE | STORE_LOAD));
 459     }
 460 
 461     private static void registerIntegerLongPlugins(InvocationPlugins plugins, JavaKind kind) {
 462         Class<?> declaringClass = kind.toBoxedJavaClass();
 463         Class<?> type = kind.toJavaClass();
 464         Registration r = new Registration(plugins, declaringClass);
 465         r.register1("reverseBytes", type, new InvocationPlugin() {
 466             @Override
 467             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 468                 b.push(kind, b.append(new ReverseBytesNode(value).canonical(null)));
 469                 return true;
 470             }
 471         });
 472         r.register2("divideUnsigned", type, type, new InvocationPlugin() {
 473             @Override
 474             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode dividend, ValueNode divisor) {
 475                 b.push(kind, b.append(UnsignedDivNode.create(dividend, divisor, null, NodeView.DEFAULT)));
 476                 return true;
 477             }
 478         });
 479         r.register2("remainderUnsigned", type, type, new InvocationPlugin() {
 480             @Override
 481             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode dividend, ValueNode divisor) {
 482                 b.push(kind, b.append(UnsignedRemNode.create(dividend, divisor, null, NodeView.DEFAULT)));
 483                 return true;
 484             }
 485         });
 486     }
 487 
 488     private static void registerCharacterPlugins(InvocationPlugins plugins) {
 489         Registration r = new Registration(plugins, Character.class);
 490         r.register1("reverseBytes", char.class, new InvocationPlugin() {
 491             @Override
 492             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 493                 // return (char) (Integer.reverse(i) >> 16);
 494                 ReverseBytesNode reverse = b.add(new ReverseBytesNode(value));
 495                 RightShiftNode rightShift = b.add(new RightShiftNode(reverse, b.add(ConstantNode.forInt(16))));
 496                 ZeroExtendNode charCast = b.add(new ZeroExtendNode(b.add(new NarrowNode(rightShift, 16)), 32));
 497                 b.push(JavaKind.Char, b.append(charCast.canonical(null)));
 498                 return true;
 499             }
 500         });
 501     }
 502 
 503     private static void registerShortPlugins(InvocationPlugins plugins) {
 504         Registration r = new Registration(plugins, Short.class);
 505         r.register1("reverseBytes", short.class, new InvocationPlugin() {
 506             @Override
 507             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 508                 // return (short) (Integer.reverse(i) >> 16);
 509                 ReverseBytesNode reverse = b.add(new ReverseBytesNode(value));
 510                 RightShiftNode rightShift = b.add(new RightShiftNode(reverse, b.add(ConstantNode.forInt(16))));
 511                 SignExtendNode charCast = b.add(new SignExtendNode(b.add(new NarrowNode(rightShift, 16)), 32));
 512                 b.push(JavaKind.Short, b.append(charCast.canonical(null)));
 513                 return true;
 514             }
 515         });
 516     }
 517 
 518     private static void registerFloatPlugins(InvocationPlugins plugins) {
 519         Registration r = new Registration(plugins, Float.class);
 520         r.register1("floatToRawIntBits", float.class, new InvocationPlugin() {
 521             @Override
 522             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 523                 b.push(JavaKind.Int, b.append(ReinterpretNode.create(JavaKind.Int, value, NodeView.DEFAULT)));
 524                 return true;
 525             }
 526         });
 527         r.register1("floatToIntBits", float.class, new InvocationPlugin() {
 528             @Override
 529             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 530                 LogicNode notNan = b.append(FloatEqualsNode.create(value, value, NodeView.DEFAULT));
 531                 ValueNode raw = b.append(ReinterpretNode.create(JavaKind.Int, value, NodeView.DEFAULT));
 532                 ValueNode result = b.append(ConditionalNode.create(notNan, raw, ConstantNode.forInt(0x7fc00000), NodeView.DEFAULT));
 533                 b.push(JavaKind.Int, result);
 534                 return true;
 535             }
 536         });
 537         r.register1("intBitsToFloat", int.class, new InvocationPlugin() {
 538             @Override
 539             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 540                 b.push(JavaKind.Float, b.append(ReinterpretNode.create(JavaKind.Float, value, NodeView.DEFAULT)));
 541                 return true;
 542             }
 543         });
 544     }
 545 
 546     private static void registerDoublePlugins(InvocationPlugins plugins) {
 547         Registration r = new Registration(plugins, Double.class);
 548         r.register1("doubleToRawLongBits", double.class, new InvocationPlugin() {
 549             @Override
 550             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 551                 b.push(JavaKind.Long, b.append(ReinterpretNode.create(JavaKind.Long, value, NodeView.DEFAULT)));
 552                 return true;
 553             }
 554         });
 555         r.register1("doubleToLongBits", double.class, new InvocationPlugin() {
 556             @Override
 557             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 558                 LogicNode notNan = b.append(FloatEqualsNode.create(value, value, NodeView.DEFAULT));
 559                 ValueNode raw = b.append(ReinterpretNode.create(JavaKind.Long, value, NodeView.DEFAULT));
 560                 ValueNode result = b.append(ConditionalNode.create(notNan, raw, ConstantNode.forLong(0x7ff8000000000000L), NodeView.DEFAULT));
 561                 b.push(JavaKind.Long, result);
 562                 return true;
 563             }
 564         });
 565         r.register1("longBitsToDouble", long.class, new InvocationPlugin() {
 566             @Override
 567             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 568                 b.push(JavaKind.Double, b.append(ReinterpretNode.create(JavaKind.Double, value, NodeView.DEFAULT)));
 569                 return true;
 570             }
 571         });
 572     }
 573 
 574     public enum IntegerExactOp {
 575         INTEGER_ADD_EXACT,
 576         INTEGER_INCREMENT_EXACT,
 577         INTEGER_SUBTRACT_EXACT,
 578         INTEGER_DECREMENT_EXACT,
 579         INTEGER_MULTIPLY_EXACT
 580     }
 581 
 582     private static GuardingNode createIntegerExactArithmeticGuardNode(GraphBuilderContext b, ValueNode x, ValueNode y, IntegerExactOp op) {
 583         LogicNode overflowCheck;
 584         switch (op) {
 585             case INTEGER_ADD_EXACT:
 586             case INTEGER_INCREMENT_EXACT: {
 587                 overflowCheck = new IntegerAddExactOverflowNode(x, y);
 588                 break;
 589             }
 590             case INTEGER_SUBTRACT_EXACT:
 591             case INTEGER_DECREMENT_EXACT: {
 592                 overflowCheck = new IntegerSubExactOverflowNode(x, y);
 593                 break;
 594             }
 595             case INTEGER_MULTIPLY_EXACT: {
 596                 overflowCheck = new IntegerMulExactOverflowNode(x, y);
 597                 break;
 598             }
 599             default:
 600                 throw GraalError.shouldNotReachHere("Unknown integer exact operation.");
 601         }
 602         return b.add(new FixedGuardNode(overflowCheck, DeoptimizationReason.ArithmeticException, DeoptimizationAction.InvalidateRecompile, true));
 603     }
 604 
 605     private static ValueNode createIntegerExactArithmeticNode(GraphBuilderContext b, ValueNode x, ValueNode y, IntegerExactOp op) {
 606         switch (op) {
 607             case INTEGER_ADD_EXACT:
 608             case INTEGER_INCREMENT_EXACT:
 609                 return new IntegerAddExactNode(x, y, createIntegerExactArithmeticGuardNode(b, x, y, op));
 610             case INTEGER_SUBTRACT_EXACT:
 611             case INTEGER_DECREMENT_EXACT:
 612                 return new IntegerSubExactNode(x, y, createIntegerExactArithmeticGuardNode(b, x, y, op));
 613             case INTEGER_MULTIPLY_EXACT:
 614                 return new IntegerMulExactNode(x, y, createIntegerExactArithmeticGuardNode(b, x, y, op));
 615             default:
 616                 throw GraalError.shouldNotReachHere("Unknown integer exact operation.");
 617         }
 618     }
 619 
 620     private static IntegerExactArithmeticSplitNode createIntegerExactSplit(ValueNode x, ValueNode y, AbstractBeginNode exceptionEdge, IntegerExactOp op) {
 621         switch (op) {
 622             case INTEGER_ADD_EXACT:
 623             case INTEGER_INCREMENT_EXACT:
 624                 return new IntegerAddExactSplitNode(x.stamp(NodeView.DEFAULT).unrestricted(), x, y, null, exceptionEdge);
 625             case INTEGER_SUBTRACT_EXACT:
 626             case INTEGER_DECREMENT_EXACT:
 627                 return new IntegerSubExactSplitNode(x.stamp(NodeView.DEFAULT).unrestricted(), x, y, null, exceptionEdge);
 628             case INTEGER_MULTIPLY_EXACT:
 629                 return new IntegerMulExactSplitNode(x.stamp(NodeView.DEFAULT).unrestricted(), x, y, null, exceptionEdge);
 630             default:
 631                 throw GraalError.shouldNotReachHere("Unknown integer exact operation.");
 632         }
 633     }
 634 
 635     private static void createIntegerExactOperation(GraphBuilderContext b, JavaKind kind, ValueNode x, ValueNode y, IntegerExactOp op) {
 636         if (b.needsExplicitException()) {
 637             BytecodeExceptionKind exceptionKind = kind == JavaKind.Int ? BytecodeExceptionKind.INTEGER_EXACT_OVERFLOW : BytecodeExceptionKind.LONG_EXACT_OVERFLOW;
 638             AbstractBeginNode exceptionEdge = b.genExplicitExceptionEdge(exceptionKind);
 639             IntegerExactArithmeticSplitNode split = b.addPush(kind, createIntegerExactSplit(x, y, exceptionEdge, op));
 640             split.setNext(b.add(new BeginNode()));
 641         } else {
 642             b.addPush(kind, createIntegerExactArithmeticNode(b, x, y, op));
 643         }
 644     }
 645 
 646     private static void registerMathPlugins(InvocationPlugins plugins, boolean allowDeoptimization) {
 647         Registration r = new Registration(plugins, Math.class);
 648         if (allowDeoptimization) {
 649             for (JavaKind kind : new JavaKind[]{JavaKind.Int, JavaKind.Long}) {
 650                 Class<?> type = kind.toJavaClass();
 651                 r.register1("decrementExact", type, new InvocationPlugin() {
 652                     @Override
 653                     public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x) {
 654                         ConstantNode y = b.add(ConstantNode.forIntegerKind(kind, 1));
 655                         createIntegerExactOperation(b, kind, x, y, IntegerExactOp.INTEGER_DECREMENT_EXACT);
 656                         return true;
 657                     }
 658                 });
 659 
 660                 r.register1("incrementExact", type, new InvocationPlugin() {
 661                     @Override
 662                     public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x) {
 663                         ConstantNode y = b.add(ConstantNode.forIntegerKind(kind, 1));
 664                         createIntegerExactOperation(b, kind, x, y, IntegerExactOp.INTEGER_INCREMENT_EXACT);
 665                         return true;
 666                     }
 667                 });
 668                 r.register2("addExact", type, type, new InvocationPlugin() {
 669                     @Override
 670                     public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
 671                         createIntegerExactOperation(b, kind, x, y, IntegerExactOp.INTEGER_ADD_EXACT);
 672                         return true;
 673                     }
 674                 });
 675                 r.register2("subtractExact", type, type, new InvocationPlugin() {
 676                     @Override
 677                     public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
 678                         createIntegerExactOperation(b, kind, x, y, IntegerExactOp.INTEGER_SUBTRACT_EXACT);
 679                         return true;
 680                     }
 681                 });
 682 
 683                 r.register2("multiplyExact", type, type, new InvocationPlugin() {
 684                     @Override
 685                     public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
 686                         createIntegerExactOperation(b, kind, x, y, IntegerExactOp.INTEGER_MULTIPLY_EXACT);
 687                         return true;
 688                     }
 689                 });
 690             }
 691         }
 692         r.register1("abs", Float.TYPE, new InvocationPlugin() {
 693 
 694             @Override
 695             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 696                 b.push(JavaKind.Float, b.append(new AbsNode(value).canonical(null)));
 697                 return true;
 698             }
 699         });
 700         r.register1("abs", Double.TYPE, new InvocationPlugin() {
 701             @Override
 702             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 703                 b.push(JavaKind.Double, b.append(new AbsNode(value).canonical(null)));
 704                 return true;
 705             }
 706         });
 707         r.register1("sqrt", Double.TYPE, new MathSqrtPlugin());
 708     }
 709 
 710     private static void registerStrictMathPlugins(InvocationPlugins plugins) {
 711         Registration r = new Registration(plugins, StrictMath.class);
 712         r.register1("sqrt", Double.TYPE, new MathSqrtPlugin());
 713     }
 714 
 715     public static final class StringIndexOfConstantPlugin implements InvocationPlugin {
 716         @Override
 717         public boolean inlineOnly() {
 718             return true;
 719         }
 720 
 721         @Override
 722         public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode source, ValueNode sourceOffset, ValueNode sourceCount,
 723                         ValueNode target, ValueNode targetOffset, ValueNode targetCount, ValueNode origFromIndex) {
 724             if (target.isConstant()) {
 725                 b.addPush(JavaKind.Int, new StringIndexOfNode(b.getInvokeKind(), targetMethod, b.bci(), b.getInvokeReturnStamp(b.getAssumptions()), source, sourceOffset, sourceCount,
 726                                 target, targetOffset, targetCount, origFromIndex));
 727                 return true;
 728             }
 729             return false;
 730         }
 731     }
 732 
 733     public static final class StringLatin1IndexOfConstantPlugin implements InvocationPlugin {
 734         @Override
 735         public boolean inlineOnly() {
 736             return true;
 737         }
 738 
 739         @Override
 740         public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver,
 741                         ValueNode source, ValueNode sourceCount, ValueNode target, ValueNode targetCount, ValueNode origFromIndex) {
 742             if (target.isConstant()) {
 743                 b.addPush(JavaKind.Int, new StringLatin1IndexOfNode(b.getInvokeKind(), targetMethod, b.bci(), b.getInvokeReturnStamp(b.getAssumptions()),
 744                                 source, sourceCount, target, targetCount, origFromIndex));
 745                 return true;
 746             }
 747             return false;
 748         }
 749     }
 750 
 751     public static final class StringUTF16IndexOfConstantPlugin implements InvocationPlugin {
 752         @Override
 753         public boolean inlineOnly() {
 754             return true;
 755         }
 756 
 757         @Override
 758         public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver,
 759                         ValueNode source, ValueNode sourceCount, ValueNode target, ValueNode targetCount, ValueNode origFromIndex) {
 760             if (target.isConstant()) {
 761                 b.addPush(JavaKind.Int, new StringUTF16IndexOfNode(b.getInvokeKind(), targetMethod, b.bci(), b.getInvokeReturnStamp(b.getAssumptions()),
 762                                 source, sourceCount, target, targetCount, origFromIndex));
 763                 return true;
 764             }
 765             return false;
 766         }
 767     }
 768 
 769     public static class UnsignedMathPlugin implements InvocationPlugin {
 770         private final Condition condition;
 771 
 772         public UnsignedMathPlugin(Condition condition) {
 773             this.condition = condition;
 774         }
 775 
 776         @Override
 777         public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
 778             CanonicalizedCondition canonical = condition.canonicalize();
 779             StructuredGraph graph = b.getGraph();
 780 
 781             ValueNode lhs = canonical.mustMirror() ? y : x;
 782             ValueNode rhs = canonical.mustMirror() ? x : y;
 783 
 784             ValueNode trueValue = ConstantNode.forBoolean(!canonical.mustNegate(), graph);
 785             ValueNode falseValue = ConstantNode.forBoolean(canonical.mustNegate(), graph);
 786 
 787             LogicNode compare = CompareNode.createCompareNode(graph, b.getConstantReflection(), b.getMetaAccess(), b.getOptions(), null, canonical.getCanonicalCondition(), lhs, rhs, NodeView.DEFAULT);
 788             b.addPush(JavaKind.Boolean, new ConditionalNode(compare, trueValue, falseValue));
 789             return true;
 790         }
 791     }
 792 
 793     private static void registerUnsignedMathPlugins(InvocationPlugins plugins) {
 794         Registration r = new Registration(plugins, UnsignedMath.class);
 795         r.register2("aboveThan", int.class, int.class, new UnsignedMathPlugin(Condition.AT));
 796         r.register2("aboveThan", long.class, long.class, new UnsignedMathPlugin(Condition.AT));
 797         r.register2("belowThan", int.class, int.class, new UnsignedMathPlugin(Condition.BT));
 798         r.register2("belowThan", long.class, long.class, new UnsignedMathPlugin(Condition.BT));
 799         r.register2("aboveOrEqual", int.class, int.class, new UnsignedMathPlugin(Condition.AE));
 800         r.register2("aboveOrEqual", long.class, long.class, new UnsignedMathPlugin(Condition.AE));
 801         r.register2("belowOrEqual", int.class, int.class, new UnsignedMathPlugin(Condition.BE));
 802         r.register2("belowOrEqual", long.class, long.class, new UnsignedMathPlugin(Condition.BE));
 803     }
 804 
 805     protected static void registerBoxingPlugins(InvocationPlugins plugins) {
 806         for (JavaKind kind : JavaKind.values()) {
 807             if (kind.isPrimitive() && kind != JavaKind.Void) {
 808                 new BoxPlugin(kind).register(plugins);
 809                 new UnboxPlugin(kind).register(plugins);
 810             }
 811         }
 812     }
 813 
 814     private static void registerObjectPlugins(InvocationPlugins plugins) {
 815         Registration r = new Registration(plugins, Object.class);
 816         r.register1("<init>", Receiver.class, new InvocationPlugin() {
 817             @Override
 818             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
 819                 /*
 820                  * Object.<init> is a common instrumentation point so only perform this rewrite if
 821                  * the current definition is the normal empty method with a single return bytecode.
 822                  * The finalizer registration will instead be performed by the BytecodeParser.
 823                  */
 824                 if (targetMethod.getCodeSize() == 1) {
 825                     ValueNode object = receiver.get();
 826                     if (RegisterFinalizerNode.mayHaveFinalizer(object, b.getAssumptions())) {
 827                         b.add(new RegisterFinalizerNode(object));
 828                     }
 829                     return true;
 830                 }
 831                 return false;
 832             }
 833         });
 834         r.register1("getClass", Receiver.class, new InvocationPlugin() {
 835             @Override
 836             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
 837                 ValueNode object = receiver.get();
 838                 ValueNode folded = GetClassNode.tryFold(b.getMetaAccess(), b.getConstantReflection(), NodeView.DEFAULT, GraphUtil.originalValue(object));
 839                 if (folded != null) {
 840                     b.addPush(JavaKind.Object, folded);
 841                 } else {
 842                     Stamp stamp = StampFactory.objectNonNull(TypeReference.createTrusted(b.getAssumptions(), b.getMetaAccess().lookupJavaType(Class.class)));
 843                     b.addPush(JavaKind.Object, new GetClassNode(stamp, object));
 844                 }
 845                 return true;
 846             }
 847         });
 848     }
 849 
 850     private static void registerClassPlugins(InvocationPlugins plugins) {
 851         Registration r = new Registration(plugins, Class.class);
 852         r.register2("isInstance", Receiver.class, Object.class, new InvocationPlugin() {
 853             @Override
 854             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver type, ValueNode object) {
 855                 LogicNode condition = b.append(InstanceOfDynamicNode.create(b.getAssumptions(), b.getConstantReflection(), type.get(), object, false));
 856                 b.push(JavaKind.Boolean, b.append(new ConditionalNode(condition).canonical(null)));
 857                 return true;
 858             }
 859         });
 860         r.register2("isAssignableFrom", Receiver.class, Class.class, new InvocationPlugin() {
 861             @Override
 862             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver type, ValueNode otherType) {
 863                 ClassIsAssignableFromNode condition = b.append(new ClassIsAssignableFromNode(type.get(), otherType));
 864                 b.push(JavaKind.Boolean, b.append(new ConditionalNode(condition).canonical(null)));
 865                 return true;
 866             }
 867         });
 868 
 869         r.register2("cast", Receiver.class, Object.class, new InvocationPlugin() {
 870             @Override
 871             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode object) {
 872                 b.genCheckcastDynamic(object, receiver.get());
 873                 return true;
 874             }
 875 
 876             @Override
 877             public boolean inlineOnly() {
 878                 return true;
 879             }
 880         });
 881     }
 882 
 883     /**
 884      * Substitutions for improving the performance of some critical methods in {@link Edges}. These
 885      * substitutions improve the performance by forcing the relevant methods to be inlined
 886      * (intrinsification being a special form of inlining) and removing a checked cast.
 887      */
 888     private static void registerEdgesPlugins(MetaAccessProvider metaAccess, InvocationPlugins plugins) {
 889         Registration r = new Registration(plugins, Edges.class);
 890         for (Class<?> c : new Class<?>[]{Node.class, NodeList.class}) {
 891             r.register2("get" + c.getSimpleName() + "Unsafe", Node.class, long.class, new InvocationPlugin() {
 892                 @Override
 893                 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode node, ValueNode offset) {
 894                     ObjectStamp stamp = StampFactory.object(TypeReference.createTrusted(b.getAssumptions(), metaAccess.lookupJavaType(c)));
 895                     RawLoadNode value = b.add(new RawLoadNode(stamp, node, offset, LocationIdentity.any(), JavaKind.Object));
 896                     b.addPush(JavaKind.Object, value);
 897                     return true;
 898                 }
 899             });
 900             r.register3("put" + c.getSimpleName() + "Unsafe", Node.class, long.class, c, new InvocationPlugin() {
 901                 @Override
 902                 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode node, ValueNode offset, ValueNode value) {
 903                     b.add(new RawStoreNode(node, offset, value, JavaKind.Object, LocationIdentity.any()));
 904                     return true;
 905                 }
 906             });
 907         }
 908     }
 909 
 910     public static class BoxPlugin implements InvocationPlugin {
 911 
 912         private final JavaKind kind;
 913 
 914         BoxPlugin(JavaKind kind) {
 915             this.kind = kind;
 916         }
 917 
 918         @Override
 919         public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 920             if (b.parsingIntrinsic()) {
 921                 ResolvedJavaMethod rootMethod = b.getGraph().method();
 922                 if (b.getMetaAccess().lookupJavaType(BoxingSnippets.class).isAssignableFrom(rootMethod.getDeclaringClass())) {
 923                     // Disable invocation plugins for boxing snippets so that the
 924                     // original JDK methods are inlined
 925                     return false;
 926                 }
 927             }
 928             ResolvedJavaType resultType = b.getMetaAccess().lookupJavaType(kind.toBoxedJavaClass());
 929             b.addPush(JavaKind.Object, new BoxNode(value, resultType, kind));
 930             return true;
 931         }
 932 
 933         void register(InvocationPlugins plugins) {
 934             plugins.register(this, kind.toBoxedJavaClass(), "valueOf", kind.toJavaClass());
 935         }
 936     }
 937 
 938     public static class UnboxPlugin implements InvocationPlugin {
 939 
 940         private final JavaKind kind;
 941 
 942         UnboxPlugin(JavaKind kind) {
 943             this.kind = kind;
 944         }
 945 
 946         @Override
 947         public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
 948             if (b.parsingIntrinsic()) {
 949                 ResolvedJavaMethod rootMethod = b.getGraph().method();
 950                 if (b.getMetaAccess().lookupJavaType(BoxingSnippets.class).isAssignableFrom(rootMethod.getDeclaringClass())) {
 951                     // Disable invocation plugins for unboxing snippets so that the
 952                     // original JDK methods are inlined
 953                     return false;
 954                 }
 955             }
 956             ValueNode valueNode = UnboxNode.create(b.getMetaAccess(), b.getConstantReflection(), receiver.get(), kind);
 957             b.addPush(kind, valueNode);
 958             return true;
 959         }
 960 
 961         void register(InvocationPlugins plugins) {
 962             String name = kind.toJavaClass().getSimpleName() + "Value";
 963             plugins.register(this, kind.toBoxedJavaClass(), name, Receiver.class);
 964         }
 965     }
 966 
 967     /**
 968      * The new memory order modes (JDK9+) are defined with cumulative effect, from weakest to
 969      * strongest: Plain, Opaque, Release/Acquire, and Volatile. The existing Plain and Volatile
 970      * modes are defined compatibly with their pre-JDK 9 forms. Any guaranteed property of a weaker
 971      * mode, plus more, holds for a stronger mode. (Conversely, implementations are allowed to use a
 972      * stronger mode than requested for any access.) In JDK 9, these are provided without a full
 973      * formal specification.
 974      */
 975     enum AccessKind {
 976         PLAIN(0, 0, 0, 0, false),
 977         /**
 978          * Opaque accesses are wrapped by dummy membars to avoid floating/hoisting, this is stronger
 979          * than required since Opaque mode does not directly impose any ordering constraints with
 980          * respect to other variables beyond Plain mode.
 981          */
 982         OPAQUE(0, 0, 0, 0, true),
 983         RELEASE_ACQUIRE(0, LOAD_LOAD | LOAD_STORE, LOAD_STORE | STORE_STORE, 0, true),
 984         VOLATILE(JMM_PRE_VOLATILE_READ, JMM_POST_VOLATILE_READ, JMM_PRE_VOLATILE_WRITE, JMM_POST_VOLATILE_WRITE, true);
 985 
 986         public final boolean emitBarriers;
 987         public final int preReadBarriers;
 988         public final int postReadBarriers;
 989         public final int preWriteBarriers;
 990         public final int postWriteBarriers;
 991 
 992         AccessKind(int preReadBarriers, int postReadBarriers, int preWriteBarriers, int postWriteBarriers, boolean emitBarriers) {
 993             this.emitBarriers = emitBarriers;
 994             this.preReadBarriers = preReadBarriers;
 995             this.postReadBarriers = postReadBarriers;
 996             this.preWriteBarriers = preWriteBarriers;
 997             this.postWriteBarriers = postWriteBarriers;
 998         }
 999     }
1000 
1001     /**
1002      * Unsafe access relative to null object is an access to off-heap memory. As linear pointer
1003      * compression uses non-zero null, here null object must be replaced with zero constant.
1004      */
1005     public abstract static class UnsafeAccessPlugin implements InvocationPlugin {
1006         @FunctionalInterface
1007         public interface UnsafeNodeConstructor {
1008             FixedWithNextNode create(ValueNode value, LocationIdentity location);
1009         }
1010 
1011         protected final JavaKind unsafeAccessKind;
1012         private final boolean explicitUnsafeNullChecks;
1013 
1014         public UnsafeAccessPlugin(JavaKind kind, boolean explicitUnsafeNullChecks) {
1015             unsafeAccessKind = kind;
1016             this.explicitUnsafeNullChecks = explicitUnsafeNullChecks;
1017         }
1018 
1019         private static FixedWithNextNode createObjectAccessNode(ValueNode value, UnsafeNodeConstructor nodeConstructor) {
1020             return nodeConstructor.create(value, LocationIdentity.ANY_LOCATION);
1021         }
1022 
1023         private static FixedWithNextNode createMemoryAccessNode(StructuredGraph graph, UnsafeNodeConstructor nodeConstructor) {
1024             return nodeConstructor.create(ConstantNode.forLong(0L, graph), OFF_HEAP_LOCATION);
1025         }
1026 
1027         private static boolean isLoad(ValueNode node) {
1028             return node.getStackKind() != JavaKind.Void;
1029         }
1030 
1031         private void setResult(ValueNode node, GraphBuilderContext b) {
1032             if (isLoad(node)) {
1033                 b.addPush(unsafeAccessKind, node);
1034             } else {
1035                 b.add(node);
1036             }
1037         }
1038 
1039         protected final void createUnsafeAccess(ValueNode value, GraphBuilderContext b, UnsafeNodeConstructor nodeConstructor) {
1040             StructuredGraph graph = b.getGraph();
1041             graph.markUnsafeAccess();
1042             /* For unsafe access object pointers can only be stored in the heap */
1043             if (unsafeAccessKind == JavaKind.Object) {
1044                 setResult(createObjectAccessNode(value, nodeConstructor), b);
1045             } else if (StampTool.isPointerAlwaysNull(value)) {
1046                 setResult(createMemoryAccessNode(graph, nodeConstructor), b);
1047             } else if (!explicitUnsafeNullChecks || StampTool.isPointerNonNull(value)) {
1048                 setResult(createObjectAccessNode(value, nodeConstructor), b);
1049             } else {
1050                 FixedWithNextNode objectAccess = graph.add(createObjectAccessNode(value, nodeConstructor));
1051                 FixedWithNextNode memoryAccess = graph.add(createMemoryAccessNode(graph, nodeConstructor));
1052                 FixedWithNextNode[] accessNodes = new FixedWithNextNode[]{objectAccess, memoryAccess};
1053 
1054                 LogicNode condition = graph.addOrUniqueWithInputs(IsNullNode.create(value));
1055                 b.add(new IfNode(condition, memoryAccess, objectAccess, 0.5));
1056 
1057                 MergeNode merge = b.append(new MergeNode());
1058                 for (FixedWithNextNode node : accessNodes) {
1059                     EndNode endNode = graph.add(new EndNode());
1060                     node.setNext(endNode);
1061                     if (node instanceof StateSplit) {
1062                         if (isLoad(node)) {
1063                             /*
1064                              * Temporarily push the access node so that the frame state has the node
1065                              * on the expression stack.
1066                              */
1067                             b.push(unsafeAccessKind, node);
1068                         }
1069                         b.setStateAfter((StateSplit) node);
1070                         if (isLoad(node)) {
1071                             ValueNode popped = b.pop(unsafeAccessKind);
1072                             assert popped == node;
1073                         }
1074                     }
1075                     merge.addForwardEnd(endNode);
1076                 }
1077 
1078                 if (isLoad(objectAccess)) {
1079                     ValuePhiNode phi = new ValuePhiNode(objectAccess.stamp(NodeView.DEFAULT), merge, accessNodes);
1080                     b.push(unsafeAccessKind, graph.addOrUnique(phi));
1081                 }
1082                 b.setStateAfter(merge);
1083             }
1084         }
1085     }
1086 
1087     public static class UnsafeGetPlugin extends UnsafeAccessPlugin {
1088         private final AccessKind accessKind;
1089 
1090         public UnsafeGetPlugin(JavaKind returnKind, boolean explicitUnsafeNullChecks) {
1091             this(returnKind, AccessKind.PLAIN, explicitUnsafeNullChecks);
1092         }
1093 
1094         public UnsafeGetPlugin(JavaKind kind, AccessKind accessKind, boolean explicitUnsafeNullChecks) {
1095             super(kind, explicitUnsafeNullChecks);
1096             this.accessKind = accessKind;
1097         }
1098 
1099         @Override
1100         public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unsafe, ValueNode address) {
1101             // Emits a null-check for the otherwise unused receiver
1102             unsafe.get();
1103             b.addPush(unsafeAccessKind, new UnsafeMemoryLoadNode(address, unsafeAccessKind, OFF_HEAP_LOCATION));
1104             b.getGraph().markUnsafeAccess();
1105             return true;
1106         }
1107 
1108         @Override
1109         public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unsafe, ValueNode object, ValueNode offset) {
1110             // Emits a null-check for the otherwise unused receiver
1111             unsafe.get();
1112             if (accessKind.emitBarriers) {
1113                 b.add(new MembarNode(accessKind.preReadBarriers));
1114             }
1115             createUnsafeAccess(object, b, (obj, loc) -> new RawLoadNode(obj, offset, unsafeAccessKind, loc));
1116             if (accessKind.emitBarriers) {
1117                 b.add(new MembarNode(accessKind.postReadBarriers));
1118             }
1119             return true;
1120         }
1121     }
1122 
1123     public static class UnsafePutPlugin extends UnsafeAccessPlugin {
1124         private final AccessKind accessKind;
1125 
1126         public UnsafePutPlugin(JavaKind kind, boolean explicitUnsafeNullChecks) {
1127             this(kind, AccessKind.PLAIN, explicitUnsafeNullChecks);
1128         }
1129 
1130         private UnsafePutPlugin(JavaKind kind, AccessKind accessKind, boolean explicitUnsafeNullChecks) {
1131             super(kind, explicitUnsafeNullChecks);
1132             this.accessKind = accessKind;
1133         }
1134 
1135         @Override
1136         public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unsafe, ValueNode address, ValueNode value) {
1137             assert !accessKind.emitBarriers : "Barriers for address based Unsafe put is not supported.";
1138             // Emits a null-check for the otherwise unused receiver
1139             unsafe.get();
1140             b.add(new UnsafeMemoryStoreNode(address, value, unsafeAccessKind, OFF_HEAP_LOCATION));
1141             b.getGraph().markUnsafeAccess();
1142             return true;
1143         }
1144 
1145         @Override
1146         public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unsafe, ValueNode object, ValueNode offset, ValueNode value) {
1147             // Emits a null-check for the otherwise unused receiver
1148             unsafe.get();
1149             if (accessKind.emitBarriers) {
1150                 b.add(new MembarNode(accessKind.preWriteBarriers));
1151             }
1152             ValueNode maskedValue = b.maskSubWordValue(value, unsafeAccessKind);
1153             createUnsafeAccess(object, b, (obj, loc) -> new RawStoreNode(obj, offset, maskedValue, unsafeAccessKind, loc));
1154             if (accessKind.emitBarriers) {
1155                 b.add(new MembarNode(accessKind.postWriteBarriers));
1156             }
1157             return true;
1158         }
1159     }
1160 
1161     public static class UnsafeFencePlugin implements InvocationPlugin {
1162 
1163         private final int barriers;
1164 
1165         public UnsafeFencePlugin(int barriers) {
1166             this.barriers = barriers;
1167         }
1168 
1169         @Override
1170         public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unsafe) {
1171             // Emits a null-check for the otherwise unused receiver
1172             unsafe.get();
1173             b.add(new MembarNode(barriers));
1174             return true;
1175         }
1176     }
1177 
1178     private static final SpeculationReasonGroup DIRECTIVE_SPECULATIONS = new SpeculationReasonGroup("GraalDirective", BytecodePosition.class);
1179 
1180     private static void registerGraalDirectivesPlugins(InvocationPlugins plugins) {
1181         Registration r = new Registration(plugins, GraalDirectives.class);
1182         r.register0("deoptimize", new InvocationPlugin() {
1183             @Override
1184             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
1185                 b.add(new DeoptimizeNode(DeoptimizationAction.None, DeoptimizationReason.TransferToInterpreter));
1186                 return true;
1187             }
1188         });
1189 
1190         r.register0("deoptimizeAndInvalidate", new InvocationPlugin() {
1191             @Override
1192             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
1193                 b.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TransferToInterpreter));
1194                 return true;
1195             }
1196         });
1197 
1198         r.register0("deoptimizeAndInvalidateWithSpeculation", new InvocationPlugin() {
1199             @Override
1200             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
1201                 GraalError.guarantee(b.getGraph().getSpeculationLog() != null, "A speculation log is needed to use `deoptimizeAndInvalidateWithSpeculation`");
1202                 BytecodePosition pos = new BytecodePosition(null, b.getMethod(), b.bci());
1203                 SpeculationReason reason = DIRECTIVE_SPECULATIONS.createSpeculationReason(pos);
1204                 Speculation speculation;
1205                 if (b.getGraph().getSpeculationLog().maySpeculate(reason)) {
1206                     speculation = b.getGraph().getSpeculationLog().speculate(reason);
1207                 } else {
1208                     speculation = SpeculationLog.NO_SPECULATION;
1209                 }
1210                 b.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TransferToInterpreter, speculation));
1211                 return true;
1212             }
1213         });
1214 
1215         r.register0("inCompiledCode", new InvocationPlugin() {
1216             @Override
1217             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
1218                 b.addPush(JavaKind.Boolean, ConstantNode.forBoolean(true));
1219                 return true;
1220             }
1221         });
1222 
1223         r.register0("controlFlowAnchor", new InvocationPlugin() {
1224             @Override
1225             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
1226                 b.add(new ControlFlowAnchorNode());
1227                 return true;
1228             }
1229         });
1230 
1231         r.register2("injectBranchProbability", double.class, boolean.class, new InvocationPlugin() {
1232             @Override
1233             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode probability, ValueNode condition) {
1234                 b.addPush(JavaKind.Boolean, new BranchProbabilityNode(probability, condition));
1235                 return true;
1236             }
1237         });
1238 
1239         InvocationPlugin blackholePlugin = new InvocationPlugin() {
1240             @Override
1241             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
1242                 b.add(new BlackholeNode(value));
1243                 return true;
1244             }
1245         };
1246 
1247         InvocationPlugin bindToRegisterPlugin = new InvocationPlugin() {
1248             @Override
1249             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
1250                 b.add(new BindToRegisterNode(value));
1251                 return true;
1252             }
1253         };
1254         for (JavaKind kind : JavaKind.values()) {
1255             if ((kind.isPrimitive() && kind != JavaKind.Void) || kind == JavaKind.Object) {
1256                 Class<?> javaClass = kind == JavaKind.Object ? Object.class : kind.toJavaClass();
1257                 r.register1("blackhole", javaClass, blackholePlugin);
1258                 r.register1("bindToRegister", javaClass, bindToRegisterPlugin);
1259 
1260                 r.register1("opaque", javaClass, new InvocationPlugin() {
1261                     @Override
1262                     public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
1263                         b.addPush(kind, new OpaqueNode(value));
1264                         return true;
1265                     }
1266                 });
1267             }
1268         }
1269 
1270         InvocationPlugin spillPlugin = new InvocationPlugin() {
1271             @Override
1272             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
1273                 b.add(new SpillRegistersNode());
1274                 return true;
1275             }
1276         };
1277         r.register0("spillRegisters", spillPlugin);
1278 
1279         r.register1("guardingNonNull", Object.class, new InvocationPlugin() {
1280             @Override
1281             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
1282                 b.addPush(value.getStackKind(), b.nullCheckedValue(value));
1283                 return true;
1284             }
1285         });
1286 
1287         r.register1("ensureVirtualized", Object.class, new InvocationPlugin() {
1288             @Override
1289             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode object) {
1290                 b.add(new EnsureVirtualizedNode(object, false));
1291                 return true;
1292             }
1293         });
1294         r.register1("ensureVirtualizedHere", Object.class, new InvocationPlugin() {
1295             @Override
1296             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode object) {
1297                 b.add(new EnsureVirtualizedNode(object, true));
1298                 return true;
1299             }
1300         });
1301     }
1302 
1303     private static void registerJMHBlackholePlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider) {
1304         InvocationPlugin blackholePlugin = new InvocationPlugin() {
1305             @Override
1306             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver blackhole, ValueNode value) {
1307                 blackhole.get();
1308                 b.add(new BlackholeNode(value));
1309                 return true;
1310             }
1311 
1312             @Override
1313             public boolean isDecorator() {
1314                 return true;
1315             }
1316         };
1317         String[] names = {"org.openjdk.jmh.infra.Blackhole", "org.openjdk.jmh.logic.BlackHole"};
1318         for (String name : names) {
1319             Registration r = new Registration(plugins, name, bytecodeProvider);
1320             for (JavaKind kind : JavaKind.values()) {
1321                 if ((kind.isPrimitive() && kind != JavaKind.Void) || kind == JavaKind.Object) {
1322                     Class<?> javaClass = kind == JavaKind.Object ? Object.class : kind.toJavaClass();
1323                     r.registerOptional2("consume", Receiver.class, javaClass, blackholePlugin);
1324                 }
1325             }
1326             r.registerOptional2("consume", Receiver.class, Object[].class, blackholePlugin);
1327         }
1328     }
1329 
1330     private static void registerJFRThrowablePlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider) {
1331         Registration r = new Registration(plugins, "oracle.jrockit.jfr.jdkevents.ThrowableTracer", bytecodeProvider);
1332         r.register2("traceThrowable", Throwable.class, String.class, new InvocationPlugin() {
1333             @Override
1334             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode throwable, ValueNode message) {
1335                 b.add(new VirtualizableInvokeMacroNode(b.getInvokeKind(), targetMethod, b.bci(), b.getInvokeReturnStamp(b.getAssumptions()), throwable, message));
1336                 return true;
1337             }
1338 
1339             @Override
1340             public boolean inlineOnly() {
1341                 return true;
1342             }
1343         });
1344     }
1345 
1346     private static void registerMethodHandleImplPlugins(InvocationPlugins plugins, SnippetReflectionProvider snippetReflection, BytecodeProvider bytecodeProvider) {
1347         Registration r = new Registration(plugins, "java.lang.invoke.MethodHandleImpl", bytecodeProvider);
1348         // In later JDKs this no longer exists and the usage is replace by Class.cast which is
1349         // already an intrinsic
1350         r.registerOptional2("castReference", Class.class, Object.class, new InvocationPlugin() {
1351             @Override
1352             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode javaClass, ValueNode object) {
1353                 b.genCheckcastDynamic(object, javaClass);
1354                 return true;
1355             }
1356 
1357             @Override
1358             public boolean inlineOnly() {
1359                 return true;
1360             }
1361         });
1362         r.register2("profileBoolean", boolean.class, int[].class, new InvocationPlugin() {
1363             @Override
1364             public boolean inlineOnly() {
1365                 return true;
1366             }
1367 
1368             @Override
1369             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode result, ValueNode counters) {
1370                 if (result.isConstant()) {
1371                     b.push(JavaKind.Boolean, result);
1372                     return true;
1373                 }
1374                 if (counters.isConstant()) {
1375                     ValueNode newResult = result;
1376                     int[] ctrs = snippetReflection.asObject(int[].class, (JavaConstant) counters.asConstant());
1377                     if (ctrs != null && ctrs.length == 2) {
1378                         int falseCount = ctrs[0];
1379                         int trueCount = ctrs[1];
1380                         int totalCount = trueCount + falseCount;
1381 
1382                         if (totalCount == 0) {
1383                             b.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TransferToInterpreter));
1384                         } else if (falseCount == 0 || trueCount == 0) {
1385                             boolean expected = falseCount == 0;
1386                             LogicNode condition = b.add(
1387                                             IntegerEqualsNode.create(b.getConstantReflection(), b.getMetaAccess(), b.getOptions(), null, result, b.add(ConstantNode.forBoolean(!expected)),
1388                                                             NodeView.DEFAULT));
1389                             b.append(new FixedGuardNode(condition, DeoptimizationReason.UnreachedCode, DeoptimizationAction.InvalidateReprofile, true));
1390                             newResult = b.add(ConstantNode.forBoolean(expected));
1391                         } else {
1392                             // We cannot use BranchProbabilityNode here since there's no guarantee
1393                             // the result of MethodHandleImpl.profileBoolean() is used as the
1394                             // test in an `if` statement (as required by BranchProbabilityNode).
1395                         }
1396                     }
1397                     b.addPush(JavaKind.Boolean, newResult);
1398                     return true;
1399                 }
1400                 b.addPush(JavaKind.Boolean,
1401                                 new ProfileBooleanNode(snippetReflection, b.getInvokeKind(), targetMethod, b.bci(), b.getInvokeReturnStamp(b.getAssumptions()), result, counters));
1402                 return true;
1403             }
1404         });
1405     }
1406 
1407     /**
1408      * Registers a plugin to ignore {@code com.sun.tdk.jcov.runtime.Collect.hit} within an
1409      * intrinsic.
1410      */
1411     private static void registerJcovCollectPlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider) {
1412         Registration r = new Registration(plugins, "com.sun.tdk.jcov.runtime.Collect", bytecodeProvider);
1413         r.register1("hit", int.class, new InvocationPlugin() {
1414             @Override
1415             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode object) {
1416                 if (b.parsingIntrinsic()) {
1417                     return true;
1418                 }
1419                 return false;
1420             }
1421         });
1422     }
1423 }