1 /*
   2  * Copyright (c) 2014, 2018, 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 
  24 
  25 package org.graalvm.compiler.replacements.test;
  26 
  27 import static org.graalvm.compiler.java.BytecodeParserOptions.InlinePartialIntrinsicExitDuringParsing;
  28 
  29 import java.util.function.Function;
  30 
  31 import org.graalvm.compiler.api.directives.GraalDirectives;
  32 import org.graalvm.compiler.api.replacements.ClassSubstitution;
  33 import org.graalvm.compiler.api.replacements.MethodSubstitution;
  34 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
  35 import org.graalvm.compiler.bytecode.BytecodeProvider;
  36 import org.graalvm.compiler.core.common.LIRKind;
  37 import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
  38 import org.graalvm.compiler.core.common.spi.ForeignCallLinkage;
  39 import org.graalvm.compiler.core.common.spi.ForeignCallsProvider;
  40 import org.graalvm.compiler.core.common.type.Stamp;
  41 import org.graalvm.compiler.core.common.type.StampFactory;
  42 import org.graalvm.compiler.debug.DebugContext;
  43 import org.graalvm.compiler.graph.GraalGraphError;
  44 import org.graalvm.compiler.graph.Node.ConstantNodeParameter;
  45 import org.graalvm.compiler.graph.Node.NodeIntrinsic;
  46 import org.graalvm.compiler.nodes.FrameState;
  47 import org.graalvm.compiler.nodes.PiNode;
  48 import org.graalvm.compiler.nodes.StructuredGraph;
  49 import org.graalvm.compiler.nodes.ValueNode;
  50 import org.graalvm.compiler.nodes.extended.OpaqueNode;
  51 import org.graalvm.compiler.nodes.extended.ForeignCallNode;
  52 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
  53 import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin;
  54 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin.Receiver;
  55 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
  56 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration;
  57 import org.graalvm.compiler.nodes.graphbuilderconf.NodeIntrinsicPluginFactory;
  58 import org.graalvm.compiler.nodes.spi.LoweringTool;
  59 import org.graalvm.compiler.options.OptionValues;
  60 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
  61 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
  62 import org.graalvm.compiler.phases.common.FloatingReadPhase;
  63 import org.graalvm.compiler.phases.common.FrameStateAssignmentPhase;
  64 import org.graalvm.compiler.phases.common.GuardLoweringPhase;
  65 import org.graalvm.compiler.phases.common.LoweringPhase;
  66 import org.graalvm.compiler.phases.tiers.HighTierContext;
  67 import jdk.internal.vm.compiler.word.LocationIdentity;
  68 import org.junit.Assert;
  69 import org.junit.BeforeClass;
  70 import org.junit.Test;
  71 
  72 import jdk.vm.ci.code.InstalledCode;
  73 import jdk.vm.ci.meta.JavaKind;
  74 import jdk.vm.ci.meta.ResolvedJavaMethod;
  75 
  76 /**
  77  * Tests for expected behavior when parsing snippets and intrinsics.
  78  */
  79 public class ReplacementsParseTest extends ReplacementsTest {
  80 
  81     private static final String IN_COMPILED_HANDLER_MARKER = "*** in compiled handler ***";
  82 
  83     /**
  84      * Marker value to indicate an exception handler was interpreted. We cannot use a complex string
  85      * expression in this context without risking non-deterministic behavior dependent on whether
  86      * String intrinsics are applied or whether String expression evaluation hit an uncommon trap
  87      * when executed by C1 or C2 (and thus potentially altering the profile such that the exception
  88      * handler is *not* compiled by Graal even when we want it to be).
  89      */
  90     private static final String IN_INTERPRETED_HANDLER_MARKER = "*** in interpreted handler ***";
  91 
  92     private InlineInvokePlugin.InlineInfo inlineInvokeDecision;
  93     private String inlineInvokeMethodName = null;
  94 
  95     @SuppressWarnings("serial")
  96     static class CustomError extends Error {
  97         CustomError(String message) {
  98             super(message);
  99         }
 100     }
 101 
 102     static final Object THROW_EXCEPTION_MARKER = new Object() {
 103         @Override
 104         public String toString() {
 105             return "THROW_EXCEPTION_MARKER";
 106         }
 107     };
 108 
 109     static int copyFirstBody(byte[] left, byte[] right, boolean left2right) {
 110         if (left2right) {
 111             byte e = left[0];
 112             right[0] = e;
 113             return e;
 114         } else {
 115             byte e = right[0];
 116             left[0] = e;
 117             return e;
 118         }
 119     }
 120 
 121     static int copyFirstL2RBody(byte[] left, byte[] right) {
 122         byte e = left[0];
 123         right[0] = e;
 124         return e;
 125     }
 126 
 127     static class TestObject {
 128         static double next(double v) {
 129             return Math.nextAfter(v, 1.0);
 130         }
 131 
 132         static double next2(double v) {
 133             return Math.nextAfter(v, 1.0);
 134         }
 135 
 136         static double nextAfter(double x, double d) {
 137             return Math.nextAfter(x, d);
 138         }
 139 
 140         TestObject() {
 141             this(null);
 142         }
 143 
 144         TestObject(Object id) {
 145             this.id = id;
 146         }
 147 
 148         final Object id;
 149 
 150         String stringizeId() {
 151             Object res = id;
 152             if (res == THROW_EXCEPTION_MARKER) {
 153                 // Tests exception throwing from partial intrinsification
 154                 throw new CustomError("ex");
 155             }
 156             return String.valueOf(res);
 157         }
 158 
 159         static String stringize(Object obj) {
 160             Object res = obj;
 161             if (res == THROW_EXCEPTION_MARKER) {
 162                 // Tests exception throwing from partial intrinsification
 163                 throw new CustomError("ex");
 164             }
 165             return String.valueOf(res);
 166         }
 167 
 168         static String identity(String s) {
 169             return s;
 170         }
 171 
 172         /**
 173          * @see TestObjectSubstitutions#copyFirst(byte[], byte[], boolean)
 174          */
 175         static int copyFirst(byte[] left, byte[] right, boolean left2right) {
 176             return copyFirstBody(left, right, left2right);
 177         }
 178 
 179         /**
 180          * @see TestObjectSubstitutions#copyFirstL2R(byte[], byte[])
 181          */
 182         static int copyFirstL2R(byte[] left, byte[] right) {
 183             return copyFirstL2RBody(left, right);
 184         }
 185 
 186         static int nonVoidIntrinsicWithCall(@SuppressWarnings("unused") int x, int y) {
 187             return y;
 188         }
 189 
 190         static int nonVoidIntrinsicWithOptimizedSplit(int x) {
 191             return x;
 192         }
 193     }
 194 
 195     @ClassSubstitution(TestObject.class)
 196     static class TestObjectSubstitutions {
 197 
 198         @MethodSubstitution(isStatic = true)
 199         static double nextAfter(double x, double d) {
 200             double xx = (x == -0.0 ? 0.0 : x);
 201             return Math.nextAfter(xx, d);
 202         }
 203 
 204         /**
 205          * Tests conditional intrinsification of a static method.
 206          */
 207         @MethodSubstitution
 208         static String stringize(Object obj) {
 209             if (obj != null && obj.getClass() == String.class) {
 210                 return asNonNullString(obj);
 211             } else {
 212                 // A recursive call denotes exiting/deoptimizing
 213                 // out of the partial intrinsification to the
 214                 // slow/uncommon case.
 215                 return stringize(obj);
 216             }
 217         }
 218 
 219         /**
 220          * Tests conditional intrinsification of a non-static method.
 221          */
 222         @MethodSubstitution(isStatic = false)
 223         static String stringizeId(TestObject thisObj) {
 224             if (thisObj.id != null && thisObj.id.getClass() == String.class) {
 225                 return asNonNullString(thisObj.id);
 226             } else {
 227                 // A recursive call denotes exiting/deoptimizing
 228                 // out of the partial intrinsification to the
 229                 // slow/uncommon case.
 230                 return outOfLinePartialIntrinsification(thisObj);
 231             }
 232         }
 233 
 234         static String outOfLinePartialIntrinsification(TestObject thisObj) {
 235             return stringizeId(thisObj);
 236         }
 237 
 238         public static String asNonNullString(Object object) {
 239             return asNonNullStringIntrinsic(object, String.class, true, true);
 240         }
 241 
 242         @NodeIntrinsic(PiNode.class)
 243         private static native String asNonNullStringIntrinsic(Object object, @ConstantNodeParameter Class<?> toType, @ConstantNodeParameter boolean exactType, @ConstantNodeParameter boolean nonNull);
 244 
 245         /**
 246          * An valid intrinsic as the frame state associated with the merge should prevent the frame
 247          * states associated with the array stores from being associated with subsequent
 248          * deoptimizing nodes.
 249          */
 250         @MethodSubstitution
 251         static int copyFirst(byte[] left, byte[] right, boolean left2right) {
 252             return copyFirstBody(left, right, left2right);
 253         }
 254 
 255         /**
 256          * An invalid intrinsic as the frame state associated with the array assignment can leak out
 257          * to subsequent deoptimizing nodes.
 258          */
 259         @MethodSubstitution
 260         static int copyFirstL2R(byte[] left, byte[] right) {
 261             return copyFirstL2RBody(left, right);
 262         }
 263 
 264         /**
 265          * Tests that non-capturing lambdas are folded away.
 266          */
 267         @MethodSubstitution
 268         static String identity(String value) {
 269             return apply(s -> s, value);
 270         }
 271 
 272         private static String apply(Function<String, String> f, String value) {
 273             return f.apply(value);
 274         }
 275 
 276         @MethodSubstitution(isStatic = true)
 277         static int nonVoidIntrinsicWithCall(int x, int y) {
 278             nonVoidIntrinsicWithCallStub(x);
 279             return y;
 280         }
 281 
 282         @MethodSubstitution(isStatic = true)
 283         static int nonVoidIntrinsicWithOptimizedSplit(int x) {
 284             if (x == GraalDirectives.opaque(x)) {
 285                 nonVoidIntrinsicWithCallStub(x);
 286             }
 287             return x;
 288         }
 289 
 290         public static void nonVoidIntrinsicWithCallStub(int zLen) {
 291             nonVoidIntrinsicWithCallStub(STUB_CALL, zLen);
 292         }
 293 
 294         static final ForeignCallDescriptor STUB_CALL = new ForeignCallDescriptor("stubCall", void.class, int.class);
 295 
 296         @NodeIntrinsic(ForeignCallNode.class)
 297         private static native void nonVoidIntrinsicWithCallStub(@ConstantNodeParameter ForeignCallDescriptor descriptor, int zLen);
 298 
 299     }
 300 
 301     @Override
 302     protected void registerInvocationPlugins(InvocationPlugins invocationPlugins) {
 303         BytecodeProvider replacementBytecodeProvider = getSystemClassLoaderBytecodeProvider();
 304         Registration r = new Registration(invocationPlugins, TestObject.class, replacementBytecodeProvider);
 305         NodeIntrinsicPluginFactory.InjectionProvider injections = new DummyInjectionProvider();
 306         new PluginFactory_ReplacementsParseTest().registerPlugins(invocationPlugins, injections);
 307         r.registerMethodSubstitution(TestObjectSubstitutions.class, "nextAfter", double.class, double.class);
 308         r.registerMethodSubstitution(TestObjectSubstitutions.class, "stringize", Object.class);
 309         r.registerMethodSubstitution(TestObjectSubstitutions.class, "stringizeId", Receiver.class);
 310         r.registerMethodSubstitution(TestObjectSubstitutions.class, "copyFirst", byte[].class, byte[].class, boolean.class);
 311         r.registerMethodSubstitution(TestObjectSubstitutions.class, "copyFirstL2R", byte[].class, byte[].class);
 312         r.registerMethodSubstitution(TestObjectSubstitutions.class, "nonVoidIntrinsicWithCall", int.class, int.class);
 313         r.registerMethodSubstitution(TestObjectSubstitutions.class, "nonVoidIntrinsicWithOptimizedSplit", int.class);
 314 
 315         if (replacementBytecodeProvider.supportsInvokedynamic()) {
 316             r.registerMethodSubstitution(TestObjectSubstitutions.class, "identity", String.class);
 317         }
 318         super.registerInvocationPlugins(invocationPlugins);
 319     }
 320 
 321     @BeforeClass
 322     public static void warmupProfiles() {
 323         for (int i = 0; i < 40000; i++) {
 324             callCopyFirst(new byte[16], new byte[16], true);
 325             callCopyFirstL2R(new byte[16], new byte[16]);
 326         }
 327     }
 328 
 329     /**
 330      * Ensure that calling the original method from the substitution binds correctly.
 331      */
 332     @Test
 333     public void test1() {
 334         test("test1Snippet", 1.0);
 335     }
 336 
 337     public double test1Snippet(double d) {
 338         return TestObject.next(d);
 339     }
 340 
 341     /**
 342      * Ensure that calling the substitution method binds to the original method properly.
 343      */
 344     @Test
 345     public void test2() {
 346         test("test2Snippet", 1.0);
 347     }
 348 
 349     public double test2Snippet(double d) {
 350         return TestObject.next2(d);
 351     }
 352 
 353     /**
 354      * Ensure that substitution methods with assertions in them don't complain when the exception
 355      * constructor is deleted.
 356      */
 357 
 358     @Test
 359     public void testNextAfter() {
 360         double[] inArray = new double[1024];
 361         double[] outArray = new double[1024];
 362         for (int i = 0; i < inArray.length; i++) {
 363             inArray[i] = -0.0;
 364         }
 365         test("doNextAfter", inArray, outArray);
 366     }
 367 
 368     public void doNextAfter(double[] outArray, double[] inArray) {
 369         for (int i = 0; i < inArray.length; i++) {
 370             double direction = (i & 1) == 0 ? Double.POSITIVE_INFINITY : -Double.NEGATIVE_INFINITY;
 371             outArray[i] = TestObject.nextAfter(inArray[i], direction);
 372         }
 373     }
 374 
 375     private void testWithDifferentReturnValues(OptionValues options, String standardReturnValue, String compiledReturnValue, String name, Object... args) {
 376         ResolvedJavaMethod method = getResolvedJavaMethod(name);
 377         Object receiver = null;
 378 
 379         Result expect = executeExpected(method, receiver, args);
 380         Assert.assertEquals(standardReturnValue, expect.returnValue);
 381         expect = new Result(compiledReturnValue, null);
 382         testAgainstExpected(options, method, expect, receiver, args);
 383     }
 384 
 385     @Override
 386     protected InstalledCode getCode(final ResolvedJavaMethod installedCodeOwner, StructuredGraph graph, boolean forceCompile, boolean installAsDefault, OptionValues options) {
 387         return super.getCode(installedCodeOwner, graph, forceCompileOverride, installAsDefault, options);
 388     }
 389 
 390     boolean forceCompileOverride;
 391 
 392     @Test
 393     public void testCallStringize() {
 394         test("callStringize", "a string");
 395         test("callStringize", Boolean.TRUE);
 396         // Unset 'exception seen' bit if testCallStringizeWithoutInlinePartialIntrinsicExit
 397         // is executed before this test
 398         getResolvedJavaMethod("callStringize").reprofile();
 399         forceCompileOverride = true;
 400         String standardReturnValue = IN_INTERPRETED_HANDLER_MARKER;
 401         String compiledReturnValue = IN_COMPILED_HANDLER_MARKER;
 402         testWithDifferentReturnValues(getInitialOptions(), standardReturnValue, compiledReturnValue, "callStringize", THROW_EXCEPTION_MARKER);
 403     }
 404 
 405     @Test
 406     public void testCallStringizeWithoutInlinePartialIntrinsicExit() {
 407         OptionValues options = new OptionValues(getInitialOptions(), InlinePartialIntrinsicExitDuringParsing, false);
 408         test(options, "callStringize", "a string");
 409         test(options, "callStringize", Boolean.TRUE);
 410         String standardReturnValue = IN_INTERPRETED_HANDLER_MARKER;
 411         String compiledReturnValue = IN_COMPILED_HANDLER_MARKER;
 412         forceCompileOverride = true;
 413         inlineInvokeDecision = InlineInvokePlugin.InlineInfo.DO_NOT_INLINE_WITH_EXCEPTION;
 414         inlineInvokeMethodName = "stringizeId";
 415         try {
 416             testWithDifferentReturnValues(options, standardReturnValue, compiledReturnValue, "callStringize", THROW_EXCEPTION_MARKER);
 417         } finally {
 418             inlineInvokeDecision = null;
 419             inlineInvokeMethodName = null;
 420         }
 421     }
 422 
 423     @Test
 424     public void testCallStringizeId() {
 425         test("callStringizeId", new TestObject("a string"));
 426         test("callStringizeId", new TestObject(Boolean.TRUE));
 427         // Unset 'exception seen' bit if testCallStringizeIdWithoutInlinePartialIntrinsicExit
 428         // is executed before this test
 429         getResolvedJavaMethod("callStringize").reprofile();
 430         forceCompileOverride = true;
 431         String standardReturnValue = IN_INTERPRETED_HANDLER_MARKER;
 432         String compiledReturnValue = IN_COMPILED_HANDLER_MARKER;
 433         testWithDifferentReturnValues(getInitialOptions(), standardReturnValue, compiledReturnValue, "callStringizeId", new TestObject(THROW_EXCEPTION_MARKER));
 434     }
 435 
 436     @Test
 437     public void testCallStringizeIdWithoutInlinePartialIntrinsicExit() {
 438         OptionValues options = new OptionValues(getInitialOptions(), InlinePartialIntrinsicExitDuringParsing, false);
 439         test(options, "callStringizeId", new TestObject("a string"));
 440         test(options, "callStringizeId", new TestObject(Boolean.TRUE));
 441         TestObject exceptionTestObject = new TestObject(THROW_EXCEPTION_MARKER);
 442         String standardReturnValue = IN_INTERPRETED_HANDLER_MARKER;
 443         String compiledReturnValue = IN_COMPILED_HANDLER_MARKER;
 444         forceCompileOverride = true;
 445         inlineInvokeDecision = InlineInvokePlugin.InlineInfo.DO_NOT_INLINE_WITH_EXCEPTION;
 446         inlineInvokeMethodName = "stringizeId";
 447         try {
 448             testWithDifferentReturnValues(options, standardReturnValue, compiledReturnValue, "callStringizeId", exceptionTestObject);
 449         } finally {
 450             inlineInvokeDecision = null;
 451             inlineInvokeMethodName = null;
 452         }
 453     }
 454 
 455     public static Object callStringize(Object obj) {
 456         try {
 457             return TestObject.stringize(obj);
 458         } catch (CustomError e) {
 459             if (GraalDirectives.inCompiledCode()) {
 460                 return IN_COMPILED_HANDLER_MARKER;
 461             }
 462             return IN_INTERPRETED_HANDLER_MARKER;
 463         }
 464     }
 465 
 466     public static Object callStringizeId(TestObject testObj) {
 467         try {
 468             return testObj.stringizeId();
 469         } catch (CustomError e) {
 470             if (GraalDirectives.inCompiledCode()) {
 471                 return IN_COMPILED_HANDLER_MARKER;
 472             }
 473             return IN_INTERPRETED_HANDLER_MARKER;
 474         }
 475     }
 476 
 477     @Test
 478     public void testRootCompileStringize() {
 479         ResolvedJavaMethod method = getResolvedJavaMethod(TestObject.class, "stringize");
 480         test(method, null, "a string");
 481         test(method, null, Boolean.TRUE);
 482         test(method, null, THROW_EXCEPTION_MARKER);
 483     }
 484 
 485     @Test
 486     public void testLambda() {
 487         test("callLambda", (String) null);
 488         test("callLambda", "a string");
 489     }
 490 
 491     public static String callLambda(String value) {
 492         return TestObject.identity(value);
 493     }
 494 
 495     public static int callCopyFirst(byte[] in, byte[] out, boolean left2right) {
 496         int res = TestObject.copyFirst(in, out, left2right);
 497         if (res == 17) {
 498             // A node after the intrinsic that needs a frame state.
 499             GraalDirectives.deoptimize();
 500         }
 501         return res;
 502     }
 503 
 504     public static int callCopyFirstWrapper(byte[] in, byte[] out, boolean left2right) {
 505         return callCopyFirst(in, out, left2right);
 506     }
 507 
 508     public static int callCopyFirstL2R(byte[] in, byte[] out) {
 509         int res = TestObject.copyFirstL2R(in, out);
 510         if (res == 17) {
 511             // A node after the intrinsic that needs a frame state.
 512             GraalDirectives.deoptimize();
 513         }
 514         return res;
 515     }
 516 
 517     @Test
 518     public void testCallCopyFirst() {
 519         byte[] in = {0, 1, 2, 3, 4};
 520         byte[] out = new byte[in.length];
 521         test("callCopyFirst", in, out, true);
 522         test("callCopyFirst", in, out, false);
 523     }
 524 
 525     @SuppressWarnings("try")
 526     @Test
 527     public void testCallCopyFirstL2R() {
 528         byte[] in = {0, 1, 2, 3, 4};
 529         byte[] out = new byte[in.length];
 530         try {
 531             test("callCopyFirstL2R", in, out);
 532         } catch (GraalGraphError e) {
 533             assertTrue(e.getMessage().startsWith("Invalid frame state"));
 534         }
 535     }
 536 
 537     @Override
 538     protected InlineInvokePlugin.InlineInfo bytecodeParserShouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) {
 539         if (inlineInvokeMethodName == null || inlineInvokeMethodName.equals(method.getName())) {
 540             return inlineInvokeDecision;
 541         }
 542         return null;
 543     }
 544 
 545     @Test
 546     public void testCallCopyFirstWithoutInlinePartialIntrinsicExit() {
 547         OptionValues options = new OptionValues(getInitialOptions(), InlinePartialIntrinsicExitDuringParsing, false);
 548         inlineInvokeDecision = InlineInvokePlugin.InlineInfo.DO_NOT_INLINE_WITH_EXCEPTION;
 549         try {
 550             byte[] in = {0, 1, 2, 3, 4};
 551             byte[] out = new byte[in.length];
 552             test(options, "callCopyFirstWrapper", in, out, true);
 553             test(options, "callCopyFirstWrapper", in, out, false);
 554         } finally {
 555             inlineInvokeDecision = null;
 556         }
 557     }
 558 
 559     public static int nonVoidIntrinsicWithCall(int x, int y) {
 560         if (TestObject.nonVoidIntrinsicWithCall(x, y) == x) {
 561             GraalDirectives.deoptimize();
 562         }
 563         return y;
 564     }
 565 
 566     /**
 567      * This tests the case where an intrinsic ends with a runtime call but returns some kind of
 568      * value. This requires that a FrameState is available after the {@link ForeignCallNode} since
 569      * the return value must be computed on return from the call.
 570      */
 571     @Test
 572     public void testNonVoidIntrinsicWithCall() {
 573         testGraph("nonVoidIntrinsicWithCall");
 574     }
 575 
 576     public static int nonVoidIntrinsicWithOptimizedSplit(int x) {
 577         if (TestObject.nonVoidIntrinsicWithOptimizedSplit(x) == x) {
 578             GraalDirectives.deoptimize();
 579         }
 580         return x;
 581     }
 582 
 583     /**
 584      * This is similar to {@link #testNonVoidIntrinsicWithCall()} but has a merge after the call
 585      * which would normally capture the {@link FrameState} but in this case we force the merge to be
 586      * optimized away.
 587      */
 588     @Test
 589     public void testNonVoidIntrinsicWithOptimizedSplit() {
 590         testGraph("nonVoidIntrinsicWithOptimizedSplit");
 591     }
 592 
 593     @SuppressWarnings("try")
 594     private void testGraph(String name) {
 595         StructuredGraph graph = parseEager(name, StructuredGraph.AllowAssumptions.YES);
 596         try (DebugContext.Scope s0 = graph.getDebug().scope(name, graph)) {
 597             for (OpaqueNode node : graph.getNodes().filter(OpaqueNode.class)) {
 598                 node.remove();
 599             }
 600             HighTierContext context = getDefaultHighTierContext();
 601             CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
 602             new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
 603             new FloatingReadPhase().apply(graph);
 604             canonicalizer.apply(graph, context);
 605             new DeadCodeEliminationPhase().apply(graph);
 606             new GuardLoweringPhase().apply(graph, getDefaultMidTierContext());
 607             new FrameStateAssignmentPhase().apply(graph);
 608         } catch (Throwable e) {
 609             throw graph.getDebug().handle(e);
 610         }
 611     }
 612 
 613     private class DummyInjectionProvider implements NodeIntrinsicPluginFactory.InjectionProvider {
 614         @SuppressWarnings("unchecked")
 615         @Override
 616         public <T> T getInjectedArgument(Class<T> type) {
 617             if (type == ForeignCallsProvider.class) {
 618                 return (T) new ForeignCallsProvider() {
 619                     @Override
 620                     public LIRKind getValueKind(JavaKind javaKind) {
 621                         return null;
 622                     }
 623 
 624                     @Override
 625                     public boolean isReexecutable(ForeignCallDescriptor descriptor) {
 626                         return false;
 627                     }
 628 
 629                     @Override
 630                     public LocationIdentity[] getKilledLocations(ForeignCallDescriptor descriptor) {
 631                         return new LocationIdentity[0];
 632                     }
 633 
 634                     @Override
 635                     public boolean canDeoptimize(ForeignCallDescriptor descriptor) {
 636                         return false;
 637                     }
 638 
 639                     @Override
 640                     public boolean isGuaranteedSafepoint(ForeignCallDescriptor descriptor) {
 641                         return false;
 642                     }
 643 
 644                     @Override
 645                     public ForeignCallLinkage lookupForeignCall(ForeignCallDescriptor descriptor) {
 646                         return null;
 647                     }
 648                 };
 649             }
 650             if (type == SnippetReflectionProvider.class) {
 651                 return (T) getSnippetReflection();
 652             }
 653             return null;
 654         }
 655 
 656         @Override
 657         public Stamp getInjectedStamp(Class<?> type, boolean nonNull) {
 658             JavaKind kind = JavaKind.fromJavaClass(type);
 659             return StampFactory.forKind(kind);
 660         }
 661     }
 662 }