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