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