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 }