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 * -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.asValueType()); 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.asValueType()); 75 MethodType test4_mt2 = MethodType.methodType(MyValue1.class.asValueType()); 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.asValueType()); 81 test5_mh = lookup.findVirtual(clazz, "test5_target", test5_mt); 82 83 MethodType test6_mt = MethodType.methodType(MyValue3.class.asValueType()); 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.asValueType()); 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.asValueType()); 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.asValueType()); 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.asValueType()); 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.asValueType(), MyValue2.class.asValueType(), MyValue2Inline.class.asValueType(), MyValue3.class.asValueType(), MyValue3Inline.class.asValueType()); 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 }