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 -noverify -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
  33  *                   -XX:-TieredCompilation compiler.valhalla.valuetypes.ValueTypeTestBench
  34  */
  35 
  36 package compiler.valhalla.valuetypes;
  37 
  38 import compiler.whitebox.CompilerWhiteBoxTest;
  39 import jdk.internal.misc.Unsafe;
  40 import jdk.test.lib.Asserts;
  41 import jdk.test.lib.Platform;
  42 import jdk.test.lib.ProcessTools;
  43 import jdk.test.lib.OutputAnalyzer;
  44 import jdk.test.lib.Utils;
  45 import sun.hotspot.WhiteBox;
  46 
  47 import java.lang.annotation.Retention;
  48 import java.lang.annotation.RetentionPolicy;
  49 import java.lang.reflect.Method;
  50 import java.util.ArrayList;
  51 import java.util.Hashtable;
  52 import java.util.regex.Matcher;
  53 import java.util.regex.Pattern;
  54 
  55 // Test value types
  56 __ByValue final class MyValue1 {
  57     static int s;
  58     static final long sf = ValueTypeTestBench.rL;
  59     final int x;
  60     final long y;
  61     final MyValue2 v1;
  62     final MyValue2 v2;
  63     static final MyValue2 v3 = MyValue2.createInline(ValueTypeTestBench.rI, true);
  64     final int c;
  65 
  66     private MyValue1(int x, long y, MyValue2 v1, MyValue2 v2, int c) {
  67         s = x;
  68         this.x = x;
  69         this.y = y;
  70         this.v1 = v1;
  71         this.v2 = v2;
  72         this.c = c;
  73     }
  74 
  75     @DontInline
  76     public static MyValue1 createDontInline(int x, long y) {
  77         return __Make MyValue1(x, y, MyValue2.createInline(x, x < y), MyValue2.createInline(x, x > y), ValueTypeTestBench.rI);
  78     }
  79 
  80     @ForceInline
  81     public static MyValue1 createInline(int x, long y) {
  82         return __Make MyValue1(x, y, MyValue2.createInline(x, x < y), MyValue2.createInline(x, x > y), ValueTypeTestBench.rI);
  83     }
  84 
  85     @ForceInline
  86     public long hash() {
  87         return s + sf + x + y + c + v1.hash() + v2.hash() + v3.hash();
  88     }
  89 
  90     @DontCompile
  91     public long hashInterpreted() {
  92         return s + sf + x + y + c + v1.hash() + v2.hash() + v3.hash();
  93     }
  94 }
  95 
  96 __ByValue final class MyValue2 {
  97     final int x;
  98     final boolean b;
  99     final long c;
 100 
 101     private MyValue2(int x, boolean b, long c) {
 102         this.x = x;
 103         this.b = b;
 104         this.c = c;
 105     }
 106 
 107     @ForceInline
 108     public static MyValue2 createInline(int x, boolean b) {
 109         return __Make MyValue2(x, b, ValueTypeTestBench.rL);
 110     }
 111 
 112     @ForceInline
 113     public long hash() {
 114         return x + (b ? 0 : 1) + c;
 115     }
 116 }
 117 
 118 public class ValueTypeTestBench {
 119     // Print ideal graph after execution of each test
 120     private static final boolean PRINT_GRAPH = true;
 121 
 122     // Random test values
 123     public static final int  rI = Utils.getRandomInstance().nextInt() % 1000;
 124     public static final long rL = Utils.getRandomInstance().nextLong() % 1000;
 125 
 126     public ValueTypeTestBench() {
 127         val3 = MyValue1.createInline(rI, rL);
 128     }
 129 
 130     // ========== Helper methods ==========
 131 
 132     public long hash() {
 133         return hash(rI, rL);
 134     }
 135 
 136     public long hash(int x, long y) {
 137         return MyValue1.createInline(x, y).hash();
 138     }
 139 
 140     // ========== Test definitions ==========
 141 
 142     // Receive value type through call to interpreter
 143     @Test(failOn = ALLOC + STORE + TRAP)
 144     public long test1() {
 145         MyValue1 v = MyValue1.createDontInline(rI, rL);
 146         return v.hash();
 147     }
 148 
 149     @DontCompile
 150     public void test1_verifier(boolean warmup) {
 151         long result = test1();
 152         Asserts.assertEQ(result, hash());
 153     }
 154 
 155     // Receive value type from interpreter via parameter
 156     @Test(failOn = ALLOC + STORE + TRAP)
 157     public long test2(MyValue1 v) {
 158         return v.hash();
 159     }
 160 
 161     @DontCompile
 162     public void test2_verifier(boolean warmup) {
 163         MyValue1 v = MyValue1.createDontInline(rI, rL);
 164         long result = test2(v);
 165         Asserts.assertEQ(result, hash());
 166     }
 167 
 168     // Return incoming value type without accessing fields
 169     @Test(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(match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP)
 218     public long test6() {
 219         MyValue1 v = MyValue1.createInline(rI, rL);
 220         // Pass to interpreter
 221         return v.hashInterpreted();
 222     }
 223 
 224     @DontCompile
 225     public void test6_verifier(boolean warmup) {
 226         long result = test6();
 227         Asserts.assertEQ(result, hash());
 228     }
 229 
 230     // Create a value type in compiled code and pass it to
 231     // the interpreter by returning.
 232     @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP)
 233     public MyValue1 test7(int x, long y) {
 234         return MyValue1.createInline(x, y);
 235     }
 236 
 237     @DontCompile
 238     public void test7_verifier(boolean warmup) {
 239         MyValue1 v = test7(rI, rL);
 240         Asserts.assertEQ(v.hash(), hash());
 241     }
 242 
 243     // Merge value types created from two branches
 244     @Test(failOn = ALLOC + STORE + TRAP)
 245     public long test8(boolean b) {
 246         MyValue1 v;
 247         if (b) {
 248             v = MyValue1.createInline(rI, rL);
 249         } else {
 250             v = MyValue1.createDontInline(rI + 1, rL + 1);
 251         }
 252         return v.hash();
 253     }
 254 
 255     @DontCompile
 256     public void test8_verifier(boolean warmup) {
 257         Asserts.assertEQ(test8(true), hash());
 258         Asserts.assertEQ(test8(false), hash(rI + 1, rL + 1));
 259     }
 260 
 261     // Merge value types created from two branches
 262     @Test(match = {ALLOC, STORE}, matchCount = {1, 9}, failOn = LOAD + TRAP)
 263     public MyValue1 test9(boolean b) {
 264         MyValue1 v;
 265         if (b) {
 266             // Value type is not allocated
 267             v = MyValue1.createInline(rI, rL);
 268         } else {
 269             // Value type is allocated by the callee
 270             v = MyValue1.createDontInline(rI + 1, rL + 1);
 271         }
 272         // Need to allocate value type if 'b' is true
 273         long sum = v.hashInterpreted();
 274         if (b) {
 275             v = MyValue1.createDontInline(rI, sum);
 276         } else {
 277             v = MyValue1.createDontInline(rI, sum + 1);
 278         }
 279         // Don't need to allocate value type because both branches allocate
 280         return v;
 281     }
 282 
 283     @DontCompile
 284     public void test9_verifier(boolean warmup) {
 285         MyValue1 v = test9(true);
 286         Asserts.assertEQ(v.x, rI);
 287         Asserts.assertEQ(v.y, hash());
 288         v = test9(false);
 289         Asserts.assertEQ(v.x, rI);
 290         Asserts.assertEQ(v.y, hash(rI + 1, rL + 1) + 1);
 291     }
 292 
 293     // Merge value types created in a loop (not inlined)
 294     @Test(failOn = ALLOC + STORE + TRAP)
 295     public long test10(int x, long y) {
 296         MyValue1 v = MyValue1.createDontInline(x, y);
 297         for (int i = 0; i < 10; ++i) {
 298             v = MyValue1.createDontInline(v.x + 1, v.y + 1);
 299         }
 300         return v.hash();
 301     }
 302 
 303     @DontCompile
 304     public void test10_verifier(boolean warmup) {
 305         long result = test10(rI, rL);
 306         Asserts.assertEQ(result, hash(rI + 10, rL + 10));
 307     }
 308 
 309     // Merge value types created in a loop (inlined)
 310     @Test(failOn = ALLOC + LOAD + STORE + LOOP + TRAP)
 311     public long test11(int x, long y) {
 312         MyValue1 v = MyValue1.createInline(x, y);
 313         for (int i = 0; i < 10; ++i) {
 314             v = MyValue1.createInline(v.x + 1, v.y + 1);
 315         }
 316         return v.hash();
 317     }
 318 
 319     @DontCompile
 320     public void test11_verifier(boolean warmup) {
 321         long result = test11(rI, rL);
 322         Asserts.assertEQ(result, hash(rI + 10, rL + 10));
 323     }
 324 
 325     // Test loop with uncommon trap referencing a value type
 326     @Test(match = {TRAP, SCOBJ}, matchCount = {1, 1}, failOn = ALLOC + LOAD + STORE)
 327     public long test12(boolean b) {
 328         MyValue1 v = MyValue1.createInline(rI, rL);
 329         long result = 42;
 330         for (int i = 0; i < 1000; ++i) {
 331             if (b) {
 332                 result += v.x;
 333             } else {
 334                 // Uncommon trap referencing v. We delegate allocation to the
 335                 // interpreter by adding a SafePointScalarObjectNode.
 336                 result = v.hashInterpreted();
 337             }
 338         }
 339         return result;
 340     }
 341 
 342     @DontCompile
 343     public void test12_verifier(boolean warmup) {
 344         long result = test12(warmup);
 345         Asserts.assertEQ(result, warmup ? 42 + (1000*rI) : hash());
 346     }
 347 
 348     // Test loop with uncommon trap referencing a value type
 349     @Test(match = {TRAP, LOAD}, matchCount = {1, 1}, failOn = ALLOC + STORE + SCOBJ)
 350     public long test13(boolean b) {
 351         MyValue1 v = MyValue1.createDontInline(rI, rL);
 352         long result = 42;
 353         for (int i = 0; i < 1000; ++i) {
 354             if (b) {
 355                 result += v.x;
 356             } else {
 357                 // Uncommon trap referencing v. Should not allocate
 358                 // but just pass the existing oop to the uncommon trap.
 359                 result = v.hashInterpreted();
 360             }
 361         }
 362         return result;
 363     }
 364 
 365     @DontCompile
 366     public void test13_verifier(boolean warmup) {
 367         long result = test13(warmup);
 368         Asserts.assertEQ(result, warmup ? 42 + (1000*rI) : hash());
 369     }
 370 
 371     // Create a value type in a non-inlined method and then call a
 372     // non-inlined method on that value type.
 373     @Test(failOn = (ALLOC + LOAD + STORE + TRAP))
 374     public long test14() {
 375         MyValue1 v = MyValue1.createDontInline(rI, rL);
 376         return v.hashInterpreted();
 377     }
 378 
 379     @DontCompile
 380     public void test14_verifier(boolean b) {
 381         long result = test14();
 382         Asserts.assertEQ(result, hash());
 383     }
 384 
 385     // Create a value type in an inlined method and then call a
 386     // non-inlined method on that value type.
 387     @Test(failOn = (LOAD + TRAP), match = {ALLOC}, matchCount = {1})
 388     public long test15() {
 389         MyValue1 v = MyValue1.createInline(rI, rL);
 390         return v.hashInterpreted();
 391     }
 392 
 393     @DontCompile
 394     public void test15_verifier(boolean b) {
 395         long result = test15();
 396         Asserts.assertEQ(result, hash());
 397     }
 398 
 399     // Create a value type in a non-inlined method and then call an
 400     // inlined method on that value type.
 401     @Test(failOn = (ALLOC + STORE + TRAP))
 402     public long test16() {
 403         MyValue1 v = MyValue1.createDontInline(rI, rL);
 404         return v.hash();
 405     }
 406 
 407     @DontCompile
 408     public void test16_verifier(boolean b) {
 409         long result = test16();
 410         Asserts.assertEQ(result, hash());
 411     }
 412 
 413     // Create a value type in an inlined method and then call an
 414     // inlined method on that value type.
 415     @Test(failOn = (ALLOC + LOAD + STORE + TRAP))
 416     public long test17() {
 417         MyValue1 v = MyValue1.createInline(rI, rL);
 418         return v.hash();
 419     }
 420 
 421     @DontCompile
 422     public void test17_verifier(boolean b) {
 423         long result = test17();
 424         Asserts.assertEQ(result, hash());
 425     }
 426 
 427     // Create a value type in compiled code and pass it to the
 428     // interpreter via a call. The value is live at the first call so
 429     // debug info should include a reference to all its fields.
 430     @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP)
 431     public long test18() {
 432         MyValue1 v = MyValue1.createInline(rI, rL);
 433         v.hashInterpreted();
 434         return v.hashInterpreted();
 435     }
 436 
 437     @DontCompile
 438     public void test18_verifier(boolean warmup) {
 439         long result = test18();
 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 type is passed twice but
 445     // should only be allocated once.
 446     @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP)
 447     public long test19() {
 448         MyValue1 v = MyValue1.createInline(rI, rL);
 449         return sumValue(v, v);
 450     }
 451 
 452     @DontCompile
 453     public long sumValue(MyValue1 v, MyValue1 dummy) {
 454         return v.hash();
 455     }
 456 
 457     @DontCompile
 458     public void test19_verifier(boolean warmup) {
 459         long result = test19();
 460         Asserts.assertEQ(result, hash());
 461     }
 462 
 463     // Create a value type in compiled code and pass it to the
 464     // interpreter via a call. The value type is live at the uncommon
 465     // trap: verify that deoptimization causes the value type to be
 466     // correctly allocated.
 467     @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD)
 468     public long test20(boolean flag) {
 469         MyValue1 v = MyValue1.createInline(rI, rL);
 470         if (flag) {
 471             // uncommon trap
 472             WHITE_BOX.deoptimizeMethod(tests.get("ValueTypeTestBench::test16"));
 473         }
 474         return v.hashInterpreted();
 475     }
 476 
 477     @DontCompile
 478     public void test20_verifier(boolean warmup) {
 479         long result = test20(false);
 480         Asserts.assertEQ(result, hash());
 481         if (!warmup) {
 482             result = test20(true);
 483             Asserts.assertEQ(result, hash());
 484         }
 485     }
 486 
 487     // Value type fields in regular object
 488     MyValue1 val1;
 489     MyValue2 val2;
 490     final MyValue1 val3;
 491     static MyValue1 val4;
 492     static final MyValue1 val5 = MyValue1.createInline(rI, rL);
 493 
 494     // Test value type fields in objects
 495     @Test(match = {ALLOC}, matchCount = {1}, failOn = (TRAP))
 496     public long test21(int x, long y) {
 497         // Compute hash of value type fields
 498         long result = val1.hash() + val2.hash() + val3.hash() + val4.hash() + val5.hash();
 499         // Update fields
 500         val1 = MyValue1.createInline(x, y);
 501         val2 = MyValue2.createInline(x, true);
 502         val4 = MyValue1.createInline(x, y);
 503         return result;
 504     }
 505 
 506     @DontCompile
 507     public void test21_verifier(boolean warmup) {
 508         // Check if hash computed by test18 is correct
 509         val1 = MyValue1.createInline(rI, rL);
 510         val2 = val1.v2;
 511         // val3 is initialized in the constructor
 512         val4 = val1;
 513         // val5 is initialized in the static initializer
 514         long hash = val1.hash() + val2.hash() + val3.hash() + val4.hash() + val5.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         Asserts.assertEQ(val4.hash(), hash(rI + 1, rL + 1));
 521     }
 522 
 523     // Test folding of constant value type fields
 524     @Test(failOn = ALLOC + LOAD + STORE + LOOP + TRAP)
 525     public long test22() {
 526         // This should be constant folded
 527         return val5.hash() + val5.v3.hash();
 528     }
 529 
 530     @DontCompile
 531     public void test22_verifier(boolean warmup) {
 532         long result = test22();
 533         Asserts.assertEQ(result, val5.hash() + val5.v3.hash());
 534     }
 535 
 536     // Test OSR compilation
 537     @Test(failOn = ALLOC + LOAD + STORE + LOOP + TRAP)
 538     public long test23() {
 539         MyValue1 v = MyValue1.createInline(rI, rL);
 540         long result = 0;
 541         // Long loop to trigger OSR compilation
 542         for (int i = 0 ; i < 100_000 ; ++i) {
 543             // Reference local value type in interpreter state
 544             result = v.hash();
 545         }
 546         return result;
 547     }
 548 
 549     @DontCompile
 550     public void test23_verifier(boolean warmup) {
 551         long result = test23();
 552         Asserts.assertEQ(result, hash());
 553     }
 554 
 555 
 556     // ========== Test infrastructure ==========
 557 
 558     private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
 559     private static final int COMP_LEVEL_ANY = -1;
 560     private static final int COMP_LEVEL_FULL_OPTIMIZATION = 4;
 561     private static final Hashtable<String, Method> tests = new Hashtable<String, Method>();
 562     private static final int WARMUP = 10;
 563     private static boolean USE_COMPILER = WHITE_BOX.getBooleanVMFlag("UseCompiler");
 564     private static boolean PRINT_IDEAL  = WHITE_BOX.getBooleanVMFlag("PrintIdeal");
 565     // TODO use Platform.isComp() after merge with JDK 9
 566     private static boolean XCOMP = System.getProperty("java.vm.info").startsWith("compiled ");
 567 
 568     // Regular expressions used  to match nodes in the PrintIdeal output
 569     private static final String START = "(\\d+\\t(.*";
 570     private static final String MID = ".*)+\\t===.*";
 571     private static final String END = ")|";
 572     private static final String ALLOC = START + "CallStaticJava" + MID + "_new_instance_Java" + END;
 573     private static final String LOAD  = START + "Load" + MID + "valuetype\\*" + END;
 574     private static final String STORE = START + "Store" + MID + "valuetype\\*" + END;
 575     private static final String LOOP  = START + "Loop" + MID + "" + END;
 576     private static final String TRAP  = START + "CallStaticJava" + MID + "uncommon_trap" + END;
 577     // TODO: match field values of scalar replaced object
 578     private static final String SCOBJ = "(.*# ScObj.*" + END;
 579 
 580     static {
 581         // Gather all test methods and put them in Hashtable
 582         for (Method m : ValueTypeTestBench.class.getDeclaredMethods()) {
 583             if (m.isAnnotationPresent(Test.class)) {
 584                 tests.put("ValueTypeTestBench::" + m.getName(), m);
 585             }
 586         }
 587     }
 588 
 589     public static void main(String[] args) throws Throwable {
 590         if (args.length == 0) {
 591             // Run tests in own process and verify output
 592             OutputAnalyzer oa = ProcessTools.executeTestJvm("-noverify",
 593                     "-XX:+UnlockDiagnosticVMOptions", "-Xbootclasspath/a:.", "-XX:+WhiteBoxAPI",
 594                     "-XX:-TieredCompilation", "-XX:-BackgroundCompilation",
 595                     "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+PrintCompilation", "-XX:+PrintIdeal", "-XX:+PrintOptoAssembly",
 596                     "-XX:CompileCommand=quiet", "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.ValueTypeTestBench::*",
 597                     "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue1::*",
 598                     "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue2::*",
 599                     ValueTypeTestBench.class.getName(), "run");
 600             // If ideal graph printing is enabled/supported, verify output
 601             String output = oa.getOutput();
 602             oa.shouldHaveExitValue(0);
 603             if (output.contains("PrintIdeal enabled")) {
 604                 parseOutput(output);
 605             } else {
 606                 System.out.println("WARNING: IR verification disabled! Running with -Xint, -Xcomp or release build?");
 607             }
 608         } else {
 609             if (USE_COMPILER && PRINT_IDEAL && !XCOMP) {
 610                 System.out.println("PrintIdeal enabled");
 611             }
 612             // Execute tests
 613             ValueTypeTestBench bench = new ValueTypeTestBench();
 614             bench.run();
 615         }
 616     }
 617 
 618     public static void parseOutput(String output) throws Exception {
 619         String split = "b        compiler.valhalla.valuetypes.";
 620         String[] compilations = output.split(split);
 621         // Print header
 622         System.out.println(compilations[0]);
 623         // Iterate over compilation output
 624         for (String graph : compilations) {
 625             String[] lines = graph.split("\\n");
 626             if (lines[0].contains("@")) {
 627                 continue; // Ignore OSR compilations
 628             }
 629             String testName = lines[0].split(" ")[0];
 630             Method test = tests.get(testName);
 631             if (test == null) {
 632                 // Skip helper methods
 633                 continue;
 634             }
 635             if (PRINT_GRAPH) {
 636                 System.out.println("\nGraph for " + graph);
 637             }
 638             // Parse graph using regular expressions to determine if it contains forbidden nodes
 639             Test anno = test.getAnnotation(Test.class);
 640             String regexFail = anno.failOn();
 641             if (!regexFail.isEmpty()) {
 642                 Pattern pattern = Pattern.compile(regexFail.substring(0, regexFail.length()-1));
 643                 Matcher matcher = pattern.matcher(graph);
 644                 boolean fail = false;
 645                 while (matcher.find()) {
 646                     System.out.println("Graph for '" + testName + "' contains forbidden node:");
 647                     System.out.println(matcher.group());
 648                     fail = true;
 649                 }
 650                 Asserts.assertFalse(fail, "Graph for '" + testName + "' contains forbidden nodes");
 651             }
 652             String[] regexMatch = anno.match();
 653             int[] matchCount = anno.matchCount();
 654             for (int i = 0; i < regexMatch.length; ++i) {
 655                 Pattern pattern = Pattern.compile(regexMatch[i].substring(0, regexMatch[i].length()-1));
 656                 Matcher matcher = pattern.matcher(graph);
 657                 int count = 0;
 658                 String nodes = "";
 659                 while (matcher.find()) {
 660                     count++;
 661                     nodes += matcher.group() + "\n";
 662                 }
 663                 if (matchCount[i] != count) {
 664                     System.out.println("Graph for '" + testName + "' contains different number of match nodes:");
 665                     System.out.println(nodes);
 666                 }
 667                 Asserts.assertEQ(matchCount[i], count, "Graph for '" + testName + "' contains different number of match nodes");
 668             }
 669             tests.remove(testName);
 670             System.out.println(testName + " passed");
 671         }
 672         // Check if all tests were compiled
 673         if (tests.size() != 0) {
 674             for (String name : tests.keySet()) {
 675                 System.out.println("Test '" + name + "' not compiled!");
 676             }
 677             throw new RuntimeException("Not all tests were compiled");
 678         }
 679     }
 680 
 681     public void setup(Method[] methods) {
 682         for (Method m : methods) {
 683             if (m.isAnnotationPresent(Test.class)) {
 684                 // Don't inline tests
 685                 WHITE_BOX.testSetDontInlineMethod(m, true);
 686             }
 687             if (m.isAnnotationPresent(DontCompile.class)) {
 688                 WHITE_BOX.makeMethodNotCompilable(m, COMP_LEVEL_ANY, true);
 689                 WHITE_BOX.makeMethodNotCompilable(m, COMP_LEVEL_ANY, false);
 690             }
 691             if (m.isAnnotationPresent(ForceInline.class)) {
 692                 WHITE_BOX.testSetForceInlineMethod(m, true);
 693             } else if (m.isAnnotationPresent(DontInline.class)) {
 694                 WHITE_BOX.testSetDontInlineMethod(m, true);
 695             }
 696         }
 697     }
 698 
 699     public void run() throws Exception {
 700         System.out.format("rI = %d, rL = %d\n", rI, rL);
 701         setup(this.getClass().getDeclaredMethods());
 702         setup(MyValue1.class.getDeclaredMethods());
 703         setup(MyValue2.class.getDeclaredMethods());
 704 
 705         // TODO enable this after JDK 9 merge and verify that it's compiled
 706         //WHITE_BOX.enqueueInitializerForCompilation(this.getClass(), COMP_LEVEL_FULL_OPTIMIZATION);
 707 
 708         // Execute tests
 709         for (Method test : tests.values()) {
 710             Method verifier = getClass().getDeclaredMethod(test.getName() + "_verifier", boolean.class);
 711             // Warmup using verifier method
 712             for (int i = 0; i < WARMUP; ++i) {
 713                 verifier.invoke(this, true);
 714             }
 715             // Trigger compilation
 716             WHITE_BOX.enqueueMethodForCompilation(test, COMP_LEVEL_FULL_OPTIMIZATION);
 717             Asserts.assertTrue(!USE_COMPILER || WHITE_BOX.isMethodCompiled(test, false), test + " not compiled");
 718             // Check result
 719             verifier.invoke(this, false);
 720         }
 721     }
 722 }
 723 
 724 // Mark method as test
 725 @Retention(RetentionPolicy.RUNTIME)
 726 @interface Test {
 727     // Regular expression used to match forbidden IR nodes
 728     // in the C2 IR emitted for this test.
 729     String failOn() default "";
 730     // Regular expressions used to match and count IR nodes.
 731     String[] match() default { };
 732     int[] matchCount() default { };
 733 }
 734 
 735 // Force method inlining during compilation
 736 @Retention(RetentionPolicy.RUNTIME)
 737 @interface ForceInline { }
 738 
 739 // Prevent method inlining during compilation
 740 @Retention(RetentionPolicy.RUNTIME)
 741 @interface DontInline { }
 742 
 743 // Prevent method compilation
 744 @Retention(RetentionPolicy.RUNTIME)
 745 @interface DontCompile { }