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