--- old/test/compiler/valhalla/valuetypes/ValueTypeTestBench.java 2016-11-24 11:13:32.426550753 +0100 +++ new/test/compiler/valhalla/valuetypes/ValueTypeTestBench.java 2016-11-24 11:13:32.360551132 +0100 @@ -29,7 +29,10 @@ * @build compiler.valhalla.valuetypes.ValueTypeTestBench * @run main ClassFileInstaller sun.hotspot.WhiteBox * @run main ClassFileInstaller jdk.test.lib.Platform - * @run main/othervm -noverify -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * @run main/othervm -ea -noverify -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * compiler.valhalla.valuetypes.ValueTypeTestBench + * @run main/othervm -ea -noverify -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+UnlockExperimentalVMOptions -XX:-ValueTypePassFieldsAsArgs * compiler.valhalla.valuetypes.ValueTypeTestBench */ @@ -46,9 +49,12 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Repeatable; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; import java.util.Hashtable; +import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -85,7 +91,7 @@ @DontCompile public long hashInterpreted() { - return x + y + c + v1.hash() + v2.hash(); + return x + y + c + v1.hashInterpreted() + v2.hashInterpreted(); } } @@ -109,6 +115,11 @@ public long hash() { return x + (b ? 0 : 1) + c; } + + @DontInline + public long hashInterpreted() { + return x + (b ? 0 : 1) + c; + } } public class ValueTypeTestBench { @@ -154,7 +165,8 @@ } // Return incoming value type without accessing fields - @Test(failOn = ALLOC + LOAD + STORE + TRAP) + @Test(valid = ValueTypePassFieldsAsArgsOn, match = {ALLOC, STORE}, matchCount = {1, 9}, failOn = LOAD + TRAP) + @Test(valid = ValueTypePassFieldsAsArgsOff, failOn = ALLOC + LOAD + STORE + TRAP) public MyValue1 test3(MyValue1 v) { return v; } @@ -202,7 +214,8 @@ // Create a value type in compiled code and pass it to // the interpreter via a call. - @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP) + @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = LOAD + TRAP + ALLOC) + @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP) public long test6() { MyValue1 v = MyValue1.createInline(rI, rL); // Pass to interpreter @@ -247,7 +260,8 @@ } // Merge value types created from two branches - @Test(match = {ALLOC, STORE}, matchCount = {1, 9}, failOn = LOAD + TRAP) + @Test(valid = ValueTypePassFieldsAsArgsOn, match = {LOAD}, matchCount = {9}, failOn = TRAP + ALLOC + STORE) + @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC, STORE}, matchCount = {1, 9}, failOn = LOAD + TRAP) public MyValue1 test9(boolean b) { MyValue1 v; if (b) { @@ -358,7 +372,8 @@ // Create a value type in a non-inlined method and then call a // non-inlined method on that value type. - @Test(failOn = (ALLOC + LOAD + STORE + TRAP)) + @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = (ALLOC + STORE + TRAP), match = {LOAD}, matchCount = {9}) + @Test(valid = ValueTypePassFieldsAsArgsOff, failOn = (ALLOC + LOAD + STORE + TRAP)) public long test14() { MyValue1 v = MyValue1.createDontInline(rI, rL); return v.hashInterpreted(); @@ -372,7 +387,8 @@ // Create a value type in an inlined method and then call a // non-inlined method on that value type. - @Test(failOn = (LOAD + TRAP), match = {ALLOC}, matchCount = {1}) + @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = (LOAD + TRAP + ALLOC)) + @Test(valid = ValueTypePassFieldsAsArgsOff, failOn = (LOAD + TRAP), match = {ALLOC}, matchCount = {1}) public long test15() { MyValue1 v = MyValue1.createInline(rI, rL); return v.hashInterpreted(); @@ -415,7 +431,8 @@ // Create a value type in compiled code and pass it to the // interpreter via a call. The value is live at the first call so // debug info should include a reference to all its fields. - @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP) + @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = ALLOC + LOAD + TRAP) + @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP) public long test18() { MyValue1 v = MyValue1.createInline(rI, rL); v.hashInterpreted(); @@ -431,7 +448,8 @@ // Create a value type in compiled code and pass it to the // interpreter via a call. The value type is passed twice but // should only be allocated once. - @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP) + @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = ALLOC + LOAD + TRAP) + @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP) public long test19() { MyValue1 v = MyValue1.createInline(rI, rL); return sumValue(v, v); @@ -452,12 +470,13 @@ // interpreter via a call. The value type is live at the uncommon // trap: verify that deoptimization causes the value type to be // correctly allocated. - @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD) + @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = LOAD + ALLOC + STORE) + @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD) public long test20(boolean flag) { MyValue1 v = MyValue1.createInline(rI, rL); if (flag) { // uncommon trap - WHITE_BOX.deoptimizeMethod(tests.get("ValueTypeTestBench::test16")); + WHITE_BOX.deoptimizeMethod(tests.get("ValueTypeTestBench::test20")); } return v.hashInterpreted(); } @@ -500,10 +519,217 @@ Asserts.assertEQ(val2.hash(), MyValue2.createInline(rI + 1, true).hash()); } + // Test interpreter to compiled code with various signatures + @Test(failOn = ALLOC + STORE + TRAP) + public long test22(MyValue2 v) { + return v.hash(); + } + + @DontCompile + public void test22_verifier(boolean warmup) { + MyValue2 v = MyValue2.createInline(rI, true); + long result = test22(v); + Asserts.assertEQ(result, v.hashInterpreted()); + } + + @Test(failOn = ALLOC + STORE + TRAP) + public long test23(int i1, MyValue2 v, int i2) { + return v.hash() + i1 - i2; + } + + @DontCompile + public void test23_verifier(boolean warmup) { + MyValue2 v = MyValue2.createInline(rI, true); + long result = test23(rI, v, 2*rI); + Asserts.assertEQ(result, v.hashInterpreted() - rI); + } + + @Test(failOn = ALLOC + STORE + TRAP) + public long test24(long l1, MyValue2 v, long l2) { + return v.hash() + l1 - l2; + } + + @DontCompile + public void test24_verifier(boolean warmup) { + MyValue2 v = MyValue2.createInline(rI, true); + long result = test24(rL, v, 2*rL); + Asserts.assertEQ(result, v.hashInterpreted() - rL); + } + + @Test(failOn = ALLOC + STORE + TRAP) + public long test25(int i, MyValue2 v, long l) { + return v.hash() + i + l; + } + + @DontCompile + public void test25_verifier(boolean warmup) { + MyValue2 v = MyValue2.createInline(rI, true); + long result = test25(rI, v, rL); + Asserts.assertEQ(result, v.hashInterpreted() + rL + rI); + } + + @Test(failOn = ALLOC + STORE + TRAP) + public long test26(long l, MyValue2 v, int i) { + return v.hash() + i + l; + } + + @DontCompile + public void test26_verifier(boolean warmup) { + MyValue2 v = MyValue2.createInline(rI, true); + long result = test26(rL, v, rI); + Asserts.assertEQ(result, v.hashInterpreted() + rL + rI); + } + + @Test(failOn = ALLOC + STORE + TRAP) + public long test27(long l, MyValue1 v1, int i, MyValue2 v2) { + return v1.hash() + i + l + v2.hash(); + } + + @DontCompile + public void test27_verifier(boolean warmup) { + MyValue1 v1 = MyValue1.createDontInline(rI, rL); + MyValue2 v2 = MyValue2.createInline(rI, true); + long result = test27(rL, v1, rI, v2); + Asserts.assertEQ(result, v1.hashInterpreted() + rL + rI + v2.hashInterpreted()); + } + + // Test compiled code to interpreter with various signatures + @DontCompile + public long test28_interp(MyValue2 v) { + return v.hash(); + } + + @Test(failOn = ALLOC + STORE + TRAP) + public long test28(MyValue2 v) { + return test28_interp(v); + } + + @DontCompile + public void test28_verifier(boolean warmup) { + MyValue2 v = MyValue2.createInline(rI, true); + long result = test28(v); + Asserts.assertEQ(result, v.hashInterpreted()); + } + + @DontCompile + public long test29_interp(int i1, MyValue2 v, int i2) { + return v.hash() + i1 - i2; + } + + @Test(failOn = ALLOC + STORE + TRAP) + public long test29(int i1, MyValue2 v, int i2) { + return test29_interp(i1, v, i2); + } + + @DontCompile + public void test29_verifier(boolean warmup) { + MyValue2 v = MyValue2.createInline(rI, true); + long result = test29(rI, v, 2*rI); + Asserts.assertEQ(result, v.hashInterpreted() - rI); + } + + @DontCompile + public long test30_interp(long l1, MyValue2 v, long l2) { + return v.hash() + l1 - l2; + } + + @Test(failOn = ALLOC + STORE + TRAP) + public long test30(long l1, MyValue2 v, long l2) { + return test30_interp(l1, v, l2); + } + + @DontCompile + public void test30_verifier(boolean warmup) { + MyValue2 v = MyValue2.createInline(rI, true); + long result = test30(rL, v, 2*rL); + Asserts.assertEQ(result, v.hashInterpreted() - rL); + } + + @DontCompile + public long test31_interp(int i, MyValue2 v, long l) { + return v.hash() + i + l; + } + + @Test(failOn = ALLOC + STORE + TRAP) + public long test31(int i, MyValue2 v, long l) { + return test31_interp(i, v, l); + } + + @DontCompile + public void test31_verifier(boolean warmup) { + MyValue2 v = MyValue2.createInline(rI, true); + long result = test31(rI, v, rL); + Asserts.assertEQ(result, v.hashInterpreted() + rL + rI); + } + + @DontCompile + public long test32_interp(long l, MyValue2 v, int i) { + return v.hash() + i + l; + } + + @Test(failOn = ALLOC + STORE + TRAP) + public long test32(long l, MyValue2 v, int i) { + return test32_interp(l, v, i); + } + + @DontCompile + public void test32_verifier(boolean warmup) { + MyValue2 v = MyValue2.createInline(rI, true); + long result = test32(rL, v, rI); + Asserts.assertEQ(result, v.hashInterpreted() + rL + rI); + } + + @DontCompile + public long test33_interp(long l, MyValue1 v1, int i, MyValue2 v2) { + return v1.hash() + i + l + v2.hash(); + } + + @Test(failOn = ALLOC + STORE + TRAP) + public long test33(long l, MyValue1 v1, int i, MyValue2 v2) { + return test33_interp(l, v1, i, v2); + } + + @DontCompile + public void test33_verifier(boolean warmup) { + MyValue1 v1 = MyValue1.createDontInline(rI, rL); + MyValue2 v2 = MyValue2.createInline(rI, true); + long result = test33(rL, v1, rI, v2); + Asserts.assertEQ(result, v1.hashInterpreted() + rL + rI + v2.hashInterpreted()); + } + + // test that debug info at a call is correct + @DontCompile + public long test34_interp(MyValue2 v, boolean flag) { + if (flag) { + // uncommon trap + WHITE_BOX.deoptimizeMethod(tests.get("ValueTypeTestBench::test34")); + } + return v.hash(); + } + + @Test(failOn = ALLOC + STORE + TRAP) + public long test34(MyValue2 v, boolean flag, long l) { + return test34_interp(v, flag) + l; + } + + @DontCompile + public void test34_verifier(boolean warmup) { + MyValue2 v = MyValue2.createInline(rI, true); + long result = test34(v, false, rL); + Asserts.assertEQ(result, v.hashInterpreted() + rL); + if (!warmup) { + result = test34(v, true, rL); + Asserts.assertEQ(result, v.hashInterpreted() + rL); + } + } // ========== Test infrastructure ========== private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + private static final int ValueTypePassFieldsAsArgsOn = 0x1; + private static final int ValueTypePassFieldsAsArgsOff = 0x2; + static final int AllFlags = ValueTypePassFieldsAsArgsOn | ValueTypePassFieldsAsArgsOff; + private static final boolean ValueTypePassFieldsAsArgs = (Boolean)WHITE_BOX.getVMFlag("ValueTypePassFieldsAsArgs"); private static final int COMP_LEVEL_ANY = -1; private static final int COMP_LEVEL_FULL_OPTIMIZATION = 4; private static final Hashtable tests = new Hashtable(); @@ -530,7 +756,8 @@ static { // Gather all test methods and put them in Hashtable for (Method m : ValueTypeTestBench.class.getDeclaredMethods()) { - if (m.isAnnotationPresent(Test.class)) { + Test[] annos = m.getAnnotationsByType(Test.class); + if (annos.length != 0) { tests.put("ValueTypeTestBench::" + m.getName(), m); } } @@ -538,15 +765,25 @@ public static void main(String[] args) throws Throwable { if (args.length == 0) { + ArrayList all_args = new ArrayList(List.of( + "-noverify", + "-XX:+UnlockDiagnosticVMOptions", "-Xbootclasspath/a:.", "-XX:+WhiteBoxAPI", + "-XX:-TieredCompilation", "-XX:-BackgroundCompilation", "-XX:-UseOnStackReplacement", + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+PrintCompilation", "-XX:+PrintIdeal", "-XX:+PrintOptoAssembly", + "-XX:CompileCommand=quiet", "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.ValueTypeTestBench::*", + "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue1::*", + "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue2::*" + )); // Run tests in own process and verify output - OutputAnalyzer oa = ProcessTools.executeTestJvm("-noverify", - "-XX:+UnlockDiagnosticVMOptions", "-Xbootclasspath/a:.", "-XX:+WhiteBoxAPI", - "-XX:-TieredCompilation", "-XX:-BackgroundCompilation", "-XX:-UseOnStackReplacement", - "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+PrintCompilation", "-XX:+PrintIdeal", "-XX:+PrintOptoAssembly", - "-XX:CompileCommand=quiet", "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.ValueTypeTestBench::*", - "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue1::*", - "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue2::*", - ValueTypeTestBench.class.getName(), "run"); + all_args.add("-XX:+UnlockExperimentalVMOptions"); + if ((Boolean)WHITE_BOX.getVMFlag("ValueTypePassFieldsAsArgs")) { + all_args.add("-XX:+ValueTypePassFieldsAsArgs"); + } else { + all_args.add("-XX:-ValueTypePassFieldsAsArgs"); + } + all_args.add(ValueTypeTestBench.class.getName()); + all_args.add("run"); + OutputAnalyzer oa = ProcessTools.executeTestJvm(all_args.toArray(new String[0])); // If ideal graph printing is enabled/supported, verify output String output = oa.getOutput(); oa.shouldHaveExitValue(0); @@ -583,7 +820,18 @@ System.out.println("\nGraph for " + graph); } // Parse graph using regular expressions to determine if it contains forbidden nodes - Test anno = test.getAnnotation(Test.class); + Test[] annos = test.getAnnotationsByType(Test.class); + Test anno = null; + for (Test a : annos) { + if ((a.valid() & ValueTypePassFieldsAsArgsOn) != 0 && ValueTypePassFieldsAsArgs) { + assert anno == null; + anno = a; + } else if ((a.valid() & ValueTypePassFieldsAsArgsOff) != 0 && !ValueTypePassFieldsAsArgs) { + assert anno == null; + anno = a; + } + } + assert anno != null; String regexFail = anno.failOn(); if (!regexFail.isEmpty()) { Pattern pattern = Pattern.compile(regexFail.substring(0, regexFail.length()-1)); @@ -667,6 +915,7 @@ // Mark method as test @Retention(RetentionPolicy.RUNTIME) +@Repeatable(Tests.class) @interface Test { // Regular expression used to match forbidden IR nodes // in the C2 IR emitted for this test. @@ -674,6 +923,12 @@ // Regular expressions used to match and count IR nodes. String[] match() default { }; int[] matchCount() default { }; + int valid() default ValueTypeTestBench.AllFlags; +} + +@Retention(RetentionPolicy.RUNTIME) +@interface Tests { + Test[] value(); } // Force method inlining during compilation