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