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