1 /*
   2  * Copyright (c) 2017, 2019, 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 package compiler.valhalla.valuetypes;
  25 
  26 import compiler.whitebox.CompilerWhiteBoxTest;
  27 import jdk.test.lib.Asserts;
  28 import jdk.test.lib.management.InputArguments;
  29 import jdk.test.lib.Platform;
  30 import jdk.test.lib.process.ProcessTools;
  31 import jdk.test.lib.process.OutputAnalyzer;
  32 import jdk.test.lib.Utils;
  33 import sun.hotspot.WhiteBox;
  34 
  35 import java.lang.annotation.Retention;
  36 import java.lang.annotation.RetentionPolicy;
  37 import java.lang.annotation.Repeatable;
  38 import java.lang.invoke.*;
  39 import java.lang.reflect.Method;
  40 import java.util.ArrayList;
  41 import java.util.Arrays;
  42 import java.util.Hashtable;
  43 import java.util.LinkedHashMap;
  44 import java.util.List;
  45 import java.util.Map;
  46 import java.util.regex.Matcher;
  47 import java.util.regex.Pattern;
  48 import java.util.TreeMap;
  49 
  50 // Mark method as test
  51 @Retention(RetentionPolicy.RUNTIME)
  52 @Repeatable(Tests.class)
  53 @interface Test {
  54     // Regular expression used to match forbidden IR nodes
  55     // in the C2 IR emitted for this test.
  56     String failOn() default "";
  57     // Regular expressions used to match and count IR nodes.
  58     String[] match() default { };
  59     int[] matchCount() default { };
  60     int compLevel() default ValueTypeTest.COMP_LEVEL_ANY;
  61     int valid() default ValueTypeTest.AllFlags;
  62 }
  63 
  64 @Retention(RetentionPolicy.RUNTIME)
  65 @interface Tests {
  66     Test[] value();
  67 }
  68 
  69 // Force method inlining during compilation
  70 @Retention(RetentionPolicy.RUNTIME)
  71 @interface ForceInline { }
  72 
  73 // Prevent method inlining during compilation
  74 @Retention(RetentionPolicy.RUNTIME)
  75 @interface DontInline { }
  76 
  77 // Prevent method compilation
  78 @Retention(RetentionPolicy.RUNTIME)
  79 @interface DontCompile { }
  80 
  81 // Force method compilation
  82 @Retention(RetentionPolicy.RUNTIME)
  83 @interface ForceCompile {
  84     int compLevel() default ValueTypeTest.COMP_LEVEL_ANY;
  85 }
  86 
  87 // Number of warmup iterations
  88 @Retention(RetentionPolicy.RUNTIME)
  89 @interface Warmup {
  90     int value();
  91 }
  92 
  93 public abstract class ValueTypeTest {
  94     // Run "jtreg -Dtest.c1=true" to enable experimental C1 testing. This forces all
  95     // compilable methods to be compiled with C1, regardless of the @Test(compLevel=?) setting.
  96     static final boolean TEST_C1 = Boolean.getBoolean("test.c1");
  97 
  98     // Should we execute tests that assume (ValueType[] <: Object[])?
  99     static final boolean ENABLE_VALUE_ARRAY_COVARIANCE = Boolean.getBoolean("ValueArrayCovariance");
 100 
 101     // Random test values
 102     public static final int  rI = Utils.getRandomInstance().nextInt() % 1000;
 103     public static final long rL = Utils.getRandomInstance().nextLong() % 1000;
 104 
 105     // User defined settings
 106     protected static final boolean XCOMP = Platform.isComp();
 107     private static final boolean PRINT_GRAPH = true;
 108     private static final boolean VERBOSE = Boolean.parseBoolean(System.getProperty("Verbose", "false"));
 109     private static final boolean PRINT_TIMES = Boolean.parseBoolean(System.getProperty("PrintTimes", "false"));
 110     private static       boolean VERIFY_IR = Boolean.parseBoolean(System.getProperty("VerifyIR", "true")) && !TEST_C1 && !XCOMP;
 111     private static final boolean VERIFY_VM = Boolean.parseBoolean(System.getProperty("VerifyVM", "false"));
 112     private static final String SCENARIOS = System.getProperty("Scenarios", "");
 113     private static final String TESTLIST = System.getProperty("Testlist", "");
 114     private static final String EXCLUDELIST = System.getProperty("Exclude", "");
 115     private static final int WARMUP = Integer.parseInt(System.getProperty("Warmup", "251"));
 116     private static final boolean DUMP_REPLAY = Boolean.parseBoolean(System.getProperty("DumpReplay", "false"));
 117 
 118     // Pre-defined settings
 119     private static final String[] defaultFlags = {
 120         "-XX:-BackgroundCompilation", "-XX:CICompilerCount=1",
 121         "-XX:CompileCommand=quiet",
 122         "-XX:CompileCommand=compileonly,java.lang.invoke.*::*",
 123         "-XX:CompileCommand=compileonly,java.lang.Long::sum",
 124         "-XX:CompileCommand=compileonly,java.lang.Object::<init>",
 125         "-XX:CompileCommand=inline,compiler.valhalla.valuetypes.MyValue*::<init>",
 126         "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.*::*"};
 127     private static final String[] printFlags = {
 128         "-XX:+PrintCompilation", "-XX:+PrintIdeal", "-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintOptoAssembly"};
 129     private static final String[] verifyFlags = {
 130         "-XX:+VerifyOops", "-XX:+VerifyStack", "-XX:+VerifyLastFrame", "-XX:+VerifyBeforeGC", "-XX:+VerifyAfterGC",
 131         "-XX:+VerifyDuringGC", "-XX:+VerifyAdapterSharing"};
 132 
 133     protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
 134     protected static final int ValueTypePassFieldsAsArgsOn = 0x1;
 135     protected static final int ValueTypePassFieldsAsArgsOff = 0x2;
 136     protected static final int ValueTypeArrayFlattenOn = 0x4;
 137     protected static final int ValueTypeArrayFlattenOff = 0x8;
 138     protected static final int ValueTypeReturnedAsFieldsOn = 0x10;
 139     protected static final int ValueTypeReturnedAsFieldsOff = 0x20;
 140     protected static final int AlwaysIncrementalInlineOn = 0x40;
 141     protected static final int AlwaysIncrementalInlineOff = 0x80;
 142     static final int AllFlags = ValueTypePassFieldsAsArgsOn | ValueTypePassFieldsAsArgsOff | ValueTypeArrayFlattenOn | ValueTypeArrayFlattenOff | ValueTypeReturnedAsFieldsOn;
 143     protected static final boolean ValueTypePassFieldsAsArgs = (Boolean)WHITE_BOX.getVMFlag("ValueTypePassFieldsAsArgs");
 144     protected static final boolean ValueTypeArrayFlatten = (WHITE_BOX.getIntxVMFlag("ValueArrayElemMaxFlatSize") == -1); // FIXME - fix this if default of ValueArrayElemMaxFlatSize is changed
 145     protected static final boolean ValueTypeReturnedAsFields = (Boolean)WHITE_BOX.getVMFlag("ValueTypeReturnedAsFields");
 146     protected static final boolean AlwaysIncrementalInline = (Boolean)WHITE_BOX.getVMFlag("AlwaysIncrementalInline");
 147     protected static final long TieredStopAtLevel = (Long)WHITE_BOX.getVMFlag("TieredStopAtLevel");
 148     protected static final int COMP_LEVEL_ANY               = -2;
 149     protected static final int COMP_LEVEL_ALL               = -2;
 150     protected static final int COMP_LEVEL_AOT               = -1;
 151     protected static final int COMP_LEVEL_NONE              =  0;
 152     protected static final int COMP_LEVEL_SIMPLE            =  1;     // C1
 153     protected static final int COMP_LEVEL_LIMITED_PROFILE   =  2;     // C1, invocation & backedge counters
 154     protected static final int COMP_LEVEL_FULL_PROFILE      =  3;     // C1, invocation & backedge counters + mdo
 155     protected static final int COMP_LEVEL_FULL_OPTIMIZATION =  4;     // C2 or JVMCI
 156 
 157     protected static final Hashtable<String, Method> tests = new Hashtable<String, Method>();
 158     protected static final boolean USE_COMPILER = WHITE_BOX.getBooleanVMFlag("UseCompiler");
 159     protected static final boolean PRINT_IDEAL  = WHITE_BOX.getBooleanVMFlag("PrintIdeal");
 160 
 161     // Regular expressions used to match nodes in the PrintIdeal output
 162     protected static final String START = "(\\d+\\t(.*";
 163     protected static final String MID = ".*)+\\t===.*";
 164     protected static final String END = ")|";
 165     protected static final String ALLOC  = "(.*precise klass compiler/valhalla/valuetypes/MyValue.*\\R(.*(nop|spill).*\\R)*.*_new_instance_Java" + END;
 166     protected static final String ALLOCA = "(.*precise klass \\[Lcompiler/valhalla/valuetypes/MyValue.*\\R(.*(nop|spill).*\\R)*.*_new_array_Java" + END;
 167     protected static final String LOAD   = START + "Load(B|S|I|L|F|D|P|N)" + MID + "@compiler/valhalla/valuetypes/MyValue.*" + END;
 168     protected static final String LOADK  = START + "LoadK" + MID + END;
 169     protected static final String STORE  = START + "Store(B|C|S|I|L|F|D|P|N)" + MID + "@compiler/valhalla/valuetypes/MyValue.*" + END;
 170     protected static final String LOOP   = START + "Loop" + MID + "" + END;
 171     protected static final String TRAP   = START + "CallStaticJava" + MID + "uncommon_trap.*(unstable_if|predicate)" + END;
 172     protected static final String RETURN = START + "Return" + MID + "returns" + END;
 173     protected static final String LINKTOSTATIC = START + "CallStaticJava" + MID + "linkToStatic" + END;
 174     protected static final String NPE = START + "CallStaticJava" + MID + "null_check" + END;
 175     protected static final String CALL = START + "CallStaticJava" + MID + END;
 176     protected static final String STOREVALUETYPEFIELDS = START + "CallStaticJava" + MID + "store_value_type_fields" + END;
 177     protected static final String SCOBJ = "(.*# ScObj.*" + END;
 178 
 179     public static String[] concat(String prefix[], String... extra) {
 180         ArrayList<String> list = new ArrayList<String>();
 181         if (prefix != null) {
 182             for (String s : prefix) {
 183                 list.add(s);
 184             }
 185         }
 186         if (extra != null) {
 187             for (String s : extra) {
 188                 list.add(s);
 189             }
 190         }
 191 
 192         return list.toArray(new String[list.size()]);
 193     }
 194 
 195     /**
 196      * Override getNumScenarios and getVMParameters if you want to run with more than
 197      * the 5 built-in scenarios
 198      */
 199     public int getNumScenarios() {
 200         if (TEST_C1) {
 201             return 1;
 202         } else {
 203             return 6;
 204         }
 205     }
 206 
 207     /**
 208      * VM paramaters for the 5 built-in test scenarios. If your test needs to append
 209      * extra parameters for (some of) these scenarios, override getExtraVMParameters().
 210      */
 211     public String[] getVMParameters(int scenario) {
 212         if (TEST_C1) {
 213             return new String[] {
 214                 "-XX:+EnableValhallaC1",
 215                 "-XX:TieredStopAtLevel=1",
 216                 "-XX:-ValueTypePassFieldsAsArgs",
 217                 "-XX:-ValueTypeReturnedAsFields"
 218             };
 219         }
 220 
 221         switch (scenario) {
 222         case 0: return new String[] {
 223                 "-XX:+AlwaysIncrementalInline",
 224                 "-XX:ValueArrayElemMaxFlatOops=-1",
 225                 "-XX:ValueArrayElemMaxFlatSize=-1",
 226                 "-XX:ValueFieldMaxFlatSize=-1",
 227                 "-XX:+ValueTypePassFieldsAsArgs",
 228                 "-XX:+ValueTypeReturnedAsFields"};
 229         case 1: return new String[] {
 230                 "-XX:-UseCompressedOops",
 231                 "-XX:ValueArrayElemMaxFlatOops=-1",
 232                 "-XX:ValueArrayElemMaxFlatSize=-1",
 233                 "-XX:ValueFieldMaxFlatSize=-1",
 234                 "-XX:-ValueTypePassFieldsAsArgs",
 235                 "-XX:-ValueTypeReturnedAsFields"};
 236         case 2: return new String[] {
 237                 "-DVerifyIR=false",
 238                 "-XX:-UseCompressedOops",
 239                 "-XX:ValueArrayElemMaxFlatOops=0",
 240                 "-XX:ValueArrayElemMaxFlatSize=0",
 241                 "-XX:ValueFieldMaxFlatSize=0",
 242                 "-XX:+ValueTypePassFieldsAsArgs",
 243                 "-XX:+ValueTypeReturnedAsFields",
 244                 "-XX:+StressValueTypeReturnedAsFields"};
 245         case 3: return new String[] {
 246                 "-DVerifyIR=false",
 247                 "-XX:+AlwaysIncrementalInline",
 248                 "-XX:-ValueTypePassFieldsAsArgs",
 249                 "-XX:-ValueTypeReturnedAsFields"};
 250         case 4: return new String[] {
 251                 "-DVerifyIR=false",
 252                 "-XX:ValueArrayElemMaxFlatOops=-1",
 253                 "-XX:ValueArrayElemMaxFlatSize=-1",
 254                 "-XX:ValueFieldMaxFlatSize=0",
 255                 "-XX:+ValueTypePassFieldsAsArgs",
 256                 "-XX:-ValueTypeReturnedAsFields"};
 257         case 5: return new String[] {
 258                 "-XX:+AlwaysIncrementalInline",
 259                 "-XX:ValueArrayElemMaxFlatOops=-1",
 260                 "-XX:ValueArrayElemMaxFlatSize=-1",
 261                 "-XX:ValueFieldMaxFlatSize=-1",
 262                 "-XX:-ValueTypePassFieldsAsArgs",
 263                 "-XX:-ValueTypeReturnedAsFields"};
 264         }
 265 
 266         return null;
 267     }
 268 
 269     /**
 270      * Override this method to provide extra parameters for selected scenarios
 271      */
 272     public String[] getExtraVMParameters(int scenario) {
 273         return null;
 274     }
 275 
 276     public static void main(String[] args) throws Throwable {
 277         if (args.length != 1) {
 278             throw new RuntimeException("Usage: @run main/othervm/timeout=120 -Xbootclasspath/a:." +
 279                                        " -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions" +
 280                                        " -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -XX:+EnableValhalla" +
 281                                        " compiler.valhalla.valuetypes.ValueTypeTest <YourTestMainClass>");
 282         }
 283         String testMainClassName = args[0];
 284         Class testMainClass = Class.forName(testMainClassName);
 285         ValueTypeTest test = (ValueTypeTest)testMainClass.newInstance();
 286         List<String> scenarios = null;
 287         if (!SCENARIOS.isEmpty()) {
 288            scenarios = Arrays.asList(SCENARIOS.split(","));
 289         }
 290         for (int i=0; i<test.getNumScenarios(); i++) {
 291             if (scenarios == null || scenarios.contains(Integer.toString(i))) {
 292                 System.out.println("Scenario #" + i + " -------- ");
 293                 String[] cmds = InputArguments.getVmInputArgs();
 294                 cmds = concat(cmds, test.getVMParameters(i));
 295                 cmds = concat(cmds, test.getExtraVMParameters(i));
 296                 cmds = concat(cmds, testMainClassName);
 297 
 298                 OutputAnalyzer oa = ProcessTools.executeTestJvm(cmds);
 299                 String output = oa.getOutput();
 300                 oa.shouldHaveExitValue(0);
 301                 System.out.println(output);
 302             } else {
 303                 System.out.println("Scenario #" + i + " is skipped due to -Dscenarios=" + SCENARIOS);
 304             }
 305         }
 306     }
 307 
 308     // To exclude test cases, use -DExclude=<case1>,<case2>,...
 309     // Each case can be just the method name, or can be <class>.<method>. The latter form is useful
 310     // when you are running several tests at the same time.
 311     //
 312     // jtreg -DExclude=test12 TestArrays.java
 313     // jtreg -DExclude=test34 TestLWorld.java
 314     // -- or --
 315     // jtreg -DExclude=TestArrays.test12,TestLWorld.test34 TestArrays.java TestLWorld.java
 316     //
 317     private List<String> buildExcludeList() {
 318         List<String> exclude = null;
 319         String classPrefix = getClass().getSimpleName() + ".";
 320         if (!EXCLUDELIST.isEmpty()) {
 321             exclude = new ArrayList(Arrays.asList(EXCLUDELIST.split(",")));
 322             for (int i = exclude.size() - 1; i >= 0; i--) {
 323                 String ex = exclude.get(i);
 324                 if (ex.indexOf(".") > 0) {
 325                     if (ex.startsWith(classPrefix)) {
 326                         ex = ex.substring(classPrefix.length());
 327                         exclude.set(i, ex);
 328                     } else {
 329                         exclude.remove(i);
 330                     }
 331                 }
 332             }
 333         }
 334         return exclude;
 335     }
 336 
 337     protected ValueTypeTest() {
 338         List<String> list = null;
 339         if (!TESTLIST.isEmpty()) {
 340            list = Arrays.asList(TESTLIST.split(","));
 341         }
 342         List<String> exclude = buildExcludeList();
 343 
 344         // Gather all test methods and put them in Hashtable
 345         for (Method m : getClass().getDeclaredMethods()) {
 346             Test[] annos = m.getAnnotationsByType(Test.class);
 347             if (annos.length != 0 &&
 348                 ((list == null || list.contains(m.getName())) && (exclude == null || !exclude.contains(m.getName())))) {
 349                 tests.put(getClass().getSimpleName() + "::" + m.getName(), m);
 350             }
 351         }
 352     }
 353 
 354     protected void run(String[] args, Class<?>... classes) throws Throwable {
 355         if (args.length == 0) {
 356             // Spawn a new VM instance
 357             execute_vm();
 358         } else {
 359             // Execute tests in the VM spawned by the above code.
 360             Asserts.assertTrue(args.length == 1 && args[0].equals("run"), "must be");
 361             run(classes);
 362         }
 363     }
 364 
 365     private void execute_vm() throws Throwable {
 366         Asserts.assertFalse(tests.isEmpty(), "no tests to execute");
 367         String[] vmInputArgs = InputArguments.getVmInputArgs();
 368         for (String arg : vmInputArgs) {
 369             if (arg.startsWith("-XX:CompileThreshold")) {
 370                 // Disable IR verification if non-default CompileThreshold is set
 371                 VERIFY_IR = false;
 372             }
 373             if (arg.startsWith("-XX:+EnableValhallaC1")) {
 374                 // Disable IR verification if C1 is used (FIXME!)
 375                 VERIFY_IR = false;
 376             }
 377         }
 378         // Each VM is launched with flags in this order, so the later ones can override the earlier one:
 379         //     defaultFlags
 380         //     VERIFY_IR/VERIFY_VM flags specified below
 381         //     vmInputArgs, which consists of:
 382         //        @run options
 383         //        getVMParameters()
 384         //        getExtraVMParameters()
 385         String cmds[] = defaultFlags;
 386         if (VERIFY_IR) {
 387             // Add print flags for IR verification
 388             cmds = concat(cmds, printFlags);
 389             // Always trap for exception throwing to not confuse IR verification
 390             cmds = concat(cmds, "-XX:-OmitStackTraceInFastThrow");
 391         }
 392         if (VERIFY_VM) {
 393             cmds = concat(cmds, verifyFlags);
 394         }
 395         cmds = concat(cmds, vmInputArgs);
 396 
 397         // Run tests in own process and verify output
 398         cmds = concat(cmds, getClass().getName(), "run");
 399         OutputAnalyzer oa = ProcessTools.executeTestJvm(cmds);
 400         // If ideal graph printing is enabled/supported, verify output
 401         String output = oa.getOutput();
 402         oa.shouldHaveExitValue(0);
 403         if (VERIFY_IR) {
 404             if (output.contains("PrintIdeal enabled")) {
 405                 parseOutput(output);
 406             } else {
 407                 System.out.println(output);
 408                 System.out.println("WARNING: IR verification failed! Running with -Xint, -Xcomp or release build?");
 409             }
 410         }
 411     }
 412 
 413     private void parseOutput(String output) throws Exception {
 414         Pattern comp_re = Pattern.compile("\\n\\s+\\d+\\s+\\d+\\s+(%| )(s| )(!| )b(n| )\\s+\\S+\\.(?<name>[^.]+::\\S+)\\s+(?<osr>@ \\d+\\s+)?[(]\\d+ bytes[)]\\n");
 415         Matcher m = comp_re.matcher(output);
 416         Map<String,String> compilations = new LinkedHashMap<>();
 417         int prev = 0;
 418         String methodName = null;
 419         while (m.find()) {
 420             if (prev == 0) {
 421                 // Print header
 422                 System.out.print(output.substring(0, m.start()+1));
 423             } else if (methodName != null) {
 424                 compilations.put(methodName, output.substring(prev, m.start()+1));
 425             }
 426             if (m.group("osr") != null) {
 427                 methodName = null;
 428             } else {
 429                 methodName = m.group("name");
 430             }
 431             prev = m.end();
 432         }
 433         if (prev == 0) {
 434             // Print header
 435             System.out.print(output);
 436         } else if (methodName != null) {
 437             compilations.put(methodName, output.substring(prev));
 438         }
 439         // Iterate over compilation output
 440         for (String testName : compilations.keySet()) {
 441             Method test = tests.get(testName);
 442             if (test == null) {
 443                 // Skip helper methods
 444                 continue;
 445             }
 446             String graph = compilations.get(testName);
 447             if (PRINT_GRAPH) {
 448                 System.out.println("\nGraph for " + testName + "\n" + graph);
 449             }
 450             // Parse graph using regular expressions to determine if it contains forbidden nodes
 451             Test[] annos = test.getAnnotationsByType(Test.class);
 452             Test anno = null;
 453             for (Test a : annos) {
 454                 if ((a.valid() & ValueTypePassFieldsAsArgsOn) != 0 && ValueTypePassFieldsAsArgs) {
 455                     assert anno == null;
 456                     anno = a;
 457                 } else if ((a.valid() & ValueTypePassFieldsAsArgsOff) != 0 && !ValueTypePassFieldsAsArgs) {
 458                     assert anno == null;
 459                     anno = a;
 460                 } else if ((a.valid() & ValueTypeArrayFlattenOn) != 0 && ValueTypeArrayFlatten) {
 461                     assert anno == null;
 462                     anno = a;
 463                 } else if ((a.valid() & ValueTypeArrayFlattenOff) != 0 && !ValueTypeArrayFlatten) {
 464                     assert anno == null;
 465                     anno = a;
 466                 } else if ((a.valid() & ValueTypeReturnedAsFieldsOn) != 0 && ValueTypeReturnedAsFields) {
 467                     assert anno == null;
 468                     anno = a;
 469                 } else if ((a.valid() & ValueTypeReturnedAsFieldsOff) != 0 && !ValueTypeReturnedAsFields) {
 470                     assert anno == null;
 471                     anno = a;
 472                 } else if ((a.valid() & AlwaysIncrementalInlineOn) != 0 && AlwaysIncrementalInline) {
 473                     assert anno == null;
 474                     anno = a;
 475                 } else if ((a.valid() & AlwaysIncrementalInlineOff) != 0 && !AlwaysIncrementalInline) {
 476                     assert anno == null;
 477                     anno = a;
 478                 }
 479             }
 480             assert anno != null;
 481             String regexFail = anno.failOn();
 482             if (!regexFail.isEmpty()) {
 483                 Pattern pattern = Pattern.compile(regexFail.substring(0, regexFail.length()-1));
 484                 Matcher matcher = pattern.matcher(graph);
 485                 boolean found = matcher.find();
 486                 Asserts.assertFalse(found, "Graph for '" + testName + "' contains forbidden node:\n" + (found ? matcher.group() : ""));
 487             }
 488             String[] regexMatch = anno.match();
 489             int[] matchCount = anno.matchCount();
 490             for (int i = 0; i < regexMatch.length; ++i) {
 491                 Pattern pattern = Pattern.compile(regexMatch[i].substring(0, regexMatch[i].length()-1));
 492                 Matcher matcher = pattern.matcher(graph);
 493                 int count = 0;
 494                 String nodes = "";
 495                 while (matcher.find()) {
 496                     count++;
 497                     nodes += matcher.group() + "\n";
 498                 }
 499                 if (matchCount[i] < 0) {
 500                     Asserts.assertLTE(Math.abs(matchCount[i]), count, "Graph for '" + testName + "' contains different number of match nodes:\n" + nodes);
 501                 } else {
 502                     Asserts.assertEQ(matchCount[i], count, "Graph for '" + testName + "' contains different number of match nodes:\n" + nodes);
 503                 }
 504             }
 505             tests.remove(testName);
 506             System.out.println(testName + " passed");
 507         }
 508         // Check if all tests were compiled
 509         if (tests.size() != 0) {
 510             for (String name : tests.keySet()) {
 511                 System.out.println("Test '" + name + "' not compiled!");
 512             }
 513             throw new RuntimeException("Not all tests were compiled");
 514         }
 515     }
 516 
 517     private void setup(Class<?> clazz) {
 518         if (XCOMP) {
 519             // Don't control compilation if -Xcomp is enabled
 520             return;
 521         }
 522         if (DUMP_REPLAY) {
 523             // Generate replay compilation files
 524             String directive = "[{ match: \"*.*\", DumpReplay: true }]";
 525             if (WHITE_BOX.addCompilerDirective(directive) != 1) {
 526                 throw new RuntimeException("Failed to add compiler directive");
 527             }
 528         }
 529 
 530         Method[] methods = clazz.getDeclaredMethods();
 531         for (Method m : methods) {
 532             if (m.isAnnotationPresent(Test.class)) {
 533                 // Don't inline tests
 534                 WHITE_BOX.testSetDontInlineMethod(m, true);
 535             }
 536             if (m.isAnnotationPresent(DontCompile.class)) {
 537                 WHITE_BOX.makeMethodNotCompilable(m, COMP_LEVEL_ANY, true);
 538                 WHITE_BOX.makeMethodNotCompilable(m, COMP_LEVEL_ANY, false);
 539                 WHITE_BOX.testSetDontInlineMethod(m, true);
 540             } else if (m.isAnnotationPresent(ForceCompile.class)) {
 541                 int compLevel = getCompLevel(m.getAnnotation(ForceCompile.class));
 542                 WHITE_BOX.enqueueMethodForCompilation(m, compLevel);
 543             }
 544             if (m.isAnnotationPresent(ForceInline.class)) {
 545                 WHITE_BOX.testSetForceInlineMethod(m, true);
 546             } else if (m.isAnnotationPresent(DontInline.class)) {
 547                 WHITE_BOX.testSetDontInlineMethod(m, true);
 548             }
 549         }
 550 
 551         // Compile class initializers
 552         int compLevel = getCompLevel(null);
 553         WHITE_BOX.enqueueInitializerForCompilation(clazz, compLevel);
 554     }
 555 
 556     private void run(Class<?>... classes) throws Exception {
 557         if (USE_COMPILER && PRINT_IDEAL && !XCOMP) {
 558             System.out.println("PrintIdeal enabled");
 559         }
 560         System.out.format("rI = %d, rL = %d\n", rI, rL);
 561 
 562         setup(getClass());
 563         for (Class<?> clazz : classes) {
 564             setup(clazz);
 565         }
 566 
 567         // Execute tests
 568         TreeMap<Long, String> durations = PRINT_TIMES ? new TreeMap<Long, String>() : null;
 569         for (Method test : tests.values()) {
 570             if (VERBOSE) {
 571                 System.out.println("Starting " + test.getName());
 572             }
 573             long startTime = System.nanoTime();
 574             Method verifier = getClass().getMethod(test.getName() + "_verifier", boolean.class);
 575             // Warmup using verifier method
 576             Warmup anno = test.getAnnotation(Warmup.class);
 577             int warmup = anno == null ? WARMUP : anno.value();
 578             for (int i = 0; i < warmup; ++i) {
 579                 verifier.invoke(this, true);
 580             }
 581             int compLevel = getCompLevel(test.getAnnotation(Test.class));
 582             // Trigger compilation
 583             WHITE_BOX.enqueueMethodForCompilation(test, compLevel);
 584             Asserts.assertTrue(!USE_COMPILER || WHITE_BOX.isMethodCompiled(test, false), test + " not compiled");
 585             // Check result
 586             verifier.invoke(this, false);
 587             if (PRINT_TIMES || VERBOSE) {
 588                 long endTime = System.nanoTime();
 589                 long duration = (endTime - startTime);
 590                 durations.put(duration, test.getName());
 591                 if (VERBOSE) {
 592                     System.out.println("Done " + test.getName() + ": " + duration + "ms");
 593                 }
 594             }
 595         }
 596 
 597         // Print execution times
 598         if (PRINT_TIMES) {
 599           System.out.println("\n\nTest execution times:");
 600           for (Map.Entry<Long, String> entry : durations.entrySet()) {
 601               System.out.format("%-10s%15d ns\n", entry.getValue() + ":", entry.getKey());
 602           }
 603         }
 604     }
 605 
 606     // Choose the appropriate compilation level for a method, according to the given annotation.
 607     //
 608     // Currently, if TEST_C1 is true, we always use COMP_LEVEL_SIMPLE. Otherwise, if the
 609     // compLevel is unspecified, the default is COMP_LEVEL_FULL_OPTIMIZATION.
 610     int getCompLevel(Object annotation) {
 611         if (TEST_C1) {
 612             return COMP_LEVEL_SIMPLE;
 613         }
 614         int compLevel;
 615         if (annotation == null) {
 616             compLevel = COMP_LEVEL_ANY;
 617         } else if (annotation instanceof Test) {
 618             compLevel = ((Test)annotation).compLevel();
 619         } else {
 620             compLevel = ((ForceCompile)annotation).compLevel();
 621         }
 622         if (compLevel == COMP_LEVEL_ANY) {
 623             compLevel = COMP_LEVEL_FULL_OPTIMIZATION;
 624         }
 625         if (compLevel > (int)TieredStopAtLevel) {
 626             compLevel = (int)TieredStopAtLevel;
 627         }
 628         return compLevel;
 629     }
 630 }