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