< prev index next >

test/compiler/valhalla/valuetypes/ValueTypeTestBench.java

Print this page
rev 10493 : keep
rev 10504 : value type calling convention

*** 27,37 **** * @test * @library /testlibrary /test/lib /compiler/whitebox / * @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 * compiler.valhalla.valuetypes.ValueTypeTestBench */ package compiler.valhalla.valuetypes; --- 27,40 ---- * @test * @library /testlibrary /test/lib /compiler/whitebox / * @build compiler.valhalla.valuetypes.ValueTypeTestBench * @run main ClassFileInstaller sun.hotspot.WhiteBox * @run main ClassFileInstaller jdk.test.lib.Platform ! * @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 */ package compiler.valhalla.valuetypes;
*** 44,56 **** --- 47,62 ---- import jdk.test.lib.Utils; import sun.hotspot.WhiteBox; 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; // Test value types __ByValue final class MyValue1 {
*** 83,93 **** return x + y + c + v1.hash() + v2.hash(); } @DontCompile public long hashInterpreted() { ! return x + y + c + v1.hash() + v2.hash(); } } __ByValue final class MyValue2 { final int x; --- 89,99 ---- return x + y + c + v1.hash() + v2.hash(); } @DontCompile public long hashInterpreted() { ! return x + y + c + v1.hashInterpreted() + v2.hashInterpreted(); } } __ByValue final class MyValue2 { final int x;
*** 107,116 **** --- 113,127 ---- @ForceInline public long hash() { return x + (b ? 0 : 1) + c; } + + @DontInline + public long hashInterpreted() { + return x + (b ? 0 : 1) + c; + } } public class ValueTypeTestBench { // Print ideal graph after execution of each test private static final boolean PRINT_GRAPH = true;
*** 152,162 **** long result = test2(v); Asserts.assertEQ(result, hash()); } // Return incoming value type without accessing fields ! @Test(failOn = ALLOC + LOAD + STORE + TRAP) public MyValue1 test3(MyValue1 v) { return v; } @DontCompile --- 163,174 ---- long result = test2(v); Asserts.assertEQ(result, hash()); } // Return incoming value type without accessing fields ! @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; } @DontCompile
*** 200,210 **** Asserts.assertEQ(result, hash()); } // Create a value type in compiled code and pass it to // the interpreter via a call. ! @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP) public long test6() { MyValue1 v = MyValue1.createInline(rI, rL); // Pass to interpreter return v.hashInterpreted(); } --- 212,223 ---- Asserts.assertEQ(result, hash()); } // Create a value type in compiled code and pass it to // the interpreter via a call. ! @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 return v.hashInterpreted(); }
*** 245,255 **** Asserts.assertEQ(test8(true), hash()); Asserts.assertEQ(test8(false), hash(rI + 1, rL + 1)); } // Merge value types created from two branches ! @Test(match = {ALLOC, STORE}, matchCount = {1, 9}, failOn = LOAD + TRAP) public MyValue1 test9(boolean b) { MyValue1 v; if (b) { // Value type is not allocated v = MyValue1.createInline(rI, rL); --- 258,269 ---- Asserts.assertEQ(test8(true), hash()); Asserts.assertEQ(test8(false), hash(rI + 1, rL + 1)); } // Merge value types created from two branches ! @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) { // Value type is not allocated v = MyValue1.createInline(rI, rL);
*** 356,366 **** Asserts.assertEQ(result, warmup ? 42 + (1000*rI) : hash()); } // 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)) public long test14() { MyValue1 v = MyValue1.createDontInline(rI, rL); return v.hashInterpreted(); } --- 370,381 ---- Asserts.assertEQ(result, warmup ? 42 + (1000*rI) : hash()); } // Create a value type in a non-inlined method and then call a // non-inlined method on that value type. ! @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(); }
*** 370,380 **** Asserts.assertEQ(result, hash()); } // 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}) public long test15() { MyValue1 v = MyValue1.createInline(rI, rL); return v.hashInterpreted(); } --- 385,396 ---- Asserts.assertEQ(result, hash()); } // Create a value type in an inlined method and then call a // non-inlined method on that value type. ! @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(); }
*** 413,423 **** } // 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) public long test18() { MyValue1 v = MyValue1.createInline(rI, rL); v.hashInterpreted(); return v.hashInterpreted(); } --- 429,440 ---- } // 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(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(); return v.hashInterpreted(); }
*** 429,439 **** } // 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) public long test19() { MyValue1 v = MyValue1.createInline(rI, rL); return sumValue(v, v); } --- 446,457 ---- } // 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(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); }
*** 450,465 **** // Create a value type in compiled code and pass it to the // 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) public long test20(boolean flag) { MyValue1 v = MyValue1.createInline(rI, rL); if (flag) { // uncommon trap ! WHITE_BOX.deoptimizeMethod(tests.get("ValueTypeTestBench::test16")); } return v.hashInterpreted(); } @DontCompile --- 468,484 ---- // Create a value type in compiled code and pass it to the // 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(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::test20")); } return v.hashInterpreted(); } @DontCompile
*** 498,511 **** --- 517,737 ---- // Check if value type fields were updated Asserts.assertEQ(val1.hash(), hash(rI + 1, rL + 1)); 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<String, Method> tests = new Hashtable<String, Method>(); private static final int WARMUP = 10; private static boolean USE_COMPILER = WHITE_BOX.getBooleanVMFlag("UseCompiler");
*** 528,554 **** public static final long rL = Utils.getRandomInstance().nextLong() % 1000; static { // Gather all test methods and put them in Hashtable for (Method m : ValueTypeTestBench.class.getDeclaredMethods()) { ! if (m.isAnnotationPresent(Test.class)) { tests.put("ValueTypeTestBench::" + m.getName(), m); } } } public static void main(String[] args) throws Throwable { if (args.length == 0) { ! // 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"); // If ideal graph printing is enabled/supported, verify output String output = oa.getOutput(); oa.shouldHaveExitValue(0); if (output.contains("PrintIdeal enabled")) { parseOutput(output); --- 754,791 ---- public static final long rL = Utils.getRandomInstance().nextLong() % 1000; static { // Gather all test methods and put them in Hashtable for (Method m : ValueTypeTestBench.class.getDeclaredMethods()) { ! Test[] annos = m.getAnnotationsByType(Test.class); ! if (annos.length != 0) { tests.put("ValueTypeTestBench::" + m.getName(), m); } } } public static void main(String[] args) throws Throwable { if (args.length == 0) { ! ArrayList<String> 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 ! 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); if (output.contains("PrintIdeal enabled")) { parseOutput(output);
*** 581,591 **** } if (PRINT_GRAPH) { System.out.println("\nGraph for " + graph); } // Parse graph using regular expressions to determine if it contains forbidden nodes ! Test anno = test.getAnnotation(Test.class); String regexFail = anno.failOn(); if (!regexFail.isEmpty()) { Pattern pattern = Pattern.compile(regexFail.substring(0, regexFail.length()-1)); Matcher matcher = pattern.matcher(graph); boolean fail = false; --- 818,839 ---- } if (PRINT_GRAPH) { System.out.println("\nGraph for " + graph); } // Parse graph using regular expressions to determine if it contains forbidden nodes ! 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)); Matcher matcher = pattern.matcher(graph); boolean fail = false;
*** 665,681 **** --- 913,936 ---- } } // 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. String failOn() default ""; // 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 @Retention(RetentionPolicy.RUNTIME) @interface ForceInline { }
< prev index next >