1 /*
   2  * Copyright (c) 2016, 2017, 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 // TODO add bugid and summary
  25 
  26 /*
  27  * @test
  28  * @library /testlibrary /test/lib /compiler/whitebox /
  29  * @build compiler.valhalla.valuetypes.ValueTypeTestBench
  30  * @run main ClassFileInstaller sun.hotspot.WhiteBox
  31  * @run main ClassFileInstaller jdk.test.lib.Platform
  32  * @run main/othervm -ea -noverify -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
  33  *                   -XX:+UnlockExperimentalVMOptions -XX:+ValueTypePassFieldsAsArgs
  34  *                   -XX:-TieredCompilation compiler.valhalla.valuetypes.ValueTypeTestBench
  35  * @run main/othervm -ea -noverify -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
  36  *                   -XX:+UnlockExperimentalVMOptions -XX:-ValueTypePassFieldsAsArgs
  37  *                   -XX:-TieredCompilation compiler.valhalla.valuetypes.ValueTypeTestBench
  38  */
  39 
  40 package compiler.valhalla.valuetypes;
  41 
  42 import compiler.whitebox.CompilerWhiteBoxTest;
  43 import jdk.internal.misc.Unsafe;
  44 import jdk.test.lib.Asserts;
  45 import jdk.test.lib.Platform;
  46 import jdk.test.lib.ProcessTools;
  47 import jdk.test.lib.OutputAnalyzer;
  48 import jdk.test.lib.Utils;
  49 import sun.hotspot.WhiteBox;
  50 
  51 import java.lang.annotation.Retention;
  52 import java.lang.annotation.RetentionPolicy;
  53 import java.lang.annotation.Repeatable;
  54 import java.lang.invoke.*;
  55 import java.lang.reflect.Method;
  56 import java.util.ArrayList;
  57 import java.util.Arrays;
  58 import java.util.Hashtable;
  59 import java.util.List;
  60 import java.util.regex.Matcher;
  61 import java.util.regex.Pattern;
  62 import jdk.experimental.value.*;
  63 
  64 // Test value types
  65 __ByValue final class MyValue1 {
  66     static int s;
  67     static final long sf = ValueTypeTestBench.rL;
  68     final int x;
  69     final long y;
  70     final MyValue2 v1;
  71     final MyValue2 v2;
  72     static final MyValue2 v3 = MyValue2.createInline(ValueTypeTestBench.rI, true);
  73     final int c;
  74 
  75     private MyValue1(int x, long y, MyValue2 v1, MyValue2 v2, int c) {
  76         s = x;
  77         this.x = x;
  78         this.y = y;
  79         this.v1 = v1;
  80         this.v2 = v2;
  81         this.c = c;
  82     }
  83 
  84     @DontInline
  85     public static MyValue1 createDontInline(int x, long y) {
  86         return __Make MyValue1(x, y, MyValue2.createInline(x, true), MyValue2.createInline(x, false), ValueTypeTestBench.rI);
  87     }
  88 
  89     @ForceInline
  90     public static MyValue1 createInline(int x, long y) {
  91         return __Make MyValue1(x, y, MyValue2.createInline(x, true), MyValue2.createInline(x, false), ValueTypeTestBench.rI);
  92     }
  93 
  94     @ForceInline
  95     public long hash() {
  96         return s + sf + x + y + c + v1.hash() + v2.hash() + v3.hash();
  97     }
  98 
  99     @DontCompile
 100     public long hashInterpreted() {
 101         return s + sf + x + y + c + v1.hashInterpreted() + v2.hashInterpreted() + v3.hashInterpreted();
 102     }
 103 }
 104 
 105 __ByValue final class MyValue2 {
 106     final int x;
 107     final boolean b;
 108     final long c;
 109 
 110     private MyValue2(int x, boolean b, long c) {
 111         this.x = x;
 112         this.b = b;
 113         this.c = c;
 114     }
 115 
 116     @ForceInline
 117     public static MyValue2 createInline(int x, boolean b) {
 118         return __Make MyValue2(x, b, ValueTypeTestBench.rL);
 119     }
 120 
 121     @ForceInline
 122     public long hash() {
 123         return x + (b ? 0 : 1) + c;
 124     }
 125 
 126     @DontInline
 127     public long hashInterpreted() {
 128         return x + (b ? 0 : 1) + c;
 129     }
 130 }
 131 
 132 public class ValueTypeTestBench {    
 133     // Print ideal graph after execution of each test
 134     private static final boolean PRINT_GRAPH = true;
 135 
 136     // Random test values
 137     public static final int  rI = Utils.getRandomInstance().nextInt() % 1000;
 138     public static final long rL = Utils.getRandomInstance().nextLong() % 1000;
 139 
 140     public ValueTypeTestBench() {
 141         val3 = MyValue1.createInline(rI, rL);
 142     }
 143 
 144     // MethodHandles and value-capable class instance needed for testing vbox/vunbox
 145     private static final MethodHandle vccUnboxLoadLongMH = generateVCCUnboxLoadLongMH();
 146     private static final MethodHandle vccUnboxLoadIntMH = generateVCCUnboxLoadIntMH();
 147     private static final MethodHandle vccUnboxBoxMH = generateVCCUnboxBoxMH();
 148     private static final MethodHandle vccUnboxBoxLoadIntMH = generateVCCUnboxBoxLoadIntMH();
 149 
 150     private static final ValueCapableClass1 vcc = ValueCapableClass1.create(rL, rI, (short)rI, (short)rI);
 151 
 152     // ========== Helper methods ==========
 153 
 154     public long hash() {
 155         return hash(rI, rL);
 156     }
 157 
 158     public long hash(int x, long y) {
 159         return MyValue1.createInline(x, y).hash();
 160     }
 161 
 162     // ========== Test definitions ==========
 163 
 164     // Receive value type through call to interpreter
 165     @Test(failOn = ALLOC + STORE + TRAP)
 166     public long test1() {
 167         MyValue1 v = MyValue1.createDontInline(rI, rL);
 168         return v.hash();
 169     }
 170 
 171     @DontCompile
 172     public void test1_verifier(boolean warmup) {
 173         long result = test1();
 174         Asserts.assertEQ(result, hash());
 175     }
 176 
 177     // Receive value type from interpreter via parameter
 178     @Test(failOn = ALLOC + STORE + TRAP)
 179     public long test2(MyValue1 v) {
 180         return v.hash();
 181     }
 182 
 183     @DontCompile
 184     public void test2_verifier(boolean warmup) {
 185         MyValue1 v = MyValue1.createDontInline(rI, rL);
 186         long result = test2(v);
 187         Asserts.assertEQ(result, hash());
 188     }
 189 
 190     // Return incoming value type without accessing fields
 191     @Test(valid = ValueTypePassFieldsAsArgsOn, match = {ALLOC, STORE}, matchCount = {1, 9}, failOn = LOAD + TRAP)
 192     @Test(valid = ValueTypePassFieldsAsArgsOff, failOn = ALLOC + LOAD + STORE + TRAP)
 193     public MyValue1 test3(MyValue1 v) {
 194         return v;
 195     }
 196 
 197     @DontCompile
 198     public void test3_verifier(boolean warmup) {
 199         MyValue1 v1 = MyValue1.createDontInline(rI, rL);
 200         MyValue1 v2 = test3(v1);
 201         Asserts.assertEQ(v1.x, v2.x);
 202         Asserts.assertEQ(v1.y, v2.y);
 203     }
 204 
 205     // Create a value type in compiled code and only use fields.
 206     // Allocation should go away because value type does not escape.
 207     @Test(failOn = ALLOC + LOAD + STORE + TRAP)
 208     public long test4() {
 209         MyValue1 v = MyValue1.createInline(rI, rL);
 210         return v.hash();
 211     }
 212 
 213     @DontCompile
 214     public void test4_verifier(boolean warmup) {
 215         long result = test4();
 216         Asserts.assertEQ(result, hash());
 217     }
 218 
 219     // Create a value type in compiled code and pass it to
 220     // an inlined compiled method via a call.
 221     @Test(failOn = ALLOC + LOAD + STORE + TRAP)
 222     public long test5() {
 223         MyValue1 v = MyValue1.createInline(rI, rL);
 224         return test5Inline(v);
 225     }
 226 
 227     @ForceInline
 228     public long test5Inline(MyValue1 v) {
 229         return v.hash();
 230     }
 231 
 232     @DontCompile
 233     public void test5_verifier(boolean warmup) {
 234         long result = test5();
 235         Asserts.assertEQ(result, hash());
 236     }
 237 
 238     // Create a value type in compiled code and pass it to
 239     // the interpreter via a call.
 240     @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = LOAD + TRAP + ALLOC)
 241     @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP)
 242     public long test6() {
 243         MyValue1 v = MyValue1.createInline(rI, rL);
 244         // Pass to interpreter
 245         return v.hashInterpreted();
 246     }
 247 
 248     @DontCompile
 249     public void test6_verifier(boolean warmup) {
 250         long result = test6();
 251         Asserts.assertEQ(result, hash());
 252     }
 253 
 254     // Create a value type in compiled code and pass it to
 255     // the interpreter by returning.
 256     @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP)
 257     public MyValue1 test7(int x, long y) {
 258         return MyValue1.createInline(x, y);
 259     }
 260 
 261     @DontCompile
 262     public void test7_verifier(boolean warmup) {
 263         MyValue1 v = test7(rI, rL);
 264         Asserts.assertEQ(v.hash(), hash());
 265     }
 266 
 267     // Merge value types created from two branches
 268     @Test(failOn = ALLOC + STORE + TRAP)
 269     public long test8(boolean b) {
 270         MyValue1 v;
 271         if (b) {
 272             v = MyValue1.createInline(rI, rL);
 273         } else {
 274             v = MyValue1.createDontInline(rI + 1, rL + 1);
 275         }
 276         return v.hash();
 277     }
 278 
 279     @DontCompile
 280     public void test8_verifier(boolean warmup) {
 281         Asserts.assertEQ(test8(true), hash());
 282         Asserts.assertEQ(test8(false), hash(rI + 1, rL + 1));
 283     }
 284 
 285     // Merge value types created from two branches
 286     @Test(valid = ValueTypePassFieldsAsArgsOn, match = {LOAD}, matchCount = {9}, failOn = TRAP + ALLOC + STORE)
 287     @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC, STORE}, matchCount = {1, 3}, failOn = LOAD + TRAP)
 288     public MyValue1 test9(boolean b) {
 289         MyValue1 v;
 290         if (b) {
 291             // Value type is not allocated
 292             v = MyValue1.createInline(rI, rL);
 293         } else {
 294             // Value type is allocated by the callee
 295             v = MyValue1.createDontInline(rI + 1, rL + 1);
 296         }
 297         // Need to allocate value type if 'b' is true
 298         long sum = v.hashInterpreted();
 299         if (b) {
 300             v = MyValue1.createDontInline(rI, sum);
 301         } else {
 302             v = MyValue1.createDontInline(rI, sum + 1);
 303         }
 304         // Don't need to allocate value type because both branches allocate
 305         return v;
 306     }
 307 
 308     @DontCompile
 309     public void test9_verifier(boolean warmup) {
 310         MyValue1 v = test9(true);
 311         Asserts.assertEQ(v.x, rI);
 312         Asserts.assertEQ(v.y, hash());
 313         v = test9(false);
 314         Asserts.assertEQ(v.x, rI);
 315         Asserts.assertEQ(v.y, hash(rI + 1, rL + 1) + 1);
 316     }
 317 
 318     // Merge value types created in a loop (not inlined)
 319     @Test(failOn = ALLOC + STORE + TRAP)
 320     public long test10(int x, long y) {
 321         MyValue1 v = MyValue1.createDontInline(x, y);
 322         for (int i = 0; i < 10; ++i) {
 323             v = MyValue1.createDontInline(v.x + 1, v.y + 1);
 324         }
 325         return v.hash();
 326     }
 327 
 328     @DontCompile
 329     public void test10_verifier(boolean warmup) {
 330         long result = test10(rI, rL);
 331         Asserts.assertEQ(result, hash(rI + 10, rL + 10));
 332     }
 333 
 334     // Merge value types created in a loop (inlined)
 335     @Test(failOn = ALLOC + LOAD + STORE + LOOP + TRAP)
 336     public long test11(int x, long y) {
 337         MyValue1 v = MyValue1.createInline(x, y);
 338         for (int i = 0; i < 10; ++i) {
 339             v = MyValue1.createInline(v.x + 1, v.y + 1);
 340         }
 341         return v.hash();
 342     }
 343 
 344     @DontCompile
 345     public void test11_verifier(boolean warmup) {
 346         long result = test11(rI, rL);
 347         Asserts.assertEQ(result, hash(rI + 10, rL + 10));
 348     }
 349 
 350     // Test loop with uncommon trap referencing a value type
 351     @Test(match = {TRAP, SCOBJ}, matchCount = {1, 1}, failOn = ALLOC + LOAD + STORE)
 352     public long test12(boolean b) {
 353         MyValue1 v = MyValue1.createInline(rI, rL);
 354         long result = 42;
 355         for (int i = 0; i < 1000; ++i) {
 356             if (b) {
 357                 result += v.x;
 358             } else {
 359                 // Uncommon trap referencing v. We delegate allocation to the
 360                 // interpreter by adding a SafePointScalarObjectNode.
 361                 result = v.hashInterpreted();
 362             }
 363         }
 364         return result;
 365     }
 366 
 367     @DontCompile
 368     public void test12_verifier(boolean warmup) {
 369         long result = test12(warmup);
 370         Asserts.assertEQ(result, warmup ? 42 + (1000*rI) : hash());
 371     }
 372 
 373     // Test loop with uncommon trap referencing a value type
 374     @Test(match = {TRAP, LOAD}, matchCount = {1, 1}, failOn = ALLOC + STORE + SCOBJ)
 375     public long test13(boolean b) {
 376         MyValue1 v = MyValue1.createDontInline(rI, rL);
 377         long result = 42;
 378         for (int i = 0; i < 1000; ++i) {
 379             if (b) {
 380                 result += v.x;
 381             } else {
 382                 // Uncommon trap referencing v. Should not allocate
 383                 // but just pass the existing oop to the uncommon trap.
 384                 result = v.hashInterpreted();
 385             }
 386         }
 387         return result;
 388     }
 389 
 390     @DontCompile
 391     public void test13_verifier(boolean warmup) {
 392         long result = test13(warmup);
 393         Asserts.assertEQ(result, warmup ? 42 + (1000*rI) : hash());
 394     }
 395 
 396     // Create a value type in a non-inlined method and then call a
 397     // non-inlined method on that value type.
 398     @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = (ALLOC + STORE + TRAP), match = {LOAD}, matchCount = {9})
 399     @Test(valid = ValueTypePassFieldsAsArgsOff, failOn = (ALLOC + LOAD + STORE + TRAP))
 400     public long test14() {
 401         MyValue1 v = MyValue1.createDontInline(rI, rL);
 402         return v.hashInterpreted();
 403     }
 404 
 405     @DontCompile
 406     public void test14_verifier(boolean b) {
 407         long result = test14();
 408         Asserts.assertEQ(result, hash());
 409     }
 410 
 411     // Create a value type in an inlined method and then call a
 412     // non-inlined method on that value type.
 413     @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = (LOAD + TRAP + ALLOC))
 414     @Test(valid = ValueTypePassFieldsAsArgsOff, failOn = (LOAD + TRAP), match = {ALLOC}, matchCount = {1})
 415     public long test15() {
 416         MyValue1 v = MyValue1.createInline(rI, rL);
 417         return v.hashInterpreted();
 418     }
 419 
 420     @DontCompile
 421     public void test15_verifier(boolean b) {
 422         long result = test15();
 423         Asserts.assertEQ(result, hash());
 424     }
 425 
 426     // Create a value type in a non-inlined method and then call an
 427     // inlined method on that value type.
 428     @Test(failOn = (ALLOC + STORE + TRAP))
 429     public long test16() {
 430         MyValue1 v = MyValue1.createDontInline(rI, rL);
 431         return v.hash();
 432     }
 433 
 434     @DontCompile
 435     public void test16_verifier(boolean b) {
 436         long result = test16();
 437         Asserts.assertEQ(result, hash());
 438     }
 439 
 440     // Create a value type in an inlined method and then call an
 441     // inlined method on that value type.
 442     @Test(failOn = (ALLOC + LOAD + STORE + TRAP))
 443     public long test17() {
 444         MyValue1 v = MyValue1.createInline(rI, rL);
 445         return v.hash();
 446     }
 447 
 448     @DontCompile
 449     public void test17_verifier(boolean b) {
 450         long result = test17();
 451         Asserts.assertEQ(result, hash());
 452     }
 453 
 454     // Create a value type in compiled code and pass it to the
 455     // interpreter via a call. The value is live at the first call so
 456     // debug info should include a reference to all its fields.
 457     @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = ALLOC + LOAD + TRAP)
 458     @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP)
 459     public long test18() {
 460         MyValue1 v = MyValue1.createInline(rI, rL);
 461         v.hashInterpreted();
 462         return v.hashInterpreted();
 463     }
 464 
 465     @DontCompile
 466     public void test18_verifier(boolean warmup) {
 467         long result = test18();
 468         Asserts.assertEQ(result, hash());
 469     }
 470 
 471     // Create a value type in compiled code and pass it to the
 472     // interpreter via a call. The value type is passed twice but
 473     // should only be allocated once.
 474     @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = ALLOC + LOAD + TRAP)
 475     @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP)
 476     public long test19() {
 477         MyValue1 v = MyValue1.createInline(rI, rL);
 478         return sumValue(v, v);
 479     }
 480 
 481     @DontCompile
 482     public long sumValue(MyValue1 v, MyValue1 dummy) {
 483         return v.hash();
 484     }
 485 
 486     @DontCompile
 487     public void test19_verifier(boolean warmup) {
 488         long result = test19();
 489         Asserts.assertEQ(result, hash());
 490     }
 491 
 492     // Create a value type in compiled code and pass it to the
 493     // interpreter via a call. The value type is live at the uncommon
 494     // trap: verify that deoptimization causes the value type to be
 495     // correctly allocated.
 496     @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = LOAD + ALLOC + STORE)
 497     @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD)
 498     public long test20(boolean flag) {
 499         MyValue1 v = MyValue1.createInline(rI, rL);
 500         if (flag) {
 501             // uncommon trap
 502             WHITE_BOX.deoptimizeMethod(tests.get("ValueTypeTestBench::test20"));
 503         }
 504         return v.hashInterpreted();
 505     }
 506 
 507     @DontCompile
 508     public void test20_verifier(boolean warmup) {
 509         long result = test20(false);
 510         Asserts.assertEQ(result, hash());
 511         if (!warmup) {
 512             result = test20(true);
 513             Asserts.assertEQ(result, hash());
 514         }
 515     }
 516 
 517     // Value type fields in regular object
 518     MyValue1 val1;
 519     MyValue2 val2;
 520     final MyValue1 val3;
 521     static MyValue1 val4;
 522     static final MyValue1 val5 = MyValue1.createInline(rI, rL);
 523 
 524     // Test value type fields in objects
 525     @Test(match = {ALLOC}, matchCount = {1}, failOn = (TRAP))
 526     public long test21(int x, long y) {
 527         // Compute hash of value type fields
 528         long result = val1.hash() + val2.hash() + val3.hash() + val4.hash() + val5.hash();
 529         // Update fields
 530         val1 = MyValue1.createInline(x, y);
 531         val2 = MyValue2.createInline(x, true);
 532         val4 = MyValue1.createInline(x, y);
 533         return result;
 534     }
 535 
 536     @DontCompile
 537     public void test21_verifier(boolean warmup) {
 538         // Check if hash computed by test18 is correct
 539         val1 = MyValue1.createInline(rI, rL);
 540         val2 = val1.v2;
 541         // val3 is initialized in the constructor
 542         val4 = val1;
 543         // val5 is initialized in the static initializer
 544         long hash = val1.hash() + val2.hash() + val3.hash() + val4.hash() + val5.hash();
 545         long result = test21(rI + 1, rL + 1);
 546         Asserts.assertEQ(result, hash);
 547         // Check if value type fields were updated
 548         Asserts.assertEQ(val1.hash(), hash(rI + 1, rL + 1));
 549         Asserts.assertEQ(val2.hash(), MyValue2.createInline(rI + 1, true).hash());
 550         Asserts.assertEQ(val4.hash(), hash(rI + 1, rL + 1));
 551     }
 552 
 553     // Test folding of constant value type fields
 554     @Test(failOn = ALLOC + LOAD + STORE + LOOP + TRAP)
 555     public long test22() {
 556         // This should be constant folded
 557         return val5.hash() + val5.v3.hash();
 558     }
 559 
 560     @DontCompile
 561     public void test22_verifier(boolean warmup) {
 562         long result = test22();
 563         Asserts.assertEQ(result, val5.hash() + val5.v3.hash());
 564     }
 565 
 566     // Test OSR compilation
 567     @Test(failOn = ALLOC + LOAD + STORE + LOOP + TRAP)
 568     public long test23() {
 569         MyValue1 v = MyValue1.createInline(rI, rL);
 570         long result = 0;
 571         // Long loop to trigger OSR compilation
 572         for (int i = 0 ; i < 100_000 ; ++i) {
 573             // Reference local value type in interpreter state
 574             result = v.hash();
 575         }
 576         return result;
 577     }
 578 
 579     @DontCompile
 580     public void test23_verifier(boolean warmup) {
 581         long result = test23();
 582         Asserts.assertEQ(result, hash());
 583     }
 584 
 585     // Test interpreter to compiled code with various signatures
 586     @Test(failOn = ALLOC + STORE + TRAP)
 587     public long test24(MyValue2 v) {
 588         return v.hash();
 589     }
 590 
 591     @DontCompile
 592     public void test24_verifier(boolean warmup) {
 593         MyValue2 v = MyValue2.createInline(rI, true);
 594         long result = test24(v);
 595         Asserts.assertEQ(result, v.hashInterpreted());
 596     }
 597 
 598     @Test(failOn = ALLOC + STORE + TRAP)
 599     public long test25(int i1, MyValue2 v, int i2) {
 600         return v.hash() + i1 - i2;
 601     }
 602 
 603     @DontCompile
 604     public void test25_verifier(boolean warmup) {
 605         MyValue2 v = MyValue2.createInline(rI, true);
 606         long result = test25(rI, v, 2*rI);
 607         Asserts.assertEQ(result, v.hashInterpreted() - rI);
 608     }
 609 
 610     @Test(failOn = ALLOC + STORE + TRAP)
 611     public long test26(long l1, MyValue2 v, long l2) {
 612         return v.hash() + l1 - l2;
 613     }
 614 
 615     @DontCompile
 616     public void test26_verifier(boolean warmup) {
 617         MyValue2 v = MyValue2.createInline(rI, true);
 618         long result = test26(rL, v, 2*rL);
 619         Asserts.assertEQ(result, v.hashInterpreted() - rL);
 620     }
 621 
 622     @Test(failOn = ALLOC + STORE + TRAP)
 623     public long test27(int i, MyValue2 v, long l) {
 624         return v.hash() + i + l;
 625     }
 626 
 627     @DontCompile
 628     public void test27_verifier(boolean warmup) {
 629         MyValue2 v = MyValue2.createInline(rI, true);
 630         long result = test27(rI, v, rL);
 631         Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
 632     }
 633 
 634     @Test(failOn = ALLOC + STORE + TRAP)
 635     public long test28(long l, MyValue2 v, int i) {
 636         return v.hash() + i + l;
 637     }
 638 
 639     @DontCompile
 640     public void test28_verifier(boolean warmup) {
 641         MyValue2 v = MyValue2.createInline(rI, true);
 642         long result = test28(rL, v, rI);
 643         Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
 644     }
 645 
 646     @Test(failOn = ALLOC + STORE + TRAP)
 647     public long test29(long l, MyValue1 v1, int i, MyValue2 v2) {
 648         return v1.hash() + i + l + v2.hash();
 649     }
 650 
 651     @DontCompile
 652     public void test29_verifier(boolean warmup) {
 653         MyValue1 v1 = MyValue1.createDontInline(rI, rL);
 654         MyValue2 v2 = MyValue2.createInline(rI, true);
 655         long result = test29(rL, v1, rI, v2);
 656         Asserts.assertEQ(result, v1.hashInterpreted() + rL + rI + v2.hashInterpreted());
 657     }
 658 
 659     // Test compiled code to interpreter with various signatures
 660     @DontCompile
 661     public long test30_interp(MyValue2 v) {
 662         return v.hash();
 663     }
 664 
 665     @Test(failOn = ALLOC + STORE + TRAP)
 666     public long test30(MyValue2 v) {
 667         return test30_interp(v);
 668     }
 669 
 670     @DontCompile
 671     public void test30_verifier(boolean warmup) {
 672         MyValue2 v = MyValue2.createInline(rI, true);
 673         long result = test30(v);
 674         Asserts.assertEQ(result, v.hashInterpreted());
 675     }
 676 
 677     @DontCompile
 678     public long test31_interp(int i1, MyValue2 v, int i2) {
 679         return v.hash() + i1 - i2;
 680     }
 681 
 682     @Test(failOn = ALLOC + STORE + TRAP)
 683     public long test31(int i1, MyValue2 v, int i2) {
 684         return test31_interp(i1, v, i2);
 685     }
 686 
 687     @DontCompile
 688     public void test31_verifier(boolean warmup) {
 689         MyValue2 v = MyValue2.createInline(rI, true);
 690         long result = test31(rI, v, 2*rI);
 691         Asserts.assertEQ(result, v.hashInterpreted() - rI);
 692     }
 693 
 694     @DontCompile
 695     public long test32_interp(long l1, MyValue2 v, long l2) {
 696         return v.hash() + l1 - l2;
 697     }
 698 
 699     @Test(failOn = ALLOC + STORE + TRAP)
 700     public long test32(long l1, MyValue2 v, long l2) {
 701         return test32_interp(l1, v, l2);
 702     }
 703 
 704     @DontCompile
 705     public void test32_verifier(boolean warmup) {
 706         MyValue2 v = MyValue2.createInline(rI, true);
 707         long result = test32(rL, v, 2*rL);
 708         Asserts.assertEQ(result, v.hashInterpreted() - rL);
 709     }
 710 
 711     @DontCompile
 712     public long test33_interp(int i, MyValue2 v, long l) {
 713         return v.hash() + i + l;
 714     }
 715 
 716     @Test(failOn = ALLOC + STORE + TRAP)
 717     public long test33(int i, MyValue2 v, long l) {
 718         return test33_interp(i, v, l);
 719     }
 720 
 721     @DontCompile
 722     public void test33_verifier(boolean warmup) {
 723         MyValue2 v = MyValue2.createInline(rI, true);
 724         long result = test33(rI, v, rL);
 725         Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
 726     }
 727 
 728     @DontCompile
 729     public long test34_interp(long l, MyValue2 v, int i) {
 730         return v.hash() + i + l;
 731     }
 732 
 733     @Test(failOn = ALLOC + STORE + TRAP)
 734     public long test34(long l, MyValue2 v, int i) {
 735         return test34_interp(l, v, i);
 736     }
 737 
 738     @DontCompile
 739     public void test34_verifier(boolean warmup) {
 740         MyValue2 v = MyValue2.createInline(rI, true);
 741         long result = test34(rL, v, rI);
 742         Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
 743     }
 744 
 745     @DontCompile
 746     public long test35_interp(long l, MyValue1 v1, int i, MyValue2 v2) {
 747         return v1.hash() + i + l + v2.hash();
 748     }
 749 
 750     @Test(failOn = ALLOC + STORE + TRAP)
 751     public long test35(long l, MyValue1 v1, int i, MyValue2 v2) {
 752         return test35_interp(l, v1, i, v2);
 753     }
 754 
 755     @DontCompile
 756     public void test35_verifier(boolean warmup) {
 757         MyValue1 v1 = MyValue1.createDontInline(rI, rL);
 758         MyValue2 v2 = MyValue2.createInline(rI, true);
 759         long result = test35(rL, v1, rI, v2);
 760         Asserts.assertEQ(result, v1.hashInterpreted() + rL + rI + v2.hashInterpreted());
 761     }
 762 
 763     // test that debug info at a call is correct
 764     @DontCompile
 765     public long test36_interp(MyValue2 v, boolean flag) {
 766         if (flag) {
 767             // uncommon trap
 768             WHITE_BOX.deoptimizeMethod(tests.get("ValueTypeTestBench::test36"));
 769         }
 770         return v.hash();
 771     }
 772 
 773     @Test(failOn = ALLOC + STORE + TRAP)
 774     public long test36(MyValue2 v, boolean flag, long l) {
 775         return test36_interp(v, flag) + l;
 776     }
 777 
 778     @DontCompile
 779     public void test36_verifier(boolean warmup) {
 780         MyValue2 v = MyValue2.createInline(rI, true);
 781         long result = test36(v, false, rL);
 782         Asserts.assertEQ(result, v.hashInterpreted() + rL);
 783         if (!warmup) {
 784             result = test36(v, true, rL);
 785             Asserts.assertEQ(result, v.hashInterpreted() + rL);
 786         }
 787     }
 788 
 789     // Test vbox and vunbox
 790     @Test
 791     public long test37() throws Throwable {
 792         return (long)vccUnboxLoadLongMH.invokeExact(vcc);
 793     }
 794 
 795     @DontCompile
 796     public void test37_verifier(boolean warmup) {
 797         try {
 798             long result = test37();
 799             Asserts.assertEQ(vcc.t, result, "Field t of input and result must be equal.");
 800         } catch (Throwable t) {
 801             throw new RuntimeException("test 37 failed", t);
 802         }
 803     }
 804 
 805     // Generate a MethodHandle that obtains field t of the
 806     // derived value type       
 807     private static MethodHandle generateVCCUnboxLoadLongMH() {
 808         return MethodHandleBuilder.loadCode(MethodHandles.lookup(),
 809                                             "vccUnboxLoadLong",
 810                                             MethodType.methodType(long.class, ValueCapableClass1.class),
 811                                             CODE -> {
 812                                                 CODE.
 813                                                     aload_0().
 814                                                     vunbox(ValueType.forClass(ValueCapableClass1.class).valueClass()).
 815                                                     vgetfield(ValueType.forClass(ValueCapableClass1.class).valueClass(), "t", "J").
 816                                                     lreturn();
 817                                             }
 818                                             );  
 819     }
 820 
 821 
 822     @Test
 823     public int test38() throws Throwable {
 824         return (int)vccUnboxLoadIntMH.invokeExact(vcc);
 825     }
 826 
 827     @DontCompile
 828     public void test38_verifier(boolean warmup) {
 829         try {
 830             int result = test38();
 831             Asserts.assertEQ(vcc.x, result, "Field x of input and result must be equal.");
 832         } catch (Throwable t) {
 833             throw new RuntimeException("test 38 failed", t);
 834         }
 835     }
 836 
 837     // Generate a MethodHandle that obtains field x of the
 838     // derived value type       
 839     private static MethodHandle generateVCCUnboxLoadIntMH() {
 840         return MethodHandleBuilder.loadCode(MethodHandles.lookup(),
 841                                             "vccUnboxLoadInt",
 842                                             MethodType.methodType(int.class, ValueCapableClass1.class),
 843                                             CODE -> {
 844                                                 CODE.
 845                                                     aload_0().
 846                                                     vunbox(ValueType.forClass(ValueCapableClass1.class).valueClass()).
 847                                                     vgetfield(ValueType.forClass(ValueCapableClass1.class).valueClass(), "x", "I").
 848                                                     ireturn();
 849                                             }
 850                                             );  
 851     }
 852 
 853     @Test
 854     public ValueCapableClass1 test39() throws Throwable {
 855         return (ValueCapableClass1)vccUnboxBoxMH.invokeExact(vcc);
 856     }
 857 
 858     @DontCompile
 859     public void test39_verifier(boolean warmup) {
 860         try {
 861             ValueCapableClass1 result = test39();
 862             Asserts.assertEQ(vcc.value(), result.value(), "Value of VCC and returned VCC must be equal");
 863         } catch (Throwable t) {
 864             throw new RuntimeException("test 39 failed", t);
 865         }
 866     }
 867 
 868     // Generate a MethodHandle that takes a value-capable class,
 869     // unboxes it, then boxes it again and returns it.
 870     private static MethodHandle generateVCCUnboxBoxMH() {
 871         return MethodHandleBuilder.loadCode(MethodHandles.lookup(),
 872                                             "vccUnboxBox",
 873                                             MethodType.methodType(ValueCapableClass1.class, ValueCapableClass1.class),
 874                                             CODE -> {
 875                                                 CODE.
 876                                                     aload_0().
 877                                                     vunbox(ValueType.forClass(ValueCapableClass1.class).valueClass()).
 878                                                     vbox(ValueCapableClass1.class).
 879                                                     areturn();
 880                                             }
 881                                             );  
 882     }
 883 
 884     @Test
 885     public int test40() throws Throwable {
 886         return (int)vccUnboxBoxLoadIntMH.invokeExact(vcc);
 887     }
 888 
 889     @DontCompile
 890     public void test40_verifier(boolean warmup) {
 891         try {
 892             int result = test40();
 893             Asserts.assertEQ(vcc.x, result, "Field x of VCC and result must be equal");
 894         } catch (Throwable t) {
 895             throw new RuntimeException("Test failed in the interpeter", t);
 896         }
 897     }
 898 
 899     // Generate a MethodHandle that takes a value-capable class,
 900     // unboxes it, boxes it, reads a field from it, and returns the
 901     // field.
 902     private static MethodHandle generateVCCUnboxBoxLoadIntMH() {
 903         return MethodHandleBuilder.loadCode(MethodHandles.lookup(),
 904                                             "vccUnboxBoxLoadInt",
 905                                             MethodType.methodType(int.class, ValueCapableClass1.class),
 906                                             CODE -> {
 907                                                 CODE.
 908                                                     aload_0().
 909                                                     vunbox(ValueType.forClass(ValueCapableClass1.class).valueClass()).
 910                                                     vbox(ValueCapableClass1.class).
 911                                                     getfield(ValueCapableClass1.class, "x", "I").
 912                                                     ireturn();
 913                                             }
 914                                             );
 915     }
 916 
 917     // ========== Test infrastructure ==========
 918 
 919     private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
 920     private static final int ValueTypePassFieldsAsArgsOn = 0x1;
 921     private static final int ValueTypePassFieldsAsArgsOff = 0x2;
 922     static final int AllFlags = ValueTypePassFieldsAsArgsOn | ValueTypePassFieldsAsArgsOff;
 923     private static final boolean ValueTypePassFieldsAsArgs = (Boolean)WHITE_BOX.getVMFlag("ValueTypePassFieldsAsArgs");
 924     private static final int COMP_LEVEL_ANY = -1;
 925     private static final int COMP_LEVEL_FULL_OPTIMIZATION = 4;
 926     private static final Hashtable<String, Method> tests = new Hashtable<String, Method>();
 927     private static final int WARMUP = 250;
 928     private static boolean USE_COMPILER = WHITE_BOX.getBooleanVMFlag("UseCompiler");
 929     private static boolean PRINT_IDEAL  = WHITE_BOX.getBooleanVMFlag("PrintIdeal");
 930     // TODO use Platform.isComp() after merge with JDK 9
 931     private static boolean XCOMP = System.getProperty("java.vm.info").startsWith("compiled ");
 932 
 933     // Regular expressions used  to match nodes in the PrintIdeal output
 934     private static final String START = "(\\d+\\t(.*";
 935     private static final String MID = ".*)+\\t===.*";
 936     private static final String END = ")|";
 937     private static final String ALLOC = START + "CallStaticJava" + MID + "_new_instance_Java" + END;
 938     private static final String LOAD  = START + "Load" + MID + "valuetype\\*" + END;
 939     private static final String STORE = START + "Store" + MID + "valuetype\\*" + END;
 940     private static final String LOOP  = START + "Loop" + MID + "" + END;
 941     private static final String TRAP  = START + "CallStaticJava" + MID + "uncommon_trap" + END;
 942     // TODO: match field values of scalar replaced object
 943     private static final String SCOBJ = "(.*# ScObj.*" + END;
 944 
 945     static {
 946         // Gather all test methods and put them in Hashtable
 947         for (Method m : ValueTypeTestBench.class.getDeclaredMethods()) {
 948             Test[] annos = m.getAnnotationsByType(Test.class);
 949             if (annos.length != 0) {
 950                 tests.put("ValueTypeTestBench::" + m.getName(), m);
 951             }
 952         }
 953     }
 954 
 955     private static void execute_vm(String... extra_args) throws Throwable {
 956         ArrayList<String> all_args = new ArrayList(List.of(
 957                 "-noverify",
 958                 "-XX:+UnlockDiagnosticVMOptions", "-Xbootclasspath/a:.", "-XX:+WhiteBoxAPI",
 959                 "-XX:-TieredCompilation", "-XX:-BackgroundCompilation",
 960                 "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+PrintCompilation", "-XX:+PrintIdeal", "-XX:+PrintOptoAssembly",
 961                 "-XX:CompileCommand=quiet", "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.ValueTypeTestBench::*",
 962                 "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue1::*",
 963                 "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue2::*"
 964                                                                ));
 965         all_args.addAll(List.of(extra_args));
 966         // Run tests in own process and verify output
 967         all_args.add(ValueTypeTestBench.class.getName());
 968         all_args.add("run");
 969         OutputAnalyzer oa = ProcessTools.executeTestJvm(all_args.toArray(new String[0]));
 970         // If ideal graph printing is enabled/supported, verify output
 971         String output = oa.getOutput();
 972         oa.shouldHaveExitValue(0);
 973         if (output.contains("PrintIdeal enabled")) {
 974             parseOutput(output);
 975         } else {
 976             System.out.println("WARNING: IR verification disabled! Running with -Xint, -Xcomp or release build?");
 977         }
 978     }
 979 
 980     public static void main(String[] args) throws Throwable {
 981         if (args.length == 0) {
 982             String field_as_args;
 983             if ((Boolean)WHITE_BOX.getVMFlag("ValueTypePassFieldsAsArgs")) {
 984                 field_as_args = "-XX:+ValueTypePassFieldsAsArgs";
 985             } else {
 986                 field_as_args = "-XX:-ValueTypePassFieldsAsArgs";
 987             }
 988             execute_vm("-XX:+UnlockExperimentalVMOptions", field_as_args);
 989             execute_vm("-XX:+AlwaysIncrementalInline", "-XX:+UnlockExperimentalVMOptions", field_as_args);
 990         } else {
 991             if (USE_COMPILER && PRINT_IDEAL && !XCOMP) {
 992                 System.out.println("PrintIdeal enabled");
 993             }
 994             // Execute tests
 995             ValueTypeTestBench bench = new ValueTypeTestBench();
 996             bench.run();
 997         }
 998     }
 999 
1000     public static void parseOutput(String output) throws Exception {
1001         String split = "b        compiler.valhalla.valuetypes.";
1002         String[] compilations = output.split(split);
1003         // Print header
1004         System.out.println(compilations[0]);
1005         // Iterate over compilation output
1006         for (String graph : compilations) {
1007             String[] lines = graph.split("\\n");
1008             if (lines[0].contains("@")) {
1009                 continue; // Ignore OSR compilations
1010             }
1011             String testName = lines[0].split(" ")[0];
1012             Method test = tests.get(testName);
1013             if (test == null) {
1014                 // Skip helper methods
1015                 continue;
1016             }
1017             if (PRINT_GRAPH) {
1018                 System.out.println("\nGraph for " + graph);
1019             }
1020             // Parse graph using regular expressions to determine if it contains forbidden nodes
1021             Test[] annos = test.getAnnotationsByType(Test.class);
1022             Test anno = null;
1023             for (Test a : annos) {
1024                 if ((a.valid() & ValueTypePassFieldsAsArgsOn) != 0 && ValueTypePassFieldsAsArgs) {
1025                     assert anno == null;
1026                     anno = a;
1027                 } else if ((a.valid() & ValueTypePassFieldsAsArgsOff) != 0 && !ValueTypePassFieldsAsArgs) {
1028                     assert anno == null;
1029                     anno = a;
1030                 }
1031             }
1032             assert anno != null;
1033             String regexFail = anno.failOn();
1034             if (!regexFail.isEmpty()) {
1035                 Pattern pattern = Pattern.compile(regexFail.substring(0, regexFail.length()-1));
1036                 Matcher matcher = pattern.matcher(graph);
1037                 boolean fail = false;
1038                 while (matcher.find()) {
1039                     System.out.println("Graph for '" + testName + "' contains forbidden node:");
1040                     System.out.println(matcher.group());
1041                     fail = true;
1042                 }
1043                 Asserts.assertFalse(fail, "Graph for '" + testName + "' contains forbidden nodes");
1044             }
1045             String[] regexMatch = anno.match();
1046             int[] matchCount = anno.matchCount();
1047             for (int i = 0; i < regexMatch.length; ++i) {
1048                 Pattern pattern = Pattern.compile(regexMatch[i].substring(0, regexMatch[i].length()-1));
1049                 Matcher matcher = pattern.matcher(graph);
1050                 int count = 0;
1051                 String nodes = "";
1052                 while (matcher.find()) {
1053                     count++;
1054                     nodes += matcher.group() + "\n";
1055                 }
1056                 if (matchCount[i] != count) {
1057                     System.out.println("Graph for '" + testName + "' contains different number of match nodes:");
1058                     System.out.println(nodes);
1059                 }
1060                 Asserts.assertEQ(matchCount[i], count, "Graph for '" + testName + "' contains different number of match nodes");
1061             }
1062             tests.remove(testName);
1063             System.out.println(testName + " passed");
1064         }
1065         // Check if all tests were compiled
1066         if (tests.size() != 0) {
1067             for (String name : tests.keySet()) {
1068                 System.out.println("Test '" + name + "' not compiled!");
1069             }
1070             throw new RuntimeException("Not all tests were compiled");
1071         }
1072     }
1073 
1074     public void setup(Method[] methods) {
1075         for (Method m : methods) {
1076             if (m.isAnnotationPresent(Test.class)) {
1077                 // Don't inline tests
1078                 WHITE_BOX.testSetDontInlineMethod(m, true);
1079             }
1080             if (m.isAnnotationPresent(DontCompile.class)) {
1081                 WHITE_BOX.makeMethodNotCompilable(m, COMP_LEVEL_ANY, true);
1082                 WHITE_BOX.makeMethodNotCompilable(m, COMP_LEVEL_ANY, false);
1083             }
1084             if (m.isAnnotationPresent(ForceInline.class)) {
1085                 WHITE_BOX.testSetForceInlineMethod(m, true);
1086             } else if (m.isAnnotationPresent(DontInline.class)) {
1087                 WHITE_BOX.testSetDontInlineMethod(m, true);
1088             }
1089         }
1090     }
1091 
1092     public void run() throws Exception {
1093         System.out.format("rI = %d, rL = %d\n", rI, rL);
1094         setup(this.getClass().getDeclaredMethods());
1095         setup(MyValue1.class.getDeclaredMethods());
1096         setup(MyValue2.class.getDeclaredMethods());
1097 
1098         // TODO enable this after JDK 9 merge and verify that it's compiled
1099         //WHITE_BOX.enqueueInitializerForCompilation(this.getClass(), COMP_LEVEL_FULL_OPTIMIZATION);
1100 
1101         // Execute tests
1102         for (Method test : tests.values()) {
1103             Method verifier = getClass().getDeclaredMethod(test.getName() + "_verifier", boolean.class);
1104             // Warmup using verifier method
1105             for (int i = 0; i < WARMUP; ++i) {
1106                 verifier.invoke(this, true);
1107             }
1108             // Trigger compilation
1109             WHITE_BOX.enqueueMethodForCompilation(test, COMP_LEVEL_FULL_OPTIMIZATION);
1110             Asserts.assertTrue(!USE_COMPILER || WHITE_BOX.isMethodCompiled(test, false), test + " not compiled");
1111             // Check result
1112             verifier.invoke(this, false);
1113         }
1114     }
1115 }
1116 
1117 // Mark method as test
1118 @Retention(RetentionPolicy.RUNTIME)
1119 @Repeatable(Tests.class)
1120 @interface Test {
1121     // Regular expression used to match forbidden IR nodes
1122     // in the C2 IR emitted for this test.
1123     String failOn() default "";
1124     // Regular expressions used to match and count IR nodes.
1125     String[] match() default { };
1126     int[] matchCount() default { };
1127     int valid() default ValueTypeTestBench.AllFlags;
1128 }
1129 
1130 @Retention(RetentionPolicy.RUNTIME)
1131 @interface Tests {
1132     Test[] value();
1133 }
1134 
1135 // Force method inlining during compilation
1136 @Retention(RetentionPolicy.RUNTIME)
1137 @interface ForceInline { }
1138 
1139 // Prevent method inlining during compilation
1140 @Retention(RetentionPolicy.RUNTIME)
1141 @interface DontInline { }
1142 
1143 // Prevent method compilation
1144 @Retention(RetentionPolicy.RUNTIME)
1145 @interface DontCompile { }