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