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