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