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