< 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 >