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