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 }