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