--- old/test/compiler/valhalla/valuetypes/ValueTypeTestBench.java 2017-05-29 18:07:49.276020295 +0200 +++ new/test/compiler/valhalla/valuetypes/ValueTypeTestBench.java 2017-05-29 18:07:49.200020377 +0200 @@ -33,16 +33,16 @@ * @run main ClassFileInstaller jdk.test.lib.Platform * @run main/othervm -Xbootclasspath/a:. -ea -noverify -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions * -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -XX:-TieredCompilation -XX:+VerifyAdapterSharing - * -XX:+EnableValhalla -XX:+EnableMVT -XX:+ValueTypePassFieldsAsArgs -XX:+ValueArrayFlatten + * -XX:+EnableValhalla -XX:+EnableMVT -XX:+ValueTypePassFieldsAsArgs -XX:+ValueTypeReturnedAsFields -XX:+ValueArrayFlatten * -XX:ValueArrayElemMaxFlatSize=-1 -XX:ValueArrayElemMaxFlatOops=-1 -XX:+FullGCALotWithValueTypes * compiler.valhalla.valuetypes.ValueTypeTestBench * @run main/othervm -Xbootclasspath/a:. -ea -noverify -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions * -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -XX:-TieredCompilation - * -XX:+EnableValhalla -XX:+EnableMVT -XX:-ValueTypePassFieldsAsArgs -XX:-ValueArrayFlatten + * -XX:+EnableValhalla -XX:+EnableMVT -XX:-ValueTypePassFieldsAsArgs -XX:-ValueTypeReturnedAsFields -XX:-ValueArrayFlatten * compiler.valhalla.valuetypes.ValueTypeTestBench * @run main/othervm -Xbootclasspath/a:. -ea -noverify -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions * -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -XX:-TieredCompilation -XX:+AlwaysIncrementalInline - * -XX:+EnableValhalla -XX:+EnableMVT -XX:+ValueTypePassFieldsAsArgs -XX:+ValueArrayFlatten + * -XX:+EnableValhalla -XX:+EnableMVT -XX:+ValueTypePassFieldsAsArgs -XX:+ValueTypeReturnedAsFields -XX:+ValueArrayFlatten * -XX:ValueArrayElemMaxFlatSize=-1 -XX:ValueArrayElemMaxFlatOops=-1 * compiler.valhalla.valuetypes.ValueTypeTestBench */ @@ -67,7 +67,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Hashtable; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import jdk.experimental.value.*; @@ -301,6 +303,248 @@ } } +// Value type definition to stress test return of a value in registers +// (uses all registers of calling convention on x86_64) +__ByValue final class MyValue3 { + final boolean b; + final char c; + final byte bb; + final short s; + final int i1; + final long l; + final float f1; + final double f2; + final float f3; + final double f4; + final float f5; + final double f6; + final float f7; + final double f8; + + private MyValue3(boolean b, + char c, + byte bb, + short s, + int i1, + long l, + float f1, + double f2, + float f3, + double f4, + float f5, + double f6, + float f7, + double f8) { + this.b = b; + this.c = c; + this.bb = bb; + this.s = s; + this.i1 = i1; + this.l = l; + this.f1 = f1; + this.f2 = f2; + this.f3 = f3; + this.f4 = f4; + this.f5 = f5; + this.f6 = f6; + this.f7 = f7; + this.f8 = f8; + } + + private MyValue3() { + this.b = false; + this.c = 0; + this.bb = 0; + this.s = 0; + this.i1 = 0; + this.l = 0; + this.f1 = 0; + this.f2 = 0; + this.f3 = 0; + this.f4 = 0; + this.f5 = 0; + this.f6 = 0; + this.f7 = 0; + this.f8 = 0; + } + + @ForceInline + __ValueFactory static MyValue3 setB(MyValue3 v, boolean b) { + v.b = b; + return v; + } + + @ForceInline + __ValueFactory static MyValue3 setC(MyValue3 v, char c) { + v.c = c; + return v; + } + + @ForceInline + __ValueFactory static MyValue3 setBB(MyValue3 v, byte bb) { + v.bb = bb; + return v; + } + + @ForceInline + __ValueFactory static MyValue3 setS(MyValue3 v, short s) { + v.s = s; + return v; + } + + @ForceInline + __ValueFactory static MyValue3 setI1(MyValue3 v, int i1) { + v.i1 = i1; + return v; + } + + @ForceInline + __ValueFactory static MyValue3 setL(MyValue3 v, long l) { + v.l = l; + return v; + } + + @ForceInline + __ValueFactory static MyValue3 setF1(MyValue3 v, float f1) { + v.f1 = f1; + return v; + } + + @ForceInline + __ValueFactory static MyValue3 setF2(MyValue3 v, double f2) { + v.f2 = f2; + return v; + } + + @ForceInline + __ValueFactory static MyValue3 setF3(MyValue3 v, float f3) { + v.f3 = f3; + return v; + } + + @ForceInline + __ValueFactory static MyValue3 setF4(MyValue3 v, double f4) { + v.f4 = f4; + return v; + } + + @ForceInline + __ValueFactory static MyValue3 setF5(MyValue3 v, float f5) { + v.f5 = f5; + return v; + } + + @ForceInline + __ValueFactory static MyValue3 setF6(MyValue3 v, double f6) { + v.f6 = f6; + return v; + } + + @ForceInline + __ValueFactory static MyValue3 setF7(MyValue3 v, float f7) { + v.f7 = f7; + return v; + } + + @ForceInline + __ValueFactory static MyValue3 setF8(MyValue3 v, double f8) { + v.f8 = f8; + return v; + } + + @ForceInline + __ValueFactory public static MyValue3 createDefault() { + return __MakeDefault MyValue3(); + } + + @ForceInline + public static MyValue3 create() { + java.util.Random r = Utils.getRandomInstance(); + MyValue3 v = createDefault(); + v = setB(v, r.nextBoolean()); + v = setC(v, (char)r.nextInt()); + v = setBB(v, (byte)r.nextInt()); + v = setS(v, (short)r.nextInt()); + v = setI1(v, r.nextInt()); + v = setL(v, r.nextLong()); + v = setF1(v, r.nextFloat()); + v = setF2(v, r.nextDouble()); + v = setF3(v, r.nextFloat()); + v = setF4(v, r.nextDouble()); + v = setF5(v, r.nextFloat()); + v = setF6(v, r.nextDouble()); + v = setF7(v, r.nextFloat()); + v = setF8(v, r.nextDouble()); + return v; + } + + public void verify(MyValue3 other) { + Asserts.assertEQ(b, other.b); + Asserts.assertEQ(c, other.c); + Asserts.assertEQ(bb, other.bb); + Asserts.assertEQ(s, other.s); + Asserts.assertEQ(i1, other.i1); + Asserts.assertEQ(l, other.l); + Asserts.assertEQ(f1, other.f1); + Asserts.assertEQ(f2, other.f2); + Asserts.assertEQ(f3, other.f3); + Asserts.assertEQ(f4, other.f4); + Asserts.assertEQ(f5, other.f5); + Asserts.assertEQ(f6, other.f6); + Asserts.assertEQ(f7, other.f7); + Asserts.assertEQ(f8, other.f8); + } +} + +// Value type definition with too many fields to return in registers +__ByValue final class MyValue4 { + final MyValue3 v1; + final MyValue3 v2; + + private MyValue4(MyValue3 v1, MyValue3 v2) { + this.v1 = v1; + this.v2 = v2; + } + + private MyValue4() { + this.v1 = MyValue3.createDefault(); + this.v2 = MyValue3.createDefault(); + } + + @ForceInline + __ValueFactory static MyValue4 setV1(MyValue4 v, MyValue3 v1) { + v.v1 = v1; + return v; + } + + @ForceInline + __ValueFactory static MyValue4 setV2(MyValue4 v, MyValue3 v2) { + v.v2 = v2; + return v; + } + + @ForceInline + __ValueFactory public static MyValue4 createDefault() { + return __MakeDefault MyValue4(); + } + + @ForceInline + public static MyValue4 create() { + MyValue4 v = createDefault(); + MyValue3 v1 = MyValue3.create(); + v = setV1(v, v1); + MyValue3 v2 = MyValue3.create(); + v = setV2(v, v2); + return v; + } + + public void verify(MyValue4 other) { + v1.verify(other.v1); + v2.verify(other.v2); + } +} + + public class ValueTypeTestBench { // Print ideal graph after execution of each test private static final boolean PRINT_GRAPH = true; @@ -1505,8 +1749,7 @@ * unboxed in nullcvvUnboxLoadLong is always null. Therefore, the * compiler generates only the path leading to the corresponding * uncommon trap. */ - @Test(valid = AlwaysIncrementalInlineOff, failOn = RETURN) - @Test(valid = AlwaysIncrementalInlineOn, match = {LINKTOSTATIC}, matchCount= {1}) + @Test(failOn = RETURN) public long test61() throws Throwable { return (long)nullvccUnboxLoadLongMH.invokeExact(); } @@ -1538,8 +1781,7 @@ * ValueCapableClass1 instance is never null (and therefore not * generate a null check). Also, the source and target type match * (known at compile time), so no type check is needed either.*/ - @Test(valid = AlwaysIncrementalInlineOff, failOn = NPE) - @Test(valid = AlwaysIncrementalInlineOn, match = {LINKTOSTATIC}, matchCount= {1}) + @Test(failOn = NPE) public long test62() throws Throwable { return (long)checkedvccUnboxLoadLongMH.invokeExact(); } @@ -1568,8 +1810,7 @@ * it does not have enough information to determine that the value * to be unboxed is not null (and either that it is null). The * declared type of the */ - @Test(valid = AlwaysIncrementalInlineOff, match = {NPE}, matchCount = {1}) - @Test(valid = AlwaysIncrementalInlineOn, match = {LINKTOSTATIC}, matchCount= {1}) + @Test(match = {NPE}, matchCount = {1}) public long test63(ValueCapableClass1 vcc) throws Throwable { return (long)vccUnboxLoadLongMH.invokeExact(vcc); } @@ -1586,8 +1827,7 @@ /* Attempt to unbox an object that is not a subclass of the * value-capable class derived from the value type specified in * the vunbox bytecode. */ - @Test(valid = AlwaysIncrementalInlineOff, match = {NPE,CCE}, matchCount = {1,1}) - @Test(valid = AlwaysIncrementalInlineOn, match = {LINKTOSTATIC}, matchCount= {1}) + @Test(match = {NPE,CCE}, matchCount = {1,1}) public long test64(Object vcc) throws Throwable { return (long)objectUnboxLoadLongMH.invokeExact(vcc); } @@ -1626,8 +1866,7 @@ /* Generate an if-then-else construct with one path that contains * an invalid boxing operation (boxing of a value-type to a * non-matching value-capable class).*/ - @Test(valid = AlwaysIncrementalInlineOff, match = {NPE,CCE}, matchCount = {2,3}) - @Test(valid = AlwaysIncrementalInlineOn, match = {LINKTOSTATIC}, matchCount= {1}) + @Test(match = {NPE,CCE}, matchCount = {2,3}) public long test65(Object obj, boolean warmup) throws Throwable { return (long)objectBoxMH.invokeExact(obj, warmup); } @@ -1678,6 +1917,229 @@ ); } + // Test deoptimization at call return with return value in registers + @DontCompile + public MyValue2 test66_interp(boolean deopt) { + if (deopt) { + // uncommon trap + WHITE_BOX.deoptimizeMethod(tests.get("ValueTypeTestBench::test66")); + } + return MyValue2.createWithFieldsInline(rI, true); + } + + @Test() + public MyValue2 test66(boolean flag) { + return test66_interp(flag); + } + + @DontCompile + public void test66_verifier(boolean warmup) { + MyValue2 result = test66(!warmup); + MyValue2 v = MyValue2.createWithFieldsInline(rI, true); + Asserts.assertEQ(result.hash(), v.hash()); + } + + // Return value types in registers from interpreter -> compiled + final MyValue3 test67_vt = MyValue3.create(); + @DontCompile + public MyValue3 test67_interp() { + return test67_vt; + } + + MyValue3 test67_vt2; + @Test(valid = ValueTypeReturnedAsFieldsOn, failOn = ALLOC + LOAD + TRAP) + @Test(valid = ValueTypeReturnedAsFieldsOff) + public void test67() { + test67_vt2 = test67_interp(); + } + + @DontCompile + public void test67_verifier(boolean warmup) { + test67(); + test67_vt.verify(test67_vt2); + } + + // Return value types in registers from compiled -> interpreter + final MyValue3 test68_vt = MyValue3.create(); + @Test(valid = ValueTypeReturnedAsFieldsOn, failOn = ALLOC + STORE + TRAP) + @Test(valid = ValueTypeReturnedAsFieldsOff) + public MyValue3 test68() { + return test68_vt; + } + + @DontCompile + public void test68_verifier(boolean warmup) { + MyValue3 vt = test68(); + test68_vt.verify(vt); + } + + // Return value types in registers from compiled -> compiled + final MyValue3 test69_vt = MyValue3.create(); + @DontInline + public MyValue3 test69_comp() { + return test69_vt; + } + + MyValue3 test69_vt2; + @Test(valid = ValueTypeReturnedAsFieldsOn, failOn = ALLOC + LOAD + TRAP) + @Test(valid = ValueTypeReturnedAsFieldsOff) + public void test69() { + test69_vt2 = test69_comp(); + } + + @DontCompile + public void test69_verifier(boolean warmup) throws Exception { + Method helper_m = getClass().getDeclaredMethod("test69_comp"); + if (!warmup && USE_COMPILER && !WHITE_BOX.isMethodCompiled(helper_m, false)) { + WHITE_BOX.enqueueMethodForCompilation(helper_m, COMP_LEVEL_FULL_OPTIMIZATION); + Asserts.assertTrue(WHITE_BOX.isMethodCompiled(helper_m, false), "test69_comp not compiled"); + } + test69(); + test69_vt.verify(test69_vt2); + } + + // Same tests as above but with a value type that cannot be returned in registers + + // Return value types in registers from interpreter -> compiled + final MyValue4 test70_vt = MyValue4.create(); + @DontCompile + public MyValue4 test70_interp() { + return test70_vt; + } + + MyValue4 test70_vt2; + @Test + public void test70() { + test70_vt2 = test70_interp(); + } + + @DontCompile + public void test70_verifier(boolean warmup) { + test70(); + test70_vt.verify(test70_vt2); + } + + // Return value types in registers from compiled -> interpreter + final MyValue4 test71_vt = MyValue4.create(); + @Test + public MyValue4 test71() { + return test71_vt; + } + + @DontCompile + public void test71_verifier(boolean warmup) { + MyValue4 vt = test71(); + test71_vt.verify(vt); + } + + // Return value types in registers from compiled -> compiled + final MyValue4 test72_vt = MyValue4.create(); + @DontInline + public MyValue4 test72_comp() { + return test72_vt; + } + + MyValue4 test72_vt2; + @Test + public void test72() { + test72_vt2 = test72_comp(); + } + + @DontCompile + public void test72_verifier(boolean warmup) throws Exception { + Method helper_m = getClass().getDeclaredMethod("test72_comp"); + if (!warmup && USE_COMPILER && !WHITE_BOX.isMethodCompiled(helper_m, false)) { + WHITE_BOX.enqueueMethodForCompilation(helper_m, COMP_LEVEL_FULL_OPTIMIZATION); + Asserts.assertTrue(WHITE_BOX.isMethodCompiled(helper_m, false), "test72_comp not compiled"); + } + test72(); + test72_vt.verify(test72_vt2); + } + + // Return values and method handles tests + + // Everything inlined + final MyValue3 test73_vt = MyValue3.create(); + @ForceInline + MyValue3 test73_target() { + return test73_vt; + } + + static final MethodHandle test73_mh; + + @Test(valid = ValueTypeReturnedAsFieldsOn, failOn = ALLOC + STORE + CALL) + @Test(valid = ValueTypeReturnedAsFieldsOff, match = { ALLOC, STORE }, matchCount = { 1, 12 }) + MyValue3 test73() throws Throwable { + return (MyValue3)test73_mh.invokeExact(this); + } + + @DontCompile + public void test73_verifier(boolean warmup) throws Throwable { + MyValue3 vt = test73(); + test73_vt.verify(vt); + } + + // Leaf method not inlined but returned type is known + final MyValue3 test74_vt = MyValue3.create(); + @DontInline + MyValue3 test74_target() { + return test74_vt; + } + + static final MethodHandle test74_mh; + + @Test + MyValue3 test74() throws Throwable { + return (MyValue3)test74_mh.invokeExact(this); + } + + @DontCompile + public void test74_verifier(boolean warmup) throws Throwable { + Method helper_m = getClass().getDeclaredMethod("test74_target"); + if (!warmup && USE_COMPILER && !WHITE_BOX.isMethodCompiled(helper_m, false)) { + WHITE_BOX.enqueueMethodForCompilation(helper_m, COMP_LEVEL_FULL_OPTIMIZATION); + Asserts.assertTrue(WHITE_BOX.isMethodCompiled(helper_m, false), "test74_target not compiled"); + } + MyValue3 vt = test74(); + test74_vt.verify(vt); + } + + // Leaf method not inlined and returned type not known + final MyValue3 test75_vt = MyValue3.create(); + @DontInline + MyValue3 test75_target() { + return test75_vt; + } + + static final MethodHandle test75_mh; + + @Test + MyValue3 test75() throws Throwable { + return (MyValue3)test75_mh.invokeExact(this); + } + + @DontCompile + public void test75_verifier(boolean warmup) throws Throwable { + // hack so C2 doesn't know the target of the invoke call + Class c = Class.forName("java.lang.invoke.DirectMethodHandle"); + Method m = c.getDeclaredMethod("internalMemberName", Object.class); + WHITE_BOX.testSetDontInlineMethod(m, warmup); + MyValue3 vt = test75(); + test75_vt.verify(vt); + } + + static { + try { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodType mt = MethodType.fromMethodDescriptorString("()Qcompiler/valhalla/valuetypes/MyValue3;", ValueTypeTestBench.class.getClassLoader()); + test73_mh = lookup.findVirtual(ValueTypeTestBench.class, "test73_target", mt); + test74_mh = lookup.findVirtual(ValueTypeTestBench.class, "test74_target", mt); + test75_mh = lookup.findVirtual(ValueTypeTestBench.class, "test75_target", mt); + } catch (NoSuchMethodException|IllegalAccessException e) { + throw new RuntimeException("method handle lookup fails"); + } + } + // ========== Test infrastructure ========== private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); @@ -1687,10 +2149,13 @@ private static final int ValueTypeArrayFlattenOff = 0x8; private static final int AlwaysIncrementalInlineOff = 0x10; private static final int AlwaysIncrementalInlineOn = 0x20; - static final int AllFlags = ValueTypePassFieldsAsArgsOn | ValueTypePassFieldsAsArgsOff | ValueTypeArrayFlattenOn | ValueTypeArrayFlattenOff | AlwaysIncrementalInlineOff | AlwaysIncrementalInlineOn; + private static final int ValueTypeReturnedAsFieldsOn = 0x40; + private static final int ValueTypeReturnedAsFieldsOff = 0x80; + static final int AllFlags = ValueTypePassFieldsAsArgsOn | ValueTypePassFieldsAsArgsOff | ValueTypeArrayFlattenOn | ValueTypeArrayFlattenOff | AlwaysIncrementalInlineOff | AlwaysIncrementalInlineOn | ValueTypeReturnedAsFieldsOn; private static final boolean ValueTypePassFieldsAsArgs = (Boolean)WHITE_BOX.getVMFlag("ValueTypePassFieldsAsArgs"); private static final boolean ValueTypeArrayFlatten = (Boolean)WHITE_BOX.getVMFlag("ValueArrayFlatten"); private static final boolean AlwaysIncrementalInline = (Boolean)WHITE_BOX.getVMFlag("AlwaysIncrementalInline"); + private static final boolean ValueTypeReturnedAsFields = (Boolean)WHITE_BOX.getVMFlag("ValueTypeReturnedAsFields"); private static final int COMP_LEVEL_ANY = -1; private static final int COMP_LEVEL_FULL_OPTIMIZATION = 4; private static final Hashtable tests = new Hashtable(); @@ -1715,6 +2180,7 @@ private static final String LINKTOSTATIC = START + "CallStaticJava" + MID + "linkToStatic" + END; private static final String NPE = START + "CallStaticJava" + MID + "null_check" + END; private static final String CCE = START + "CallStaticJava" + MID + "class_check" + END; + private static final String CALL = START + "CallStaticJava" + MID + END; private static final String SCOBJ = "(.*# ScObj.*" + END; static { @@ -1755,11 +2221,13 @@ if (args.length == 0) { execute_vm("-XX:+IgnoreUnrecognizedVMOptions", "-XX:-BackgroundCompilation", "-XX:+PrintCompilation", "-XX:+PrintInlining", "-XX:+PrintIdeal", "-XX:+PrintOptoAssembly", + "-XX:CICompilerCount=1", "-XX:CompileCommand=quiet", "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.ValueTypeTestBench::*", "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue1::*", "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue2::*", "-XX:CompileCommand=compileonly,java.lang.Object::", - "-XX:CompileCommand=inline,java.lang.__Value::hashCode"); + "-XX:CompileCommand=inline,java.lang.__Value::hashCode", + "-XX:CompileCommand=compileonly,java.lang.invoke.*::*"); } else { // Execute tests ValueTypeTestBench bench = new ValueTypeTestBench(); @@ -1768,24 +2236,41 @@ } public static void parseOutput(String output) throws Exception { - String split = "b compiler.valhalla.valuetypes."; - String[] compilations = output.split(split); - // Print header - System.out.println(compilations[0]); - // Iterate over compilation output - for (String graph : compilations) { - String[] lines = graph.split("\\n"); - if (lines[0].contains("@")) { - continue; // Ignore OSR compilations + Pattern comp_re = Pattern.compile("\\n\\s+\\d+\\s+\\d+\\s+(%| )(s| )(!| )b(n| )\\s+\\S+\\.(?[^.]+::\\S+)\\s+(?@ \\d+\\s+)?[(]\\d+ bytes[)]\\n"); + Matcher m = comp_re.matcher(output); + Map compilations = new LinkedHashMap<>(); + int prev = 0; + String methodName = null; + while (m.find()) { + if (prev == 0) { + // Print header + System.out.print(output.substring(0, m.start()+1)); + } else if (methodName != null) { + compilations.put(methodName, output.substring(prev, m.start()+1)); } - String testName = lines[0].split(" ")[0]; + if (m.group("osr") != null) { + methodName = null; + } else { + methodName = m.group("name"); + } + prev = m.end(); + } + if (prev == 0) { + // Print header + System.out.print(output); + } else if (methodName != null) { + compilations.put(methodName, output.substring(prev)); + } + // Iterate over compilation output + for (String testName : compilations.keySet()) { Method test = tests.get(testName); if (test == null) { // Skip helper methods continue; } + String graph = compilations.get(testName); if (PRINT_GRAPH) { - System.out.println("\nGraph for " + graph); + System.out.println("\nGraph for " + testName + "\n" + graph); } // Parse graph using regular expressions to determine if it contains forbidden nodes Test[] annos = test.getAnnotationsByType(Test.class); @@ -1809,6 +2294,12 @@ } else if ((a.valid() & AlwaysIncrementalInlineOn) != 0 && AlwaysIncrementalInline) { assert anno == null; anno = a; + } else if ((a.valid() & ValueTypeReturnedAsFieldsOn) != 0 && ValueTypeReturnedAsFields) { + assert anno == null; + anno = a; + } else if ((a.valid() & ValueTypeReturnedAsFieldsOff) != 0 && !ValueTypeReturnedAsFields) { + assert anno == null; + anno = a; } } assert anno != null;