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