--- old/test/compiler/valhalla/valuetypes/ValueTypeTestBench.java 2016-11-16 09:06:15.086498661 +0100 +++ new/test/compiler/valhalla/valuetypes/ValueTypeTestBench.java 2016-11-16 09:06:15.018498659 +0100 @@ -52,36 +52,62 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -// Test value type -__ByValue final class MyValue { +// Test value types +__ByValue final class MyValue1 { final int x; final long y; - final double z; + final MyValue2 v1; + final MyValue2 v2; + final int c; - private MyValue(int x, long y, double z) { + private MyValue1(int x, long y, MyValue2 v1, MyValue2 v2, int c) { this.x = x; this.y = y; - this.z = z; + this.v1 = v1; + this.v2 = v2; + this.c = c; } @DontInline - public static MyValue createDontInline(int x, long y, double z) { - return __Make MyValue(x, y, z); + public static MyValue1 createDontInline(int x, long y) { + return __Make MyValue1(x, y, MyValue2.createInline(x, x < y), MyValue2.createInline(x, x > y), ValueTypeTestBench.rI); } @ForceInline - public static MyValue createInline(int x, long y, double z) { - return __Make MyValue(x, y, z); + public static MyValue1 createInline(int x, long y) { + return __Make MyValue1(x, y, MyValue2.createInline(x, x < y), MyValue2.createInline(x, x > y), ValueTypeTestBench.rI); } - @DontInline - public String toStringDontInline() { - return "MyValue: x=" + x + " y=" + y + " z=" + z; + @ForceInline + public long hash() { + 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; + final boolean b; + final long c; + + private MyValue2(int x, boolean b, long c) { + this.x = x; + this.b = b; + this.c = c; } @ForceInline - public String toStringInline() { - return "MyValue: x=" + x + " y=" + y + " z=" + z; + public static MyValue2 createInline(int x, boolean b) { + return __Make MyValue2(x, b, ValueTypeTestBench.rL); + } + + @ForceInline + public long hash() { + return x + (b ? 0 : 1) + c; } } @@ -89,146 +115,154 @@ // Print ideal graph after execution of each test private static final boolean PRINT_GRAPH = true; + // ========== Helper methods ========== + + public long hash() { + return hash(rI, rL); + } + + public long hash(int x, long y) { + return MyValue1.createInline(x, y).hash(); + } + // ========== Test definitions ========== // Receive value type through call to interpreter - @Test(failOn = ALLOC + STORE) - public double test1() { - MyValue v = MyValue.createDontInline(rI, rL, rD); - return v.x + v.y + v.z; + @Test(failOn = ALLOC + STORE + TRAP) + public long test1() { + MyValue1 v = MyValue1.createDontInline(rI, rL); + return v.hash(); } @DontCompile public void test1_verifier(boolean warmup) { - double result = test1(); - Asserts.assertEQ(result, rI + rL + rD); + long result = test1(); + Asserts.assertEQ(result, hash()); } // Receive value type from interpreter via parameter - @Test(failOn = ALLOC + STORE) - public double test2(MyValue v) { - return v.x + v.y + v.z; + @Test(failOn = ALLOC + STORE + TRAP) + public long test2(MyValue1 v) { + return v.hash(); } @DontCompile public void test2_verifier(boolean warmup) { - MyValue v = MyValue.createDontInline(rI, rL, rD); - double result = test2(v); - Asserts.assertEQ(result, rI + rL + rD); + MyValue1 v = MyValue1.createDontInline(rI, rL); + long result = test2(v); + Asserts.assertEQ(result, hash()); } // Return incoming value type without accessing fields - @Test(failOn = ALLOC + LOAD + STORE) - public MyValue test3(MyValue v) { + @Test(failOn = ALLOC + LOAD + STORE + TRAP) + public MyValue1 test3(MyValue1 v) { return v; } @DontCompile public void test3_verifier(boolean warmup) { - MyValue v1 = MyValue.createDontInline(rI, rL, rD); - MyValue v2 = test3(v1); + MyValue1 v1 = MyValue1.createDontInline(rI, rL); + MyValue1 v2 = test3(v1); Asserts.assertEQ(v1.x, v2.x); Asserts.assertEQ(v1.y, v2.y); - Asserts.assertEQ(v1.z, v2.z); } // Create a value type in compiled code and only use fields. // Allocation should go away because value type does not escape. - @Test(failOn = ALLOC + LOAD + STORE) - public double test4() { - MyValue v = MyValue.createInline(rI, rL, rD); - return v.x + v.y + v.z; + @Test(failOn = ALLOC + LOAD + STORE + TRAP) + public long test4() { + MyValue1 v = MyValue1.createInline(rI, rL); + return v.hash(); } @DontCompile public void test4_verifier(boolean warmup) { - double result = test4(); - Asserts.assertEQ(result, rI + rL + rD); + long result = test4(); + Asserts.assertEQ(result, hash()); } // Create a value type in compiled code and pass it to // an inlined compiled method via a call. - @Test(failOn = ALLOC + LOAD + STORE) - public double test5() { - MyValue v = MyValue.createInline(rI, rL, rD); + @Test(failOn = ALLOC + LOAD + STORE + TRAP) + public long test5() { + MyValue1 v = MyValue1.createInline(rI, rL); return test5Inline(v); } @ForceInline - public double test5Inline(MyValue v) { - return v.x + v.y + v.z; + public long test5Inline(MyValue1 v) { + return v.hash(); } @DontCompile public void test5_verifier(boolean warmup) { - double result = test5(); - Asserts.assertEQ(result, rI + rL + rD); + long result = test5(); + 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) - public double test6() { - MyValue v = MyValue.createInline(rI, rL, rD); + @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP) + public long test6() { + MyValue1 v = MyValue1.createInline(rI, rL); // Pass to interpreter - return sumValue(v); + return v.hashInterpreted(); } @DontCompile public void test6_verifier(boolean warmup) { - double result = test6(); - Asserts.assertEQ(result, rI + rL + rD); + long result = test6(); + Asserts.assertEQ(result, hash()); } // Create a value type in compiled code and pass it to // the interpreter by returning. - @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD) - public MyValue test7(int x, long y, double z) { - return MyValue.createInline(x, y, z); + @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP) + public MyValue1 test7(int x, long y) { + return MyValue1.createInline(x, y); } @DontCompile public void test7_verifier(boolean warmup) { - MyValue v = test7(rI, rL, rD); - double result = v.x + v.y + v.z; - Asserts.assertEQ(result, rI + rL + rD); + MyValue1 v = test7(rI, rL); + Asserts.assertEQ(v.hash(), hash()); } // Merge value types created from two branches - @Test(failOn = ALLOC + STORE) - public double test8(boolean b) { - MyValue v; + @Test(failOn = ALLOC + STORE + TRAP) + public long test8(boolean b) { + MyValue1 v; if (b) { - v = MyValue.createInline(rI, rL, rD); + v = MyValue1.createInline(rI, rL); } else { - v = MyValue.createDontInline(rI + 1, rL + 1, rD + 1); + v = MyValue1.createDontInline(rI + 1, rL + 1); } - return v.x + v.y + v.z; + return v.hash(); } @DontCompile public void test8_verifier(boolean warmup) { - Asserts.assertEQ(test8(true), rI + rL + rD); - Asserts.assertEQ(test8(false), rI + 1 + rL + 1 + ((double)rD + 1)); + 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, 3}, failOn = LOAD) - public MyValue test9(boolean b) { - MyValue v; + @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 = MyValue.createInline(rI, rL, rD); + v = MyValue1.createInline(rI, rL); } else { // Value type is allocated by the callee - v = MyValue.createDontInline(rI + 1, rL + 1, rD + 1); + v = MyValue1.createDontInline(rI + 1, rL + 1); } // Need to allocate value type if 'b' is true - double sum = sumValue(v); + long sum = v.hashInterpreted(); if (b) { - v = MyValue.createDontInline(rI, rL, sum); + v = MyValue1.createDontInline(rI, sum); } else { - v = MyValue.createDontInline(rI, rL, sum + 1); + v = MyValue1.createDontInline(rI, sum + 1); } // Don't need to allocate value type because both branches allocate return v; @@ -236,61 +270,58 @@ @DontCompile public void test9_verifier(boolean warmup) { - MyValue v = test9(true); + MyValue1 v = test9(true); Asserts.assertEQ(v.x, rI); - Asserts.assertEQ(v.y, rL); - Asserts.assertEQ(v.z, rI + rL + rD); - + Asserts.assertEQ(v.y, hash()); v = test9(false); Asserts.assertEQ(v.x, rI); - Asserts.assertEQ(v.y, rL); - Asserts.assertEQ(v.z, rI + rL + ((double)rD + 1)); + Asserts.assertEQ(v.y, hash(rI + 1, rL + 1) + 1); } // Merge value types created in a loop (not inlined) - @Test(failOn = ALLOC + STORE) - public double test10(int x, long y, double z) { - MyValue v = MyValue.createDontInline(x, y, z); + @Test(failOn = ALLOC + STORE + TRAP) + public long test10(int x, long y) { + MyValue1 v = MyValue1.createDontInline(x, y); for (int i = 0; i < 10; ++i) { - v = MyValue.createDontInline(v.x + 1, v.y + 1, v.z + 1); + v = MyValue1.createDontInline(v.x + 1, v.y + 1); } - return v.x + v.y + v.z; + return v.hash(); } @DontCompile public void test10_verifier(boolean warmup) { - double result = test10(rI, rL, rD); - Asserts.assertEQ(result, rI + rL + 20 + ((double)rD + 10)); + long result = test10(rI, rL); + Asserts.assertEQ(result, hash(rI + 10, rL + 10)); } // Merge value types created in a loop (inlined) - @Test(failOn = ALLOC + LOAD + STORE + LOOP) - public double test11(int x, long y, double z) { - MyValue v = MyValue.createInline(x, y, z); + @Test(failOn = ALLOC + LOAD + STORE + LOOP + TRAP) + public long test11(int x, long y) { + MyValue1 v = MyValue1.createInline(x, y); for (int i = 0; i < 10; ++i) { - v = MyValue.createInline(v.x + 1, v.y + 1, v.z + 1); + v = MyValue1.createInline(v.x + 1, v.y + 1); } - return v.x + v.y + v.z; + return v.hash(); } @DontCompile public void test11_verifier(boolean warmup) { - double result = test11(rI, rL, rD); - Asserts.assertEQ(result, rI + rL + 20 + ((double)rD + 10)); + long result = test11(rI, rL); + Asserts.assertEQ(result, hash(rI + 10, rL + 10)); } // Test loop with uncommon trap referencing a value type @Test(match = {TRAP, SCOBJ}, matchCount = {1, 1}, failOn = ALLOC + LOAD + STORE) - public double test12(boolean b) { - MyValue v = MyValue.createInline(rI, rL, rD); - double result = 42; + public long test12(boolean b) { + MyValue1 v = MyValue1.createInline(rI, rL); + long result = 42; for (int i = 0; i < 1000; ++i) { if (b) { result += v.x; } else { // Uncommon trap referencing v. We delegate allocation to the // interpreter by adding a SafePointScalarObjectNode. - result = sumValue(v); + result = v.hashInterpreted(); } } return result; @@ -298,22 +329,22 @@ @DontCompile public void test12_verifier(boolean warmup) { - double result = test12(warmup); - Asserts.assertEQ(result, warmup ? 42 + (1000*(double)rI) : (rI + rL + rD)); + long result = test12(warmup); + Asserts.assertEQ(result, warmup ? 42 + (1000*rI) : hash()); } // Test loop with uncommon trap referencing a value type @Test(match = {TRAP, LOAD}, matchCount = {1, 1}, failOn = ALLOC + STORE + SCOBJ) - public double test13(boolean b) { - MyValue v = MyValue.createDontInline(rI, rL, rD); - double result = 42; + public long test13(boolean b) { + MyValue1 v = MyValue1.createDontInline(rI, rL); + long result = 42; for (int i = 0; i < 1000; ++i) { if (b) { result += v.x; } else { // Uncommon trap referencing v. Should not allocate // but just pass the existing oop to the uncommon trap. - result = sumValue(v); + result = v.hashInterpreted(); } } return result; @@ -321,101 +352,100 @@ @DontCompile public void test13_verifier(boolean warmup) { - double result = test13(warmup); - Asserts.assertEQ(result, warmup ? 42 + (1000*(double)rI) : (rI + rL + rD)); + long result = test13(warmup); + 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 + STORE)) - public String test14() { - MyValue v = MyValue.createDontInline(32, 64L, 128.0); - String s = v.toStringDontInline(); - return s; + @Test(failOn = (ALLOC + LOAD + STORE + TRAP)) + public long test14() { + MyValue1 v = MyValue1.createDontInline(rI, rL); + return v.hashInterpreted(); } @DontCompile public void test14_verifier(boolean b) { - String s = test14(); - System.out.println("Result is: " + s); + long result = test14(); + Asserts.assertEQ(result, hash()); } // Create a value type in an inlined method and then call a // non-inlined method on that value type. - @Test(match = {ALLOC}, matchCount = {1}) - public String test15() { - MyValue v = MyValue.createInline(65, 129L, 257.0); - String s = v.toStringDontInline(); - return s; + @Test(failOn = (LOAD + TRAP), match = {ALLOC}, matchCount = {1}) + public long test15() { + MyValue1 v = MyValue1.createInline(rI, rL); + return v.hashInterpreted(); } @DontCompile public void test15_verifier(boolean b) { - String s = test15(); - System.out.println("Result is: " + s); + long result = test15(); + Asserts.assertEQ(result, hash()); } // Create a value type in a non-inlined method and then call an - // inlined method on that value type. Allocations are due to building - // String objects and not due to allocating value types. - @Test(match = {ALLOC}, matchCount = {2}) - public String test16() { - MyValue v = MyValue.createDontInline(130, 258L, 514.0); - String s = v.toStringInline(); - return s; + // inlined method on that value type. + @Test(failOn = (ALLOC + STORE + TRAP)) + public long test16() { + MyValue1 v = MyValue1.createDontInline(rI, rL); + return v.hash(); } @DontCompile public void test16_verifier(boolean b) { - String s = test16(); - System.out.println("Result is: " + s); + long result = test16(); + Asserts.assertEQ(result, hash()); } // Create a value type in an inlined method and then call an - // inlined method on that value type. Allocations are due to - // building String objects and not due to allocating value types. - @Test(match = {ALLOC}, matchCount = {2}) - public String test17() { - MyValue v = MyValue.createInline(259, 515L, 1027.0); - String s = v.toStringInline(); - return s; + // inlined method on that value type. + @Test(failOn = (ALLOC + LOAD + STORE + TRAP)) + public long test17() { + MyValue1 v = MyValue1.createInline(rI, rL); + return v.hash(); } @DontCompile public void test17_verifier(boolean b) { - String s = test17(); - System.out.println("Result is: " + s); + long result = test17(); + Asserts.assertEQ(result, hash()); } // 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) - public double test18() { - MyValue v = MyValue.createInline(rI, rL, rD); - sumValue(v); - return sumValue(v); + @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP) + public long test18() { + MyValue1 v = MyValue1.createInline(rI, rL); + v.hashInterpreted(); + return v.hashInterpreted(); } @DontCompile public void test18_verifier(boolean warmup) { - double result = test18(); - Asserts.assertEQ(result, rI + rL + rD); + long result = test18(); + Asserts.assertEQ(result, hash()); } // 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) - public double test19() { - MyValue v = MyValue.createInline(rI, rL, rD); + @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP) + public long test19() { + MyValue1 v = MyValue1.createInline(rI, rL); return sumValue(v, v); } @DontCompile + public long sumValue(MyValue1 v, MyValue1 dummy) { + return v.hash(); + } + + @DontCompile public void test19_verifier(boolean warmup) { - double result = test19(); - Asserts.assertEQ(result, rI + rL + rD); + long result = test19(); + Asserts.assertEQ(result, hash()); } // Create a value type in compiled code and pass it to the @@ -423,37 +453,54 @@ // trap: verify that deoptimization causes the value type to be // correctly allocated. @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD) - public double test20(boolean flag) { - MyValue v = MyValue.createInline(rI, rL, rD); - if (flag) { + public long test20(boolean flag) { + MyValue1 v = MyValue1.createInline(rI, rL); + if (flag) { // uncommon trap WHITE_BOX.deoptimizeMethod(tests.get("ValueTypeTestBench::test16")); } - return sumValue(v); + return v.hashInterpreted(); } @DontCompile public void test20_verifier(boolean warmup) { - double result = test20(false); - Asserts.assertEQ(result, rI + rL + rD); + long result = test20(false); + Asserts.assertEQ(result, hash()); if (!warmup) { result = test20(true); - Asserts.assertEQ(result, rI + rL + rD); + Asserts.assertEQ(result, hash()); } } - // ========== Helper methods ========== - - @DontCompile - public double sumValue(MyValue v) { - return v.x + v.y + v.z; - } + // Value type fields in regular object + MyValue1 val1; + MyValue2 val2; + + // Test value type fields in objects + @Test(failOn = (ALLOC + TRAP)) + public long test21(int x, long y) { + // Compute hash of value type fields + long result = val1.hash() + val2.hash(); + // Update fields + val1 = MyValue1.createInline(x, y); + val2 = MyValue2.createInline(x, true); + return result; + } @DontCompile - public double sumValue(MyValue v, MyValue dummy) { - return v.x + v.y + v.z; + public void test21_verifier(boolean warmup) { + // Check if hash computed by test18 is correct + val1 = MyValue1.createInline(rI, rL); + val2 = val1.v2; + long hash = val1.hash() + val2.hash(); + long result = test21(rI + 1, rL + 1); + Asserts.assertEQ(result, hash); + // 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 infrastructure ========== private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); @@ -461,6 +508,8 @@ private static final int COMP_LEVEL_FULL_OPTIMIZATION = 4; private static final Hashtable tests = new Hashtable(); private static final int WARMUP = 10; + private static boolean USE_COMPILER = WHITE_BOX.getBooleanVMFlag("UseCompiler"); + private static boolean PRINT_IDEAL = WHITE_BOX.getBooleanVMFlag("PrintIdeal"); // Regular expressions used to match nodes in the PrintIdeal output private static final String START = "(\\d+\\t(.*"; @@ -474,11 +523,9 @@ // TODO: match field values of scalar replaced object private static final String SCOBJ = "(.*# ScObj.*" + END; - // Random test values - private static final int rI = Utils.getRandomInstance().nextInt(); - private static final long rL = Utils.getRandomInstance().nextLong(); - private static final double rD = Utils.getRandomInstance().nextDouble(); + public static final int rI = Utils.getRandomInstance().nextInt() % 1000; + public static final long rL = Utils.getRandomInstance().nextLong() % 1000; static { // Gather all test methods and put them in Hashtable @@ -493,16 +540,25 @@ 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:CompileCommand=quiet", "-XX:+PrintCompilation", "-XX:+PrintIdeal", "-XX:+PrintOptoAssembly", - "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.ValueTypeTestBench::*", - "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue::*", - ValueTypeTestBench.class.getName(), "run"); + "-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); - parseOutput(output); + if (output.contains("PrintIdeal enabled")) { + parseOutput(output); + } else { + System.out.println("WARNING: test methods not compiled or PrintIdeal disabled! Running with -Xint or release build?"); + } } else { + if (USE_COMPILER && PRINT_IDEAL) { + System.out.println("PrintIdeal enabled"); + } // Execute tests ValueTypeTestBench bench = new ValueTypeTestBench(); bench.run(); @@ -588,9 +644,10 @@ } public void run() throws Exception { - System.out.format("rI = %d, rL = %d, rD = %f\n", rI, rL, rD); + System.out.format("rI = %d, rL = %d\n", rI, rL); setup(this.getClass().getDeclaredMethods()); - setup(MyValue.class.getDeclaredMethods()); + setup(MyValue1.class.getDeclaredMethods()); + setup(MyValue2.class.getDeclaredMethods()); // Execute tests for (Method test : tests.values()) { @@ -601,7 +658,7 @@ } // Trigger compilation WHITE_BOX.enqueueMethodForCompilation(test, COMP_LEVEL_FULL_OPTIMIZATION); - Asserts.assertTrue(WHITE_BOX.isMethodCompiled(test, false)); + Asserts.assertTrue(!USE_COMPILER || WHITE_BOX.isMethodCompiled(test, false), test + " not compiled"); // Check result verifier.invoke(this, false); }