1 /*
   2  * Copyright (c) 2017, 2019, 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 package compiler.valhalla.valuetypes;
  25 
  26 import java.lang.invoke.*;
  27 import java.lang.reflect.Method;
  28 
  29 import jdk.test.lib.Asserts;
  30 
  31 /*
  32  * @test
  33  * @summary Test method handle support for value types
  34  * @library /testlibrary /test/lib /compiler/whitebox /
  35  * @requires os.simpleArch == "x64"
  36  * @compile TestMethodHandles.java
  37  * @run driver ClassFileInstaller sun.hotspot.WhiteBox jdk.test.lib.Platform
  38  * @run main/othervm/timeout=300 -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions
  39  *                               -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI
  40  *                               compiler.valhalla.valuetypes.ValueTypeTest
  41  *                               compiler.valhalla.valuetypes.TestMethodHandles
  42  */
  43 public class TestMethodHandles extends ValueTypeTest {
  44     // Extra VM parameters for some test scenarios. See ValueTypeTest.getVMParameters()
  45     @Override
  46     public String[] getExtraVMParameters(int scenario) {
  47         switch (scenario) {
  48         // Prevent inlining through MethodHandle linkTo adapters to stress the calling convention
  49         case 2: return new String[] {"-DVerifyIR=false", "-XX:CompileCommand=dontinline,java.lang.invoke.DirectMethodHandle::internalMemberName"};
  50         case 4: return new String[] {"-XX:CompileCommand=dontinline,java.lang.invoke.DirectMethodHandle::internalMemberName"};
  51         }
  52         return null;
  53     }
  54 
  55     static {
  56         try {
  57             Class<?> clazz = TestMethodHandles.class;
  58             ClassLoader loader = clazz.getClassLoader();
  59             MethodHandles.Lookup lookup = MethodHandles.lookup();
  60 
  61             MethodType mt = MethodType.methodType(MyValue3.class);
  62             test1_mh = lookup.findVirtual(clazz, "test1_target", mt);
  63             test2_mh = lookup.findVirtual(clazz, "test2_target", mt);
  64             test3_mh = lookup.findVirtual(clazz, "test3_target", mt);
  65 
  66             MethodType test4_mt1 = MethodType.methodType(int.class, MyValue1.class);
  67             MethodType test4_mt2 = MethodType.methodType(MyValue1.class);
  68             MethodHandle test4_mh1 = lookup.findStatic(clazz, "test4_helper1", test4_mt1);
  69             MethodHandle test4_mh2 = lookup.findStatic(clazz, "test4_helper2", test4_mt2);
  70             test4_mh = MethodHandles.filterReturnValue(test4_mh2, test4_mh1);
  71 
  72             MethodType test5_mt = MethodType.methodType(int.class, MyValue1.class);
  73             test5_mh = lookup.findVirtual(clazz, "test5_target", test5_mt);
  74 
  75             MethodType test6_mt = MethodType.methodType(MyValue3.class);
  76             MethodHandle test6_mh1 = lookup.findVirtual(clazz, "test6_target1", test6_mt);
  77             MethodHandle test6_mh2 = lookup.findVirtual(clazz, "test6_target2", test6_mt);
  78             MethodType boolean_mt = MethodType.methodType(boolean.class);
  79             MethodHandle test6_mh_test = lookup.findVirtual(clazz, "test6_test", boolean_mt);
  80             test6_mh = MethodHandles.guardWithTest(test6_mh_test, test6_mh1, test6_mh2);
  81 
  82             MethodType myvalue2_mt = MethodType.methodType(MyValue2.class);
  83             test7_mh1 = lookup.findStatic(clazz, "test7_target1", myvalue2_mt);
  84             MethodHandle test7_mh2 = lookup.findStatic(clazz, "test7_target2", myvalue2_mt);
  85             MethodHandle test7_mh_test = lookup.findStatic(clazz, "test7_test", boolean_mt);
  86             test7_mh = MethodHandles.guardWithTest(test7_mh_test,
  87                                                     MethodHandles.invoker(myvalue2_mt),
  88                                                     MethodHandles.dropArguments(test7_mh2, 0, MethodHandle.class));
  89 
  90             MethodHandle test8_mh1 = lookup.findStatic(clazz, "test8_target1", myvalue2_mt);
  91             test8_mh2 = lookup.findStatic(clazz, "test8_target2", myvalue2_mt);
  92             MethodHandle test8_mh_test = lookup.findStatic(clazz, "test8_test", boolean_mt);
  93             test8_mh = MethodHandles.guardWithTest(test8_mh_test,
  94                                                     MethodHandles.dropArguments(test8_mh1, 0, MethodHandle.class),
  95                                                     MethodHandles.invoker(myvalue2_mt));
  96 
  97             MethodType test9_mt = MethodType.methodType(MyValue3.class);
  98             MethodHandle test9_mh1 = lookup.findVirtual(clazz, "test9_target1", test9_mt);
  99             MethodHandle test9_mh2 = lookup.findVirtual(clazz, "test9_target2", test9_mt);
 100             MethodHandle test9_mh3 = lookup.findVirtual(clazz, "test9_target3", test9_mt);
 101             MethodType test9_mt2 = MethodType.methodType(boolean.class);
 102             MethodHandle test9_mh_test1 = lookup.findVirtual(clazz, "test9_test1", test9_mt2);
 103             MethodHandle test9_mh_test2 = lookup.findVirtual(clazz, "test9_test2", test9_mt2);
 104             test9_mh = MethodHandles.guardWithTest(test9_mh_test1,
 105                                                     test9_mh1,
 106                                                     MethodHandles.guardWithTest(test9_mh_test2, test9_mh2, test9_mh3));
 107 
 108             MethodType test10_mt = MethodType.methodType(MyValue2.class);
 109             MethodHandle test10_mh1 = lookup.findStatic(clazz, "test10_target1", test10_mt);
 110             test10_mh2 = lookup.findStatic(clazz, "test10_target2", test10_mt);
 111             test10_mh3 = lookup.findStatic(clazz, "test10_target3", test10_mt);
 112             MethodType test10_mt2 = MethodType.methodType(boolean.class);
 113             MethodType test10_mt3 = MethodType.methodType(MyValue2.class);
 114             MethodHandle test10_mh_test1 = lookup.findStatic(clazz, "test10_test1", test10_mt2);
 115             MethodHandle test10_mh_test2 = lookup.findStatic(clazz, "test10_test2", test10_mt2);
 116             test10_mh = MethodHandles.guardWithTest(test10_mh_test1,
 117                                                     MethodHandles.dropArguments(test10_mh1, 0, MethodHandle.class, MethodHandle.class),
 118                                                     MethodHandles.guardWithTest(test10_mh_test2,
 119                                                                                 MethodHandles.dropArguments(MethodHandles.invoker(test10_mt3), 1, MethodHandle.class),
 120                                                                                 MethodHandles.dropArguments(MethodHandles.invoker(test10_mt3), 0, MethodHandle.class))
 121                                                     );
 122 
 123             MethodHandle test11_mh1 = lookup.findStatic(clazz, "test11_target1", myvalue2_mt);
 124             test11_mh2 = lookup.findStatic(clazz, "test11_target2", myvalue2_mt);
 125             MethodHandle test11_mh_test = lookup.findStatic(clazz, "test11_test", boolean_mt);
 126             test11_mh = MethodHandles.guardWithTest(test11_mh_test,
 127                                                     MethodHandles.dropArguments(test11_mh1, 0, MethodHandle.class),
 128                                                     MethodHandles.invoker(myvalue2_mt));
 129         } catch (NoSuchMethodException | IllegalAccessException e) {
 130             e.printStackTrace();
 131             throw new RuntimeException("Method handle lookup failed");
 132         }
 133     }
 134 
 135     public static void main(String[] args) throws Throwable {
 136         TestMethodHandles test = new TestMethodHandles();
 137         test.run(args, MyValue1.class, MyValue2.class, MyValue2Inline.class, MyValue3.class, MyValue3Inline.class);
 138     }
 139 
 140     // Everything inlined
 141     final MyValue3 test1_vt = MyValue3.create();
 142 
 143     @ForceInline
 144     MyValue3 test1_target() {
 145         return test1_vt;
 146     }
 147 
 148     static final MethodHandle test1_mh;
 149 
 150     @Test(valid = ValueTypeReturnedAsFieldsOn, failOn = ALLOC + STORE + CALL)
 151     @Test(valid = ValueTypeReturnedAsFieldsOff, match = { ALLOC, STORE }, matchCount = { 1, 14 })
 152     public MyValue3 test1() throws Throwable {
 153         return (MyValue3)test1_mh.invokeExact(this);
 154     }
 155 
 156     @DontCompile
 157     public void test1_verifier(boolean warmup) throws Throwable {
 158         MyValue3 vt = test1();
 159         test1_vt.verify(vt);
 160     }
 161 
 162     // Leaf method not inlined but returned type is known
 163     final MyValue3 test2_vt = MyValue3.create();
 164     @DontInline
 165     MyValue3 test2_target() {
 166         return test2_vt;
 167     }
 168 
 169     static final MethodHandle test2_mh;
 170 
 171     @Test
 172     public MyValue3 test2() throws Throwable {
 173         return (MyValue3)test2_mh.invokeExact(this);
 174     }
 175 
 176     @DontCompile
 177     public void test2_verifier(boolean warmup) throws Throwable {
 178         Method helper_m = getClass().getDeclaredMethod("test2_target");
 179         if (!warmup && USE_COMPILER && !WHITE_BOX.isMethodCompiled(helper_m, false)) {
 180             enqueueMethodForCompilation(helper_m, COMP_LEVEL_FULL_OPTIMIZATION);
 181             Asserts.assertTrue(WHITE_BOX.isMethodCompiled(helper_m, false), "test2_target not compiled");
 182         }
 183         MyValue3 vt = test2();
 184         test2_vt.verify(vt);
 185     }
 186 
 187     // Leaf method not inlined and returned type not known
 188     final MyValue3 test3_vt = MyValue3.create();
 189     @DontInline
 190     MyValue3 test3_target() {
 191         return test3_vt;
 192     }
 193 
 194     static final MethodHandle test3_mh;
 195 
 196     @Test
 197     public MyValue3 test3() throws Throwable {
 198         return (MyValue3)test3_mh.invokeExact(this);
 199     }
 200 
 201     @DontCompile
 202     public void test3_verifier(boolean warmup) throws Throwable {
 203         // hack so C2 doesn't know the target of the invoke call
 204         Class c = Class.forName("java.lang.invoke.DirectMethodHandle");
 205         Method m = c.getDeclaredMethod("internalMemberName", Object.class);
 206         WHITE_BOX.testSetDontInlineMethod(m, warmup);
 207         MyValue3 vt = test3();
 208         test3_vt.verify(vt);
 209     }
 210 
 211     // When test75_helper1 is inlined in test75, the method handle
 212     // linker that called it is passed a pointer to a copy of vt
 213     // stored in memory. The method handle linker needs to load the
 214     // fields from memory before it inlines test75_helper1.
 215     static public int test4_helper1(MyValue1 vt) {
 216         return vt.x;
 217     }
 218 
 219     static MyValue1 test4_vt = MyValue1.createWithFieldsInline(rI, rL);
 220     static public MyValue1 test4_helper2() {
 221         return test4_vt;
 222     }
 223 
 224     static final MethodHandle test4_mh;
 225 
 226     @Test
 227     public int test4() throws Throwable {
 228         return (int)test4_mh.invokeExact();
 229     }
 230 
 231     @DontCompile
 232     public void test4_verifier(boolean warmup) throws Throwable {
 233         int i = test4();
 234         Asserts.assertEQ(i, test4_vt.x);
 235     }
 236 
 237     // Test method handle call with value type argument
 238     public int test5_target(MyValue1 vt) {
 239         return vt.x;
 240     }
 241 
 242     static final MethodHandle test5_mh;
 243     MyValue1 test5_vt = MyValue1.createWithFieldsInline(rI, rL);
 244 
 245     @Test
 246     public int test5() throws Throwable {
 247         return (int)test5_mh.invokeExact(this, test5_vt);
 248     }
 249 
 250     @DontCompile
 251     public void test5_verifier(boolean warmup) throws Throwable {
 252         int i = test5();
 253         Asserts.assertEQ(i, test5_vt.x);
 254     }
 255 
 256     // Return of target1 and target2 merged in a Lambda Form as an
 257     // Object. Shouldn't cause any allocation
 258     final MyValue3 test6_vt1 = MyValue3.create();
 259     @ForceInline
 260     MyValue3 test6_target1() {
 261         return test6_vt1;
 262     }
 263 
 264     final MyValue3 test6_vt2 = MyValue3.create();
 265     @ForceInline
 266     MyValue3 test6_target2() {
 267         return test6_vt2;
 268     }
 269 
 270     boolean test6_bool = true;
 271     @ForceInline
 272     boolean test6_test() {
 273         return test6_bool;
 274     }
 275 
 276     static final MethodHandle test6_mh;
 277 
 278     @Test(valid = ValueTypeReturnedAsFieldsOn, failOn = ALLOC + ALLOCA + STORE + STOREVALUETYPEFIELDS)
 279     @Test(valid = ValueTypeReturnedAsFieldsOff)
 280     public MyValue3 test6() throws Throwable {
 281         return (MyValue3)test6_mh.invokeExact(this);
 282     }
 283 
 284     @DontCompile
 285     public void test6_verifier(boolean warmup) throws Throwable {
 286         test6_bool = !test6_bool;
 287         MyValue3 vt = test6();
 288         vt.verify(test6_bool ? test6_vt1 : test6_vt2);
 289     }
 290 
 291     // Similar as above but with the method handle for target1 not
 292     // constant. Shouldn't cause any allocation.
 293     @ForceInline
 294     static MyValue2 test7_target1() {
 295         return MyValue2.createWithFieldsInline(rI, true);
 296     }
 297 
 298     @ForceInline
 299     static MyValue2 test7_target2() {
 300         return MyValue2.createWithFieldsInline(rI+1, false);
 301     }
 302 
 303     static boolean test7_bool = true;
 304     @ForceInline
 305     static boolean test7_test() {
 306         return test7_bool;
 307     }
 308 
 309     static final MethodHandle test7_mh;
 310     static MethodHandle test7_mh1;
 311 
 312     @Test
 313     public long test7() throws Throwable {
 314         return ((MyValue2)test7_mh.invokeExact(test7_mh1)).hash();
 315     }
 316 
 317     @DontCompile
 318     public void test7_verifier(boolean warmup) throws Throwable {
 319         test7_bool = !test7_bool;
 320         long hash = test7();
 321         Asserts.assertEQ(hash, MyValue2.createWithFieldsInline(rI+(test7_bool ? 0 : 1), test7_bool).hash());
 322     }
 323 
 324     // Same as above but with the method handle for target2 not
 325     // constant. Shouldn't cause any allocation.
 326     @ForceInline
 327     static MyValue2 test8_target1() {
 328         return MyValue2.createWithFieldsInline(rI, true);
 329     }
 330 
 331     @ForceInline
 332     static MyValue2 test8_target2() {
 333         return MyValue2.createWithFieldsInline(rI+1, false);
 334     }
 335 
 336     static boolean test8_bool = true;
 337     @ForceInline
 338     static boolean test8_test() {
 339         return test8_bool;
 340     }
 341 
 342     static final MethodHandle test8_mh;
 343     static MethodHandle test8_mh2;
 344 
 345     @Test
 346     public long test8() throws Throwable {
 347         return ((MyValue2)test8_mh.invokeExact(test8_mh2)).hash();
 348     }
 349 
 350     @DontCompile
 351     public void test8_verifier(boolean warmup) throws Throwable {
 352         test8_bool = !test8_bool;
 353         long hash = test8();
 354         Asserts.assertEQ(hash, MyValue2.createWithFieldsInline(rI+(test8_bool ? 0 : 1), test8_bool).hash());
 355     }
 356 
 357     // Return of target1, target2 and target3 merged in Lambda Forms
 358     // as an Object. Shouldn't cause any allocation
 359     final MyValue3 test9_vt1 = MyValue3.create();
 360     @ForceInline
 361     MyValue3 test9_target1() {
 362         return test9_vt1;
 363     }
 364 
 365     final MyValue3 test9_vt2 = MyValue3.create();
 366     @ForceInline
 367     MyValue3 test9_target2() {
 368         return test9_vt2;
 369     }
 370 
 371     final MyValue3 test9_vt3 = MyValue3.create();
 372     @ForceInline
 373     MyValue3 test9_target3() {
 374         return test9_vt3;
 375     }
 376 
 377     boolean test9_bool1 = true;
 378     @ForceInline
 379     boolean test9_test1() {
 380         return test9_bool1;
 381     }
 382 
 383     boolean test9_bool2 = true;
 384     @ForceInline
 385     boolean test9_test2() {
 386         return test9_bool2;
 387     }
 388 
 389     static final MethodHandle test9_mh;
 390 
 391     @Test(valid = ValueTypeReturnedAsFieldsOn, failOn = ALLOC + ALLOCA + STORE + STOREVALUETYPEFIELDS)
 392     @Test(valid = ValueTypeReturnedAsFieldsOff)
 393     public MyValue3 test9() throws Throwable {
 394         return (MyValue3)test9_mh.invokeExact(this);
 395     }
 396 
 397     static int test9_i = 0;
 398     @DontCompile
 399     public void test9_verifier(boolean warmup) throws Throwable {
 400         test9_i++;
 401         test9_bool1 = (test9_i % 2) == 0;
 402         test9_bool2 = (test9_i % 3) == 0;
 403         MyValue3 vt = test9();
 404         vt.verify(test9_bool1 ? test9_vt1 : (test9_bool2 ? test9_vt2 : test9_vt3));
 405     }
 406 
 407     // Same as above but with non constant target2 and target3
 408     @ForceInline
 409     static MyValue2 test10_target1() {
 410         return MyValue2.createWithFieldsInline(rI, true);
 411     }
 412 
 413     @ForceInline
 414     static MyValue2 test10_target2() {
 415         return MyValue2.createWithFieldsInline(rI+1, false);
 416     }
 417 
 418     @ForceInline
 419     static MyValue2 test10_target3() {
 420         return MyValue2.createWithFieldsInline(rI+2, true);
 421     }
 422 
 423     static boolean test10_bool1 = true;
 424     @ForceInline
 425     static boolean test10_test1() {
 426         return test10_bool1;
 427     }
 428 
 429     static boolean test10_bool2 = true;
 430     @ForceInline
 431     static boolean test10_test2() {
 432         return test10_bool2;
 433     }
 434 
 435     static final MethodHandle test10_mh;
 436     static MethodHandle test10_mh2;
 437     static MethodHandle test10_mh3;
 438 
 439     @Test
 440     public long test10() throws Throwable {
 441         return ((MyValue2)test10_mh.invokeExact(test10_mh2, test10_mh3)).hash();
 442     }
 443 
 444     static int test10_i = 0;
 445 
 446     @DontCompile
 447     public void test10_verifier(boolean warmup) throws Throwable {
 448         test10_i++;
 449         test10_bool1 = (test10_i % 2) == 0;
 450         test10_bool2 = (test10_i % 3) == 0;
 451         long hash = test10();
 452         int i = rI+(test10_bool1 ? 0 : (test10_bool2 ? 1 : 2));
 453         boolean b = test10_bool1 ? true : (test10_bool2 ? false : true);
 454         Asserts.assertEQ(hash, MyValue2.createWithFieldsInline(i, b).hash());
 455     }
 456 
 457     static int test11_i = 0;
 458 
 459     @ForceInline
 460     static MyValue2 test11_target1() {
 461         return MyValue2.createWithFieldsInline(rI+test11_i, true);
 462     }
 463 
 464     @ForceInline
 465     static MyValue2 test11_target2() {
 466         return MyValue2.createWithFieldsInline(rI-test11_i, false);
 467     }
 468 
 469     @ForceInline
 470     static boolean test11_test() {
 471         return (test11_i % 100) == 0;
 472     }
 473 
 474     static final MethodHandle test11_mh;
 475     static MethodHandle test11_mh2;
 476 
 477     // Check that a buffered value returned by a compiled lambda form
 478     // is properly handled by the caller.
 479     @Test
 480     @Warmup(11000)
 481     public long test11() throws Throwable {
 482         return ((MyValue2)test11_mh.invokeExact(test11_mh2)).hash();
 483     }
 484 
 485     @DontCompile
 486     public void test11_verifier(boolean warmup) throws Throwable {
 487         test11_i++;
 488         long hash = test11();
 489         boolean b = (test11_i % 100) == 0;
 490         Asserts.assertEQ(hash, MyValue2.createWithFieldsInline(rI+test11_i * (b ? 1 : -1), b).hash());
 491     }
 492 }