1 /*
   2  * Copyright (c) 2015, 2016, 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 package org.graalvm.compiler.replacements;
  24 
  25 import static org.graalvm.compiler.core.common.util.Util.Java8OrEarlier;
  26 import static jdk.vm.ci.code.MemoryBarriers.JMM_POST_VOLATILE_READ;
  27 import static jdk.vm.ci.code.MemoryBarriers.JMM_POST_VOLATILE_WRITE;
  28 import static jdk.vm.ci.code.MemoryBarriers.JMM_PRE_VOLATILE_READ;
  29 import static jdk.vm.ci.code.MemoryBarriers.JMM_PRE_VOLATILE_WRITE;
  30 import static jdk.vm.ci.code.MemoryBarriers.LOAD_LOAD;
  31 import static jdk.vm.ci.code.MemoryBarriers.LOAD_STORE;
  32 import static jdk.vm.ci.code.MemoryBarriers.STORE_LOAD;
  33 import static jdk.vm.ci.code.MemoryBarriers.STORE_STORE;
  34 
  35 import java.lang.reflect.Array;
  36 import java.lang.reflect.Field;
  37 import java.util.Arrays;
  38 
  39 import org.graalvm.compiler.api.directives.GraalDirectives;
  40 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
  41 import org.graalvm.compiler.bytecode.BytecodeProvider;
  42 import org.graalvm.compiler.core.common.LocationIdentity;
  43 import org.graalvm.compiler.core.common.calc.Condition;
  44 import org.graalvm.compiler.core.common.calc.UnsignedMath;
  45 import org.graalvm.compiler.core.common.type.ObjectStamp;
  46 import org.graalvm.compiler.core.common.type.Stamp;
  47 import org.graalvm.compiler.core.common.type.StampFactory;
  48 import org.graalvm.compiler.core.common.type.TypeReference;
  49 import org.graalvm.compiler.debug.GraalError;
  50 import org.graalvm.compiler.graph.Edges;
  51 import org.graalvm.compiler.graph.Node;
  52 import org.graalvm.compiler.graph.NodeList;
  53 import org.graalvm.compiler.nodes.ConstantNode;
  54 import org.graalvm.compiler.nodes.DeoptimizeNode;
  55 import org.graalvm.compiler.nodes.FixedGuardNode;
  56 import org.graalvm.compiler.nodes.LogicNode;
  57 import org.graalvm.compiler.nodes.PiNode;
  58 import org.graalvm.compiler.nodes.StructuredGraph;
  59 import org.graalvm.compiler.nodes.ValueNode;
  60 import org.graalvm.compiler.nodes.calc.AbsNode;
  61 import org.graalvm.compiler.nodes.calc.CompareNode;
  62 import org.graalvm.compiler.nodes.calc.ConditionalNode;
  63 import org.graalvm.compiler.nodes.calc.IntegerEqualsNode;
  64 import org.graalvm.compiler.nodes.calc.IsNullNode;
  65 import org.graalvm.compiler.nodes.calc.NarrowNode;
  66 import org.graalvm.compiler.nodes.calc.ReinterpretNode;
  67 import org.graalvm.compiler.nodes.calc.RightShiftNode;
  68 import org.graalvm.compiler.nodes.calc.SignExtendNode;
  69 import org.graalvm.compiler.nodes.calc.SqrtNode;
  70 import org.graalvm.compiler.nodes.calc.UnsignedDivNode;
  71 import org.graalvm.compiler.nodes.calc.UnsignedRemNode;
  72 import org.graalvm.compiler.nodes.calc.ZeroExtendNode;
  73 import org.graalvm.compiler.nodes.debug.BindToRegisterNode;
  74 import org.graalvm.compiler.nodes.debug.BlackholeNode;
  75 import org.graalvm.compiler.nodes.debug.ControlFlowAnchorNode;
  76 import org.graalvm.compiler.nodes.debug.OpaqueNode;
  77 import org.graalvm.compiler.nodes.debug.SpillRegistersNode;
  78 import org.graalvm.compiler.nodes.debug.instrumentation.InstrumentationBeginNode;
  79 import org.graalvm.compiler.nodes.debug.instrumentation.InstrumentationEndNode;
  80 import org.graalvm.compiler.nodes.debug.instrumentation.IsMethodInlinedNode;
  81 import org.graalvm.compiler.nodes.debug.instrumentation.RootNameNode;
  82 import org.graalvm.compiler.nodes.extended.BoxNode;
  83 import org.graalvm.compiler.nodes.extended.BranchProbabilityNode;
  84 import org.graalvm.compiler.nodes.extended.GetClassNode;
  85 import org.graalvm.compiler.nodes.extended.MembarNode;
  86 import org.graalvm.compiler.nodes.extended.UnboxNode;
  87 import org.graalvm.compiler.nodes.extended.UnsafeLoadNode;
  88 import org.graalvm.compiler.nodes.extended.UnsafeMemoryLoadNode;
  89 import org.graalvm.compiler.nodes.extended.UnsafeMemoryStoreNode;
  90 import org.graalvm.compiler.nodes.extended.UnsafeStoreNode;
  91 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
  92 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;
  93 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin.Receiver;
  94 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
  95 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration;
  96 import org.graalvm.compiler.nodes.java.ClassIsAssignableFromNode;
  97 import org.graalvm.compiler.nodes.java.CompareAndSwapNode;
  98 import org.graalvm.compiler.nodes.java.DynamicNewArrayNode;
  99 import org.graalvm.compiler.nodes.java.DynamicNewInstanceNode;
 100 import org.graalvm.compiler.nodes.java.InstanceOfDynamicNode;
 101 import org.graalvm.compiler.nodes.java.LoadFieldNode;
 102 import org.graalvm.compiler.nodes.java.RegisterFinalizerNode;
 103 import org.graalvm.compiler.nodes.util.GraphUtil;
 104 import org.graalvm.compiler.nodes.virtual.EnsureVirtualizedNode;
 105 import org.graalvm.compiler.replacements.nodes.ReverseBytesNode;
 106 import org.graalvm.compiler.replacements.nodes.VirtualizableInvokeMacroNode;
 107 import org.graalvm.compiler.replacements.nodes.arithmetic.IntegerAddExactNode;
 108 import org.graalvm.compiler.replacements.nodes.arithmetic.IntegerMulExactNode;
 109 import org.graalvm.compiler.replacements.nodes.arithmetic.IntegerSubExactNode;
 110 
 111 import jdk.vm.ci.meta.DeoptimizationAction;
 112 import jdk.vm.ci.meta.DeoptimizationReason;
 113 import jdk.vm.ci.meta.JavaConstant;
 114 import jdk.vm.ci.meta.JavaKind;
 115 import jdk.vm.ci.meta.MetaAccessProvider;
 116 import jdk.vm.ci.meta.ResolvedJavaField;
 117 import jdk.vm.ci.meta.ResolvedJavaMethod;
 118 import jdk.vm.ci.meta.ResolvedJavaType;
 119 import sun.misc.Unsafe;
 120 
 121 /**
 122  * Provides non-runtime specific {@link InvocationPlugin}s.
 123  */
 124 public class StandardGraphBuilderPlugins {
 125 
 126     public static void registerInvocationPlugins(MetaAccessProvider metaAccess, SnippetReflectionProvider snippetReflection, InvocationPlugins plugins, BytecodeProvider bytecodeProvider,
 127                     boolean allowDeoptimization) {
 128         registerObjectPlugins(plugins);
 129         registerClassPlugins(plugins);
 130         registerMathPlugins(plugins, allowDeoptimization);
 131         registerUnsignedMathPlugins(plugins);
 132         registerStringPlugins(plugins, bytecodeProvider, snippetReflection);
 133         registerCharacterPlugins(plugins);
 134         registerShortPlugins(plugins);
 135         registerIntegerLongPlugins(plugins, JavaKind.Int);
 136         registerIntegerLongPlugins(plugins, JavaKind.Long);
 137         registerFloatPlugins(plugins);
 138         registerDoublePlugins(plugins);
 139         registerArraysPlugins(plugins, bytecodeProvider);
 140         registerArrayPlugins(plugins, bytecodeProvider);
 141         registerUnsafePlugins(plugins, bytecodeProvider);
 142         registerEdgesPlugins(metaAccess, plugins);
 143         registerGraalDirectivesPlugins(plugins);
 144         registerBoxingPlugins(plugins);
 145         registerJMHBlackholePlugins(plugins, bytecodeProvider);
 146         registerJFRThrowablePlugins(plugins, bytecodeProvider);
 147         registerMethodHandleImplPlugins(plugins, snippetReflection, bytecodeProvider);
 148     }
 149 
 150     private static final Field STRING_VALUE_FIELD;
 151 
 152     static {
 153         try {
 154             STRING_VALUE_FIELD = String.class.getDeclaredField("value");
 155         } catch (NoSuchFieldException e) {
 156             throw new GraalError(e);
 157         }
 158     }
 159 
 160     private static void registerStringPlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider, SnippetReflectionProvider snippetReflection) {
 161         Registration r = new Registration(plugins, String.class, bytecodeProvider);
 162         r.register1("hashCode", Receiver.class, new InvocationPlugin() {
 163             @Override
 164             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
 165                 if (receiver.isConstant()) {
 166                     String s = snippetReflection.asObject(String.class, (JavaConstant) receiver.get().asConstant());
 167                     b.addPush(JavaKind.Int, b.add(ConstantNode.forInt(s.hashCode())));
 168                     return true;
 169                 }
 170                 return false;
 171             }
 172         });
 173         if (Java8OrEarlier) {
 174             r.registerMethodSubstitution(StringSubstitutions.class, "equals", Receiver.class, Object.class);
 175 
 176             r = new Registration(plugins, StringSubstitutions.class);
 177             r.register1("getValue", String.class, new InvocationPlugin() {
 178                 @Override
 179                 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 180                     ResolvedJavaField field = b.getMetaAccess().lookupJavaField(STRING_VALUE_FIELD);
 181                     b.addPush(JavaKind.Object, LoadFieldNode.create(b.getAssumptions(), value, field));
 182                     return true;
 183                 }
 184             });
 185         }
 186     }
 187 
 188     private static void registerArraysPlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider) {
 189         Registration r = new Registration(plugins, Arrays.class, bytecodeProvider);
 190         r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", boolean[].class, boolean[].class);
 191         r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", byte[].class, byte[].class);
 192         r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", short[].class, short[].class);
 193         r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", char[].class, char[].class);
 194         r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", int[].class, int[].class);
 195         r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", float[].class, float[].class);
 196         r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", long[].class, long[].class);
 197         r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", double[].class, double[].class);
 198     }
 199 
 200     private static void registerArrayPlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider) {
 201         Registration r = new Registration(plugins, Array.class, bytecodeProvider);
 202         r.register2("newInstance", Class.class, int.class, new InvocationPlugin() {
 203             @Override
 204             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unused, ValueNode componentType, ValueNode length) {
 205                 b.addPush(JavaKind.Object, new DynamicNewArrayNode(componentType, length, true));
 206                 return true;
 207             }
 208         });
 209         r.registerMethodSubstitution(ArraySubstitutions.class, "getLength", Object.class);
 210     }
 211 
 212     private static void registerUnsafePlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider) {
 213         Registration r;
 214         if (Java8OrEarlier) {
 215             r = new Registration(plugins, Unsafe.class);
 216         } else {
 217             r = new Registration(plugins, "jdk.internal.misc.Unsafe", bytecodeProvider);
 218         }
 219 
 220         for (JavaKind kind : JavaKind.values()) {
 221             if ((kind.isPrimitive() && kind != JavaKind.Void) || kind == JavaKind.Object) {
 222                 Class<?> javaClass = kind == JavaKind.Object ? Object.class : kind.toJavaClass();
 223                 String kindName = kind.name();
 224                 String getName = "get" + kindName;
 225                 String putName = "put" + kindName;
 226                 // Object-based accesses
 227                 r.register3(getName, Receiver.class, Object.class, long.class, new UnsafeGetPlugin(kind, false));
 228                 r.register4(putName, Receiver.class, Object.class, long.class, javaClass, new UnsafePutPlugin(kind, false));
 229                 // Volatile object-based accesses
 230                 r.register3(getName + "Volatile", Receiver.class, Object.class, long.class, new UnsafeGetPlugin(kind, true));
 231                 r.register4(putName + "Volatile", Receiver.class, Object.class, long.class, javaClass, new UnsafePutPlugin(kind, true));
 232                 // Ordered object-based accesses
 233                 if (Java8OrEarlier) {
 234                     if (kind == JavaKind.Int || kind == JavaKind.Long || kind == JavaKind.Object) {
 235                         r.register4("putOrdered" + kindName, Receiver.class, Object.class, long.class, javaClass, new UnsafePutPlugin(kind, true));
 236                     }
 237                 } else {
 238                     r.register4("put" + kindName + "Release", Receiver.class, Object.class, long.class, javaClass, new UnsafePutPlugin(kind, true));
 239                 }
 240                 if (kind != JavaKind.Boolean && kind != JavaKind.Object) {
 241                     // Raw accesses to memory addresses
 242                     r.register2(getName, Receiver.class, long.class, new UnsafeGetPlugin(kind, false));
 243                     r.register3(putName, Receiver.class, long.class, kind.toJavaClass(), new UnsafePutPlugin(kind, false));
 244                 }
 245             }
 246         }
 247 
 248         // Accesses to native memory addresses.
 249         r.register2("getAddress", Receiver.class, long.class, new UnsafeGetPlugin(JavaKind.Long, false));
 250         r.register3("putAddress", Receiver.class, long.class, long.class, new UnsafePutPlugin(JavaKind.Long, false));
 251 
 252         for (JavaKind kind : new JavaKind[]{JavaKind.Int, JavaKind.Long, JavaKind.Object}) {
 253             Class<?> javaClass = kind == JavaKind.Object ? Object.class : kind.toJavaClass();
 254             r.register5("compareAndSwap" + kind.name(), Receiver.class, Object.class, long.class, javaClass, javaClass, new InvocationPlugin() {
 255                 @Override
 256                 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unsafe, ValueNode object, ValueNode offset, ValueNode expected, ValueNode x) {
 257                     // Emits a null-check for the otherwise unused receiver
 258                     unsafe.get();
 259                     b.addPush(JavaKind.Int, new CompareAndSwapNode(object, offset, expected, x, kind, LocationIdentity.any()));
 260                     b.getGraph().markUnsafeAccess();
 261                     return true;
 262                 }
 263             });
 264         }
 265 
 266         r.register2("allocateInstance", Receiver.class, Class.class, new InvocationPlugin() {
 267 
 268             @Override
 269             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unsafe, ValueNode clazz) {
 270                 // Emits a null-check for the otherwise unused receiver
 271                 unsafe.get();
 272                 b.addPush(JavaKind.Object, new DynamicNewInstanceNode(clazz, true));
 273                 return true;
 274             }
 275 
 276         });
 277 
 278         r.register1("loadFence", Receiver.class, new UnsafeFencePlugin(LOAD_LOAD | LOAD_STORE));
 279         r.register1("storeFence", Receiver.class, new UnsafeFencePlugin(STORE_STORE | LOAD_STORE));
 280         r.register1("fullFence", Receiver.class, new UnsafeFencePlugin(LOAD_LOAD | STORE_STORE | LOAD_STORE | STORE_LOAD));
 281     }
 282 
 283     private static void registerIntegerLongPlugins(InvocationPlugins plugins, JavaKind kind) {
 284         Class<?> declaringClass = kind.toBoxedJavaClass();
 285         Class<?> type = kind.toJavaClass();
 286         Registration r = new Registration(plugins, declaringClass);
 287         r.register1("reverseBytes", type, new InvocationPlugin() {
 288             @Override
 289             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 290                 b.push(kind, b.recursiveAppend(new ReverseBytesNode(value).canonical(null)));
 291                 return true;
 292             }
 293         });
 294         r.register2("divideUnsigned", type, type, new InvocationPlugin() {
 295             @Override
 296             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode dividend, ValueNode divisor) {
 297                 b.push(kind, b.recursiveAppend(new UnsignedDivNode(dividend, divisor).canonical(null)));
 298                 return true;
 299             }
 300         });
 301         r.register2("remainderUnsigned", type, type, new InvocationPlugin() {
 302             @Override
 303             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode dividend, ValueNode divisor) {
 304                 b.push(kind, b.recursiveAppend(new UnsignedRemNode(dividend, divisor).canonical(null)));
 305                 return true;
 306             }
 307         });
 308     }
 309 
 310     private static void registerCharacterPlugins(InvocationPlugins plugins) {
 311         Registration r = new Registration(plugins, Character.class);
 312         r.register1("reverseBytes", char.class, new InvocationPlugin() {
 313             @Override
 314             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 315                 // return (char) (Integer.reverse(i) >> 16);
 316                 ReverseBytesNode reverse = b.add(new ReverseBytesNode(value));
 317                 RightShiftNode rightShift = b.add(new RightShiftNode(reverse, b.add(ConstantNode.forInt(16))));
 318                 ZeroExtendNode charCast = b.add(new ZeroExtendNode(b.add(new NarrowNode(rightShift, 16)), 32));
 319                 b.push(JavaKind.Char, b.recursiveAppend(charCast.canonical(null)));
 320                 return true;
 321             }
 322         });
 323     }
 324 
 325     private static void registerShortPlugins(InvocationPlugins plugins) {
 326         Registration r = new Registration(plugins, Short.class);
 327         r.register1("reverseBytes", short.class, new InvocationPlugin() {
 328             @Override
 329             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 330                 // return (short) (Integer.reverse(i) >> 16);
 331                 ReverseBytesNode reverse = b.add(new ReverseBytesNode(value));
 332                 RightShiftNode rightShift = b.add(new RightShiftNode(reverse, b.add(ConstantNode.forInt(16))));
 333                 SignExtendNode charCast = b.add(new SignExtendNode(b.add(new NarrowNode(rightShift, 16)), 32));
 334                 b.push(JavaKind.Short, b.recursiveAppend(charCast.canonical(null)));
 335                 return true;
 336             }
 337         });
 338     }
 339 
 340     private static void registerFloatPlugins(InvocationPlugins plugins) {
 341         Registration r = new Registration(plugins, Float.class);
 342         r.register1("floatToRawIntBits", float.class, new InvocationPlugin() {
 343             @Override
 344             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 345                 b.push(JavaKind.Int, b.recursiveAppend(new ReinterpretNode(JavaKind.Int, value).canonical(null)));
 346                 return true;
 347             }
 348         });
 349         r.register1("intBitsToFloat", int.class, new InvocationPlugin() {
 350             @Override
 351             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 352                 b.push(JavaKind.Float, b.recursiveAppend(new ReinterpretNode(JavaKind.Float, value).canonical(null)));
 353                 return true;
 354             }
 355         });
 356     }
 357 
 358     private static void registerDoublePlugins(InvocationPlugins plugins) {
 359         Registration r = new Registration(plugins, Double.class);
 360         r.register1("doubleToRawLongBits", double.class, new InvocationPlugin() {
 361             @Override
 362             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 363                 b.push(JavaKind.Long, b.recursiveAppend(new ReinterpretNode(JavaKind.Long, value).canonical(null)));
 364                 return true;
 365             }
 366         });
 367         r.register1("longBitsToDouble", long.class, new InvocationPlugin() {
 368             @Override
 369             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 370                 b.push(JavaKind.Double, b.recursiveAppend(new ReinterpretNode(JavaKind.Double, value).canonical(null)));
 371                 return true;
 372             }
 373         });
 374     }
 375 
 376     private static void registerMathPlugins(InvocationPlugins plugins, boolean allowDeoptimization) {
 377         Registration r = new Registration(plugins, Math.class);
 378         if (allowDeoptimization) {
 379             for (JavaKind kind : new JavaKind[]{JavaKind.Int, JavaKind.Long}) {
 380                 Class<?> type = kind.toJavaClass();
 381                 r.register2("addExact", type, type, new InvocationPlugin() {
 382                     @Override
 383                     public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
 384                         b.addPush(kind, new IntegerAddExactNode(x, y));
 385                         return true;
 386                     }
 387                 });
 388                 r.register2("subtractExact", type, type, new InvocationPlugin() {
 389                     @Override
 390                     public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
 391                         b.addPush(kind, new IntegerSubExactNode(x, y));
 392                         return true;
 393                     }
 394                 });
 395                 r.register2("multiplyExact", type, type, new InvocationPlugin() {
 396                     @Override
 397                     public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
 398                         b.addPush(kind, new IntegerMulExactNode(x, y));
 399                         return true;
 400                     }
 401                 });
 402             }
 403         }
 404         r.register1("abs", Float.TYPE, new InvocationPlugin() {
 405 
 406             @Override
 407             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 408                 b.push(JavaKind.Float, b.recursiveAppend(new AbsNode(value).canonical(null)));
 409                 return true;
 410             }
 411         });
 412         r.register1("abs", Double.TYPE, new InvocationPlugin() {
 413             @Override
 414             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 415                 b.push(JavaKind.Double, b.recursiveAppend(new AbsNode(value).canonical(null)));
 416                 return true;
 417             }
 418         });
 419         r.register1("sqrt", Double.TYPE, new InvocationPlugin() {
 420             @Override
 421             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 422                 b.push(JavaKind.Double, b.recursiveAppend(new SqrtNode(value).canonical(null)));
 423                 return true;
 424             }
 425         });
 426     }
 427 
 428     public static class UnsignedMathPlugin implements InvocationPlugin {
 429         private final Condition condition;
 430 
 431         public UnsignedMathPlugin(Condition condition) {
 432             this.condition = condition;
 433         }
 434 
 435         @Override
 436         public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) {
 437             // the mirroring and negation operations get the condition into canonical form
 438             boolean mirror = condition.canonicalMirror();
 439             boolean negate = condition.canonicalNegate();
 440             StructuredGraph graph = b.getGraph();
 441 
 442             ValueNode lhs = mirror ? y : x;
 443             ValueNode rhs = mirror ? x : y;
 444 
 445             ValueNode trueValue = ConstantNode.forBoolean(!negate, graph);
 446             ValueNode falseValue = ConstantNode.forBoolean(negate, graph);
 447 
 448             Condition cond = mirror ? condition.mirror() : condition;
 449             if (negate) {
 450                 cond = cond.negate();
 451             }
 452 
 453             LogicNode compare = CompareNode.createCompareNode(graph, cond, lhs, rhs, b.getConstantReflection());
 454             b.addPush(JavaKind.Boolean, new ConditionalNode(compare, trueValue, falseValue));
 455             return true;
 456         }
 457     }
 458 
 459     private static void registerUnsignedMathPlugins(InvocationPlugins plugins) {
 460         Registration r = new Registration(plugins, UnsignedMath.class);
 461         r.register2("aboveThan", int.class, int.class, new UnsignedMathPlugin(Condition.AT));
 462         r.register2("aboveThan", long.class, long.class, new UnsignedMathPlugin(Condition.AT));
 463         r.register2("belowThan", int.class, int.class, new UnsignedMathPlugin(Condition.BT));
 464         r.register2("belowThan", long.class, long.class, new UnsignedMathPlugin(Condition.BT));
 465         r.register2("aboveOrEqual", int.class, int.class, new UnsignedMathPlugin(Condition.AE));
 466         r.register2("aboveOrEqual", long.class, long.class, new UnsignedMathPlugin(Condition.AE));
 467         r.register2("belowOrEqual", int.class, int.class, new UnsignedMathPlugin(Condition.BE));
 468         r.register2("belowOrEqual", long.class, long.class, new UnsignedMathPlugin(Condition.BE));
 469     }
 470 
 471     protected static void registerBoxingPlugins(InvocationPlugins plugins) {
 472         for (JavaKind kind : JavaKind.values()) {
 473             if (kind.isPrimitive() && kind != JavaKind.Void) {
 474                 new BoxPlugin(kind).register(plugins);
 475                 new UnboxPlugin(kind).register(plugins);
 476             }
 477         }
 478     }
 479 
 480     private static void registerObjectPlugins(InvocationPlugins plugins) {
 481         Registration r = new Registration(plugins, Object.class);
 482         r.register1("<init>", Receiver.class, new InvocationPlugin() {
 483             @Override
 484             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
 485                 /*
 486                  * Object.<init> is a common instrumentation point so only perform this rewrite if
 487                  * the current definition is the normal empty method with a single return bytecode.
 488                  * The finalizer registration will instead be performed by the BytecodeParser.
 489                  */
 490                 if (targetMethod.getCodeSize() == 1) {
 491                     ValueNode object = receiver.get();
 492                     if (RegisterFinalizerNode.mayHaveFinalizer(object, b.getAssumptions())) {
 493                         b.add(new RegisterFinalizerNode(object));
 494                     }
 495                     return true;
 496                 }
 497                 return false;
 498             }
 499         });
 500         r.register1("getClass", Receiver.class, new InvocationPlugin() {
 501             @Override
 502             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
 503                 ValueNode object = receiver.get();
 504                 ValueNode folded = GetClassNode.tryFold(b.getMetaAccess(), b.getConstantReflection(), GraphUtil.originalValue(object));
 505                 if (folded != null) {
 506                     b.addPush(JavaKind.Object, folded);
 507                 } else {
 508                     Stamp stamp = StampFactory.objectNonNull(TypeReference.createTrusted(b.getAssumptions(), b.getMetaAccess().lookupJavaType(Class.class)));
 509                     b.addPush(JavaKind.Object, new GetClassNode(stamp, object));
 510                 }
 511                 return true;
 512             }
 513         });
 514     }
 515 
 516     private static void registerClassPlugins(InvocationPlugins plugins) {
 517         Registration r = new Registration(plugins, Class.class);
 518         r.register2("isInstance", Receiver.class, Object.class, new InvocationPlugin() {
 519             @Override
 520             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver type, ValueNode object) {
 521                 LogicNode condition = b.recursiveAppend(InstanceOfDynamicNode.create(b.getAssumptions(), b.getConstantReflection(), type.get(), object, false));
 522                 b.push(JavaKind.Boolean, b.recursiveAppend(new ConditionalNode(condition).canonical(null)));
 523                 return true;
 524             }
 525         });
 526         r.register2("isAssignableFrom", Receiver.class, Class.class, new InvocationPlugin() {
 527             @Override
 528             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver type, ValueNode otherType) {
 529                 ClassIsAssignableFromNode condition = b.recursiveAppend(new ClassIsAssignableFromNode(type.get(), otherType));
 530                 b.push(JavaKind.Boolean, b.recursiveAppend(new ConditionalNode(condition).canonical(null)));
 531                 return true;
 532             }
 533         });
 534     }
 535 
 536     /**
 537      * Substitutions for improving the performance of some critical methods in {@link Edges}. These
 538      * substitutions improve the performance by forcing the relevant methods to be inlined
 539      * (intrinsification being a special form of inlining) and removing a checked cast.
 540      */
 541     private static void registerEdgesPlugins(MetaAccessProvider metaAccess, InvocationPlugins plugins) {
 542         Registration r = new Registration(plugins, Edges.class);
 543         for (Class<?> c : new Class<?>[]{Node.class, NodeList.class}) {
 544             r.register2("get" + c.getSimpleName() + "Unsafe", Node.class, long.class, new InvocationPlugin() {
 545                 @Override
 546                 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode node, ValueNode offset) {
 547                     UnsafeLoadNode value = b.add(new UnsafeLoadNode(node, offset, JavaKind.Object, LocationIdentity.any()));
 548                     value.setStamp(StampFactory.object(TypeReference.createTrusted(b.getAssumptions(), metaAccess.lookupJavaType(c))));
 549                     b.addPush(JavaKind.Object, value);
 550                     return true;
 551                 }
 552             });
 553             r.register3("put" + c.getSimpleName() + "Unsafe", Node.class, long.class, c, new InvocationPlugin() {
 554                 @Override
 555                 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode node, ValueNode offset, ValueNode value) {
 556                     b.add(new UnsafeStoreNode(node, offset, value, JavaKind.Object, LocationIdentity.any()));
 557                     return true;
 558                 }
 559             });
 560         }
 561     }
 562 
 563     public static class BoxPlugin implements InvocationPlugin {
 564 
 565         private final JavaKind kind;
 566 
 567         BoxPlugin(JavaKind kind) {
 568             this.kind = kind;
 569         }
 570 
 571         @Override
 572         public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 573             if (b.parsingIntrinsic()) {
 574                 ResolvedJavaMethod rootMethod = b.getGraph().method();
 575                 if (b.getMetaAccess().lookupJavaType(BoxingSnippets.class).isAssignableFrom(rootMethod.getDeclaringClass())) {
 576                     // Disable invocation plugins for boxing snippets so that the
 577                     // original JDK methods are inlined
 578                     return false;
 579                 }
 580             }
 581             ResolvedJavaType resultType = b.getMetaAccess().lookupJavaType(kind.toBoxedJavaClass());
 582             b.addPush(JavaKind.Object, new BoxNode(value, resultType, kind));
 583             return true;
 584         }
 585 
 586         void register(InvocationPlugins plugins) {
 587             plugins.register(this, kind.toBoxedJavaClass(), "valueOf", kind.toJavaClass());
 588         }
 589     }
 590 
 591     public static class UnboxPlugin implements InvocationPlugin {
 592 
 593         private final JavaKind kind;
 594 
 595         UnboxPlugin(JavaKind kind) {
 596             this.kind = kind;
 597         }
 598 
 599         @Override
 600         public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
 601             if (b.parsingIntrinsic()) {
 602                 ResolvedJavaMethod rootMethod = b.getGraph().method();
 603                 if (b.getMetaAccess().lookupJavaType(BoxingSnippets.class).isAssignableFrom(rootMethod.getDeclaringClass())) {
 604                     // Disable invocation plugins for unboxing snippets so that the
 605                     // original JDK methods are inlined
 606                     return false;
 607                 }
 608             }
 609             ValueNode valueNode = UnboxNode.create(b.getMetaAccess(), b.getConstantReflection(), receiver.get(), kind);
 610             b.addPush(kind, valueNode);
 611             return true;
 612         }
 613 
 614         void register(InvocationPlugins plugins) {
 615             String name = kind.toJavaClass().getSimpleName() + "Value";
 616             plugins.register(this, kind.toBoxedJavaClass(), name, Receiver.class);
 617         }
 618     }
 619 
 620     public static class UnsafeGetPlugin implements InvocationPlugin {
 621 
 622         private final JavaKind returnKind;
 623         private final boolean isVolatile;
 624 
 625         public UnsafeGetPlugin(JavaKind returnKind, boolean isVolatile) {
 626             this.returnKind = returnKind;
 627             this.isVolatile = isVolatile;
 628         }
 629 
 630         @Override
 631         public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unsafe, ValueNode address) {
 632             // Emits a null-check for the otherwise unused receiver
 633             unsafe.get();
 634             b.addPush(returnKind, new UnsafeMemoryLoadNode(address, returnKind, LocationIdentity.any()));
 635             b.getGraph().markUnsafeAccess();
 636             return true;
 637         }
 638 
 639         @Override
 640         public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unsafe, ValueNode object, ValueNode offset) {
 641             // Emits a null-check for the otherwise unused receiver
 642             unsafe.get();
 643             if (isVolatile) {
 644                 b.add(new MembarNode(JMM_PRE_VOLATILE_READ));
 645             }
 646             b.addPush(returnKind, new UnsafeLoadNode(object, offset, returnKind, LocationIdentity.any()));
 647             if (isVolatile) {
 648                 b.add(new MembarNode(JMM_POST_VOLATILE_READ));
 649             }
 650             b.getGraph().markUnsafeAccess();
 651             return true;
 652         }
 653     }
 654 
 655     public static class UnsafePutPlugin implements InvocationPlugin {
 656 
 657         private final JavaKind kind;
 658         private final boolean isVolatile;
 659 
 660         public UnsafePutPlugin(JavaKind kind, boolean isVolatile) {
 661             this.kind = kind;
 662             this.isVolatile = isVolatile;
 663         }
 664 
 665         @Override
 666         public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unsafe, ValueNode address, ValueNode value) {
 667             // Emits a null-check for the otherwise unused receiver
 668             unsafe.get();
 669             b.add(new UnsafeMemoryStoreNode(address, value, kind, LocationIdentity.any()));
 670             b.getGraph().markUnsafeAccess();
 671             return true;
 672         }
 673 
 674         @Override
 675         public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unsafe, ValueNode object, ValueNode offset, ValueNode value) {
 676             // Emits a null-check for the otherwise unused receiver
 677             unsafe.get();
 678             if (isVolatile) {
 679                 b.add(new MembarNode(JMM_PRE_VOLATILE_WRITE));
 680             }
 681             b.add(new UnsafeStoreNode(object, offset, value, kind, LocationIdentity.any()));
 682             if (isVolatile) {
 683                 b.add(new MembarNode(JMM_POST_VOLATILE_WRITE));
 684             }
 685             b.getGraph().markUnsafeAccess();
 686             return true;
 687         }
 688     }
 689 
 690     public static class UnsafeFencePlugin implements InvocationPlugin {
 691 
 692         private final int barriers;
 693 
 694         public UnsafeFencePlugin(int barriers) {
 695             this.barriers = barriers;
 696         }
 697 
 698         @Override
 699         public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unsafe) {
 700             // Emits a null-check for the otherwise unused receiver
 701             unsafe.get();
 702             b.add(new MembarNode(barriers));
 703             return true;
 704         }
 705     }
 706 
 707     private static void registerGraalDirectivesPlugins(InvocationPlugins plugins) {
 708         Registration r = new Registration(plugins, GraalDirectives.class);
 709         r.register0("deoptimize", new InvocationPlugin() {
 710             @Override
 711             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
 712                 b.add(new DeoptimizeNode(DeoptimizationAction.None, DeoptimizationReason.TransferToInterpreter));
 713                 return true;
 714             }
 715         });
 716 
 717         r.register0("deoptimizeAndInvalidate", new InvocationPlugin() {
 718             @Override
 719             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
 720                 b.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TransferToInterpreter));
 721                 return true;
 722             }
 723         });
 724 
 725         r.register0("inCompiledCode", new InvocationPlugin() {
 726             @Override
 727             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
 728                 b.addPush(JavaKind.Boolean, ConstantNode.forBoolean(true));
 729                 return true;
 730             }
 731         });
 732 
 733         r.register0("controlFlowAnchor", new InvocationPlugin() {
 734             @Override
 735             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
 736                 b.add(new ControlFlowAnchorNode());
 737                 return true;
 738             }
 739         });
 740 
 741         r.register2("injectBranchProbability", double.class, boolean.class, new InvocationPlugin() {
 742             @Override
 743             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode probability, ValueNode condition) {
 744                 b.addPush(JavaKind.Boolean, new BranchProbabilityNode(probability, condition));
 745                 return true;
 746             }
 747         });
 748 
 749         InvocationPlugin blackholePlugin = new InvocationPlugin() {
 750             @Override
 751             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 752                 b.add(new BlackholeNode(value));
 753                 return true;
 754             }
 755         };
 756 
 757         InvocationPlugin bindToRegisterPlugin = new InvocationPlugin() {
 758             @Override
 759             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 760                 b.add(new BindToRegisterNode(value));
 761                 return true;
 762             }
 763         };
 764         for (JavaKind kind : JavaKind.values()) {
 765             if ((kind.isPrimitive() && kind != JavaKind.Void) || kind == JavaKind.Object) {
 766                 Class<?> javaClass = kind == JavaKind.Object ? Object.class : kind.toJavaClass();
 767                 r.register1("blackhole", javaClass, blackholePlugin);
 768                 r.register1("bindToRegister", javaClass, bindToRegisterPlugin);
 769 
 770                 r.register1("opaque", javaClass, new InvocationPlugin() {
 771                     @Override
 772                     public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 773                         b.addPush(kind, new OpaqueNode(value));
 774                         return true;
 775                     }
 776                 });
 777             }
 778         }
 779 
 780         InvocationPlugin spillPlugin = new InvocationPlugin() {
 781             @Override
 782             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
 783                 b.add(new SpillRegistersNode());
 784                 return true;
 785             }
 786         };
 787         r.register0("spillRegisters", spillPlugin);
 788 
 789         r.register1("guardingNonNull", Object.class, new InvocationPlugin() {
 790             @Override
 791             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
 792                 ObjectStamp objectStamp = (ObjectStamp) value.stamp();
 793                 if (objectStamp.nonNull()) {
 794                     b.addPush(value.getStackKind(), value);
 795                     return true;
 796                 } else if (objectStamp.alwaysNull()) {
 797                     b.add(new DeoptimizeNode(DeoptimizationAction.None, DeoptimizationReason.NullCheckException));
 798                     return true;
 799                 }
 800                 LogicNode isNull = b.add(IsNullNode.create(value));
 801                 FixedGuardNode fixedGuard = b.add(new FixedGuardNode(isNull, DeoptimizationReason.NullCheckException, DeoptimizationAction.None, true));
 802                 Stamp newStamp = objectStamp.improveWith(StampFactory.objectNonNull());
 803                 b.addPush(value.getStackKind(), new PiNode(value, newStamp, fixedGuard));
 804                 return true;
 805             }
 806         });
 807 
 808         r.register1("ensureVirtualized", Object.class, new InvocationPlugin() {
 809             @Override
 810             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode object) {
 811                 b.add(new EnsureVirtualizedNode(object, false));
 812                 return true;
 813             }
 814         });
 815         r.register1("ensureVirtualizedHere", Object.class, new InvocationPlugin() {
 816             @Override
 817             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode object) {
 818                 b.add(new EnsureVirtualizedNode(object, true));
 819                 return true;
 820             }
 821         });
 822         r.register0("isMethodInlined", new InvocationPlugin() {
 823             @Override
 824             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
 825                 b.addPush(JavaKind.Boolean, new IsMethodInlinedNode(b.getDepth()));
 826                 return true;
 827             }
 828         });
 829         r.register0("rawRootName", new InvocationPlugin() {
 830             @Override
 831             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
 832                 b.addPush(JavaKind.Object, new RootNameNode(b.getInvokeReturnStamp(b.getAssumptions()).getTrustedStamp()));
 833                 return true;
 834             }
 835         });
 836         r.register0("instrumentationBegin", new InvocationPlugin() {
 837             @Override
 838             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
 839                 b.add(new InstrumentationBeginNode(true));
 840                 return true;
 841             }
 842         });
 843         r.register0("instrumentationBeginForPredecessor", new InvocationPlugin() {
 844             @Override
 845             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
 846                 b.add(new InstrumentationBeginNode(false));
 847                 return true;
 848             }
 849         });
 850         r.register0("instrumentationEnd", new InvocationPlugin() {
 851             @Override
 852             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
 853                 b.add(new InstrumentationEndNode());
 854                 return true;
 855             }
 856         });
 857     }
 858 
 859     private static void registerJMHBlackholePlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider) {
 860         InvocationPlugin blackholePlugin = new InvocationPlugin() {
 861             @Override
 862             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver blackhole, ValueNode value) {
 863                 blackhole.get();
 864                 b.add(new BlackholeNode(value));
 865                 return true;
 866             }
 867         };
 868         String[] names = {"org.openjdk.jmh.infra.Blackhole", "org.openjdk.jmh.logic.BlackHole"};
 869         for (String name : names) {
 870             Registration r = new Registration(plugins, name, bytecodeProvider);
 871             for (JavaKind kind : JavaKind.values()) {
 872                 if ((kind.isPrimitive() && kind != JavaKind.Void) || kind == JavaKind.Object) {
 873                     Class<?> javaClass = kind == JavaKind.Object ? Object.class : kind.toJavaClass();
 874                     r.register2("consume", Receiver.class, javaClass, blackholePlugin);
 875                 }
 876             }
 877             r.register2("consume", Receiver.class, Object[].class, blackholePlugin);
 878         }
 879     }
 880 
 881     private static void registerJFRThrowablePlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider) {
 882         Registration r = new Registration(plugins, "oracle.jrockit.jfr.jdkevents.ThrowableTracer", bytecodeProvider);
 883         r.register2("traceThrowable", Throwable.class, String.class, new InvocationPlugin() {
 884             @Override
 885             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode throwable, ValueNode message) {
 886                 b.add(new VirtualizableInvokeMacroNode(b.getInvokeKind(), targetMethod, b.bci(), b.getInvokeReturnStamp(b.getAssumptions()), throwable, message));
 887                 return true;
 888             }
 889 
 890             @Override
 891             public boolean inlineOnly() {
 892                 return true;
 893             }
 894         });
 895     }
 896 
 897     private static void registerMethodHandleImplPlugins(InvocationPlugins plugins, SnippetReflectionProvider snippetReflection, BytecodeProvider bytecodeProvider) {
 898         Registration r = new Registration(plugins, "java.lang.invoke.MethodHandleImpl", bytecodeProvider);
 899         r.register2("profileBoolean", boolean.class, int[].class, new InvocationPlugin() {
 900             @Override
 901             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode result, ValueNode counters) {
 902                 if (result.isConstant()) {
 903                     b.push(JavaKind.Boolean, result);
 904                     return true;
 905                 }
 906                 if (counters.isConstant()) {
 907                     ValueNode newResult = result;
 908                     int[] ctrs = snippetReflection.asObject(int[].class, (JavaConstant) counters.asConstant());
 909                     if (ctrs != null && ctrs.length == 2) {
 910                         int falseCount = ctrs[0];
 911                         int trueCount = ctrs[1];
 912                         int totalCount = trueCount + falseCount;
 913 
 914                         if (totalCount == 0) {
 915                             b.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TransferToInterpreter));
 916                         } else if (falseCount == 0 || trueCount == 0) {
 917                             boolean expected = falseCount == 0 ? true : false;
 918                             LogicNode condition = b.add(IntegerEqualsNode.create(result, b.add(ConstantNode.forBoolean(!expected)), /* constantReflection */ null));
 919                             b.append(new FixedGuardNode(condition, DeoptimizationReason.UnreachedCode, DeoptimizationAction.InvalidateReprofile, true));
 920                             newResult = b.add(ConstantNode.forBoolean(expected));
 921                         } else {
 922                             // We cannot use BranchProbabilityNode here since there's no guarantee
 923                             // the result of MethodHandleImpl.profileBoolean() is used as the
 924                             // test in an `if` statement (as required by BranchProbabilityNode).
 925                         }
 926                     }
 927                     b.addPush(JavaKind.Boolean, newResult);
 928                     return true;
 929                 }
 930                 return false;
 931             }
 932         });
 933     }
 934 }