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 }