1 /*
   2  * Copyright (c) 2017, 2020, 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.stream.Stream;
  49 import java.util.TreeMap;
  50 import java.util.function.BooleanSupplier;
  51 
  52 // Mark method as test
  53 @Retention(RetentionPolicy.RUNTIME)
  54 @Repeatable(Tests.class)
  55 @interface Test {
  56     // Regular expression used to match forbidden IR nodes
  57     // in the C2 IR emitted for this test.
  58     String failOn() default "";
  59     // Regular expressions used to match and count IR nodes.
  60     String[] match() default { };
  61     int[] matchCount() default { };
  62     int compLevel() default ValueTypeTest.COMP_LEVEL_ANY;
  63     int valid() default 0;
  64 }
  65 
  66 @Retention(RetentionPolicy.RUNTIME)
  67 @interface Tests {
  68     Test[] value();
  69 }
  70 
  71 // Force method inlining during compilation
  72 @Retention(RetentionPolicy.RUNTIME)
  73 @interface ForceInline { }
  74 
  75 // Prevent method inlining during compilation
  76 @Retention(RetentionPolicy.RUNTIME)
  77 @interface DontInline { }
  78 
  79 // Prevent method compilation
  80 @Retention(RetentionPolicy.RUNTIME)
  81 @interface DontCompile { }
  82 
  83 // Force method compilation
  84 @Retention(RetentionPolicy.RUNTIME)
  85 @interface ForceCompile {
  86     int compLevel() default ValueTypeTest.COMP_LEVEL_ANY;
  87 }
  88 
  89 // Number of warmup iterations
  90 @Retention(RetentionPolicy.RUNTIME)
  91 @interface Warmup {
  92     int value();
  93 }
  94 
  95 // Do not enqueue the test method for compilation immediately after warmup loops have finished. Instead
  96 // let the test method be compiled with on-stack-replacement.
  97 @Retention(RetentionPolicy.RUNTIME)
  98 @interface OSRCompileOnly {}
  99 
 100 // Skip this test temporarily for C1 testing
 101 @Retention(RetentionPolicy.RUNTIME)
 102 @interface TempSkipForC1 {
 103     String reason() default "";
 104 }
 105 
 106 public abstract class ValueTypeTest {
 107     protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
 108 
 109     protected static final int COMP_LEVEL_ANY               = -2;
 110     protected static final int COMP_LEVEL_ALL               = -2;
 111     protected static final int COMP_LEVEL_AOT               = -1;
 112     protected static final int COMP_LEVEL_NONE              =  0;
 113     protected static final int COMP_LEVEL_SIMPLE            =  1;     // C1
 114     protected static final int COMP_LEVEL_LIMITED_PROFILE   =  2;     // C1, invocation & backedge counters
 115     protected static final int COMP_LEVEL_FULL_PROFILE      =  3;     // C1, invocation & backedge counters + mdo
 116     protected static final int COMP_LEVEL_FULL_OPTIMIZATION =  4;     // C2 or JVMCI
 117 
 118     protected static final boolean TieredCompilation = (Boolean)WHITE_BOX.getVMFlag("TieredCompilation");
 119     protected static final long TieredStopAtLevel = (Long)WHITE_BOX.getVMFlag("TieredStopAtLevel");
 120     static final boolean TEST_C1 = TieredStopAtLevel < COMP_LEVEL_FULL_OPTIMIZATION;
 121 
 122     // Should we execute tests that assume (ValueType[] <: Object[])?
 123     static final boolean ENABLE_VALUE_ARRAY_COVARIANCE = Boolean.getBoolean("ValueArrayCovariance");
 124 
 125     // Random test values
 126     public static final int  rI = Utils.getRandomInstance().nextInt() % 1000;
 127     public static final long rL = Utils.getRandomInstance().nextLong() % 1000;
 128 
 129     // User defined settings
 130     protected static final boolean XCOMP = Platform.isComp();
 131     private static final boolean PRINT_GRAPH = true;
 132     private static final boolean VERBOSE = Boolean.parseBoolean(System.getProperty("Verbose", "false"));
 133     private static final boolean PRINT_TIMES = Boolean.parseBoolean(System.getProperty("PrintTimes", "false"));
 134     private static final boolean COMPILE_COMMANDS = Boolean.parseBoolean(System.getProperty("CompileCommands", "true"));
 135     private static       boolean VERIFY_IR = Boolean.parseBoolean(System.getProperty("VerifyIR", "true")) && !XCOMP && !TEST_C1 && COMPILE_COMMANDS;
 136     private static final boolean VERIFY_VM = Boolean.parseBoolean(System.getProperty("VerifyVM", "false"));
 137     private static final String SCENARIOS = System.getProperty("Scenarios", "");
 138     private static final String TESTLIST = System.getProperty("Testlist", "");
 139     private static final String EXCLUDELIST = System.getProperty("Exclude", "");
 140     private static final int WARMUP = Integer.parseInt(System.getProperty("Warmup", "251"));
 141     private static final boolean DUMP_REPLAY = Boolean.parseBoolean(System.getProperty("DumpReplay", "false"));
 142     private static final boolean FLIP_C1_C2 = Boolean.parseBoolean(System.getProperty("FlipC1C2", "false"));
 143     private static final boolean GC_AFTER = Boolean.parseBoolean(System.getProperty("GCAfter", "false"));
 144     private static final int OSR_TEST_TIMEOUT = Integer.parseInt(System.getProperty("OSRTestTimeOut", "5000"));
 145 
 146     // "jtreg -DXcomp=true" runs all the scenarios with -Xcomp. This is faster than "jtreg -javaoptions:-Xcomp".
 147     protected static final boolean RUN_SCENARIOS_WITH_XCOMP = Boolean.parseBoolean(System.getProperty("Xcomp", "false"));
 148 
 149     // Pre-defined settings
 150     private static final String[] defaultFlags = {
 151         "-XX:-BackgroundCompilation"};
 152     private static final String[] compileCommandFlags = {
 153         "-XX:CompileCommand=quiet",
 154         "-XX:CompileCommand=compileonly,java.lang.invoke.*::*",
 155         "-XX:CompileCommand=compileonly,java.lang.Long::sum",
 156         "-XX:CompileCommand=compileonly,java.lang.Object::<init>",
 157         "-XX:CompileCommand=inline,compiler.valhalla.valuetypes.MyValue*::<init>",
 158         "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.*::*"};
 159     private static final String[] printFlags = {
 160         "-XX:+PrintCompilation", "-XX:+PrintIdeal", "-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintOptoAssembly"};
 161     private static final String[] verifyFlags = {
 162         "-XX:+VerifyOops", "-XX:+VerifyStack", "-XX:+VerifyLastFrame", "-XX:+VerifyBeforeGC", "-XX:+VerifyAfterGC",
 163         "-XX:+VerifyDuringGC", "-XX:+VerifyAdapterSharing"};
 164 
 165     protected static final int ValueTypePassFieldsAsArgsOn = 0x1;
 166     protected static final int ValueTypePassFieldsAsArgsOff = 0x2;
 167     protected static final int ValueTypeArrayFlattenOn = 0x4;
 168     protected static final int ValueTypeArrayFlattenOff = 0x8;
 169     protected static final int ValueTypeReturnedAsFieldsOn = 0x10;
 170     protected static final int ValueTypeReturnedAsFieldsOff = 0x20;
 171     protected static final int AlwaysIncrementalInlineOn = 0x40;
 172     protected static final int AlwaysIncrementalInlineOff = 0x80;
 173     protected static final int G1GCOn = 0x100;
 174     protected static final int G1GCOff = 0x200;
 175     protected static final int ZGCOn = 0x400;
 176     protected static final int ZGCOff = 0x800;
 177     protected static final int ArrayLoadStoreProfileOn = 0x1000;
 178     protected static final int ArrayLoadStoreProfileOff = 0x2000;
 179     protected static final int TypeProfileOn = 0x4000;
 180     protected static final int TypeProfileOff = 0x8000;
 181     protected static final boolean ValueTypePassFieldsAsArgs = (Boolean)WHITE_BOX.getVMFlag("ValueTypePassFieldsAsArgs");
 182     protected static final boolean ValueTypeArrayFlatten = (WHITE_BOX.getIntxVMFlag("ValueArrayElemMaxFlatSize") == -1); // FIXME - fix this if default of ValueArrayElemMaxFlatSize is changed
 183     protected static final boolean ValueTypeReturnedAsFields = (Boolean)WHITE_BOX.getVMFlag("ValueTypeReturnedAsFields");
 184     protected static final boolean AlwaysIncrementalInline = (Boolean)WHITE_BOX.getVMFlag("AlwaysIncrementalInline");
 185     protected static final boolean G1GC = (Boolean)WHITE_BOX.getVMFlag("UseG1GC");
 186     protected static final boolean ZGC = (Boolean)WHITE_BOX.getVMFlag("UseZGC");
 187     protected static final boolean VerifyOops = (Boolean)WHITE_BOX.getVMFlag("VerifyOops");
 188     protected static final boolean UseArrayLoadStoreProfile = (Boolean)WHITE_BOX.getVMFlag("UseArrayLoadStoreProfile");
 189     protected static final long TypeProfileLevel = (Long)WHITE_BOX.getVMFlag("TypeProfileLevel");
 190 
 191     protected static final Hashtable<String, Method> tests = new Hashtable<String, Method>();
 192     protected static final boolean USE_COMPILER = WHITE_BOX.getBooleanVMFlag("UseCompiler");
 193     protected static final boolean PRINT_IDEAL  = WHITE_BOX.getBooleanVMFlag("PrintIdeal");
 194 
 195     // Regular expressions used to match nodes in the PrintIdeal output
 196     protected static final String START = "(\\d+\\t(.*";
 197     protected static final String MID = ".*)+\\t===.*";
 198     protected static final String END = ")|";
 199     // Generic allocation
 200     protected static final String ALLOC_G  = "(.*call,static  wrapper for: _new_instance_Java" + END;
 201     protected static final String ALLOCA_G = "(.*call,static  wrapper for: _new_array_Java" + END;
 202     // Value type allocation
 203     protected static final String ALLOC  = "(.*precise klass compiler/valhalla/valuetypes/MyValue.*\\R(.*(nop|spill).*\\R)*.*_new_instance_Java" + END;
 204     protected static final String ALLOCA = "(.*precise klass \\[Lcompiler/valhalla/valuetypes/MyValue.*\\R(.*(nop|spill).*\\R)*.*_new_array_Java" + END;
 205     protected static final String LOAD   = START + "Load(B|S|I|L|F|D|P|N)" + MID + "@compiler/valhalla/valuetypes/MyValue.*" + END;
 206     protected static final String LOADK  = START + "LoadK" + MID + END;
 207     protected static final String STORE  = START + "Store(B|C|S|I|L|F|D|P|N)" + MID + "@compiler/valhalla/valuetypes/MyValue.*" + END;
 208     protected static final String LOOP   = START + "Loop" + MID + "" + END;
 209     protected static final String COUNTEDLOOP = START + "CountedLoop\\b" + MID + "" + END;
 210     protected static final String COUNTEDLOOP_MAIN = START + "CountedLoop\\b" + MID + "main" + END;
 211     protected static final String TRAP   = START + "CallStaticJava" + MID + "uncommon_trap.*(unstable_if|predicate)" + END;
 212     protected static final String RETURN = START + "Return" + MID + "returns" + END;
 213     protected static final String LINKTOSTATIC = START + "CallStaticJava" + MID + "linkToStatic" + END;
 214     protected static final String NPE = START + "CallStaticJava" + MID + "null_check" + END;
 215     protected static final String CALL = START + "CallStaticJava" + MID + END;
 216     protected static final String STOREVALUETYPEFIELDS = START + "CallStaticJava" + MID + "store_value_type_fields" + END;
 217     protected static final String SCOBJ = "(.*# ScObj.*" + END;
 218     protected static final String LOAD_UNKNOWN_VALUE = "(.*call_leaf,runtime  load_unknown_value.*" + END;
 219     protected static final String STORE_UNKNOWN_VALUE = "(.*call_leaf,runtime  store_unknown_value.*" + END;
 220     protected static final String VALUE_ARRAY_NULL_GUARD = "(.*call,static  wrapper for: uncommon_trap.*reason='null_check' action='none'.*" + END;
 221     protected static final String STORAGE_PROPERTY_CLEARING = "(.*((int:536870911)|(salq.*3\\R.*sarq.*3)).*" + END;
 222     protected static final String CLASS_CHECK_TRAP = START + "CallStaticJava" + MID + "uncommon_trap.*class_check" + END;
 223     protected static final String NULL_CHECK_TRAP = START + "CallStaticJava" + MID + "uncommon_trap.*null_check" + END;
 224     protected static final String RANGE_CHECK_TRAP = START + "CallStaticJava" + MID + "uncommon_trap.*range_check" + END;
 225     protected static final String UNHANDLED_TRAP = START + "CallStaticJava" + MID + "uncommon_trap.*unhandled" + END;
 226 
 227     public static String[] concat(String prefix[], String... extra) {
 228         ArrayList<String> list = new ArrayList<String>();
 229         if (prefix != null) {
 230             for (String s : prefix) {
 231                 list.add(s);
 232             }
 233         }
 234         if (extra != null) {
 235             for (String s : extra) {
 236                 list.add(s);
 237             }
 238         }
 239 
 240         return list.toArray(new String[list.size()]);
 241     }
 242 
 243     /**
 244      * Override getNumScenarios and getVMParameters if you want to run with more than
 245      * the 6 built-in scenarios
 246      */
 247     public int getNumScenarios() {
 248         return 6;
 249     }
 250 
 251     /**
 252      * VM parameters for the 5 built-in test scenarios. If your test needs to append
 253      * extra parameters for (some of) these scenarios, override getExtraVMParameters().
 254      */
 255     public String[] getVMParameters(int scenario) {
 256         switch (scenario) {
 257         case 0: return new String[] {
 258                 "-XX:-UseArrayLoadStoreProfile",
 259                 "-XX:+AlwaysIncrementalInline",
 260                 "-XX:ValueArrayElemMaxFlatOops=5",
 261                 "-XX:ValueArrayElemMaxFlatSize=-1",
 262                 "-XX:ValueFieldMaxFlatSize=-1",
 263                 "-XX:+ValueTypePassFieldsAsArgs",
 264                 "-XX:+ValueTypeReturnedAsFields"};
 265         case 1: return new String[] {
 266                 "-XX:-UseArrayLoadStoreProfile",
 267                 "-XX:-UseCompressedOops",
 268                 "-XX:ValueArrayElemMaxFlatOops=5",
 269                 "-XX:ValueArrayElemMaxFlatSize=-1",
 270                 "-XX:ValueFieldMaxFlatSize=-1",
 271                 "-XX:-ValueTypePassFieldsAsArgs",
 272                 "-XX:-ValueTypeReturnedAsFields"};
 273         case 2: return new String[] {
 274                 "-XX:-UseArrayLoadStoreProfile",
 275                 "-XX:-UseCompressedOops",
 276                 "-XX:ValueArrayElemMaxFlatOops=0",
 277                 "-XX:ValueArrayElemMaxFlatSize=0",
 278                 "-XX:ValueFieldMaxFlatSize=-1",
 279                 "-XX:+ValueTypePassFieldsAsArgs",
 280                 "-XX:+ValueTypeReturnedAsFields",
 281                 "-XX:+StressValueTypeReturnedAsFields"};
 282         case 3: return new String[] {
 283                 "-XX:-UseArrayLoadStoreProfile",
 284                 "-DVerifyIR=false",
 285                 "-XX:+AlwaysIncrementalInline",
 286                 "-XX:ValueArrayElemMaxFlatOops=0",
 287                 "-XX:ValueArrayElemMaxFlatSize=0",
 288                 "-XX:ValueFieldMaxFlatSize=0",
 289                 "-XX:+ValueTypePassFieldsAsArgs",
 290                 "-XX:+ValueTypeReturnedAsFields"};
 291         case 4: return new String[] {
 292                 "-XX:-UseArrayLoadStoreProfile",
 293                 "-DVerifyIR=false",
 294                 "-XX:ValueArrayElemMaxFlatOops=-1",
 295                 "-XX:ValueArrayElemMaxFlatSize=-1",
 296                 "-XX:ValueFieldMaxFlatSize=0",
 297                 "-XX:+ValueTypePassFieldsAsArgs",
 298                 "-XX:-ValueTypeReturnedAsFields",
 299                 "-XX:-ReduceInitialCardMarks"};
 300         case 5: return new String[] {
 301                 "-XX:-UseArrayLoadStoreProfile",
 302                 "-XX:+AlwaysIncrementalInline",
 303                 "-XX:ValueArrayElemMaxFlatOops=5",
 304                 "-XX:ValueArrayElemMaxFlatSize=-1",
 305                 "-XX:ValueFieldMaxFlatSize=-1",
 306                 "-XX:-ValueTypePassFieldsAsArgs",
 307                 "-XX:-ValueTypeReturnedAsFields"};
 308         }
 309         return null;
 310     }
 311 
 312     /**
 313      * Override this method and return a non-null reason if the given scenario should be
 314      * ignored (due to an existing bug, etc).
 315      */
 316     String isScenarioIgnored(int scenario) {
 317         return null;
 318     }
 319 
 320     /**
 321      * Override this method to provide extra parameters for selected scenarios
 322      */
 323     public String[] getExtraVMParameters(int scenario) {
 324         return null;
 325     }
 326 
 327     public static void main(String[] args) throws Throwable {
 328         if (args.length != 1) {
 329             throw new RuntimeException("Usage: @run main/othervm/timeout=120 -Xbootclasspath/a:." +
 330                                        " -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions" +
 331                                        " -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI" +
 332                                        " compiler.valhalla.valuetypes.ValueTypeTest <YourTestMainClass>");
 333         }
 334         String testMainClassName = args[0];
 335         Class testMainClass = Class.forName(testMainClassName);
 336         ValueTypeTest test = (ValueTypeTest)testMainClass.newInstance();
 337         List<String> scenarios = null;
 338         if (!SCENARIOS.isEmpty()) {
 339            scenarios = Arrays.asList(SCENARIOS.split(","));
 340         }
 341         for (int i=0; i<test.getNumScenarios(); i++) {
 342             String reason;
 343             if ((reason = test.isScenarioIgnored(i)) != null) {
 344                 System.out.println("Scenario #" + i + " is ignored: " + reason);
 345             } else if (scenarios != null && !scenarios.contains(Integer.toString(i))) {
 346                 System.out.println("Scenario #" + i + " is skipped due to -Dscenarios=" + SCENARIOS);
 347             } else {
 348                 System.out.println("Scenario #" + i + " -------- ");
 349                 String[] cmds = InputArguments.getVmInputArgs();
 350                 if (RUN_SCENARIOS_WITH_XCOMP) {
 351                     cmds = concat(cmds, "-Xcomp");
 352                 }
 353                 cmds = concat(cmds, test.getVMParameters(i));
 354                 cmds = concat(cmds, test.getExtraVMParameters(i));
 355                 cmds = concat(cmds, testMainClassName);
 356 
 357                 OutputAnalyzer oa = ProcessTools.executeTestJvm(cmds);
 358                 String output = oa.getOutput();
 359                 oa.shouldHaveExitValue(0);
 360                 System.out.println(output);
 361             }
 362         }
 363     }
 364 
 365     // To exclude test cases, use -DExclude=<case1>,<case2>,...
 366     // Each case can be just the method name, or can be <class>.<method>. The latter form is useful
 367     // when you are running several tests at the same time.
 368     //
 369     // jtreg -DExclude=test12 TestArrays.java
 370     // jtreg -DExclude=test34 TestLWorld.java
 371     // -- or --
 372     // jtreg -DExclude=TestArrays.test12,TestLWorld.test34 TestArrays.java TestLWorld.java
 373     //
 374     private List<String> buildExcludeList() {
 375         List<String> exclude = null;
 376         String classPrefix = getClass().getSimpleName() + ".";
 377         if (!EXCLUDELIST.isEmpty()) {
 378             exclude = new ArrayList(Arrays.asList(EXCLUDELIST.split(",")));
 379             for (int i = exclude.size() - 1; i >= 0; i--) {
 380                 String ex = exclude.get(i);
 381                 if (ex.indexOf(".") > 0) {
 382                     if (ex.startsWith(classPrefix)) {
 383                         ex = ex.substring(classPrefix.length());
 384                         exclude.set(i, ex);
 385                     } else {
 386                         exclude.remove(i);
 387                     }
 388                 }
 389             }
 390         }
 391         return exclude;
 392     }
 393 
 394     protected ValueTypeTest() {
 395         List<String> list = null;
 396         if (!TESTLIST.isEmpty()) {
 397            list = Arrays.asList(TESTLIST.split(","));
 398         }
 399         List<String> exclude = buildExcludeList();
 400 
 401         // Gather all test methods and put them in Hashtable
 402         for (Method m : getClass().getDeclaredMethods()) {
 403             Test[] annos = m.getAnnotationsByType(Test.class);
 404             if (annos.length != 0 &&
 405                 ((list == null || list.contains(m.getName())) && (exclude == null || !exclude.contains(m.getName())))) {
 406                 tests.put(getClass().getSimpleName() + "::" + m.getName(), m);
 407             }
 408         }
 409     }
 410 
 411     protected void run(String[] args, Class<?>... classes) throws Throwable {
 412         if (args.length == 0) {
 413             // Spawn a new VM instance
 414             execute_vm();
 415         } else {
 416             // Execute tests in the VM spawned by the above code.
 417             Asserts.assertTrue(args.length == 1 && args[0].equals("run"), "must be");
 418             run(classes);
 419         }
 420     }
 421 
 422     private void execute_vm() throws Throwable {
 423         Asserts.assertFalse(tests.isEmpty(), "no tests to execute");
 424         String[] vmInputArgs = InputArguments.getVmInputArgs();
 425         for (String arg : vmInputArgs) {
 426             if (arg.startsWith("-XX:CompileThreshold")) {
 427                 // Disable IR verification if non-default CompileThreshold is set
 428                 VERIFY_IR = false;
 429             }
 430         }
 431         // Each VM is launched with flags in this order, so the later ones can override the earlier one:
 432         //     VERIFY_IR/VERIFY_VM flags specified below
 433         //     vmInputArgs, which consists of:
 434         //        @run options
 435         //        getVMParameters()
 436         //        getExtraVMParameters()
 437         //     defaultFlags
 438         //     compileCommandFlags
 439         String cmds[] = null;
 440         if (VERIFY_IR) {
 441             // Add print flags for IR verification
 442             cmds = concat(cmds, printFlags);
 443             // Always trap for exception throwing to not confuse IR verification
 444             cmds = concat(cmds, "-XX:-OmitStackTraceInFastThrow");
 445         }
 446         if (VERIFY_VM) {
 447             cmds = concat(cmds, verifyFlags);
 448         }
 449         cmds = concat(cmds, vmInputArgs);
 450         cmds = concat(cmds, defaultFlags);
 451         if (COMPILE_COMMANDS) {
 452           cmds = concat(cmds, compileCommandFlags);
 453         }
 454 
 455         // Run tests in own process and verify output
 456         cmds = concat(cmds, getClass().getName(), "run");
 457         OutputAnalyzer oa = ProcessTools.executeTestJvm(cmds);
 458         // If ideal graph printing is enabled/supported, verify output
 459         String output = oa.getOutput();
 460         oa.shouldHaveExitValue(0);
 461         if (VERIFY_IR) {
 462             if (output.contains("PrintIdeal enabled")) {
 463                 parseOutput(output);
 464             } else {
 465                 System.out.println(output);
 466                 System.out.println("WARNING: IR verification failed! Running with -Xint, -Xcomp or release build?");
 467             }
 468         }
 469     }
 470 
 471     static final class TestAnnotation {
 472         private final int flag;
 473         private final BooleanSupplier predicate;
 474 
 475         private static final TestAnnotation testAnnotations[] = {
 476             new TestAnnotation(ValueTypePassFieldsAsArgsOn, () -> ValueTypePassFieldsAsArgs),
 477             new TestAnnotation(ValueTypePassFieldsAsArgsOff, () -> !ValueTypePassFieldsAsArgs),
 478             new TestAnnotation(ValueTypeArrayFlattenOn, () -> ValueTypeArrayFlatten),
 479             new TestAnnotation(ValueTypeArrayFlattenOff, () -> !ValueTypeArrayFlatten),
 480             new TestAnnotation(ValueTypeReturnedAsFieldsOn, () -> ValueTypeReturnedAsFields),
 481             new TestAnnotation(ValueTypeReturnedAsFieldsOff, () -> !ValueTypeReturnedAsFields),
 482             new TestAnnotation(AlwaysIncrementalInlineOn, () -> AlwaysIncrementalInline),
 483             new TestAnnotation(AlwaysIncrementalInlineOff, () -> !AlwaysIncrementalInline),
 484             new TestAnnotation(G1GCOn, () -> G1GC),
 485             new TestAnnotation(G1GCOff, () -> !G1GC),
 486             new TestAnnotation(ZGCOn, () -> ZGC),
 487             new TestAnnotation(ZGCOff, () -> !ZGC),
 488             new TestAnnotation(ArrayLoadStoreProfileOn, () -> UseArrayLoadStoreProfile),
 489             new TestAnnotation(ArrayLoadStoreProfileOff, () -> !UseArrayLoadStoreProfile),
 490             new TestAnnotation(TypeProfileOn, () -> TypeProfileLevel == 222),
 491             new TestAnnotation(TypeProfileOff, () -> TypeProfileLevel == 0),
 492         };
 493 
 494         private TestAnnotation(int flag, BooleanSupplier predicate) {
 495             this.flag = flag;
 496             this.predicate = predicate;
 497         }
 498 
 499         private boolean match(Test a) {
 500             return (a.valid() & flag) != 0 && predicate.getAsBoolean();
 501         }
 502 
 503         static boolean find(Test a) {
 504             Stream<TestAnnotation> s = Arrays.stream(testAnnotations).filter(t -> t.match(a));
 505             long c = s.count();
 506             if (c > 1) {
 507                 throw new RuntimeException("At most one Test annotation should match");
 508             }
 509             return c > 0;
 510         }
 511     }
 512 
 513     private void parseOutput(String output) throws Exception {
 514         Pattern comp_re = Pattern.compile("\\n\\s+\\d+\\s+\\d+\\s+(%| )(s| )(!| )b(n| )\\s+\\d?\\s+\\S+\\.(?<name>[^.]+::\\S+)\\s+(?<osr>@ \\d+\\s+)?[(]\\d+ bytes[)]");
 515         Matcher m = comp_re.matcher(output);
 516         Map<String,String> compilations = new LinkedHashMap<>();
 517         int prev = 0;
 518         String methodName = null;
 519         while (m.find()) {
 520             if (prev == 0) {
 521                 // Print header
 522                 System.out.print(output.substring(0, m.start()+1));
 523             } else if (methodName != null) {
 524                 compilations.put(methodName, output.substring(prev, m.start()+1));
 525             }
 526             if (m.group("osr") != null) {
 527                 methodName = null;
 528             } else {
 529                 methodName = m.group("name");
 530             }
 531             prev = m.end();
 532         }
 533         if (prev == 0) {
 534             // Print header
 535             System.out.print(output);
 536         } else if (methodName != null) {
 537             compilations.put(methodName, output.substring(prev));
 538         }
 539         // Iterate over compilation output
 540         for (String testName : compilations.keySet()) {
 541             Method test = tests.get(testName);
 542             if (test == null) {
 543                 // Skip helper methods
 544                 continue;
 545             }
 546             String graph = compilations.get(testName);
 547             if (PRINT_GRAPH) {
 548                 System.out.println("\nGraph for " + testName + "\n" + graph);
 549             }
 550             // Parse graph using regular expressions to determine if it contains forbidden nodes
 551             Test[] annos = test.getAnnotationsByType(Test.class);
 552             Test anno = Arrays.stream(annos).filter(TestAnnotation::find).findFirst().orElse(null);
 553             if (anno == null) {
 554                 Object[] res = Arrays.stream(annos).filter(a -> a.valid() == 0).toArray();
 555                 if (res.length != 1) {
 556                     throw new RuntimeException("Only one Test annotation should match");
 557                 }
 558                 anno = (Test)res[0];
 559             }
 560             String regexFail = anno.failOn();
 561             if (!regexFail.isEmpty()) {
 562                 Pattern pattern = Pattern.compile(regexFail.substring(0, regexFail.length()-1));
 563                 Matcher matcher = pattern.matcher(graph);
 564                 boolean found = matcher.find();
 565                 Asserts.assertFalse(found, "Graph for '" + testName + "' contains forbidden node:\n" + (found ? matcher.group() : ""));
 566             }
 567             String[] regexMatch = anno.match();
 568             int[] matchCount = anno.matchCount();
 569             for (int i = 0; i < regexMatch.length; ++i) {
 570                 Pattern pattern = Pattern.compile(regexMatch[i].substring(0, regexMatch[i].length()-1));
 571                 Matcher matcher = pattern.matcher(graph);
 572                 int count = 0;
 573                 String nodes = "";
 574                 while (matcher.find()) {
 575                     count++;
 576                     nodes += matcher.group() + "\n";
 577                 }
 578                 if (matchCount[i] < 0) {
 579                     Asserts.assertLTE(Math.abs(matchCount[i]), count, "Graph for '" + testName + "' contains different number of match nodes (expected <= " + matchCount[i] + " but got " + count + "):\n" + nodes);
 580                 } else {
 581                     Asserts.assertEQ(matchCount[i], count, "Graph for '" + testName + "' contains different number of match nodes (expected " + matchCount[i] + " but got " + count + "):\n" + nodes);
 582                 }
 583             }
 584             tests.remove(testName);
 585             System.out.println(testName + " passed");
 586         }
 587         // Check if all tests were compiled
 588         if (tests.size() != 0) {
 589             for (String name : tests.keySet()) {
 590                 System.out.println("Test '" + name + "' not compiled!");
 591             }
 592             throw new RuntimeException("Not all tests were compiled");
 593         }
 594     }
 595 
 596     private void setup(Class<?> clazz) {
 597         if (XCOMP) {
 598             // Don't control compilation if -Xcomp is enabled
 599             return;
 600         }
 601         if (DUMP_REPLAY) {
 602             // Generate replay compilation files
 603             String directive = "[{ match: \"*.*\", DumpReplay: true }]";
 604             if (WHITE_BOX.addCompilerDirective(directive) != 1) {
 605                 throw new RuntimeException("Failed to add compiler directive");
 606             }
 607         }
 608 
 609         Method[] methods = clazz.getDeclaredMethods();
 610         for (Method m : methods) {
 611             if (m.isAnnotationPresent(Test.class)) {
 612                 // Don't inline tests
 613                 WHITE_BOX.testSetDontInlineMethod(m, true);
 614             }
 615             if (m.isAnnotationPresent(DontCompile.class)) {
 616                 WHITE_BOX.makeMethodNotCompilable(m, COMP_LEVEL_ANY, true);
 617                 WHITE_BOX.makeMethodNotCompilable(m, COMP_LEVEL_ANY, false);
 618                 WHITE_BOX.testSetDontInlineMethod(m, true);
 619             }
 620             if (m.isAnnotationPresent(ForceInline.class)) {
 621                 Asserts.assertFalse(m.isAnnotationPresent(DontInline.class), "Method " + m.getName() + " has contradicting DontInline annotation");
 622                 WHITE_BOX.testSetForceInlineMethod(m, true);
 623             }
 624             if (m.isAnnotationPresent(DontInline.class)) {
 625                 Asserts.assertFalse(m.isAnnotationPresent(ForceInline.class), "Method " + m.getName() + " has contradicting ForceInline annotation");
 626                 WHITE_BOX.testSetDontInlineMethod(m, true);
 627             }
 628         }
 629         // Only force compilation now because above annotations affect inlining
 630         for (Method m : methods) {
 631             if (m.isAnnotationPresent(ForceCompile.class)) {
 632                 Asserts.assertFalse(m.isAnnotationPresent(DontCompile.class), "Method " + m.getName() + " has contradicting DontCompile annotation");
 633                 int compLevel = getCompLevel(m.getAnnotation(ForceCompile.class));
 634                 enqueueMethodForCompilation(m, compLevel);
 635             }
 636         }
 637         // Compile class initializers
 638         int compLevel = getCompLevel(null);
 639         WHITE_BOX.enqueueInitializerForCompilation(clazz, compLevel);
 640     }
 641 
 642     private void run(Class<?>... classes) throws Exception {
 643         if (USE_COMPILER && PRINT_IDEAL && !XCOMP) {
 644             System.out.println("PrintIdeal enabled");
 645         }
 646         System.out.format("rI = %d, rL = %d\n", rI, rL);
 647 
 648         setup(getClass());
 649         for (Class<?> clazz : classes) {
 650             setup(clazz);
 651         }
 652 
 653         // Execute tests
 654         TreeMap<Long, String> durations = (PRINT_TIMES || VERBOSE) ? new TreeMap<Long, String>() : null;
 655         for (Method test : tests.values()) {
 656             if (VERBOSE) {
 657                 System.out.println("Starting " + test.getName());
 658             }
 659             TempSkipForC1 c1skip = test.getAnnotation(TempSkipForC1.class);
 660             if (TEST_C1 && c1skip != null) {
 661                 System.out.println("Skipped " + test.getName() + " for C1 testing: " + c1skip.reason());
 662                 continue;
 663             }
 664             long startTime = System.nanoTime();
 665             Method verifier = getClass().getMethod(test.getName() + "_verifier", boolean.class);
 666             // Warmup using verifier method
 667             Warmup anno = test.getAnnotation(Warmup.class);
 668             int warmup = anno == null ? WARMUP : anno.value();
 669             for (int i = 0; i < warmup; ++i) {
 670                 verifier.invoke(this, true);
 671             }
 672             boolean osrOnly = (test.getAnnotation(OSRCompileOnly.class) != null);
 673 
 674             // C1 generates a lot of code when VerifyOops is enabled and may run out of space (for a small
 675             // number of test cases).
 676             boolean maybeCodeBufferOverflow = (TEST_C1 && VerifyOops);
 677 
 678             if (osrOnly) {
 679                 long started = System.currentTimeMillis();
 680                 boolean stateCleared = false;
 681                 for (;;) {
 682                     long elapsed = System.currentTimeMillis() - started;
 683                     if (maybeCodeBufferOverflow && elapsed > 5000 && !WHITE_BOX.isMethodCompiled(test, false)) {
 684                         System.out.println("Temporarily disabling VerifyOops");
 685                         try {
 686                             WHITE_BOX.setBooleanVMFlag("VerifyOops", false);
 687                             if (!stateCleared) {
 688                                 WHITE_BOX.clearMethodState(test);
 689                                 stateCleared = true;
 690                             }
 691                             verifier.invoke(this, false);
 692                         } finally {
 693                             WHITE_BOX.setBooleanVMFlag("VerifyOops", true);
 694                             System.out.println("Re-enabled VerifyOops");
 695                         }
 696                     } else {
 697                         verifier.invoke(this, false);
 698                     }
 699 
 700                     boolean b = WHITE_BOX.isMethodCompiled(test, false);
 701                     if (VERBOSE) {
 702                         System.out.println("Is " + test.getName() + " compiled? " + b);
 703                     }
 704                     if (b || XCOMP || !USE_COMPILER) {
 705                         // Don't control compilation if -Xcomp is enabled, or if compiler is disabled
 706                         break;
 707                     }
 708                     Asserts.assertTrue(OSR_TEST_TIMEOUT < 0 || elapsed < OSR_TEST_TIMEOUT, test + " not compiled after " + OSR_TEST_TIMEOUT + " ms");
 709                 }
 710                 if (!XCOMP) {
 711                     Asserts.assertTrue(!USE_COMPILER || WHITE_BOX.isMethodCompiled(test, false), test + " not compiled");
 712                 }
 713             } else {
 714                 int compLevel = getCompLevel(test.getAnnotation(Test.class));
 715                 // Trigger compilation
 716                 enqueueMethodForCompilation(test, compLevel);
 717                 if (maybeCodeBufferOverflow && !WHITE_BOX.isMethodCompiled(test, false)) {
 718                     // Let's disable VerifyOops temporarily and retry.
 719                     WHITE_BOX.setBooleanVMFlag("VerifyOops", false);
 720                     WHITE_BOX.clearMethodState(test);
 721                     enqueueMethodForCompilation(test, compLevel);
 722                     WHITE_BOX.setBooleanVMFlag("VerifyOops", true);
 723                 }
 724                 Asserts.assertTrue(!USE_COMPILER || WHITE_BOX.isMethodCompiled(test, false), test + " not compiled");
 725                 // Check result
 726                 verifier.invoke(this, false);
 727             }
 728             if (PRINT_TIMES || VERBOSE) {
 729                 long endTime = System.nanoTime();
 730                 long duration = (endTime - startTime);
 731                 durations.put(duration, test.getName());
 732                 if (VERBOSE) {
 733                     System.out.println("Done " + test.getName() + ": " + duration + " ns = " + (duration / 1000000) + " ms");
 734                 }
 735             }
 736             if (GC_AFTER) {
 737                 System.out.println("doing GC");
 738                 System.gc();
 739             }
 740         }
 741 
 742         // Print execution times
 743         if (PRINT_TIMES) {
 744           System.out.println("\n\nTest execution times:");
 745           for (Map.Entry<Long, String> entry : durations.entrySet()) {
 746               System.out.format("%-10s%15d ns\n", entry.getValue() + ":", entry.getKey());
 747           }
 748         }
 749     }
 750 
 751     // Get the appropriate compilation level for a method, according to the
 752     // given annotation, as well as the current test scenario and VM options.
 753     //
 754     private int getCompLevel(Object annotation) {
 755         int compLevel;
 756         if (annotation == null) {
 757             compLevel = COMP_LEVEL_ANY;
 758         } else if (annotation instanceof Test) {
 759             compLevel = ((Test)annotation).compLevel();
 760         } else {
 761             compLevel = ((ForceCompile)annotation).compLevel();
 762         }
 763 
 764         return restrictCompLevel(compLevel);
 765     }
 766 
 767     // Get the appropriate level as permitted by the test scenario and VM options.
 768     private static int restrictCompLevel(int compLevel) {
 769         if (compLevel == COMP_LEVEL_ANY) {
 770             compLevel = COMP_LEVEL_FULL_OPTIMIZATION;
 771         }
 772         if (FLIP_C1_C2) {
 773             // Effectively treat all (compLevel = C1) as (compLevel = C2), and
 774             //                       (compLevel = C2) as (compLevel = C1).
 775             if (compLevel == COMP_LEVEL_SIMPLE) {
 776                 compLevel = COMP_LEVEL_FULL_OPTIMIZATION;
 777             } else if (compLevel == COMP_LEVEL_FULL_OPTIMIZATION) {
 778                 compLevel = COMP_LEVEL_SIMPLE;
 779             }
 780         }
 781         if (!TEST_C1 && compLevel < COMP_LEVEL_FULL_OPTIMIZATION) {
 782             compLevel = COMP_LEVEL_FULL_OPTIMIZATION;
 783         }
 784         if (compLevel > (int)TieredStopAtLevel) {
 785             compLevel = (int)TieredStopAtLevel;
 786         }
 787         return compLevel;
 788     }
 789 
 790     public static void enqueueMethodForCompilation(Method m, int level) {
 791         level = restrictCompLevel(level);
 792         if (VERBOSE) {
 793             System.out.println("enqueueMethodForCompilation " + m + ", level = " + level);
 794         }
 795         WHITE_BOX.enqueueMethodForCompilation(m, level);
 796     }
 797 
 798     // Unlike C2, C1 intrinsics never deoptimize System.arraycopy. Instead, we fall back to
 799     // a normal method invocation when encountering flattened arrays.
 800     static boolean isCompiledByC2(Method m) {
 801         return USE_COMPILER && !XCOMP && WHITE_BOX.isMethodCompiled(m, false) &&
 802             WHITE_BOX.getMethodCompilationLevel(m, false) >= COMP_LEVEL_FULL_OPTIMIZATION;
 803     }
 804 }