< prev index next >

test/compiler/valhalla/valuetypes/ValueTypeTestBench.java

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

@@ -27,11 +27,14 @@
  * @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
+ * @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,13 +47,16 @@
 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,11 +89,11 @@
         return x + y + c + v1.hash() + v2.hash();
     }
 
     @DontCompile
     public long hashInterpreted() {
-        return x + y + c + v1.hash() + v2.hash();
+        return x + y + c + v1.hashInterpreted() + v2.hashInterpreted();
     }
 }
 
 __ByValue final class MyValue2 {
     final int x;

@@ -107,10 +113,15 @@
 
     @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,11 +163,12 @@
         long result = test2(v);
         Asserts.assertEQ(result, hash());
     }
 
     // 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;
     }
 
     @DontCompile

@@ -200,11 +212,12 @@
         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)
+    @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,11 +258,12 @@
         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)
+    @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,11 +370,12 @@
         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))
+    @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,11 +385,12 @@
         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})
+    @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,11 +429,12 @@
     }
 
     // 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();
         return v.hashInterpreted();
     }

@@ -429,11 +446,12 @@
     }
 
     // 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);
     }
 

@@ -450,16 +468,17 @@
 
     // 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)
+    @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();
     }
 
     @DontCompile

@@ -498,14 +517,221 @@
         // 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,27 +754,38 @@
     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)) {
+            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) {
-            // Run tests in own process and verify output
-            OutputAnalyzer oa = ProcessTools.executeTestJvm("-noverify",
+            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::*",
-                    ValueTypeTestBench.class.getName(), "run");
+                "-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,11 +818,22 @@
             }
             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);
+            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,17 +913,24 @@
     }
 }
 
 // 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 >