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 }