1 /* 2 * Copyright (c) 2017, 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 // Random test values 88 public static final int rI = Utils.getRandomInstance().nextInt() % 1000; 89 public static final long rL = Utils.getRandomInstance().nextLong() % 1000; 90 91 // User defined settings 92 private static final boolean PRINT_GRAPH = true; 93 private static final boolean PRINT_TIMES = Boolean.parseBoolean(System.getProperty("PrintTimes", "false")); 94 private static final boolean VERIFY_IR = Boolean.parseBoolean(System.getProperty("VerifyIR", "true")); 95 private static final boolean VERIFY_VM = Boolean.parseBoolean(System.getProperty("VerifyVM", "false")); 96 private static final String TESTLIST = System.getProperty("Testlist", ""); 97 private static final int WARMUP = Integer.parseInt(System.getProperty("Warmup", "251")); 98 private static final boolean DUMP_REPLAY = Boolean.parseBoolean(System.getProperty("DumpReplay", "false")); 99 100 // Pre-defined settings 101 private static final List<String> defaultFlags = Arrays.asList( 102 "-XX:-BackgroundCompilation", "-XX:CICompilerCount=1", 103 "-XX:+PrintCompilation", "-XX:+PrintInlining", "-XX:+PrintIdeal", "-XX:+PrintOptoAssembly", 104 "-XX:CompileCommand=quiet", 105 "-XX:CompileCommand=compileonly,java.lang.invoke.*::*", 106 "-XX:CompileCommand=compileonly,java.lang.Long::sum", 107 "-XX:CompileCommand=compileonly,java.lang.Object::<init>", 108 "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue1::*", 109 "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue2::*", 110 "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue2Inline::*", 111 "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue3::*", 112 "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue3Inline::*", 113 "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue4::*", 114 "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.ValueCapableClass2_*::*", 115 "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.*::*", 116 "-XX:CompileCommand=inline,java.lang.__Value::hashCode"); 117 private static final List<String> verifyFlags = Arrays.asList( 118 "-XX:+VerifyOops", "-XX:+VerifyStack", "-XX:+VerifyLastFrame", "-XX:+VerifyBeforeGC", "-XX:+VerifyAfterGC", 119 "-XX:+VerifyDuringGC", "-XX:+VerifyAdapterSharing", "-XX:+StressValueTypeReturnedAsFields"); 120 121 protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); 122 protected static final int ValueTypePassFieldsAsArgsOn = 0x1; 123 protected static final int ValueTypePassFieldsAsArgsOff = 0x2; 124 protected static final int ValueTypeArrayFlattenOn = 0x4; 125 protected static final int ValueTypeArrayFlattenOff = 0x8; 126 protected static final int ValueTypeReturnedAsFieldsOn = 0x10; 127 protected static final int ValueTypeReturnedAsFieldsOff = 0x20; 128 static final int AllFlags = ValueTypePassFieldsAsArgsOn | ValueTypePassFieldsAsArgsOff | ValueTypeArrayFlattenOn | ValueTypeArrayFlattenOff | ValueTypeReturnedAsFieldsOn; 129 protected static final boolean ValueTypePassFieldsAsArgs = (Boolean)WHITE_BOX.getVMFlag("ValueTypePassFieldsAsArgs"); 130 protected static final boolean ValueTypeArrayFlatten = (Boolean)WHITE_BOX.getVMFlag("ValueArrayFlatten"); 131 protected static final boolean ValueTypeReturnedAsFields = (Boolean)WHITE_BOX.getVMFlag("ValueTypeReturnedAsFields"); 132 protected static final int COMP_LEVEL_ANY = -2; 133 protected static final int COMP_LEVEL_FULL_OPTIMIZATION = 4; 134 protected static final Hashtable<String, Method> tests = new Hashtable<String, Method>(); 135 protected static final boolean USE_COMPILER = WHITE_BOX.getBooleanVMFlag("UseCompiler"); 136 protected static final boolean PRINT_IDEAL = WHITE_BOX.getBooleanVMFlag("PrintIdeal"); 137 protected static final boolean XCOMP = Platform.isComp(); 138 139 // Regular expressions used to match nodes in the PrintIdeal output 140 protected static final String START = "(\\d+\\t(.*"; 141 protected static final String MID = ".*)+\\t===.*"; 142 protected static final String END = ")|"; 143 protected static final String ALLOC = "(.*precise klass compiler/valhalla/valuetypes/MyValue.*\\R(.*(nop|spill).*\\R)*.*_new_instance_Java" + END; 144 protected static final String ALLOCA = "(.*precise klass \\[Qcompiler/valhalla/valuetypes/MyValue.*\\R(.*(nop|spill).*\\R)*.*_new_array_Java" + END; 145 protected static final String LOAD = START + "Load(B|S|I|L|F|D)" + MID + "valuetype\\*" + END; 146 protected static final String LOADP = START + "Load(P|N)" + MID + "valuetype\\*" + END; 147 protected static final String LOADK = START + "LoadK" + MID + END; 148 protected static final String STORE = START + "Store(B|S|I|L|F|D)" + MID + "valuetype\\*" + END; 149 protected static final String STOREP = START + "Store(P|N)" + MID + "valuetype\\*" + END; 150 protected static final String LOOP = START + "Loop" + MID + "" + END; 151 protected static final String TRAP = START + "CallStaticJava" + MID + "uncommon_trap.*(unstable_if|predicate)" + END; 152 protected static final String RETURN = START + "Return" + MID + "returns" + END; 153 protected static final String LINKTOSTATIC = START + "CallStaticJava" + MID + "linkToStatic" + END; 154 protected static final String NPE = START + "CallStaticJava" + MID + "null_check" + END; 155 protected static final String CCE = "((.*cmp.*precise klass compiler/valhalla/valuetypes/ValueCapableClass.*)|" + 156 "(.*mov.*precise klass compiler/valhalla/valuetypes/ValueCapableClass.*\\R.*cmp.*)" + 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 162 protected ValueTypeTest() { 163 List<String> list = null; 164 if (!TESTLIST.isEmpty()) { 165 list = Arrays.asList(TESTLIST.split(",")); 166 } 167 // Gather all test methods and put them in Hashtable 168 for (Method m : getClass().getMethods()) { 169 Test[] annos = m.getAnnotationsByType(Test.class); 170 if (annos.length != 0 && 171 (list == null || list.contains(m.getName()))) { 172 tests.put(getClass().getSimpleName() + "::" + m.getName(), m); 173 } 174 } 175 } 176 177 protected void run(String[] args, Class<?>... classes) throws Throwable { 178 if (args.length == 0) { 179 // Spawn a new VM instance 180 execute_vm(); 181 } else { 182 // Execute tests 183 run(classes); 184 } 185 } 186 187 private void execute_vm() throws Throwable { 188 Asserts.assertFalse(tests.isEmpty(), "no tests to execute"); 189 ArrayList<String> args = new ArrayList<String>(defaultFlags); 190 if (VERIFY_IR) { 191 // Always trap for exception throwing to not confuse IR verification 192 args.add("-XX:-OmitStackTraceInFastThrow"); 193 } 194 if (VERIFY_VM) { 195 args.addAll(verifyFlags); 196 } 197 // Run tests in own process and verify output 198 args.add(getClass().getName()); 199 args.add("run"); 200 // Spawn process with default JVM options from the test's run command 201 String[] vmInputArgs = InputArguments.getVmInputArgs(); 202 String[] cmds = Arrays.copyOf(vmInputArgs, vmInputArgs.length + args.size()); 203 System.arraycopy(args.toArray(), 0, cmds, vmInputArgs.length, args.size()); 204 OutputAnalyzer oa = ProcessTools.executeTestJvm(cmds); 205 // If ideal graph printing is enabled/supported, verify output 206 String output = oa.getOutput(); 207 oa.shouldHaveExitValue(0); 208 if (VERIFY_IR) { 209 if (output.contains("PrintIdeal enabled") && !output.contains("ValueTypePassFieldsAsArgs is not supported on this platform")) { 210 parseOutput(output); 211 } else { 212 System.out.println(output); 213 System.out.println("WARNING: IR verification failed! Running with -Xint, -Xcomp or release build?"); 214 } 215 } 216 } 217 218 private void parseOutput(String output) throws Exception { 219 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"); 220 Matcher m = comp_re.matcher(output); 221 Map<String,String> compilations = new LinkedHashMap<>(); 222 int prev = 0; 223 String methodName = null; 224 while (m.find()) { 225 if (prev == 0) { 226 // Print header 227 System.out.print(output.substring(0, m.start()+1)); 228 } else if (methodName != null) { 229 compilations.put(methodName, output.substring(prev, m.start()+1)); 230 } 231 if (m.group("osr") != null) { 232 methodName = null; 233 } else { 234 methodName = m.group("name"); 235 } 236 prev = m.end(); 237 } 238 if (prev == 0) { 239 // Print header 240 System.out.print(output); 241 } else if (methodName != null) { 242 compilations.put(methodName, output.substring(prev)); 243 } 244 // Iterate over compilation output 245 for (String testName : compilations.keySet()) { 246 Method test = tests.get(testName); 247 if (test == null) { 248 // Skip helper methods 249 continue; 250 } 251 String graph = compilations.get(testName); 252 if (PRINT_GRAPH) { 253 System.out.println("\nGraph for " + testName + "\n" + graph); 254 } 255 // Parse graph using regular expressions to determine if it contains forbidden nodes 256 Test[] annos = test.getAnnotationsByType(Test.class); 257 Test anno = null; 258 for (Test a : annos) { 259 if ((a.valid() & ValueTypePassFieldsAsArgsOn) != 0 && ValueTypePassFieldsAsArgs) { 260 assert anno == null; 261 anno = a; 262 } else if ((a.valid() & ValueTypePassFieldsAsArgsOff) != 0 && !ValueTypePassFieldsAsArgs) { 263 assert anno == null; 264 anno = a; 265 } else if ((a.valid() & ValueTypeArrayFlattenOn) != 0 && ValueTypeArrayFlatten) { 266 assert anno == null; 267 anno = a; 268 } else if ((a.valid() & ValueTypeArrayFlattenOff) != 0 && !ValueTypeArrayFlatten) { 269 assert anno == null; 270 anno = a; 271 } else if ((a.valid() & ValueTypeReturnedAsFieldsOn) != 0 && ValueTypeReturnedAsFields) { 272 assert anno == null; 273 anno = a; 274 } else if ((a.valid() & ValueTypeReturnedAsFieldsOff) != 0 && !ValueTypeReturnedAsFields) { 275 assert anno == null; 276 anno = a; 277 } 278 } 279 assert anno != null; 280 String regexFail = anno.failOn(); 281 if (!regexFail.isEmpty()) { 282 Pattern pattern = Pattern.compile(regexFail.substring(0, regexFail.length()-1)); 283 Matcher matcher = pattern.matcher(graph); 284 boolean found = matcher.find(); 285 Asserts.assertFalse(found, "Graph for '" + testName + "' contains forbidden node:\n" + (found ? matcher.group() : "")); 286 } 287 String[] regexMatch = anno.match(); 288 int[] matchCount = anno.matchCount(); 289 for (int i = 0; i < regexMatch.length; ++i) { 290 Pattern pattern = Pattern.compile(regexMatch[i].substring(0, regexMatch[i].length()-1)); 291 Matcher matcher = pattern.matcher(graph); 292 int count = 0; 293 String nodes = ""; 294 while (matcher.find()) { 295 count++; 296 nodes += matcher.group() + "\n"; 297 } 298 if (matchCount[i] < 0) { 299 Asserts.assertLTE(Math.abs(matchCount[i]), count, "Graph for '" + testName + "' contains different number of match nodes:\n" + nodes); 300 } else { 301 Asserts.assertEQ(matchCount[i], count, "Graph for '" + testName + "' contains different number of match nodes:\n" + nodes); 302 } 303 } 304 tests.remove(testName); 305 System.out.println(testName + " passed"); 306 } 307 // Check if all tests were compiled 308 if (tests.size() != 0) { 309 for (String name : tests.keySet()) { 310 System.out.println("Test '" + name + "' not compiled!"); 311 } 312 throw new RuntimeException("Not all tests were compiled"); 313 } 314 } 315 316 private void setup(Class<?> clazz) { 317 if (XCOMP) { 318 // Don't control compilation if -Xcomp is enabled 319 return; 320 } 321 if (DUMP_REPLAY) { 322 // Generate replay compilation files 323 String directive = "[{ match: \"*.*\", DumpReplay: true }]"; 324 if (WHITE_BOX.addCompilerDirective(directive) != 1) { 325 throw new RuntimeException("Failed to add compiler directive"); 326 } 327 } 328 329 Method[] methods = clazz.getDeclaredMethods(); 330 for (Method m : methods) { 331 if (m.isAnnotationPresent(Test.class)) { 332 // Don't inline tests 333 WHITE_BOX.testSetDontInlineMethod(m, true); 334 } 335 if (m.isAnnotationPresent(DontCompile.class)) { 336 WHITE_BOX.makeMethodNotCompilable(m, COMP_LEVEL_ANY, true); 337 WHITE_BOX.makeMethodNotCompilable(m, COMP_LEVEL_ANY, false); 338 WHITE_BOX.testSetDontInlineMethod(m, true); 339 } 340 if (m.isAnnotationPresent(ForceInline.class)) { 341 WHITE_BOX.testSetForceInlineMethod(m, true); 342 } else if (m.isAnnotationPresent(DontInline.class)) { 343 WHITE_BOX.testSetDontInlineMethod(m, true); 344 } 345 } 346 347 // Compile class initializers 348 WHITE_BOX.enqueueInitializerForCompilation(clazz, COMP_LEVEL_FULL_OPTIMIZATION); 349 } 350 351 private void run(Class<?>... classes) throws Exception { 352 if (USE_COMPILER && PRINT_IDEAL && !XCOMP) { 353 System.out.println("PrintIdeal enabled"); 354 } 355 System.out.format("rI = %d, rL = %d\n", rI, rL); 356 357 setup(getClass()); 358 for (Class<?> clazz : classes) { 359 setup(clazz); 360 } 361 362 // Execute tests 363 TreeMap<Long, String> durations = PRINT_TIMES ? new TreeMap<Long, String>() : null; 364 for (Method test : tests.values()) { 365 long startTime = System.nanoTime(); 366 Method verifier = getClass().getMethod(test.getName() + "_verifier", boolean.class); 367 // Warmup using verifier method 368 Warmup anno = test.getAnnotation(Warmup.class); 369 int warmup = anno == null ? WARMUP : anno.value(); 370 for (int i = 0; i < warmup; ++i) { 371 verifier.invoke(this, true); 372 } 373 // Trigger compilation 374 WHITE_BOX.enqueueMethodForCompilation(test, COMP_LEVEL_FULL_OPTIMIZATION); 375 Asserts.assertTrue(!USE_COMPILER || WHITE_BOX.isMethodCompiled(test, false), test + " not compiled"); 376 // Check result 377 verifier.invoke(this, false); 378 if (PRINT_TIMES) { 379 long endTime = System.nanoTime(); 380 long duration = (endTime - startTime); 381 durations.put(duration, test.getName()); 382 } 383 } 384 385 // Print execution times 386 if (PRINT_TIMES) { 387 System.out.println("\n\nTest execution times:"); 388 for (Map.Entry<Long, String> entry : durations.entrySet()) { 389 System.out.format("%-10s%15d ns\n", entry.getValue() + ":", entry.getKey()); 390 } 391 } 392 } 393 }