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