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 * @requires os.simpleArch == "x64"
30 * @build compiler.valhalla.valuetypes.ValueTypeTestBench
31 * @run main ClassFileInstaller sun.hotspot.WhiteBox
32 * @run main ClassFileInstaller jdk.test.lib.Platform
33 * @run main/othervm -ea -noverify -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
34 * -XX:+UnlockExperimentalVMOptions -XX:+ValueTypePassFieldsAsArgs -XX:+ValueArrayFlatten -XX:+VerifyAdapterSharing
35 * -XX:-TieredCompilation compiler.valhalla.valuetypes.ValueTypeTestBench
36 * @run main/othervm -ea -noverify -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
37 * -XX:+UnlockExperimentalVMOptions -XX:-ValueTypePassFieldsAsArgs -XX:-ValueArrayFlatten
38 * -XX:-TieredCompilation compiler.valhalla.valuetypes.ValueTypeTestBench
39 * @run main/othervm -ea -noverify -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
40 * -XX:+UnlockExperimentalVMOptions -XX:+AlwaysIncrementalInline -XX:+ValueArrayFlatten
41 * -XX:-TieredCompilation compiler.valhalla.valuetypes.ValueTypeTestBench
42 */
43
44 package compiler.valhalla.valuetypes;
45
46 import compiler.whitebox.CompilerWhiteBoxTest;
47 import jdk.internal.misc.Unsafe;
48 import jdk.test.lib.Asserts;
49 import jdk.test.lib.Platform;
50 import jdk.test.lib.ProcessTools;
51 import jdk.test.lib.OutputAnalyzer;
52 import jdk.test.lib.Utils;
53 import sun.hotspot.WhiteBox;
54
55 import java.lang.annotation.Retention;
56 import java.lang.annotation.RetentionPolicy;
57 import java.lang.annotation.Repeatable;
58 import java.lang.invoke.*;
59 import java.lang.reflect.Method;
60 import java.util.ArrayList;
61 import java.util.Arrays;
62 import java.util.Hashtable;
63 import java.util.List;
64 import java.util.regex.Matcher;
65 import java.util.regex.Pattern;
66 import jdk.experimental.value.*;
67
68 // Test value types
69 __ByValue final class MyValue1 {
70 static int s;
71 static final long sf = ValueTypeTestBench.rL;
72 final int x;
73 final long y;
74 final short z;
75 final MyValue2 v1;
76 final MyValue2 v2;
77 static final MyValue2 v3 = MyValue2.createInline(ValueTypeTestBench.rI, true);
78 final int c;
79
80 private MyValue1(int x, long y, short z, MyValue2 v1, MyValue2 v2, int c) {
81 s = x;
82 this.x = x;
83 this.y = y;
84 this.z = z;
85 this.v1 = v1;
86 this.v2 = v2;
87 this.c = c;
88 }
89
90 private MyValue1() {
91 s = 0;
92 this.x = 0;
93 this.y = 0;
94 this.z = 0;
95 this.v1 = MyValue2.createDefaultInline();
96 this.v2 = MyValue2.createDefaultInline();
97 this.c = 0;
98 }
99
100 @DontInline
101 public static MyValue1 createDontInline(int x, long y) {
102 return __Make MyValue1(x, y, (short)x, MyValue2.createInline(x, true), MyValue2.createInline(x, false), ValueTypeTestBench.rI);
103 }
104
105 @ForceInline
106 public static MyValue1 createInline(int x, long y) {
107 return __Make MyValue1(x, y, (short)x, MyValue2.createInline(x, true), MyValue2.createInline(x, false), ValueTypeTestBench.rI);
108 }
109
110 @DontInline
111 __ValueFactory static MyValue1 createDefaultDontInline() {
112 return __MakeDefault MyValue1();
113 }
114
115 @ForceInline
116 __ValueFactory static MyValue1 createDefaultInline() {
117 return __MakeDefault MyValue1();
118 }
119
120 @DontInline
121 static MyValue1 createWithFieldsDontInline(int x, long y) {
122 MyValue1 v = createDefaultInline();
123 v = setX(v, x);
124 v = setY(v, y);
125 v = setZ(v, (short)x);
126 v = setV1(v, MyValue2.createWithFieldsInline(x, x < y));
127 v = setV2(v, MyValue2.createWithFieldsInline(x, x > y));
128 v = setC(v, ValueTypeTestBench.rI);
129 return v;
130 }
131
132 @ForceInline
133 static MyValue1 createWithFieldsInline(int x, long y) {
134 MyValue1 v = createDefaultInline();
135 v = setX(v, x);
136 v = setY(v, y);
137 v = setZ(v, (short)x);
138 v = setV1(v, MyValue2.createWithFieldsInline(x, x < y));
139 v = setV2(v, MyValue2.createWithFieldsInline(x, x > y));
140 v = setC(v, ValueTypeTestBench.rI);
141 return v;
142 }
143
144 @ForceInline
145 public long hash() {
146 return s + sf + x + y + z + c + v1.hash() + v2.hash() + v3.hash();
147 }
148
149 @DontCompile
150 public long hashInterpreted() {
151 return s + sf + x + y + z + c + v1.hashInterpreted() + v2.hashInterpreted() + v3.hashInterpreted();
152 }
153
154 @ForceInline
155 public void print() {
156 System.out.print("s=" + s + ", sf=" + sf + ", x=" + x + ", y=" + y + ", z=" + z + ", v1[");
157 v1.print();
158 System.out.print("], v2[");
159 v2.print();
160 System.out.print("], v3[");
161 v3.print();
162 System.out.print("], c=" + c);
163 }
164
165 @ForceInline
166 __ValueFactory static MyValue1 setX(MyValue1 v, int x) {
167 v.x = x;
168 return v;
169 }
170
171 @ForceInline
172 __ValueFactory static MyValue1 setY(MyValue1 v, long y) {
173 v.y = y;
174 return v;
175 }
176
177 @ForceInline
178 __ValueFactory static MyValue1 setZ(MyValue1 v, short z) {
179 v.z = z;
180 return v;
181 }
182
183 @ForceInline
184 __ValueFactory static MyValue1 setC(MyValue1 v, int c) {
185 v.c = c;
186 return v;
187 }
188
189 @ForceInline
190 __ValueFactory static MyValue1 setV1(MyValue1 v, MyValue2 v1) {
191 v.v1 = v1;
192 return v;
193 }
194
195 @ForceInline
196 __ValueFactory static MyValue1 setV2(MyValue1 v, MyValue2 v2) {
197 v.v2 = v2;
198 return v;
199 }
200 }
201
202 __ByValue final class MyValue2 {
203 final int x;
470 Asserts.assertEQ(v.y, hash(rI + 1, rL + 1) + 1);
471 }
472
473 // Merge value types created in a loop (not inlined)
474 @Test(failOn = ALLOC + STORE + TRAP)
475 public long test10(int x, long y) {
476 MyValue1 v = MyValue1.createDontInline(x, y);
477 for (int i = 0; i < 10; ++i) {
478 v = MyValue1.createDontInline(v.x + 1, v.y + 1);
479 }
480 return v.hash();
481 }
482
483 @DontCompile
484 public void test10_verifier(boolean warmup) {
485 long result = test10(rI, rL);
486 Asserts.assertEQ(result, hash(rI + 10, rL + 10));
487 }
488
489 // Merge value types created in a loop (inlined)
490 @Test(failOn = ALLOC + LOAD + STORE + LOOP + TRAP)
491 public long test11(int x, long y) {
492 MyValue1 v = MyValue1.createInline(x, y);
493 for (int i = 0; i < 10; ++i) {
494 v = MyValue1.createInline(v.x + 1, v.y + 1);
495 }
496 return v.hash();
497 }
498
499 @DontCompile
500 public void test11_verifier(boolean warmup) {
501 long result = test11(rI, rL);
502 Asserts.assertEQ(result, hash(rI + 10, rL + 10));
503 }
504
505 // Test loop with uncommon trap referencing a value type
506 @Test(match = {TRAP, SCOBJ}, matchCount = {1, -1 /* at least 1 */}, failOn = LOAD)
507 public long test12(boolean b) {
508 MyValue1 v = MyValue1.createInline(rI, rL);
509 MyValue1[] va = new MyValue1[Math.abs(rI) % 10];
510 for (int i = 0; i < va.length; ++i) {
649
650 @DontCompile
651 public long sumValue(MyValue1 v, MyValue1 dummy) {
652 return v.hash();
653 }
654
655 @DontCompile
656 public void test19_verifier(boolean warmup) {
657 long result = test19();
658 Asserts.assertEQ(result, hash());
659 }
660
661 // Create a value type (array) in compiled code and pass it to the
662 // interpreter via a call. The value type is live at the uncommon
663 // trap: verify that deoptimization causes the value type to be
664 // correctly allocated.
665 @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = LOAD + ALLOC + STORE)
666 @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {2}, failOn = LOAD)
667 public long test20(boolean deopt) {
668 MyValue1 v = MyValue1.createInline(rI, rL);
669 MyValue1[] va = new MyValue1[3];
670 if (deopt) {
671 // uncommon trap
672 WHITE_BOX.deoptimizeMethod(tests.get("ValueTypeTestBench::test20"));
673 }
674 return v.hashInterpreted() + va[0].hashInterpreted() +
675 va[1].hashInterpreted() + va[2].hashInterpreted();
676 }
677
678 @DontCompile
679 public void test20_verifier(boolean warmup) {
680 MyValue1[] va = new MyValue1[42];
681 long result = test20(!warmup);
682 Asserts.assertEQ(result, hash() + va[0].hash() + va[1].hash() + va[2].hash());
683 }
684
685 // Value type fields in regular object
686 MyValue1 val1;
687 MyValue2 val2;
688 final MyValue1 val3;
689 static MyValue1 val4;
690 static final MyValue1 val5 = MyValue1.createInline(rI, rL);
691
692 // Test value type fields in objects
693 @Test(match = {ALLOC}, matchCount = {1}, failOn = (TRAP))
694 public long test21(int x, long y) {
695 // Compute hash of value type fields
696 long result = val1.hash() + val2.hash() + val3.hash() + val4.hash() + val5.hash();
697 // Update fields
698 val1 = MyValue1.createInline(x, y);
699 val2 = MyValue2.createInline(x, true);
700 val4 = MyValue1.createInline(x, y);
1153 }
1154
1155 // Test returning a value type array received from the interpreter
1156 @Test(failOn = ALLOC + ALLOCA + LOAD + LOADP + STORE + LOOP + TRAP)
1157 public MyValue1[] test44(MyValue1[] va) {
1158 return va;
1159 }
1160
1161 @DontCompile
1162 public void test44_verifier(boolean warmup) {
1163 MyValue1[] va = new MyValue1[10];
1164 for (int i = 0; i < 10; ++i) {
1165 va[i] = MyValue1.createDontInline(rI + i, rL + i);
1166 }
1167 va = test44(va);
1168 for (int i = 0; i < 10; ++i) {
1169 Asserts.assertEQ(va[i].hash(), hash(rI + i, rL + i));
1170 }
1171 }
1172
1173 @Test(failOn = (TRAP))
1174 public MyValue1[] test45(boolean b) {
1175 MyValue1[] va;
1176 if (b) {
1177 va = new MyValue1[5];
1178 for (int i = 0; i < 5; ++i) {
1179 va[i] = MyValue1.createInline(rI, rL);
1180 }
1181 } else {
1182 va = new MyValue1[10];
1183 for (int i = 0; i < 10; ++i) {
1184 va[i] = MyValue1.createInline(rI + i, rL + i);
1185 }
1186 }
1187 long sum = va[0].hashInterpreted();
1188 if (b) {
1189 va[0] = MyValue1.createDontInline(rI, sum);
1190 } else {
1191 va[0] = MyValue1.createDontInline(rI + 1, sum + 1);
1192 }
1204 va = test45(false);
1205 Asserts.assertEQ(va.length, 10);
1206 Asserts.assertEQ(va[0].hash(), hash(rI + 1, hash(rI, rL) + 1));
1207 for (int i = 1; i < 10; ++i) {
1208 Asserts.assertEQ(va[i].hash(), hash(rI + i, rL + i));
1209 }
1210 }
1211
1212 // Test creation of value type array with single element
1213 @Test(valid = ValueTypeArrayFlattenOff, failOn = (LOAD + LOOP + TRAP))
1214 @Test(valid = ValueTypeArrayFlattenOn, failOn = (ALLOCA + LOAD + LOOP + TRAP))
1215 public MyValue1 test46() {
1216 MyValue1[] va = new MyValue1[1];
1217 return va[0];
1218 }
1219
1220 @DontCompile
1221 public void test46_verifier(boolean warmup) {
1222 MyValue1[] va = new MyValue1[1];
1223 MyValue1 v = test46();
1224 Asserts.assertEQ(v.hash(), va[0].hash());
1225 }
1226
1227 // Test default initialization of value type arrays
1228 @Test(failOn = LOAD)
1229 public MyValue1[] test47(int len) {
1230 return new MyValue1[len];
1231 }
1232
1233 @DontCompile
1234 public void test47_verifier(boolean warmup) {
1235 int len = Math.abs(rI % 10);
1236 MyValue1[] va = new MyValue1[len];
1237 MyValue1[] var = test47(len);
1238 for (int i = 0; i < len; ++i) {
1239 Asserts.assertEQ(va[i].hash(), var[i].hash());
1240 }
1241 }
1242
1243 // Test creation of value type array with zero length
1244 @Test(failOn = ALLOC + LOAD + STORE + LOOP + TRAP)
1245 public MyValue1[] test48() {
1246 return new MyValue1[0];
1247 }
1248
1249 @DontCompile
1250 public void test48_verifier(boolean warmup) {
1251 MyValue1[] va = test48();
1252 Asserts.assertEQ(va.length, 0);
1253 }
1254
1255 static MyValue1[] test49_va;
1256
1257 // Test that value type array loaded from field has correct type
1258 @Test(failOn = (LOOP))
1259 public long test49() {
1269 }
1270
1271 // test vdefault
1272 @Test(failOn = ALLOC + LOAD + LOADP + STORE + LOOP + TRAP)
1273 public long test50() {
1274 MyValue2 v = MyValue2.createDefaultInline();
1275 return v.hash();
1276 }
1277
1278 @DontCompile
1279 public void test50_verifier(boolean warmup) {
1280 long result = test50();
1281 Asserts.assertEQ(result, MyValue2.createDefaultInline().hash());
1282 }
1283
1284 // test vdefault
1285 @Test(failOn = ALLOC + STORE + LOOP + TRAP)
1286 public long test51() {
1287 MyValue1 v1 = MyValue1.createDefaultInline();
1288 MyValue1 v2 = MyValue1.createDefaultDontInline();
1289 return v1.hash() + v2.hash();
1290 }
1291
1292 @DontCompile
1293 public void test51_verifier(boolean warmup) {
1294 long result = test51();
1295 Asserts.assertEQ(result, 2 * MyValue1.createDefaultInline().hash());
1296 }
1297
1298 // test vwithfield
1299 @Test(failOn = ALLOC + LOAD + LOADP + STORE + LOOP + TRAP)
1300 public long test52() {
1301 MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
1302 return v.hash();
1303 }
1304
1305 @DontCompile
1306 public void test52_verifier(boolean warmup) {
1307 long result = test52();
1308 Asserts.assertEQ(result, MyValue2.createWithFieldsInline(rI, true).hash());
1309 }
1310
1311 // test vwithfield
1312 @Test(failOn = ALLOC + STORE + LOOP + TRAP)
1313 public long test53() {
1314 MyValue1 v1 = MyValue1.createWithFieldsInline(rI, rL);
1315 MyValue1 v2 = MyValue1.createWithFieldsDontInline(rI, rL);
1427 public void test58() {
1428 MyValue1 v1 = MyValue1.createInline(0, 0);
1429 MyValue1 v2 = MyValue1.createInline(1, 1);
1430 // Trigger OSR compilation and loop peeling
1431 for (int i = 0; i < 100_000; ++i) {
1432 if (v1.x != 2*i || v2.x != i+1 || v2.y != i+1) {
1433 // Uncommon trap
1434 throw new RuntimeException("test58 failed");
1435 }
1436 v1 = MyValue1.createInline(2*(i+1), 0);
1437 v2 = MyValue1.createInline(i+2, i+2);
1438 }
1439 }
1440
1441 @DontCompile
1442 public void test58_verifier(boolean warmup) {
1443 test58();
1444 }
1445
1446 // When calling a method on __Value, passing fields as arguments is impossible
1447 @Test(valid = ValueTypePassFieldsAsArgsOn, match = {ALLOC, STORE}, matchCount={1, 0})
1448 @Test(valid = ValueTypePassFieldsAsArgsOff, failOn = STORE + LOAD)
1449 public String test59(MyValue1 v) {
1450 return v.toString();
1451 }
1452
1453 @DontCompile
1454 public void test59_verifier(boolean warmup) {
1455 boolean failed = false;
1456 try {
1457 test59(val1);
1458 failed = true;
1459 } catch (UnsupportedOperationException uoe) {
1460 }
1461 Asserts.assertFalse(failed);
1462 }
1463
1464 // Same as above, but the method on __Value is inlined
1465 // hashCode allocates an exception so can't really check the graph shape
1466 @Test()
1467 public int test60(MyValue1 v) {
1468 return v.hashCode();
1639 MethodType.methodType(long.class, Object.class, boolean.class),
1640 CODE -> {
1641 CODE.
1642 iload_1().
1643 iconst_1().
1644 if_icmpne((short)14).
1645 aload_0().
1646 vunbox(ValueType.forClass(ValueCapableClass1.class).valueClass()).
1647 vbox(ValueCapableClass1.class).
1648 getfield(ValueCapableClass1.class, "t", "J").
1649 lreturn().
1650 aload_0().
1651 vunbox(ValueType.forClass(ValueCapableClass2.class).valueClass()).
1652 vbox(ValueCapableClass1.class).
1653 getfield(ValueCapableClass1.class, "t", "J").
1654 lreturn();
1655 }
1656 );
1657 }
1658
1659 // ========== Test infrastructure ==========
1660
1661 private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
1662 private static final int ValueTypePassFieldsAsArgsOn = 0x1;
1663 private static final int ValueTypePassFieldsAsArgsOff = 0x2;
1664 private static final int ValueTypeArrayFlattenOn = 0x4;
1665 private static final int ValueTypeArrayFlattenOff = 0x8;
1666 private static final int AlwaysIncrementalInlineOff = 0x10;
1667 private static final int AlwaysIncrementalInlineOn = 0x20;
1668 static final int AllFlags = ValueTypePassFieldsAsArgsOn | ValueTypePassFieldsAsArgsOff | ValueTypeArrayFlattenOn | ValueTypeArrayFlattenOff | AlwaysIncrementalInlineOff | AlwaysIncrementalInlineOn;
1669 private static final boolean ValueTypePassFieldsAsArgs = (Boolean)WHITE_BOX.getVMFlag("ValueTypePassFieldsAsArgs");
1670 private static final boolean ValueTypeArrayFlatten = (Boolean)WHITE_BOX.getVMFlag("ValueArrayFlatten");
1671 private static final boolean AlwaysIncrementalInline = (Boolean)WHITE_BOX.getVMFlag("AlwaysIncrementalInline");
1672 private static final int COMP_LEVEL_ANY = -1;
1673 private static final int COMP_LEVEL_FULL_OPTIMIZATION = 4;
1674 private static final Hashtable<String, Method> tests = new Hashtable<String, Method>();
1675 private static final int WARMUP = 251;
1676 private static boolean USE_COMPILER = WHITE_BOX.getBooleanVMFlag("UseCompiler");
1677 private static boolean PRINT_IDEAL = WHITE_BOX.getBooleanVMFlag("PrintIdeal");
1678 // TODO use Platform.isComp() after merge with JDK 9
1679 private static boolean XCOMP = System.getProperty("java.vm.info").startsWith("compiled ");
1680
1681 // Regular expressions used to match nodes in the PrintIdeal output
1682 private static final String START = "(\\d+\\t(.*";
1683 private static final String MID = ".*)+\\t===.*";
1684 private static final String END = ")|";
1685 private static final String ALLOC = START + "CallStaticJava" + MID + "_new_instance_Java" + END;
1686 private static final String ALLOCA = START + "CallStaticJava" + MID + "_new_array_Java" + END;
1687 private static final String LOAD = START + "Load(B|S|I|L|F|D)" + MID + "valuetype\\*" + END;
1688 private static final String LOADP = START + "Load(P|N)" + MID + "valuetype\\*" + END;
1689 private static final String STORE = START + "Store(B|S|I|L|F|D)" + MID + "valuetype\\*" + END;
1690 private static final String STOREP = START + "Store(P|N)" + MID + "valuetype\\*" + END;
1691 private static final String LOOP = START + "Loop" + MID + "" + END;
1692 private static final String TRAP = START + "CallStaticJava" + MID + "uncommon_trap" + END;
1693 private static final String RETURN = START + "Return" + MID + "returns" + END;
1694 private static final String LINKTOSTATIC = START + "CallStaticJava" + MID + "linkToStatic" + END;
1695 private static final String NPE = START + "CallStaticJava" + MID + "null_check" + END;
1696 private static final String CCE = START + "CallStaticJava" + MID + "class_check" + END;
1697 // TODO: match field values of scalar replaced object
1698 private static final String SCOBJ = "(.*# ScObj.*" + END;
1699
1700 static {
1701 // Gather all test methods and put them in Hashtable
1702 for (Method m : ValueTypeTestBench.class.getDeclaredMethods()) {
1703 Test[] annos = m.getAnnotationsByType(Test.class);
1704 if (annos.length != 0) {
1705 tests.put("ValueTypeTestBench::" + m.getName(), m);
1706 }
1707 }
1708 }
1709
1710 private static void execute_vm(String... args) throws Throwable {
1711 Asserts.assertFalse(tests.isEmpty(), "no tests to execute");
1712 ArrayList<String> all_args = new ArrayList(List.of(args));
|
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 * @requires os.simpleArch == "x64"
30 * @build compiler.valhalla.valuetypes.ValueTypeTestBench
31 * @run main ClassFileInstaller sun.hotspot.WhiteBox
32 * @run main ClassFileInstaller jdk.test.lib.Platform
33 * @run main/othervm -ea -noverify -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
34 * -XX:+UnlockExperimentalVMOptions -XX:+ValueTypePassFieldsAsArgs -XX:+ValueArrayFlatten -XX:+VerifyAdapterSharing
35 * -XX:ValueArrayElemMaxFlatSize=-1 -XX:ValueArrayElemMaxFlatOops=-1 -XX:-TieredCompilation
36 * compiler.valhalla.valuetypes.ValueTypeTestBench
37 * @run main/othervm -ea -noverify -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
38 * -XX:+UnlockExperimentalVMOptions -XX:-ValueTypePassFieldsAsArgs -XX:-ValueArrayFlatten
39 * -XX:ValueArrayElemMaxFlatSize=-1 -XX:ValueArrayElemMaxFlatOops=-1 -XX:-TieredCompilation
40 * compiler.valhalla.valuetypes.ValueTypeTestBench
41 * @run main/othervm -ea -noverify -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
42 * -XX:+UnlockExperimentalVMOptions -XX:+AlwaysIncrementalInline -XX:+ValueArrayFlatten
43 * -XX:ValueArrayElemMaxFlatSize=-1 -XX:ValueArrayElemMaxFlatOops=-1 -XX:-TieredCompilation
44 * compiler.valhalla.valuetypes.ValueTypeTestBench
45 */
46
47 package compiler.valhalla.valuetypes;
48
49 import compiler.whitebox.CompilerWhiteBoxTest;
50 import jdk.internal.misc.Unsafe;
51 import jdk.test.lib.Asserts;
52 import jdk.test.lib.Platform;
53 import jdk.test.lib.ProcessTools;
54 import jdk.test.lib.OutputAnalyzer;
55 import jdk.test.lib.Utils;
56 import sun.hotspot.WhiteBox;
57
58 import java.lang.annotation.Retention;
59 import java.lang.annotation.RetentionPolicy;
60 import java.lang.annotation.Repeatable;
61 import java.lang.invoke.*;
62 import java.lang.reflect.Method;
63 import java.util.ArrayList;
64 import java.util.Arrays;
65 import java.util.Hashtable;
66 import java.util.List;
67 import java.util.regex.Matcher;
68 import java.util.regex.Pattern;
69 import jdk.experimental.value.*;
70
71 // Test value types
72 __ByValue final class MyValue1 {
73 static int s;
74 static final long sf = ValueTypeTestBench.rL;
75 final int x;
76 final long y;
77 final short z;
78 final Integer o;
79 final int[] oa;
80 final MyValue2 v1;
81 final MyValue2 v2;
82 static final MyValue2 v3 = MyValue2.createInline(ValueTypeTestBench.rI, true);
83 final int c;
84
85 private MyValue1(int x, long y, short z, Integer o, int[] oa, MyValue2 v1, MyValue2 v2, int c) {
86 s = x;
87 this.x = x;
88 this.y = y;
89 this.z = z;
90 this.o = o;
91 this.oa = oa;
92 this.v1 = v1;
93 this.v2 = v2;
94 this.c = c;
95 }
96
97 private MyValue1() {
98 s = 0;
99 this.x = 0;
100 this.y = 0;
101 this.z = 0;
102 this.o = null;
103 this.oa = null;
104 this.v1 = MyValue2.createDefaultInline();
105 this.v2 = MyValue2.createDefaultInline();
106 this.c = 0;
107 }
108
109 @DontInline
110 public static MyValue1 createDontInline(int x, long y) {
111 int[] oa = {x};
112 return __Make MyValue1(x, y, (short)x, new Integer(x), oa, MyValue2.createInline(x, true), MyValue2.createInline(x, false), ValueTypeTestBench.rI);
113 }
114
115 @ForceInline
116 public static MyValue1 createInline(int x, long y) {
117 int[] oa = {x};
118 return __Make MyValue1(x, y, (short)x, new Integer(x), oa, MyValue2.createInline(x, true), MyValue2.createInline(x, false), ValueTypeTestBench.rI);
119 }
120
121 @DontInline
122 __ValueFactory static MyValue1 createDefaultDontInline() {
123 return __MakeDefault MyValue1();
124 }
125
126 @ForceInline
127 __ValueFactory static MyValue1 createDefaultInline() {
128 return __MakeDefault MyValue1();
129 }
130
131 @DontInline
132 static MyValue1 createWithFieldsDontInline(int x, long y) {
133 MyValue1 v = createDefaultInline();
134 v = setX(v, x);
135 v = setY(v, y);
136 v = setZ(v, (short)x);
137 v = setO(v, new Integer(x));
138 int[] oa = {x};
139 v = setOA(v, oa);
140 v = setV1(v, MyValue2.createWithFieldsInline(x, x < y));
141 v = setV2(v, MyValue2.createWithFieldsInline(x, x > y));
142 v = setC(v, ValueTypeTestBench.rI);
143 return v;
144 }
145
146 @ForceInline
147 static MyValue1 createWithFieldsInline(int x, long y) {
148 MyValue1 v = createDefaultInline();
149 v = setX(v, x);
150 v = setY(v, y);
151 v = setZ(v, (short)x);
152 v = setO(v, new Integer(x));
153 int[] oa = {x};
154 v = setOA(v, oa);
155 v = setV1(v, MyValue2.createWithFieldsInline(x, x < y));
156 v = setV2(v, MyValue2.createWithFieldsInline(x, x > y));
157 v = setC(v, ValueTypeTestBench.rI);
158 return v;
159 }
160
161 // Hash only primitive and value type fields to avoid NullPointerException
162 @ForceInline
163 public long hashPrimitive() {
164 return s + sf + x + y + z + c + v1.hash() + v2.hash() + v3.hash();
165 }
166
167 @ForceInline
168 public long hash() {
169 return hashPrimitive() + o + oa[0];
170 }
171
172 @DontCompile
173 public long hashInterpreted() {
174 return s + sf + x + y + z + o + oa[0] + c + v1.hashInterpreted() + v2.hashInterpreted() + v3.hashInterpreted();
175 }
176
177 @ForceInline
178 public void print() {
179 System.out.print("s=" + s + ", sf=" + sf + ", x=" + x + ", y=" + y + ", z=" + z + ", o=" + (o != null ? (Integer)o : "NULL") + ", v1[");
180 v1.print();
181 System.out.print("], v2[");
182 v2.print();
183 System.out.print("], v3[");
184 v3.print();
185 System.out.print("], c=" + c);
186 }
187
188 @ForceInline
189 __ValueFactory static MyValue1 setX(MyValue1 v, int x) {
190 v.x = x;
191 return v;
192 }
193
194 @ForceInline
195 __ValueFactory static MyValue1 setY(MyValue1 v, long y) {
196 v.y = y;
197 return v;
198 }
199
200 @ForceInline
201 __ValueFactory static MyValue1 setZ(MyValue1 v, short z) {
202 v.z = z;
203 return v;
204 }
205
206 @ForceInline
207 __ValueFactory static MyValue1 setO(MyValue1 v, Integer o) {
208 v.o = o;
209 return v;
210 }
211
212 @ForceInline
213 __ValueFactory static MyValue1 setOA(MyValue1 v, int[] oa) {
214 v.oa = oa;
215 return v;
216 }
217
218 @ForceInline
219 __ValueFactory static MyValue1 setC(MyValue1 v, int c) {
220 v.c = c;
221 return v;
222 }
223
224 @ForceInline
225 __ValueFactory static MyValue1 setV1(MyValue1 v, MyValue2 v1) {
226 v.v1 = v1;
227 return v;
228 }
229
230 @ForceInline
231 __ValueFactory static MyValue1 setV2(MyValue1 v, MyValue2 v2) {
232 v.v2 = v2;
233 return v;
234 }
235 }
236
237 __ByValue final class MyValue2 {
238 final int x;
505 Asserts.assertEQ(v.y, hash(rI + 1, rL + 1) + 1);
506 }
507
508 // Merge value types created in a loop (not inlined)
509 @Test(failOn = ALLOC + STORE + TRAP)
510 public long test10(int x, long y) {
511 MyValue1 v = MyValue1.createDontInline(x, y);
512 for (int i = 0; i < 10; ++i) {
513 v = MyValue1.createDontInline(v.x + 1, v.y + 1);
514 }
515 return v.hash();
516 }
517
518 @DontCompile
519 public void test10_verifier(boolean warmup) {
520 long result = test10(rI, rL);
521 Asserts.assertEQ(result, hash(rI + 10, rL + 10));
522 }
523
524 // Merge value types created in a loop (inlined)
525 @Test(failOn = ALLOC + LOAD + STORE + TRAP)
526 public long test11(int x, long y) {
527 MyValue1 v = MyValue1.createInline(x, y);
528 for (int i = 0; i < 10; ++i) {
529 v = MyValue1.createInline(v.x + 1, v.y + 1);
530 }
531 return v.hash();
532 }
533
534 @DontCompile
535 public void test11_verifier(boolean warmup) {
536 long result = test11(rI, rL);
537 Asserts.assertEQ(result, hash(rI + 10, rL + 10));
538 }
539
540 // Test loop with uncommon trap referencing a value type
541 @Test(match = {TRAP, SCOBJ}, matchCount = {1, -1 /* at least 1 */}, failOn = LOAD)
542 public long test12(boolean b) {
543 MyValue1 v = MyValue1.createInline(rI, rL);
544 MyValue1[] va = new MyValue1[Math.abs(rI) % 10];
545 for (int i = 0; i < va.length; ++i) {
684
685 @DontCompile
686 public long sumValue(MyValue1 v, MyValue1 dummy) {
687 return v.hash();
688 }
689
690 @DontCompile
691 public void test19_verifier(boolean warmup) {
692 long result = test19();
693 Asserts.assertEQ(result, hash());
694 }
695
696 // Create a value type (array) in compiled code and pass it to the
697 // interpreter via a call. The value type is live at the uncommon
698 // trap: verify that deoptimization causes the value type to be
699 // correctly allocated.
700 @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = LOAD + ALLOC + STORE)
701 @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {2}, failOn = LOAD)
702 public long test20(boolean deopt) {
703 MyValue1 v = MyValue1.createInline(rI, rL);
704 MyValue2[] va = new MyValue2[3];
705 if (deopt) {
706 // uncommon trap
707 WHITE_BOX.deoptimizeMethod(tests.get("ValueTypeTestBench::test20"));
708 }
709 return v.hashInterpreted() + va[0].hashInterpreted() +
710 va[1].hashInterpreted() + va[2].hashInterpreted();
711 }
712
713 @DontCompile
714 public void test20_verifier(boolean warmup) {
715 MyValue2[] va = new MyValue2[42];
716 long result = test20(!warmup);
717 Asserts.assertEQ(result, hash() + va[0].hash() + va[1].hash() + va[2].hash());
718 }
719
720 // Value type fields in regular object
721 MyValue1 val1;
722 MyValue2 val2;
723 final MyValue1 val3;
724 static MyValue1 val4;
725 static final MyValue1 val5 = MyValue1.createInline(rI, rL);
726
727 // Test value type fields in objects
728 @Test(match = {ALLOC}, matchCount = {1}, failOn = (TRAP))
729 public long test21(int x, long y) {
730 // Compute hash of value type fields
731 long result = val1.hash() + val2.hash() + val3.hash() + val4.hash() + val5.hash();
732 // Update fields
733 val1 = MyValue1.createInline(x, y);
734 val2 = MyValue2.createInline(x, true);
735 val4 = MyValue1.createInline(x, y);
1188 }
1189
1190 // Test returning a value type array received from the interpreter
1191 @Test(failOn = ALLOC + ALLOCA + LOAD + LOADP + STORE + LOOP + TRAP)
1192 public MyValue1[] test44(MyValue1[] va) {
1193 return va;
1194 }
1195
1196 @DontCompile
1197 public void test44_verifier(boolean warmup) {
1198 MyValue1[] va = new MyValue1[10];
1199 for (int i = 0; i < 10; ++i) {
1200 va[i] = MyValue1.createDontInline(rI + i, rL + i);
1201 }
1202 va = test44(va);
1203 for (int i = 0; i < 10; ++i) {
1204 Asserts.assertEQ(va[i].hash(), hash(rI + i, rL + i));
1205 }
1206 }
1207
1208 // Merge value type arrays created from two branches
1209 @Test(failOn = (TRAP))
1210 public MyValue1[] test45(boolean b) {
1211 MyValue1[] va;
1212 if (b) {
1213 va = new MyValue1[5];
1214 for (int i = 0; i < 5; ++i) {
1215 va[i] = MyValue1.createInline(rI, rL);
1216 }
1217 } else {
1218 va = new MyValue1[10];
1219 for (int i = 0; i < 10; ++i) {
1220 va[i] = MyValue1.createInline(rI + i, rL + i);
1221 }
1222 }
1223 long sum = va[0].hashInterpreted();
1224 if (b) {
1225 va[0] = MyValue1.createDontInline(rI, sum);
1226 } else {
1227 va[0] = MyValue1.createDontInline(rI + 1, sum + 1);
1228 }
1240 va = test45(false);
1241 Asserts.assertEQ(va.length, 10);
1242 Asserts.assertEQ(va[0].hash(), hash(rI + 1, hash(rI, rL) + 1));
1243 for (int i = 1; i < 10; ++i) {
1244 Asserts.assertEQ(va[i].hash(), hash(rI + i, rL + i));
1245 }
1246 }
1247
1248 // Test creation of value type array with single element
1249 @Test(valid = ValueTypeArrayFlattenOff, failOn = (LOAD + LOOP + TRAP))
1250 @Test(valid = ValueTypeArrayFlattenOn, failOn = (ALLOCA + LOAD + LOOP + TRAP))
1251 public MyValue1 test46() {
1252 MyValue1[] va = new MyValue1[1];
1253 return va[0];
1254 }
1255
1256 @DontCompile
1257 public void test46_verifier(boolean warmup) {
1258 MyValue1[] va = new MyValue1[1];
1259 MyValue1 v = test46();
1260 Asserts.assertEQ(v.hashPrimitive(), va[0].hashPrimitive());
1261 }
1262
1263 // Test default initialization of value type arrays
1264 @Test(failOn = LOAD)
1265 public MyValue1[] test47(int len) {
1266 return new MyValue1[len];
1267 }
1268
1269 @DontCompile
1270 public void test47_verifier(boolean warmup) {
1271 int len = Math.abs(rI % 10);
1272 MyValue1[] va = new MyValue1[len];
1273 MyValue1[] var = test47(len);
1274 for (int i = 0; i < len; ++i) {
1275 Asserts.assertEQ(va[i].hashPrimitive(), var[i].hashPrimitive());
1276 }
1277 }
1278
1279 // Test creation of value type array with zero length
1280 @Test(failOn = ALLOC + LOAD + STORE + LOOP + TRAP)
1281 public MyValue1[] test48() {
1282 return new MyValue1[0];
1283 }
1284
1285 @DontCompile
1286 public void test48_verifier(boolean warmup) {
1287 MyValue1[] va = test48();
1288 Asserts.assertEQ(va.length, 0);
1289 }
1290
1291 static MyValue1[] test49_va;
1292
1293 // Test that value type array loaded from field has correct type
1294 @Test(failOn = (LOOP))
1295 public long test49() {
1305 }
1306
1307 // test vdefault
1308 @Test(failOn = ALLOC + LOAD + LOADP + STORE + LOOP + TRAP)
1309 public long test50() {
1310 MyValue2 v = MyValue2.createDefaultInline();
1311 return v.hash();
1312 }
1313
1314 @DontCompile
1315 public void test50_verifier(boolean warmup) {
1316 long result = test50();
1317 Asserts.assertEQ(result, MyValue2.createDefaultInline().hash());
1318 }
1319
1320 // test vdefault
1321 @Test(failOn = ALLOC + STORE + LOOP + TRAP)
1322 public long test51() {
1323 MyValue1 v1 = MyValue1.createDefaultInline();
1324 MyValue1 v2 = MyValue1.createDefaultDontInline();
1325 return v1.hashPrimitive() + v2.hashPrimitive();
1326 }
1327
1328 @DontCompile
1329 public void test51_verifier(boolean warmup) {
1330 long result = test51();
1331 Asserts.assertEQ(result, 2 * MyValue1.createDefaultInline().hashPrimitive());
1332 }
1333
1334 // test vwithfield
1335 @Test(failOn = ALLOC + LOAD + LOADP + STORE + LOOP + TRAP)
1336 public long test52() {
1337 MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
1338 return v.hash();
1339 }
1340
1341 @DontCompile
1342 public void test52_verifier(boolean warmup) {
1343 long result = test52();
1344 Asserts.assertEQ(result, MyValue2.createWithFieldsInline(rI, true).hash());
1345 }
1346
1347 // test vwithfield
1348 @Test(failOn = ALLOC + STORE + LOOP + TRAP)
1349 public long test53() {
1350 MyValue1 v1 = MyValue1.createWithFieldsInline(rI, rL);
1351 MyValue1 v2 = MyValue1.createWithFieldsDontInline(rI, rL);
1463 public void test58() {
1464 MyValue1 v1 = MyValue1.createInline(0, 0);
1465 MyValue1 v2 = MyValue1.createInline(1, 1);
1466 // Trigger OSR compilation and loop peeling
1467 for (int i = 0; i < 100_000; ++i) {
1468 if (v1.x != 2*i || v2.x != i+1 || v2.y != i+1) {
1469 // Uncommon trap
1470 throw new RuntimeException("test58 failed");
1471 }
1472 v1 = MyValue1.createInline(2*(i+1), 0);
1473 v2 = MyValue1.createInline(i+2, i+2);
1474 }
1475 }
1476
1477 @DontCompile
1478 public void test58_verifier(boolean warmup) {
1479 test58();
1480 }
1481
1482 // When calling a method on __Value, passing fields as arguments is impossible
1483 @Test(failOn = ALLOC + STORE + LOAD)
1484 public String test59(MyValue1 v) {
1485 return v.toString();
1486 }
1487
1488 @DontCompile
1489 public void test59_verifier(boolean warmup) {
1490 boolean failed = false;
1491 try {
1492 test59(val1);
1493 failed = true;
1494 } catch (UnsupportedOperationException uoe) {
1495 }
1496 Asserts.assertFalse(failed);
1497 }
1498
1499 // Same as above, but the method on __Value is inlined
1500 // hashCode allocates an exception so can't really check the graph shape
1501 @Test()
1502 public int test60(MyValue1 v) {
1503 return v.hashCode();
1674 MethodType.methodType(long.class, Object.class, boolean.class),
1675 CODE -> {
1676 CODE.
1677 iload_1().
1678 iconst_1().
1679 if_icmpne((short)14).
1680 aload_0().
1681 vunbox(ValueType.forClass(ValueCapableClass1.class).valueClass()).
1682 vbox(ValueCapableClass1.class).
1683 getfield(ValueCapableClass1.class, "t", "J").
1684 lreturn().
1685 aload_0().
1686 vunbox(ValueType.forClass(ValueCapableClass2.class).valueClass()).
1687 vbox(ValueCapableClass1.class).
1688 getfield(ValueCapableClass1.class, "t", "J").
1689 lreturn();
1690 }
1691 );
1692 }
1693
1694 // Test GC with heap allocated value type containing reference field
1695 @Test()
1696 public long test66() {
1697 MyValue1 v = MyValue1.createInline(rI, rL);
1698 long result = 0;
1699 for (int i = 0 ; i < 10_000; ++i) {
1700 // The C2I adapter allocates the value type at every call which
1701 // triggers intermittent GCs that scan the stack frames for oops.
1702 result = v.hashInterpreted();
1703 }
1704 return result;
1705 }
1706
1707 @DontCompile
1708 public void test66_verifier(boolean warmup) {
1709 long result = test66();
1710 Asserts.assertEQ(result, hash());
1711 }
1712
1713 // ========== Test infrastructure ==========
1714
1715 private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
1716 private static final int ValueTypePassFieldsAsArgsOn = 0x1;
1717 private static final int ValueTypePassFieldsAsArgsOff = 0x2;
1718 private static final int ValueTypeArrayFlattenOn = 0x4;
1719 private static final int ValueTypeArrayFlattenOff = 0x8;
1720 private static final int AlwaysIncrementalInlineOff = 0x10;
1721 private static final int AlwaysIncrementalInlineOn = 0x20;
1722 static final int AllFlags = ValueTypePassFieldsAsArgsOn | ValueTypePassFieldsAsArgsOff | ValueTypeArrayFlattenOn | ValueTypeArrayFlattenOff | AlwaysIncrementalInlineOff | AlwaysIncrementalInlineOn;
1723 private static final boolean ValueTypePassFieldsAsArgs = (Boolean)WHITE_BOX.getVMFlag("ValueTypePassFieldsAsArgs");
1724 private static final boolean ValueTypeArrayFlatten = (Boolean)WHITE_BOX.getVMFlag("ValueArrayFlatten");
1725 private static final boolean AlwaysIncrementalInline = (Boolean)WHITE_BOX.getVMFlag("AlwaysIncrementalInline");
1726 private static final int COMP_LEVEL_ANY = -1;
1727 private static final int COMP_LEVEL_FULL_OPTIMIZATION = 4;
1728 private static final Hashtable<String, Method> tests = new Hashtable<String, Method>();
1729 private static final int WARMUP = 251;
1730 private static boolean USE_COMPILER = WHITE_BOX.getBooleanVMFlag("UseCompiler");
1731 private static boolean PRINT_IDEAL = WHITE_BOX.getBooleanVMFlag("PrintIdeal");
1732 // TODO use Platform.isComp() after merge with JDK 9
1733 private static boolean XCOMP = System.getProperty("java.vm.info").startsWith("compiled ");
1734
1735 // Regular expressions used to match nodes in the PrintIdeal output
1736 private static final String START = "(\\d+\\t(.*";
1737 private static final String MID = ".*)+\\t===.*";
1738 private static final String END = ")|";
1739 private static final String ALLOC = "(.*RSI, precise klass compiler/valhalla/valuetypes/MyValue.*" + END;
1740 private static final String ALLOCA = "(.*RSI, precise klass \\[Qcompiler/valhalla/valuetypes/MyValue.*" + END;
1741 private static final String LOAD = START + "Load(B|S|I|L|F|D)" + MID + "valuetype\\*" + END;
1742 private static final String LOADP = START + "Load(P|N)" + MID + "valuetype\\*" + END;
1743 private static final String STORE = START + "Store(B|S|I|L|F|D)" + MID + "valuetype\\*" + END;
1744 private static final String STOREP = START + "Store(P|N)" + MID + "valuetype\\*" + END;
1745 private static final String LOOP = START + "Loop" + MID + "" + END;
1746 private static final String TRAP = START + "CallStaticJava" + MID + "uncommon_trap.*(unstable_if|predicate)" + END;
1747 private static final String RETURN = START + "Return" + MID + "returns" + END;
1748 private static final String LINKTOSTATIC = START + "CallStaticJava" + MID + "linkToStatic" + END;
1749 private static final String NPE = START + "CallStaticJava" + MID + "null_check" + END;
1750 private static final String CCE = START + "CallStaticJava" + MID + "class_check" + END;
1751 // TODO: match field values of scalar replaced object
1752 private static final String SCOBJ = "(.*# ScObj.*" + END;
1753
1754 static {
1755 // Gather all test methods and put them in Hashtable
1756 for (Method m : ValueTypeTestBench.class.getDeclaredMethods()) {
1757 Test[] annos = m.getAnnotationsByType(Test.class);
1758 if (annos.length != 0) {
1759 tests.put("ValueTypeTestBench::" + m.getName(), m);
1760 }
1761 }
1762 }
1763
1764 private static void execute_vm(String... args) throws Throwable {
1765 Asserts.assertFalse(tests.isEmpty(), "no tests to execute");
1766 ArrayList<String> all_args = new ArrayList(List.of(args));
|