13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24 // TODO add bugid and summary
25
26 /*
27 * @test
28 * @library /testlibrary /test/lib /compiler/whitebox /
29 * @build compiler.valhalla.valuetypes.ValueTypeTestBench
30 * @run main ClassFileInstaller sun.hotspot.WhiteBox
31 * @run main ClassFileInstaller jdk.test.lib.Platform
32 * @run main/othervm -ea -noverify -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
33 * -XX:+UnlockExperimentalVMOptions -XX:+ValueTypePassFieldsAsArgs
34 * -XX:-TieredCompilation compiler.valhalla.valuetypes.ValueTypeTestBench
35 * @run main/othervm -ea -noverify -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
36 * -XX:+UnlockExperimentalVMOptions -XX:-ValueTypePassFieldsAsArgs
37 * -XX:-TieredCompilation compiler.valhalla.valuetypes.ValueTypeTestBench
38 */
39
40 package compiler.valhalla.valuetypes;
41
42 import compiler.whitebox.CompilerWhiteBoxTest;
43 import jdk.internal.misc.Unsafe;
44 import jdk.test.lib.Asserts;
45 import jdk.test.lib.Platform;
46 import jdk.test.lib.ProcessTools;
47 import jdk.test.lib.OutputAnalyzer;
48 import jdk.test.lib.Utils;
49 import sun.hotspot.WhiteBox;
50
51 import java.lang.annotation.Retention;
52 import java.lang.annotation.RetentionPolicy;
53 import java.lang.annotation.Repeatable;
54 import java.lang.invoke.*;
55 import java.lang.reflect.Method;
56 import java.util.ArrayList;
83
84 @DontInline
85 public static MyValue1 createDontInline(int x, long y) {
86 return __Make MyValue1(x, y, MyValue2.createInline(x, true), MyValue2.createInline(x, false), ValueTypeTestBench.rI);
87 }
88
89 @ForceInline
90 public static MyValue1 createInline(int x, long y) {
91 return __Make MyValue1(x, y, MyValue2.createInline(x, true), MyValue2.createInline(x, false), ValueTypeTestBench.rI);
92 }
93
94 @ForceInline
95 public long hash() {
96 return s + sf + x + y + c + v1.hash() + v2.hash() + v3.hash();
97 }
98
99 @DontCompile
100 public long hashInterpreted() {
101 return s + sf + x + y + c + v1.hashInterpreted() + v2.hashInterpreted() + v3.hashInterpreted();
102 }
103 }
104
105 __ByValue final class MyValue2 {
106 final int x;
107 final boolean b;
108 final long c;
109
110 private MyValue2(int x, boolean b, long c) {
111 this.x = x;
112 this.b = b;
113 this.c = c;
114 }
115
116 @ForceInline
117 public static MyValue2 createInline(int x, boolean b) {
118 return __Make MyValue2(x, b, ValueTypeTestBench.rL);
119 }
120
121 @ForceInline
122 public long hash() {
123 return x + (b ? 0 : 1) + c;
124 }
125
126 @DontInline
127 public long hashInterpreted() {
128 return x + (b ? 0 : 1) + c;
129 }
130 }
131
132 public class ValueTypeTestBench {
133 // Print ideal graph after execution of each test
134 private static final boolean PRINT_GRAPH = true;
135
136 // Random test values
137 public static final int rI = Utils.getRandomInstance().nextInt() % 1000;
138 public static final long rL = Utils.getRandomInstance().nextLong() % 1000;
139
140 public ValueTypeTestBench() {
141 val3 = MyValue1.createInline(rI, rL);
142 }
143
144 // MethodHandles and value-capable class instance needed for testing vbox/vunbox
145 private static final MethodHandle vccUnboxLoadLongMH = generateVCCUnboxLoadLongMH();
146 private static final MethodHandle vccUnboxLoadIntMH = generateVCCUnboxLoadIntMH();
147 private static final MethodHandle vccUnboxBoxMH = generateVCCUnboxBoxMH();
148 private static final MethodHandle vccUnboxBoxLoadIntMH = generateVCCUnboxBoxLoadIntMH();
149
266
267 // Merge value types created from two branches
268 @Test(failOn = ALLOC + STORE + TRAP)
269 public long test8(boolean b) {
270 MyValue1 v;
271 if (b) {
272 v = MyValue1.createInline(rI, rL);
273 } else {
274 v = MyValue1.createDontInline(rI + 1, rL + 1);
275 }
276 return v.hash();
277 }
278
279 @DontCompile
280 public void test8_verifier(boolean warmup) {
281 Asserts.assertEQ(test8(true), hash());
282 Asserts.assertEQ(test8(false), hash(rI + 1, rL + 1));
283 }
284
285 // Merge value types created from two branches
286 @Test(valid = ValueTypePassFieldsAsArgsOn, match = {LOAD}, matchCount = {9}, failOn = TRAP + ALLOC + STORE)
287 @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC, STORE}, matchCount = {1, 3}, failOn = LOAD + TRAP)
288 public MyValue1 test9(boolean b) {
289 MyValue1 v;
290 if (b) {
291 // Value type is not allocated
292 v = MyValue1.createInline(rI, rL);
293 } else {
294 // Value type is allocated by the callee
295 v = MyValue1.createDontInline(rI + 1, rL + 1);
296 }
297 // Need to allocate value type if 'b' is true
298 long sum = v.hashInterpreted();
299 if (b) {
300 v = MyValue1.createDontInline(rI, sum);
301 } else {
302 v = MyValue1.createDontInline(rI, sum + 1);
303 }
304 // Don't need to allocate value type because both branches allocate
305 return v;
306 }
378 for (int i = 0; i < 1000; ++i) {
379 if (b) {
380 result += v.x;
381 } else {
382 // Uncommon trap referencing v. Should not allocate
383 // but just pass the existing oop to the uncommon trap.
384 result = v.hashInterpreted();
385 }
386 }
387 return result;
388 }
389
390 @DontCompile
391 public void test13_verifier(boolean warmup) {
392 long result = test13(warmup);
393 Asserts.assertEQ(result, warmup ? 42 + (1000*rI) : hash());
394 }
395
396 // Create a value type in a non-inlined method and then call a
397 // non-inlined method on that value type.
398 @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = (ALLOC + STORE + TRAP), match = {LOAD}, matchCount = {9})
399 @Test(valid = ValueTypePassFieldsAsArgsOff, failOn = (ALLOC + LOAD + STORE + TRAP))
400 public long test14() {
401 MyValue1 v = MyValue1.createDontInline(rI, rL);
402 return v.hashInterpreted();
403 }
404
405 @DontCompile
406 public void test14_verifier(boolean b) {
407 long result = test14();
408 Asserts.assertEQ(result, hash());
409 }
410
411 // Create a value type in an inlined method and then call a
412 // non-inlined method on that value type.
413 @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = (LOAD + TRAP + ALLOC))
414 @Test(valid = ValueTypePassFieldsAsArgsOff, failOn = (LOAD + TRAP), match = {ALLOC}, matchCount = {1})
415 public long test15() {
416 MyValue1 v = MyValue1.createInline(rI, rL);
417 return v.hashInterpreted();
418 }
472 // interpreter via a call. The value type is passed twice but
473 // should only be allocated once.
474 @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = ALLOC + LOAD + TRAP)
475 @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP)
476 public long test19() {
477 MyValue1 v = MyValue1.createInline(rI, rL);
478 return sumValue(v, v);
479 }
480
481 @DontCompile
482 public long sumValue(MyValue1 v, MyValue1 dummy) {
483 return v.hash();
484 }
485
486 @DontCompile
487 public void test19_verifier(boolean warmup) {
488 long result = test19();
489 Asserts.assertEQ(result, hash());
490 }
491
492 // Create a value type in compiled code and pass it to the
493 // interpreter via a call. The value type is live at the uncommon
494 // trap: verify that deoptimization causes the value type to be
495 // correctly allocated.
496 @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = LOAD + ALLOC + STORE)
497 @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD)
498 public long test20(boolean flag) {
499 MyValue1 v = MyValue1.createInline(rI, rL);
500 if (flag) {
501 // uncommon trap
502 WHITE_BOX.deoptimizeMethod(tests.get("ValueTypeTestBench::test20"));
503 }
504 return v.hashInterpreted();
505 }
506
507 @DontCompile
508 public void test20_verifier(boolean warmup) {
509 long result = test20(false);
510 Asserts.assertEQ(result, hash());
511 if (!warmup) {
512 result = test20(true);
513 Asserts.assertEQ(result, hash());
514 }
515 }
516
517 // Value type fields in regular object
518 MyValue1 val1;
519 MyValue2 val2;
520 final MyValue1 val3;
521 static MyValue1 val4;
522 static final MyValue1 val5 = MyValue1.createInline(rI, rL);
523
524 // Test value type fields in objects
525 @Test(match = {ALLOC}, matchCount = {1}, failOn = (TRAP))
526 public long test21(int x, long y) {
527 // Compute hash of value type fields
528 long result = val1.hash() + val2.hash() + val3.hash() + val4.hash() + val5.hash();
529 // Update fields
530 val1 = MyValue1.createInline(x, y);
531 val2 = MyValue2.createInline(x, true);
532 val4 = MyValue1.createInline(x, y);
533 return result;
550 Asserts.assertEQ(val4.hash(), hash(rI + 1, rL + 1));
551 }
552
553 // Test folding of constant value type fields
554 @Test(failOn = ALLOC + LOAD + STORE + LOOP + TRAP)
555 public long test22() {
556 // This should be constant folded
557 return val5.hash() + val5.v3.hash();
558 }
559
560 @DontCompile
561 public void test22_verifier(boolean warmup) {
562 long result = test22();
563 Asserts.assertEQ(result, val5.hash() + val5.v3.hash());
564 }
565
566 // Test OSR compilation
567 @Test(failOn = ALLOC + LOAD + STORE + LOOP + TRAP)
568 public long test23() {
569 MyValue1 v = MyValue1.createInline(rI, rL);
570 long result = 0;
571 // Long loop to trigger OSR compilation
572 for (int i = 0 ; i < 100_000 ; ++i) {
573 // Reference local value type in interpreter state
574 result = v.hash();
575 }
576 return result;
577 }
578
579 @DontCompile
580 public void test23_verifier(boolean warmup) {
581 long result = test23();
582 Asserts.assertEQ(result, hash());
583 }
584
585 // Test interpreter to compiled code with various signatures
586 @Test(failOn = ALLOC + STORE + TRAP)
587 public long test24(MyValue2 v) {
588 return v.hash();
589 }
590
591 @DontCompile
592 public void test24_verifier(boolean warmup) {
593 MyValue2 v = MyValue2.createInline(rI, true);
594 long result = test24(v);
595 Asserts.assertEQ(result, v.hashInterpreted());
596 }
597
598 @Test(failOn = ALLOC + STORE + TRAP)
599 public long test25(int i1, MyValue2 v, int i2) {
600 return v.hash() + i1 - i2;
601 }
602
801 throw new RuntimeException("test 37 failed", t);
802 }
803 }
804
805 // Generate a MethodHandle that obtains field t of the
806 // derived value type
807 private static MethodHandle generateVCCUnboxLoadLongMH() {
808 return MethodHandleBuilder.loadCode(MethodHandles.lookup(),
809 "vccUnboxLoadLong",
810 MethodType.methodType(long.class, ValueCapableClass1.class),
811 CODE -> {
812 CODE.
813 aload_0().
814 vunbox(ValueType.forClass(ValueCapableClass1.class).valueClass()).
815 vgetfield(ValueType.forClass(ValueCapableClass1.class).valueClass(), "t", "J").
816 lreturn();
817 }
818 );
819 }
820
821
822 @Test
823 public int test38() throws Throwable {
824 return (int)vccUnboxLoadIntMH.invokeExact(vcc);
825 }
826
827 @DontCompile
828 public void test38_verifier(boolean warmup) {
829 try {
830 int result = test38();
831 Asserts.assertEQ(vcc.x, result, "Field x of input and result must be equal.");
832 } catch (Throwable t) {
833 throw new RuntimeException("test 38 failed", t);
834 }
835 }
836
837 // Generate a MethodHandle that obtains field x of the
838 // derived value type
839 private static MethodHandle generateVCCUnboxLoadIntMH() {
840 return MethodHandleBuilder.loadCode(MethodHandles.lookup(),
841 "vccUnboxLoadInt",
895 throw new RuntimeException("Test failed in the interpeter", t);
896 }
897 }
898
899 // Generate a MethodHandle that takes a value-capable class,
900 // unboxes it, boxes it, reads a field from it, and returns the
901 // field.
902 private static MethodHandle generateVCCUnboxBoxLoadIntMH() {
903 return MethodHandleBuilder.loadCode(MethodHandles.lookup(),
904 "vccUnboxBoxLoadInt",
905 MethodType.methodType(int.class, ValueCapableClass1.class),
906 CODE -> {
907 CODE.
908 aload_0().
909 vunbox(ValueType.forClass(ValueCapableClass1.class).valueClass()).
910 vbox(ValueCapableClass1.class).
911 getfield(ValueCapableClass1.class, "x", "I").
912 ireturn();
913 }
914 );
915 }
916
917 // ========== Test infrastructure ==========
918
919 private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
920 private static final int ValueTypePassFieldsAsArgsOn = 0x1;
921 private static final int ValueTypePassFieldsAsArgsOff = 0x2;
922 static final int AllFlags = ValueTypePassFieldsAsArgsOn | ValueTypePassFieldsAsArgsOff;
923 private static final boolean ValueTypePassFieldsAsArgs = (Boolean)WHITE_BOX.getVMFlag("ValueTypePassFieldsAsArgs");
924 private static final int COMP_LEVEL_ANY = -1;
925 private static final int COMP_LEVEL_FULL_OPTIMIZATION = 4;
926 private static final Hashtable<String, Method> tests = new Hashtable<String, Method>();
927 private static final int WARMUP = 250;
928 private static boolean USE_COMPILER = WHITE_BOX.getBooleanVMFlag("UseCompiler");
929 private static boolean PRINT_IDEAL = WHITE_BOX.getBooleanVMFlag("PrintIdeal");
930 // TODO use Platform.isComp() after merge with JDK 9
931 private static boolean XCOMP = System.getProperty("java.vm.info").startsWith("compiled ");
932
933 // Regular expressions used to match nodes in the PrintIdeal output
934 private static final String START = "(\\d+\\t(.*";
935 private static final String MID = ".*)+\\t===.*";
936 private static final String END = ")|";
937 private static final String ALLOC = START + "CallStaticJava" + MID + "_new_instance_Java" + END;
938 private static final String LOAD = START + "Load" + MID + "valuetype\\*" + END;
939 private static final String STORE = START + "Store" + MID + "valuetype\\*" + END;
940 private static final String LOOP = START + "Loop" + MID + "" + END;
941 private static final String TRAP = START + "CallStaticJava" + MID + "uncommon_trap" + END;
942 // TODO: match field values of scalar replaced object
943 private static final String SCOBJ = "(.*# ScObj.*" + END;
944
945 static {
946 // Gather all test methods and put them in Hashtable
947 for (Method m : ValueTypeTestBench.class.getDeclaredMethods()) {
948 Test[] annos = m.getAnnotationsByType(Test.class);
949 if (annos.length != 0) {
950 tests.put("ValueTypeTestBench::" + m.getName(), m);
951 }
952 }
953 }
954
955 private static void execute_vm(String... extra_args) throws Throwable {
956 ArrayList<String> all_args = new ArrayList(List.of(
957 "-noverify",
958 "-XX:+UnlockDiagnosticVMOptions", "-Xbootclasspath/a:.", "-XX:+WhiteBoxAPI",
959 "-XX:-TieredCompilation", "-XX:-BackgroundCompilation",
963 "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue2::*"
964 ));
965 all_args.addAll(List.of(extra_args));
966 // Run tests in own process and verify output
967 all_args.add(ValueTypeTestBench.class.getName());
968 all_args.add("run");
969 OutputAnalyzer oa = ProcessTools.executeTestJvm(all_args.toArray(new String[0]));
970 // If ideal graph printing is enabled/supported, verify output
971 String output = oa.getOutput();
972 oa.shouldHaveExitValue(0);
973 if (output.contains("PrintIdeal enabled")) {
974 parseOutput(output);
975 } else {
976 System.out.println("WARNING: IR verification disabled! Running with -Xint, -Xcomp or release build?");
977 }
978 }
979
980 public static void main(String[] args) throws Throwable {
981 if (args.length == 0) {
982 String field_as_args;
983 if ((Boolean)WHITE_BOX.getVMFlag("ValueTypePassFieldsAsArgs")) {
984 field_as_args = "-XX:+ValueTypePassFieldsAsArgs";
985 } else {
986 field_as_args = "-XX:-ValueTypePassFieldsAsArgs";
987 }
988 execute_vm("-XX:+UnlockExperimentalVMOptions", field_as_args);
989 execute_vm("-XX:+AlwaysIncrementalInline", "-XX:+UnlockExperimentalVMOptions", field_as_args);
990 } else {
991 if (USE_COMPILER && PRINT_IDEAL && !XCOMP) {
992 System.out.println("PrintIdeal enabled");
993 }
994 // Execute tests
995 ValueTypeTestBench bench = new ValueTypeTestBench();
996 bench.run();
997 }
998 }
999
1000 public static void parseOutput(String output) throws Exception {
1001 String split = "b compiler.valhalla.valuetypes.";
1002 String[] compilations = output.split(split);
1003 // Print header
1004 System.out.println(compilations[0]);
1005 // Iterate over compilation output
1006 for (String graph : compilations) {
1007 String[] lines = graph.split("\\n");
1008 if (lines[0].contains("@")) {
1009 continue; // Ignore OSR compilations
1010 }
1011 String testName = lines[0].split(" ")[0];
1012 Method test = tests.get(testName);
1013 if (test == null) {
1014 // Skip helper methods
1015 continue;
1016 }
1017 if (PRINT_GRAPH) {
1018 System.out.println("\nGraph for " + graph);
1019 }
1020 // Parse graph using regular expressions to determine if it contains forbidden nodes
1021 Test[] annos = test.getAnnotationsByType(Test.class);
1022 Test anno = null;
1023 for (Test a : annos) {
1024 if ((a.valid() & ValueTypePassFieldsAsArgsOn) != 0 && ValueTypePassFieldsAsArgs) {
1025 assert anno == null;
1026 anno = a;
1027 } else if ((a.valid() & ValueTypePassFieldsAsArgsOff) != 0 && !ValueTypePassFieldsAsArgs) {
1028 assert anno == null;
1029 anno = a;
1030 }
1031 }
1032 assert anno != null;
1033 String regexFail = anno.failOn();
1034 if (!regexFail.isEmpty()) {
1035 Pattern pattern = Pattern.compile(regexFail.substring(0, regexFail.length()-1));
1036 Matcher matcher = pattern.matcher(graph);
1037 boolean fail = false;
1038 while (matcher.find()) {
1039 System.out.println("Graph for '" + testName + "' contains forbidden node:");
1040 System.out.println(matcher.group());
1041 fail = true;
1042 }
1043 Asserts.assertFalse(fail, "Graph for '" + testName + "' contains forbidden nodes");
1044 }
1045 String[] regexMatch = anno.match();
1046 int[] matchCount = anno.matchCount();
1047 for (int i = 0; i < regexMatch.length; ++i) {
1048 Pattern pattern = Pattern.compile(regexMatch[i].substring(0, regexMatch[i].length()-1));
1049 Matcher matcher = pattern.matcher(graph);
1050 int count = 0;
1051 String nodes = "";
1052 while (matcher.find()) {
1053 count++;
1054 nodes += matcher.group() + "\n";
1055 }
1056 if (matchCount[i] != count) {
1057 System.out.println("Graph for '" + testName + "' contains different number of match nodes:");
1058 System.out.println(nodes);
1059 }
1060 Asserts.assertEQ(matchCount[i], count, "Graph for '" + testName + "' contains different number of match nodes");
1061 }
1062 tests.remove(testName);
1063 System.out.println(testName + " passed");
1064 }
1065 // Check if all tests were compiled
1066 if (tests.size() != 0) {
1067 for (String name : tests.keySet()) {
1068 System.out.println("Test '" + name + "' not compiled!");
1069 }
1070 throw new RuntimeException("Not all tests were compiled");
1071 }
1072 }
1073
1074 public void setup(Method[] methods) {
1075 for (Method m : methods) {
1076 if (m.isAnnotationPresent(Test.class)) {
1077 // Don't inline tests
1078 WHITE_BOX.testSetDontInlineMethod(m, true);
1079 }
1080 if (m.isAnnotationPresent(DontCompile.class)) {
|
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24 // TODO add bugid and summary
25
26 /*
27 * @test
28 * @library /testlibrary /test/lib /compiler/whitebox /
29 * @build compiler.valhalla.valuetypes.ValueTypeTestBench
30 * @run main ClassFileInstaller sun.hotspot.WhiteBox
31 * @run main ClassFileInstaller jdk.test.lib.Platform
32 * @run main/othervm -ea -noverify -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
33 * -XX:+UnlockExperimentalVMOptions -XX:+ValueTypePassFieldsAsArgs -XX:+ValueArrayFlatten
34 * -XX:-TieredCompilation compiler.valhalla.valuetypes.ValueTypeTestBench
35 * @run main/othervm -ea -noverify -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
36 * -XX:+UnlockExperimentalVMOptions -XX:-ValueTypePassFieldsAsArgs -XX:-ValueArrayFlatten
37 * -XX:-TieredCompilation compiler.valhalla.valuetypes.ValueTypeTestBench
38 */
39
40 package compiler.valhalla.valuetypes;
41
42 import compiler.whitebox.CompilerWhiteBoxTest;
43 import jdk.internal.misc.Unsafe;
44 import jdk.test.lib.Asserts;
45 import jdk.test.lib.Platform;
46 import jdk.test.lib.ProcessTools;
47 import jdk.test.lib.OutputAnalyzer;
48 import jdk.test.lib.Utils;
49 import sun.hotspot.WhiteBox;
50
51 import java.lang.annotation.Retention;
52 import java.lang.annotation.RetentionPolicy;
53 import java.lang.annotation.Repeatable;
54 import java.lang.invoke.*;
55 import java.lang.reflect.Method;
56 import java.util.ArrayList;
83
84 @DontInline
85 public static MyValue1 createDontInline(int x, long y) {
86 return __Make MyValue1(x, y, MyValue2.createInline(x, true), MyValue2.createInline(x, false), ValueTypeTestBench.rI);
87 }
88
89 @ForceInline
90 public static MyValue1 createInline(int x, long y) {
91 return __Make MyValue1(x, y, MyValue2.createInline(x, true), MyValue2.createInline(x, false), ValueTypeTestBench.rI);
92 }
93
94 @ForceInline
95 public long hash() {
96 return s + sf + x + y + c + v1.hash() + v2.hash() + v3.hash();
97 }
98
99 @DontCompile
100 public long hashInterpreted() {
101 return s + sf + x + y + c + v1.hashInterpreted() + v2.hashInterpreted() + v3.hashInterpreted();
102 }
103
104 @ForceInline
105 public void print() {
106 System.out.print("s=" + s + ", sf=" + sf + ", x=" + x + ", y=" + y + ", v1[");
107 v1.print();
108 System.out.print("], v2[");
109 v2.print();
110 System.out.print("], v3[");
111 v3.print();
112 System.out.print("], c=" + c);
113 }
114 }
115
116 __ByValue final class MyValue2 {
117 final int x;
118 final boolean b;
119 final long c;
120
121 private MyValue2(int x, boolean b, long c) {
122 this.x = x;
123 this.b = b;
124 this.c = c;
125 }
126
127 @ForceInline
128 public static MyValue2 createInline(int x, boolean b) {
129 return __Make MyValue2(x, b, ValueTypeTestBench.rL);
130 }
131
132 @ForceInline
133 public long hash() {
134 return x + (b ? 0 : 1) + c;
135 }
136
137 @DontInline
138 public long hashInterpreted() {
139 return x + (b ? 0 : 1) + c;
140 }
141
142 @ForceInline
143 public void print() {
144 System.out.print("x=" + x + ", b=" + b + ", c=" + c);
145 }
146 }
147
148 public class ValueTypeTestBench {
149 // Print ideal graph after execution of each test
150 private static final boolean PRINT_GRAPH = true;
151
152 // Random test values
153 public static final int rI = Utils.getRandomInstance().nextInt() % 1000;
154 public static final long rL = Utils.getRandomInstance().nextLong() % 1000;
155
156 public ValueTypeTestBench() {
157 val3 = MyValue1.createInline(rI, rL);
158 }
159
160 // MethodHandles and value-capable class instance needed for testing vbox/vunbox
161 private static final MethodHandle vccUnboxLoadLongMH = generateVCCUnboxLoadLongMH();
162 private static final MethodHandle vccUnboxLoadIntMH = generateVCCUnboxLoadIntMH();
163 private static final MethodHandle vccUnboxBoxMH = generateVCCUnboxBoxMH();
164 private static final MethodHandle vccUnboxBoxLoadIntMH = generateVCCUnboxBoxLoadIntMH();
165
282
283 // Merge value types created from two branches
284 @Test(failOn = ALLOC + STORE + TRAP)
285 public long test8(boolean b) {
286 MyValue1 v;
287 if (b) {
288 v = MyValue1.createInline(rI, rL);
289 } else {
290 v = MyValue1.createDontInline(rI + 1, rL + 1);
291 }
292 return v.hash();
293 }
294
295 @DontCompile
296 public void test8_verifier(boolean warmup) {
297 Asserts.assertEQ(test8(true), hash());
298 Asserts.assertEQ(test8(false), hash(rI + 1, rL + 1));
299 }
300
301 // Merge value types created from two branches
302 @Test(valid = ValueTypePassFieldsAsArgsOn, match = {LOAD}, matchCount = {7}, failOn = TRAP + ALLOC + STORE)
303 // FIXME
304 //@Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC, STORE}, matchCount = {1, 9}, failOn = LOAD + TRAP)
305 @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC, STORE}, matchCount = {1, 3}, failOn = LOAD + TRAP)
306 public MyValue1 test9(boolean b) {
307 MyValue1 v;
308 if (b) {
309 // Value type is not allocated
310 v = MyValue1.createInline(rI, rL);
311 } else {
312 // Value type is allocated by the callee
313 v = MyValue1.createDontInline(rI + 1, rL + 1);
314 }
315 // Need to allocate value type if 'b' is true
316 long sum = v.hashInterpreted();
317 if (b) {
318 v = MyValue1.createDontInline(rI, sum);
319 } else {
320 v = MyValue1.createDontInline(rI, sum + 1);
321 }
322 // Don't need to allocate value type because both branches allocate
323 return v;
324 }
396 for (int i = 0; i < 1000; ++i) {
397 if (b) {
398 result += v.x;
399 } else {
400 // Uncommon trap referencing v. Should not allocate
401 // but just pass the existing oop to the uncommon trap.
402 result = v.hashInterpreted();
403 }
404 }
405 return result;
406 }
407
408 @DontCompile
409 public void test13_verifier(boolean warmup) {
410 long result = test13(warmup);
411 Asserts.assertEQ(result, warmup ? 42 + (1000*rI) : hash());
412 }
413
414 // Create a value type in a non-inlined method and then call a
415 // non-inlined method on that value type.
416 @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = (ALLOC + STORE + TRAP), match = {LOAD}, matchCount = {7})
417 @Test(valid = ValueTypePassFieldsAsArgsOff, failOn = (ALLOC + LOAD + STORE + TRAP))
418 public long test14() {
419 MyValue1 v = MyValue1.createDontInline(rI, rL);
420 return v.hashInterpreted();
421 }
422
423 @DontCompile
424 public void test14_verifier(boolean b) {
425 long result = test14();
426 Asserts.assertEQ(result, hash());
427 }
428
429 // Create a value type in an inlined method and then call a
430 // non-inlined method on that value type.
431 @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = (LOAD + TRAP + ALLOC))
432 @Test(valid = ValueTypePassFieldsAsArgsOff, failOn = (LOAD + TRAP), match = {ALLOC}, matchCount = {1})
433 public long test15() {
434 MyValue1 v = MyValue1.createInline(rI, rL);
435 return v.hashInterpreted();
436 }
490 // interpreter via a call. The value type is passed twice but
491 // should only be allocated once.
492 @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = ALLOC + LOAD + TRAP)
493 @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP)
494 public long test19() {
495 MyValue1 v = MyValue1.createInline(rI, rL);
496 return sumValue(v, v);
497 }
498
499 @DontCompile
500 public long sumValue(MyValue1 v, MyValue1 dummy) {
501 return v.hash();
502 }
503
504 @DontCompile
505 public void test19_verifier(boolean warmup) {
506 long result = test19();
507 Asserts.assertEQ(result, hash());
508 }
509
510 // Create a value type (array) in compiled code and pass it to the
511 // interpreter via a call. The value type is live at the uncommon
512 // trap: verify that deoptimization causes the value type to be
513 // correctly allocated.
514 @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = LOAD + ALLOC + STORE)
515 @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD)
516 public long test20(boolean flag) {
517 MyValue1 v = MyValue1.createInline(rI, rL);
518 // TODO add value type array testcase
519 // MyValue1[] va = new MyValue1[42];
520 if (flag) {
521 // uncommon trap
522 WHITE_BOX.deoptimizeMethod(tests.get("ValueTypeTestBench::test20"));
523 }
524 return v.hashInterpreted(); // + va[0].hashInterpreted();
525 }
526
527 @DontCompile
528 public void test20_verifier(boolean warmup) {
529 MyValue1[] va = new MyValue1[42];
530 long result = test20(false);
531 Asserts.assertEQ(result, hash() /* + va[0].hash() */);
532 if (!warmup) {
533 result = test20(true);
534 Asserts.assertEQ(result, hash() /* + va[0].hash() */);
535 }
536 }
537
538 // Value type fields in regular object
539 MyValue1 val1;
540 MyValue2 val2;
541 final MyValue1 val3;
542 static MyValue1 val4;
543 static final MyValue1 val5 = MyValue1.createInline(rI, rL);
544
545 // Test value type fields in objects
546 @Test(match = {ALLOC}, matchCount = {1}, failOn = (TRAP))
547 public long test21(int x, long y) {
548 // Compute hash of value type fields
549 long result = val1.hash() + val2.hash() + val3.hash() + val4.hash() + val5.hash();
550 // Update fields
551 val1 = MyValue1.createInline(x, y);
552 val2 = MyValue2.createInline(x, true);
553 val4 = MyValue1.createInline(x, y);
554 return result;
571 Asserts.assertEQ(val4.hash(), hash(rI + 1, rL + 1));
572 }
573
574 // Test folding of constant value type fields
575 @Test(failOn = ALLOC + LOAD + STORE + LOOP + TRAP)
576 public long test22() {
577 // This should be constant folded
578 return val5.hash() + val5.v3.hash();
579 }
580
581 @DontCompile
582 public void test22_verifier(boolean warmup) {
583 long result = test22();
584 Asserts.assertEQ(result, val5.hash() + val5.v3.hash());
585 }
586
587 // Test OSR compilation
588 @Test(failOn = ALLOC + LOAD + STORE + LOOP + TRAP)
589 public long test23() {
590 MyValue1 v = MyValue1.createInline(rI, rL);
591 // TODO add OSR testcase for value type arrays
592 //MyValue1[] va = new MyValue1[10];
593 long result = 0;
594 // Long loop to trigger OSR compilation
595 for (int i = 0 ; i < 100_000 ; ++i) {
596 // Reference local value type in interpreter state
597 result = v.hash(); // + va[0].hash();
598 }
599 return result;
600 }
601
602 @DontCompile
603 public void test23_verifier(boolean warmup) {
604 //MyValue1[] va = new MyValue1[10];
605 long result = test23();
606 Asserts.assertEQ(result, hash() /* + va[0].hash() */);
607 }
608
609 // Test interpreter to compiled code with various signatures
610 @Test(failOn = ALLOC + STORE + TRAP)
611 public long test24(MyValue2 v) {
612 return v.hash();
613 }
614
615 @DontCompile
616 public void test24_verifier(boolean warmup) {
617 MyValue2 v = MyValue2.createInline(rI, true);
618 long result = test24(v);
619 Asserts.assertEQ(result, v.hashInterpreted());
620 }
621
622 @Test(failOn = ALLOC + STORE + TRAP)
623 public long test25(int i1, MyValue2 v, int i2) {
624 return v.hash() + i1 - i2;
625 }
626
825 throw new RuntimeException("test 37 failed", t);
826 }
827 }
828
829 // Generate a MethodHandle that obtains field t of the
830 // derived value type
831 private static MethodHandle generateVCCUnboxLoadLongMH() {
832 return MethodHandleBuilder.loadCode(MethodHandles.lookup(),
833 "vccUnboxLoadLong",
834 MethodType.methodType(long.class, ValueCapableClass1.class),
835 CODE -> {
836 CODE.
837 aload_0().
838 vunbox(ValueType.forClass(ValueCapableClass1.class).valueClass()).
839 vgetfield(ValueType.forClass(ValueCapableClass1.class).valueClass(), "t", "J").
840 lreturn();
841 }
842 );
843 }
844
845 @Test
846 public int test38() throws Throwable {
847 return (int)vccUnboxLoadIntMH.invokeExact(vcc);
848 }
849
850 @DontCompile
851 public void test38_verifier(boolean warmup) {
852 try {
853 int result = test38();
854 Asserts.assertEQ(vcc.x, result, "Field x of input and result must be equal.");
855 } catch (Throwable t) {
856 throw new RuntimeException("test 38 failed", t);
857 }
858 }
859
860 // Generate a MethodHandle that obtains field x of the
861 // derived value type
862 private static MethodHandle generateVCCUnboxLoadIntMH() {
863 return MethodHandleBuilder.loadCode(MethodHandles.lookup(),
864 "vccUnboxLoadInt",
918 throw new RuntimeException("Test failed in the interpeter", t);
919 }
920 }
921
922 // Generate a MethodHandle that takes a value-capable class,
923 // unboxes it, boxes it, reads a field from it, and returns the
924 // field.
925 private static MethodHandle generateVCCUnboxBoxLoadIntMH() {
926 return MethodHandleBuilder.loadCode(MethodHandles.lookup(),
927 "vccUnboxBoxLoadInt",
928 MethodType.methodType(int.class, ValueCapableClass1.class),
929 CODE -> {
930 CODE.
931 aload_0().
932 vunbox(ValueType.forClass(ValueCapableClass1.class).valueClass()).
933 vbox(ValueCapableClass1.class).
934 getfield(ValueCapableClass1.class, "x", "I").
935 ireturn();
936 }
937 );
938
939 }
940
941 // Test value type array creation and initialization
942 @Test(valid = ValueTypeArrayFlattenOff, failOn = (LOAD))
943 @Test(valid = ValueTypeArrayFlattenOn)
944 public MyValue1[] test41(int len) {
945 MyValue1[] va = new MyValue1[len];
946 for (int i = 0; i < len; ++i) {
947 va[i] = MyValue1.createDontInline(rI, rL);
948 }
949 return va;
950 }
951
952 @DontCompile
953 public void test41_verifier(boolean warmup) {
954 int len = Math.abs(rI % 10);
955 MyValue1[] va = test41(len);
956 for (int i = 0; i < len; ++i) {
957 Asserts.assertEQ(va[i].hash(), hash());
958 }
959 }
960
961 // Test creation of a value type array and element access
962 @Test(failOn = (LOOP + LOAD + TRAP))
963 public long test42() {
964 MyValue1[] va = new MyValue1[1];
965 va[0] = MyValue1.createInline(rI, rL);
966 return va[0].hash();
967 }
968
969 @DontCompile
970 public void test42_verifier(boolean warmup) {
971 long result = test42();
972 Asserts.assertEQ(result, hash());
973 }
974
975 // Test receiving a value type array from the interpreter,
976 // updating its elements in a loop and computing a hash.
977 @Test(failOn = (ALLOCA))
978 public long test43(MyValue1[] va) {
979 long result = 0;
980 for (int i = 0; i < 10; ++i) {
981 result += va[i].hash();
982 va[i] = MyValue1.createInline(rI + 1, rL + 1);
983 }
984 return result;
985 }
986
987 @DontCompile
988 public void test43_verifier(boolean warmup) {
989 MyValue1[] va = new MyValue1[10];
990 long expected = 0;
991 for (int i = 0; i < 10; ++i) {
992 va[i] = MyValue1.createDontInline(rI + i, rL + i);
993 expected += va[i].hash();
994 }
995 long result = test43(va);
996 Asserts.assertEQ(expected, result);
997 for (int i = 0; i < 10; ++i) {
998 if (va[i].hash() != hash(rI + 1, rL + 1)) {
999 Asserts.assertEQ(va[i].hash(), hash(rI + 1, rL + 1));
1000 }
1001 }
1002 }
1003
1004 // Test returning a value type array received from the interpreter
1005 @Test(failOn = ALLOC + ALLOCA + LOAD + LOADP + STORE + LOOP + TRAP)
1006 public MyValue1[] test44(MyValue1[] va) {
1007 return va;
1008 }
1009
1010 @DontCompile
1011 public void test44_verifier(boolean warmup) {
1012 MyValue1[] va = new MyValue1[10];
1013 for (int i = 0; i < 10; ++i) {
1014 va[i] = MyValue1.createDontInline(rI + i, rL + i);
1015 }
1016 va = test44(va);
1017 for (int i = 0; i < 10; ++i) {
1018 Asserts.assertEQ(va[i].hash(), hash(rI + i, rL + i));
1019 }
1020 }
1021
1022 // TODO add match rules
1023 @Test()
1024 public MyValue1[] test45(boolean b) {
1025 MyValue1[] va;
1026 if (b) {
1027 va = new MyValue1[5];
1028 for (int i = 0; i < 5; ++i) {
1029 va[i] = MyValue1.createInline(rI, rL);
1030 }
1031 } else {
1032 va = new MyValue1[10];
1033 for (int i = 0; i < 10; ++i) {
1034 va[i] = MyValue1.createInline(rI + i, rL + i);
1035 }
1036 }
1037 long sum = va[0].hashInterpreted();
1038 if (b) {
1039 va[0] = MyValue1.createDontInline(rI, sum);
1040 } else {
1041 va[0] = MyValue1.createDontInline(rI + 1, sum + 1);
1042 }
1043 return va;
1044 }
1045
1046 @DontCompile
1047 public void test45_verifier(boolean warmup) {
1048 MyValue1[] va = test45(true);
1049 Asserts.assertEQ(va.length, 5);
1050 Asserts.assertEQ(va[0].hash(), hash(rI, hash()));
1051 for (int i = 1; i < 5; ++i) {
1052 Asserts.assertEQ(va[i].hash(), hash());
1053 }
1054 va = test45(false);
1055 Asserts.assertEQ(va.length, 10);
1056 Asserts.assertEQ(va[0].hash(), hash(rI + 1, hash(rI, rL) + 1));
1057 for (int i = 1; i < 10; ++i) {
1058 Asserts.assertEQ(va[i].hash(), hash(rI + i, rL + i));
1059 }
1060 }
1061
1062 // Test creation of value type array with single element
1063 @Test(failOn = LOOP + TRAP)
1064 public MyValue1 test46() {
1065 MyValue1[] va = new MyValue1[1];
1066 return va[0];
1067 }
1068
1069 @DontCompile
1070 public void test46_verifier(boolean warmup) {
1071 MyValue1[] va = new MyValue1[1];
1072 MyValue1 v = test46();
1073 Asserts.assertEQ(v.hash(), va[0].hash());
1074 }
1075
1076 // Test default initialization of value type arrays
1077 @Test(failOn = LOAD)
1078 public MyValue1[] test47(int len) {
1079 return new MyValue1[len];
1080 }
1081
1082 @DontCompile
1083 public void test47_verifier(boolean warmup) {
1084 int len = Math.abs(rI % 10);
1085 MyValue1[] va = new MyValue1[len];
1086 MyValue1[] var = test47(len);
1087 for (int i = 0; i < len; ++i) {
1088 Asserts.assertEQ(va[i].hash(), var[i].hash());
1089 }
1090 }
1091
1092 // Test creation of value type array with zero length
1093 @Test(failOn = ALLOC + LOAD + STORE + LOOP + TRAP)
1094 public MyValue1[] test48() {
1095 return new MyValue1[0];
1096 }
1097
1098 @DontCompile
1099 public void test48_verifier(boolean warmup) {
1100 MyValue1[] va = test48();
1101 Asserts.assertEQ(va.length, 0);
1102 }
1103
1104 // ========== Test infrastructure ==========
1105
1106 private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
1107 private static final int ValueTypePassFieldsAsArgsOn = 0x1;
1108 private static final int ValueTypePassFieldsAsArgsOff = 0x2;
1109 private static final int ValueTypeArrayFlattenOn = 0x4;
1110 private static final int ValueTypeArrayFlattenOff = 0x8;
1111 static final int AllFlags = ValueTypePassFieldsAsArgsOn | ValueTypePassFieldsAsArgsOff | ValueTypeArrayFlattenOn | ValueTypeArrayFlattenOff;
1112 private static final boolean ValueTypePassFieldsAsArgs = (Boolean)WHITE_BOX.getVMFlag("ValueTypePassFieldsAsArgs");
1113 private static final boolean ValueTypeArrayFlatten = (Boolean)WHITE_BOX.getVMFlag("ValueArrayFlatten");
1114 private static final int COMP_LEVEL_ANY = -1;
1115 private static final int COMP_LEVEL_FULL_OPTIMIZATION = 4;
1116 private static final Hashtable<String, Method> tests = new Hashtable<String, Method>();
1117 private static final int WARMUP = 250;
1118 private static boolean USE_COMPILER = WHITE_BOX.getBooleanVMFlag("UseCompiler");
1119 private static boolean PRINT_IDEAL = WHITE_BOX.getBooleanVMFlag("PrintIdeal");
1120 // TODO use Platform.isComp() after merge with JDK 9
1121 private static boolean XCOMP = System.getProperty("java.vm.info").startsWith("compiled ");
1122
1123 // Regular expressions used to match nodes in the PrintIdeal output
1124 private static final String START = "(\\d+\\t(.*";
1125 private static final String MID = ".*)+\\t===.*";
1126 private static final String END = ")|";
1127 private static final String ALLOC = START + "CallStaticJava" + MID + "_new_instance_Java" + END;
1128 private static final String ALLOCA = START + "CallStaticJava" + MID + "_new_array_Java" + END;
1129 private static final String LOAD = START + "Load(B|S|I|L|F|D)" + MID + "valuetype\\*" + END;
1130 private static final String LOADP = START + "Load(P|N)" + MID + "valuetype\\*" + END;
1131 private static final String STORE = START + "Store(B|S|I|L|F|D)" + MID + "valuetype\\*" + END;
1132 private static final String STOREP = START + "Store(P|N)" + MID + "valuetype\\*" + END;
1133 private static final String LOOP = START + "Loop" + MID + "" + END;
1134 private static final String TRAP = START + "CallStaticJava" + MID + "uncommon_trap" + END;
1135 // TODO: match field values of scalar replaced object
1136 private static final String SCOBJ = "(.*# ScObj.*" + END;
1137
1138 static {
1139 // Gather all test methods and put them in Hashtable
1140 for (Method m : ValueTypeTestBench.class.getDeclaredMethods()) {
1141 Test[] annos = m.getAnnotationsByType(Test.class);
1142 if (annos.length != 0) {
1143 tests.put("ValueTypeTestBench::" + m.getName(), m);
1144 }
1145 }
1146 }
1147
1148 private static void execute_vm(String... extra_args) throws Throwable {
1149 ArrayList<String> all_args = new ArrayList(List.of(
1150 "-noverify",
1151 "-XX:+UnlockDiagnosticVMOptions", "-Xbootclasspath/a:.", "-XX:+WhiteBoxAPI",
1152 "-XX:-TieredCompilation", "-XX:-BackgroundCompilation",
1156 "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue2::*"
1157 ));
1158 all_args.addAll(List.of(extra_args));
1159 // Run tests in own process and verify output
1160 all_args.add(ValueTypeTestBench.class.getName());
1161 all_args.add("run");
1162 OutputAnalyzer oa = ProcessTools.executeTestJvm(all_args.toArray(new String[0]));
1163 // If ideal graph printing is enabled/supported, verify output
1164 String output = oa.getOutput();
1165 oa.shouldHaveExitValue(0);
1166 if (output.contains("PrintIdeal enabled")) {
1167 parseOutput(output);
1168 } else {
1169 System.out.println("WARNING: IR verification disabled! Running with -Xint, -Xcomp or release build?");
1170 }
1171 }
1172
1173 public static void main(String[] args) throws Throwable {
1174 if (args.length == 0) {
1175 String field_as_args;
1176 String array_flatten;
1177 if (ValueTypePassFieldsAsArgs) {
1178 field_as_args = "-XX:+ValueTypePassFieldsAsArgs";
1179 } else {
1180 field_as_args = "-XX:-ValueTypePassFieldsAsArgs";
1181 }
1182 if (ValueTypeArrayFlatten) {
1183 array_flatten = "-XX:+ValueArrayFlatten";
1184 } else {
1185 array_flatten = "-XX:-ValueArrayFlatten";
1186 }
1187 execute_vm("-XX:+UnlockExperimentalVMOptions", field_as_args, array_flatten);
1188 execute_vm("-XX:+AlwaysIncrementalInline", "-XX:+UnlockExperimentalVMOptions", field_as_args, array_flatten);
1189 } else {
1190 if (USE_COMPILER && PRINT_IDEAL && !XCOMP) {
1191 System.out.println("PrintIdeal enabled");
1192 }
1193 // Execute tests
1194 ValueTypeTestBench bench = new ValueTypeTestBench();
1195 bench.run();
1196 }
1197 }
1198
1199 public static void parseOutput(String output) throws Exception {
1200 String split = "b compiler.valhalla.valuetypes.";
1201 String[] compilations = output.split(split);
1202 // Print header
1203 System.out.println(compilations[0]);
1204 // Iterate over compilation output
1205 for (String graph : compilations) {
1206 String[] lines = graph.split("\\n");
1207 if (lines[0].contains("@")) {
1208 continue; // Ignore OSR compilations
1209 }
1210 String testName = lines[0].split(" ")[0];
1211 Method test = tests.get(testName);
1212 if (test == null) {
1213 // Skip helper methods
1214 continue;
1215 }
1216 if (PRINT_GRAPH) {
1217 System.out.println("\nGraph for " + graph);
1218 }
1219 // Parse graph using regular expressions to determine if it contains forbidden nodes
1220 Test[] annos = test.getAnnotationsByType(Test.class);
1221 Test anno = null;
1222 for (Test a : annos) {
1223 if ((a.valid() & ValueTypePassFieldsAsArgsOn) != 0 && ValueTypePassFieldsAsArgs) {
1224 assert anno == null;
1225 anno = a;
1226 } else if ((a.valid() & ValueTypePassFieldsAsArgsOff) != 0 && !ValueTypePassFieldsAsArgs) {
1227 assert anno == null;
1228 anno = a;
1229 } else if ((a.valid() & ValueTypeArrayFlattenOn) != 0 && ValueTypeArrayFlatten) {
1230 assert anno == null;
1231 anno = a;
1232 } else if ((a.valid() & ValueTypeArrayFlattenOff) != 0 && !ValueTypeArrayFlatten) {
1233 assert anno == null;
1234 anno = a;
1235 }
1236 }
1237 assert anno != null;
1238 String regexFail = anno.failOn();
1239 if (!regexFail.isEmpty()) {
1240 Pattern pattern = Pattern.compile(regexFail.substring(0, regexFail.length()-1));
1241 Matcher matcher = pattern.matcher(graph);
1242 boolean found = matcher.find();
1243 Asserts.assertFalse(found, "Graph for '" + testName + "' contains forbidden node:\n" + (found ? matcher.group() : ""));
1244 }
1245 String[] regexMatch = anno.match();
1246 int[] matchCount = anno.matchCount();
1247 for (int i = 0; i < regexMatch.length; ++i) {
1248 Pattern pattern = Pattern.compile(regexMatch[i].substring(0, regexMatch[i].length()-1));
1249 Matcher matcher = pattern.matcher(graph);
1250 int count = 0;
1251 String nodes = "";
1252 while (matcher.find()) {
1253 count++;
1254 nodes += matcher.group() + "\n";
1255 }
1256 Asserts.assertEQ(matchCount[i], count, "Graph for '" + testName + "' contains different number of match nodes:\n" + nodes);
1257 }
1258 tests.remove(testName);
1259 System.out.println(testName + " passed");
1260 }
1261 // Check if all tests were compiled
1262 if (tests.size() != 0) {
1263 for (String name : tests.keySet()) {
1264 System.out.println("Test '" + name + "' not compiled!");
1265 }
1266 throw new RuntimeException("Not all tests were compiled");
1267 }
1268 }
1269
1270 public void setup(Method[] methods) {
1271 for (Method m : methods) {
1272 if (m.isAnnotationPresent(Test.class)) {
1273 // Don't inline tests
1274 WHITE_BOX.testSetDontInlineMethod(m, true);
1275 }
1276 if (m.isAnnotationPresent(DontCompile.class)) {
|