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.incubator.mvt.ValueType; 30 import jdk.test.lib.Asserts; 31 32 import java.lang.invoke.*; 33 import java.lang.reflect.Method; 34 35 /* 36 * @test 37 * @summary Test Minimal Value Types 38 * @library /testlibrary /test/lib /compiler/whitebox / 39 * @requires os.simpleArch == "x64" 40 * @modules java.base/jdk.experimental.bytecode 41 * java.base/jdk.experimental.value 42 * java.base/jdk.internal.misc:+open 43 * jdk.incubator.mvt 44 * @compile -XDenableValueTypes ValueCapableClass1.java ValueCapableClass2.java TestMinimalValueTypes.java 45 * @run main ClassFileInstaller sun.hotspot.WhiteBox 46 * @run main ClassFileInstaller jdk.test.lib.Platform 47 * @run main/othervm/timeout=120 -Xbootclasspath/a:. -ea -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions 48 * -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -XX:+AlwaysIncrementalInline 49 * -XX:+ValueTypePassFieldsAsArgs -XX:+ValueTypeReturnedAsFields -XX:+ValueArrayFlatten 50 * -XX:ValueFieldMaxFlatSize=-1 -XX:ValueArrayElemMaxFlatSize=-1 -XX:ValueArrayElemMaxFlatOops=-1 51 * compiler.valhalla.valuetypes.TestMinimalValueTypes 52 * @run main/othervm/timeout=120 -Xbootclasspath/a:. -ea -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions 53 * -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -XX:-UseCompressedOops 54 * -XX:-ValueTypePassFieldsAsArgs -XX:-ValueTypeReturnedAsFields -XX:+ValueArrayFlatten 55 * -XX:ValueFieldMaxFlatSize=-1 -XX:ValueArrayElemMaxFlatSize=-1 -XX:ValueArrayElemMaxFlatOops=-1 56 * compiler.valhalla.valuetypes.TestMinimalValueTypes 57 * @run main/othervm/timeout=120 -Xbootclasspath/a:. -ea -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions 58 * -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -XX:-UseCompressedOops 59 * -XX:+ValueTypePassFieldsAsArgs -XX:+ValueTypeReturnedAsFields -XX:-ValueArrayFlatten 60 * -XX:ValueFieldMaxFlatSize=0 -XX:ValueArrayElemMaxFlatSize=0 -XX:ValueArrayElemMaxFlatOops=0 61 * -DVerifyIR=false compiler.valhalla.valuetypes.TestMinimalValueTypes 62 * @run main/othervm/timeout=120 -Xbootclasspath/a:. -ea -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions 63 * -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -XX:+AlwaysIncrementalInline 64 * -XX:-ValueTypePassFieldsAsArgs -XX:-ValueTypeReturnedAsFields -XX:-ValueArrayFlatten 65 * -XX:ValueFieldMaxFlatSize=0 -XX:ValueArrayElemMaxFlatSize=0 -XX:ValueArrayElemMaxFlatOops=0 66 * -DVerifyIR=false compiler.valhalla.valuetypes.TestMinimalValueTypes 67 * @run main/othervm/timeout=120 -Xbootclasspath/a:. -ea -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions 68 * -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI 69 * -XX:+ValueTypePassFieldsAsArgs -XX:-ValueTypeReturnedAsFields -XX:+ValueArrayFlatten 70 * -XX:ValueFieldMaxFlatSize=0 -XX:ValueArrayElemMaxFlatSize=-1 -XX:ValueArrayElemMaxFlatOops=-1 71 * -DVerifyIR=false compiler.valhalla.valuetypes.TestMinimalValueTypes 72 */ 73 public class TestMinimalValueTypes extends ValueTypeTest { 74 75 static { 76 try { 77 MethodHandles.Lookup lookup = MethodHandles.lookup(); 78 79 // Generate a MethodHandle that obtains field t of the derived value type 80 vccUnboxLoadLongMH = MethodHandleBuilder.loadCode(lookup, 81 "vccUnboxLoadLong", 82 MethodType.methodType(long.class, ValueCapableClass1.class), 83 CODE -> { 84 CODE. 85 aload_0(). 86 vunbox(ValueType.forClass(ValueCapableClass1.class).valueClass()). 87 getfield(ValueType.forClass(ValueCapableClass1.class).valueClass(), "t", "J"). 88 lreturn(); 89 } 90 ); 91 92 // Generate a MethodHandle that obtains field x of the derived value type 93 vccUnboxLoadIntMH = MethodHandleBuilder.loadCode(MethodHandles.lookup(), 94 "vccUnboxLoadInt", 95 MethodType.methodType(int.class, ValueCapableClass1.class), 96 CODE -> { 97 CODE. 98 aload_0(). 99 vunbox(ValueType.forClass(ValueCapableClass1.class).valueClass()). 100 getfield(ValueType.forClass(ValueCapableClass1.class).valueClass(), "x", "I"). 101 ireturn(); 102 } 103 ); 104 105 106 // Generate a MethodHandle that takes a value-capable class, 107 // unboxes it, then boxes it again and returns it. 108 vccUnboxBoxMH = MethodHandleBuilder.loadCode(MethodHandles.lookup(), 109 "vccUnboxBox", 110 MethodType.methodType(ValueCapableClass1.class, ValueCapableClass1.class), 111 CODE -> { 112 CODE. 113 aload_0(). 114 vunbox(ValueType.forClass(ValueCapableClass1.class).valueClass()). 115 vbox(ValueCapableClass1.class). 116 areturn(); 117 } 118 ); 119 120 // Generate a MethodHandle that takes a value-capable class, 121 // unboxes it, boxes it, reads a field from it, and returns the field. 122 vccUnboxBoxLoadIntMH = MethodHandleBuilder.loadCode(MethodHandles.lookup(), 123 "vccUnboxBoxLoadInt", 124 MethodType.methodType(int.class, ValueCapableClass1.class), 125 CODE -> { 126 CODE. 127 aload_0(). 128 vunbox(ValueType.forClass(ValueCapableClass1.class).valueClass()). 129 vbox(ValueCapableClass1.class). 130 getfield(ValueCapableClass1.class, "x", "I"). 131 ireturn(); 132 } 133 ); 134 135 nullvccUnboxLoadLongMH = MethodHandleBuilder.loadCode(MethodHandles.lookup(), 136 "nullvccUnboxLoadLong", 137 MethodType.methodType(long.class), 138 CODE -> { 139 CODE. 140 aconst_null(). 141 vunbox(ValueType.forClass(ValueCapableClass1.class).valueClass()). 142 getfield(ValueType.forClass(ValueCapableClass1.class).valueClass(), "t", "J"). 143 lreturn(); 144 } 145 ); 146 147 objectUnboxLoadLongMH = MethodHandleBuilder.loadCode(MethodHandles.lookup(), 148 "ObjectUnboxLoadLong", 149 MethodType.methodType(long.class, Object.class), 150 CODE -> { 151 CODE. 152 aload_0(). 153 vunbox(ValueType.forClass(ValueCapableClass1.class).valueClass()). 154 getfield(ValueType.forClass(ValueCapableClass1.class).valueClass(), "t", "J"). 155 lreturn(); 156 } 157 ); 158 159 objectBoxMH = MethodHandleBuilder.loadCode(MethodHandles.lookup(), 160 "ObjectBox", 161 MethodType.methodType(long.class, Object.class, boolean.class), 162 CODE -> { 163 CODE. 164 iload_1(). 165 iconst_1(). 166 ifcmp(TypeTag.I, CondKind.NE, "not_equal"). 167 aload_0(). 168 vunbox(ValueType.forClass(ValueCapableClass1.class).valueClass()). 169 vbox(ValueCapableClass1.class). 170 getfield(ValueCapableClass1.class, "t", "J"). 171 lreturn(). 172 label("not_equal"). 173 aload_0(). 174 vunbox(ValueType.forClass(ValueCapableClass2.class).valueClass()). 175 vbox(ValueCapableClass1.class). 176 getfield(ValueCapableClass1.class, "t", "J"). 177 lreturn(); 178 } 179 ); 180 181 checkedvccUnboxLoadLongMH = MethodHandleBuilder.loadCode(MethodHandles.lookup(), 182 "checkedVCCUnboxLoadLongMH", 183 MethodType.methodType(long.class), 184 CODE -> { 185 CODE. 186 invokestatic(ValueCapableClass1.class, "createInline", "()Lcompiler/valhalla/valuetypes/ValueCapableClass1;", false). 187 vunbox(ValueType.forClass(ValueCapableClass1.class).valueClass()). 188 getfield(ValueType.forClass(ValueCapableClass1.class).valueClass(), "t", "J"). 189 lreturn(); 190 } 191 ); 192 193 vastoreMH = MethodHandleBuilder.loadCode(MethodHandles.lookup(), 194 "Vastore", 195 MethodType.methodType(void.class, ValueCapableClass1.class), 196 CODE -> { 197 CODE. 198 iconst_1(). 199 anewarray(ValueType.forClass(ValueCapableClass1.class).valueClass()). 200 iconst_0(). 201 aload_0(). 202 vunbox(ValueType.forClass(ValueCapableClass1.class).valueClass()). 203 vastore(). 204 return_(); 205 } 206 ); 207 208 invalidVastoreMH = MethodHandleBuilder.loadCode(MethodHandles.lookup(), 209 "Vastore", 210 MethodType.methodType(void.class, ValueCapableClass2.class), 211 CODE -> { 212 CODE. 213 iconst_1(). 214 anewarray(ValueType.forClass(ValueCapableClass1.class).valueClass()). 215 iconst_0(). 216 aload_0(). 217 vunbox(ValueType.forClass(ValueCapableClass2.class).valueClass()). 218 vastore(). 219 return_(); 220 } 221 ); 222 223 MethodHandle test13_count = MethodHandles.constant(int.class, 100); 224 ValueType<?> test13_VT = ValueType.forClass(ValueCapableClass2.class); 225 MethodHandle test13_init = test13_VT.defaultValueConstant(); 226 MethodHandle test13_getfield = test13_VT.findGetter(lookup, "u", long.class); 227 MethodHandle test13_add = lookup.findStatic(Long.class, "sum", MethodType.methodType(long.class, long.class, long.class)); 228 MethodHandle test13_body = MethodHandles.collectArguments(ValueCapableClass2.FACTORY, 229 0, 230 MethodHandles.dropArguments(MethodHandles.collectArguments(MethodHandles.insertArguments(test13_add, 231 0, 232 1L), 233 0, 234 test13_getfield), 235 1, 236 int.class)); 237 test13_mh = MethodHandles.collectArguments(test13_getfield, 238 0, 239 MethodHandles.countedLoop(test13_count, test13_init, test13_body)); 240 } catch (NoSuchMethodException | IllegalAccessException | NoSuchFieldException e) { 241 e.printStackTrace(); 242 throw new RuntimeException("Method handle lookup failed"); 243 } 244 } 245 246 public static void main(String[] args) throws Throwable { 247 TestMinimalValueTypes test = new TestMinimalValueTypes(); 248 test.run(args); 249 } 250 251 private static final ValueCapableClass1 vcc = ValueCapableClass1.create(rL, rI, (short)rI, (short)rI); 252 private static final ValueCapableClass2 vcc2 = ValueCapableClass2.create(rL + 1); 253 254 // Test vbox and vunbox 255 256 private static final MethodHandle vccUnboxLoadLongMH; 257 258 @Test 259 public long test1() throws Throwable { 260 return (long)vccUnboxLoadLongMH.invokeExact(vcc); 261 } 262 263 @DontCompile 264 public void test1_verifier(boolean warmup) { 265 try { 266 long result = test1(); 267 Asserts.assertEQ(vcc.t, result, "Field t of input and result must be equal."); 268 } catch (Throwable t) { 269 throw new RuntimeException("Test1 failed", t); 270 } 271 } 272 273 private static final MethodHandle vccUnboxLoadIntMH; 274 275 @Test 276 public int test2() throws Throwable { 277 return (int)vccUnboxLoadIntMH.invokeExact(vcc); 278 } 279 280 @DontCompile 281 public void test2_verifier(boolean warmup) { 282 try { 283 int result = test2(); 284 Asserts.assertEQ(vcc.x, result, "Field x of input and result must be equal."); 285 } catch (Throwable t) { 286 throw new RuntimeException("Test2 failed", t); 287 } 288 } 289 290 291 private static final MethodHandle vccUnboxBoxMH; 292 293 @Test 294 public ValueCapableClass1 test3() throws Throwable { 295 return (ValueCapableClass1)vccUnboxBoxMH.invokeExact(vcc); 296 } 297 298 @DontCompile 299 public void test3_verifier(boolean warmup) { 300 try { 301 ValueCapableClass1 result = test3(); 302 Asserts.assertEQ(vcc.value(), result.value(), "Value of VCC and returned VCC must be equal"); 303 } catch (Throwable t) { 304 throw new RuntimeException("Test3 failed", t); 305 } 306 } 307 308 private static final MethodHandle vccUnboxBoxLoadIntMH; 309 310 @Test 311 public int test4() throws Throwable { 312 return (int)vccUnboxBoxLoadIntMH.invokeExact(vcc); 313 } 314 315 @DontCompile 316 public void test4_verifier(boolean warmup) { 317 try { 318 int result = test4(); 319 Asserts.assertEQ(vcc.x, result, "Field x of VCC and result must be equal"); 320 } catch (Throwable t) { 321 throw new RuntimeException("Test4 failed in the interpeter", t); 322 } 323 } 324 325 326 /* The compiler is supposed to determine that the value to be 327 * unboxed in nullcvvUnboxLoadLong is always null. Therefore, the 328 * compiler generates only the path leading to the corresponding 329 * uncommon trap. */ 330 331 private static final MethodHandle nullvccUnboxLoadLongMH; 332 333 @Test(failOn = RETURN) 334 public long test5() throws Throwable { 335 return (long)nullvccUnboxLoadLongMH.invokeExact(); 336 } 337 338 @DontCompile 339 public void test5_verifier(boolean warmup) throws Throwable { 340 try { 341 long result = test5(); 342 throw new RuntimeException("Test failed because no exception was thrown"); 343 } catch (NullPointerException e) { 344 // Expected 345 } 346 } 347 348 349 /* The compiler is supposed to determine that the allocated 350 * ValueCapableClass1 instance is never null (and therefore not 351 * generate a null check). Also, the source and target type match 352 * (known at compile time), so no type check is needed either.*/ 353 354 private static final MethodHandle checkedvccUnboxLoadLongMH; 355 356 @Test(failOn = NPE) 357 public long test6() throws Throwable { 358 return (long)checkedvccUnboxLoadLongMH.invokeExact(); 359 } 360 361 @DontCompile 362 public void test6_verifier(boolean warmup) throws Throwable { 363 long result = test6(); 364 Asserts.assertEQ(result, 17L); 365 } 366 367 /* The compiler is supposed to emit a runtime null check because 368 * it does not have enough information to determine that the value 369 * to be unboxed is not null (and either that it is null). The 370 * declared type of the */ 371 @Test(match = {NPE}, matchCount = {1}) 372 public long test7(ValueCapableClass1 vcc) throws Throwable { 373 return (long)vccUnboxLoadLongMH.invokeExact(vcc); 374 } 375 376 @DontCompile 377 public void test7_verifier(boolean warmup) throws Throwable { 378 try { 379 long result = test7(null); 380 throw new RuntimeException("Test failed because no exception was thrown"); 381 } catch (NullPointerException e) { 382 } 383 } 384 385 386 /* Attempt to unbox an object that is not a subclass of the 387 * value-capable class derived from the value type specified in 388 * the vunbox bytecode. */ 389 390 private static final MethodHandle objectUnboxLoadLongMH; 391 392 @Test(match = {NPE,CCE}, matchCount = {1,1}) 393 @Warmup(5000) 394 public long test8(Object vcc) throws Throwable { 395 return (long)objectUnboxLoadLongMH.invokeExact(vcc); 396 } 397 398 @DontCompile 399 public void test8_verifier(boolean warmup) throws Throwable { 400 try { 401 long result = test8(new Object()); 402 throw new RuntimeException("Test failed because no exception was thrown"); 403 } catch (ClassCastException e) { 404 } 405 406 try { 407 long result = test8(vcc2); 408 throw new RuntimeException("Test failed because no exception was thrown"); 409 } catch (ClassCastException e) { 410 } 411 412 Asserts.assertEQ(test8(vcc), rL); 413 } 414 415 416 /* Generate an if-then-else construct with one path that contains 417 * an invalid boxing operation (boxing of a value-type to a 418 * non-matching value-capable class).*/ 419 420 private static final MethodHandle objectBoxMH; 421 422 @Test(match = {NPE, CCE}, matchCount = {2, 2}) 423 @Warmup(5000) 424 public long test9(Object obj, boolean warmup) throws Throwable { 425 return (long)objectBoxMH.invokeExact(obj, warmup); 426 } 427 428 @DontCompile 429 public void test9_verifier(boolean warmup) throws Throwable { 430 try { 431 long result = test9(vcc2, true); 432 throw new RuntimeException("Test failed because no exception was thrown"); 433 } catch (ClassCastException e) { 434 } 435 436 Asserts.assertEQ(test9(vcc, true), rL); 437 438 try { 439 long result = test9(vcc2, false); 440 throw new RuntimeException("Test failed because no exception was thrown"); 441 } catch (ClassCastException e) { 442 } 443 444 try { 445 long result = test9(vcc, false); 446 throw new RuntimeException("Test failed because no exception was thrown"); 447 } catch (ClassCastException e) { 448 } 449 } 450 451 452 /* Create a new value type array and store a value type into 453 * it. The test should pass without throwing an exception. */ 454 455 private static final MethodHandle vastoreMH; 456 457 @Test 458 public void test10() throws Throwable { 459 vastoreMH.invokeExact(vcc); 460 } 461 462 public void test10_verifier(boolean warmup) throws Throwable { 463 test10(); 464 } 465 466 467 /* Create a new value type array with element type 468 * ValueCapableClass1 and attempt to store a value type of type 469 * ValueCapableClass2 into it. */ 470 471 private static final MethodHandle invalidVastoreMH; 472 473 @Test 474 public void test11() throws Throwable { 475 invalidVastoreMH.invokeExact(vcc2); 476 } 477 478 public void test11_verifier(boolean warmup) throws Throwable { 479 boolean exceptionThrown = false; 480 try { 481 test11(); 482 } catch (ArrayStoreException e) { 483 exceptionThrown = true; 484 } 485 Asserts.assertTrue(exceptionThrown, "ArrayStoreException must be thrown"); 486 } 487 488 // Test Class::cast intrinsic 489 @Test() 490 public Object test12(Class<?> cls, Object o) throws ClassCastException { 491 return cls.cast(o); 492 } 493 494 public void test12_verifier(boolean warmup) { 495 try { 496 test12(ValueCapableClass1.class, vcc); 497 } catch (ClassCastException e) { 498 throw new RuntimeException("test12_1 failed"); 499 } 500 try { 501 test12(__Value.class, new Object()); 502 throw new RuntimeException("test12_2 failed"); 503 } catch (ClassCastException e) { 504 // Expected 505 } 506 } 507 508 // Simple reduction with intermediate result merged in a Lambda 509 // Form as an __Value. Shouldn't cause any allocations. The entire 510 // loop should go away as the result is a constant. 511 static final MethodHandle test13_mh; 512 513 @Test(failOn = ALLOC + STORE + LOOP + STOREVALUETYPEFIELDS) 514 long test13() throws Throwable { 515 return (long)test13_mh.invokeExact(); 516 } 517 518 @DontCompile 519 public void test13_verifier(boolean warmup) throws Throwable { 520 long v = test13(); 521 Asserts.assertEQ(v, 100L); 522 } 523 }