1 /*
2 * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
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 /*
25 * @test id=serialgc
26 * @bug 8321734 8348961 8332406
27 * @requires vm.gc.Serial
28 * @summary Test that CmpPNode::sub and SubTypeCheckNode::sub correctly identify unrelated classes based on the flat
29 * in array property of the types. Additionally check that the type system properly handles the case of a
30 * super class being flat in array while the sub klass could be flat in array.
31 * @library /test/lib /
32 * @enablePreview
33 * @modules java.base/jdk.internal.value
34 * java.base/jdk.internal.vm.annotation
35 * @run main compiler.valhalla.inlinetypes.TestFlatInArraysFolding serial
36 */
37
38 /*
39 * @test
40 * @bug 8321734 8348961 8332406
41 * @summary Test that CmpPNode::sub and SubTypeCheckNode::sub correctly identify unrelated classes based on the flat
42 * in array property of the types. Additionally check that the type system properly handles the case of a
43 * super class being flat in array while the sub klass could be flat in array.
44 * @library /test/lib /
45 * @enablePreview
46 * @modules java.base/jdk.internal.value
47 * java.base/jdk.internal.vm.annotation
48 * @run main compiler.valhalla.inlinetypes.TestFlatInArraysFolding
49 */
50
51 package compiler.valhalla.inlinetypes;
52
53 import compiler.lib.ir_framework.*;
54
55 import jdk.internal.vm.annotation.LooselyConsistentValue;
56
57 public class TestFlatInArraysFolding {
58 static Object[] oArrArr = new Object[100][100];
59 static Object[] oArr = new Object[100];
60 static Object o = new Object();
61 static Object[] oArrSmall = new Object[2];
62 static Object[] vArr = new V[2];
63 static {
64 oArrSmall[0] = new Object();
65 oArrSmall[1] = new Object();
66 vArr[0] = new V();
67 vArr[1] = new V();
68 }
69 static int limit = 2;
70
71 // Make sure these are loaded such that A has a flat in array and a not flat in array sub class.
72 static FlatInArray flat = new FlatInArray(34);
73 static NotFlatInArray notFlat = new NotFlatInArray(34);
74
75 // Make sure PUnique is the unique concrete sub class loaded from AUnique.
76 static PUnique pUnique = new PUnique(34);
77
78 static int iFld;
79
80 public static void main(String[] args) {
81 // TODO 8350865 Scenarios are equivalent, FlatArrayElementMaxSize does not exist anymore
82 Scenario flatArrayElementMaxSize1Scenario = new Scenario(1, "-XX:-UseArrayFlattening");
83 Scenario flatArrayElementMaxSize4Scenario = new Scenario(2, "-XX:-UseArrayFlattening");
84 Scenario noFlagsScenario = new Scenario(3);
85 TestFramework testFramework = new TestFramework();
86 testFramework.setDefaultWarmup(0)
87 .addFlags("--enable-preview",
88 "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED",
89 "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED")
90 .addScenarios(flatArrayElementMaxSize1Scenario,
91 flatArrayElementMaxSize4Scenario, noFlagsScenario);
92
93 if (args.length > 0) {
94 // Disable Loop Unrolling for IR matching in testCmpP().
95 // Use IgnoreUnrecognizedVMOptions since LoopMaxUnroll is a C2 flag.
96 // testSubTypeCheck() only triggers with SerialGC.
97 Scenario serialGCScenario = new Scenario(4, "-XX:+UseSerialGC", "-XX:+IgnoreUnrecognizedVMOptions",
98 "-XX:LoopMaxUnroll=0", "-XX:+UseArrayFlattening");
99 testFramework.addScenarios(serialGCScenario);
100 }
101 Scenario noMethodTraps = new Scenario(5, "-XX:PerMethodTrapLimit=0", "-Xbatch");
102 testFramework.addScenarios(noMethodTraps);
103 testFramework.start();
104 }
105
106 // SubTypeCheck is not folded while CheckCastPPNode is replaced by top which results in a bad graph (data dies while
107 // control does not).
108 @Test
109 static void testSubTypeCheck() {
110 for (int i = 0; i < 100; i++) {
111 Object arrayElement = oArrArr[i];
112 oArr = (Object[])arrayElement;
113 }
114 }
115
116 @Test
117 @IR(counts = {IRNode.COUNTED_LOOP, "2", // Loop Unswitching done?
118 IRNode.STORE_I, "1"}, // CmpP folded in unswitched loop version with flat in array?
119 applyIf = {"LoopMaxUnroll", "0"})
120 static void testCmpP() {
121 Object[] arr = oArr;
122 if (arr == null) {
123 // Needed for the 'arrayElement == arr' to fold in
124 // the flat array case because both could be null.
125 throw new NullPointerException("arr is null");
126 }
127 for (int i = 0; i < 100; i++) {
128 Object arrayElement = oArrArr[i];
129 if (arrayElement == arr) {
130 iFld = 34;
131 }
132 }
133 }
134
135 // Type system does not correctly handle the case that a super klass is flat in array while the sub klass is
136 // maybe flat in array. This leads to a bad graph.
137 @Test
138 static void testUnswitchingAbstractClass() {
139 Object[] arr = oArr;
140 for (int i = 0; i < 100; i++) {
141 Object arrayElement = arr[i];
142 if (arrayElement instanceof A) {
143 A a = (A)arrayElement;
144 if (a == o) {
145 a.foo();
146 }
147 }
148 }
149 }
150
151 // Same as testUnswitchingAbstractClass() but with interfaces. This worked before because I has type Object(I)
152 // from which Object is a sub type of.
153 @Test
154 static void testUnswitchingInterface() {
155 Object[] arr = oArr;
156 for (int i = 0; i < 100; i++) {
157 Object arrayElement = arr[i];
158 if (arrayElement instanceof I) {
159 I iVar = (I)arrayElement;
160 if (iVar == o) {
161 iVar.bar();
162 }
163 }
164 }
165 }
166
167 // TODO 8350865 FlatArrayElementMaxSize does not exist anymore
168 // PUnique is the unique concrete sub class of AUnique and is not flat in array (with FlatArrayElementMaxSize=4).
169 // The CheckCastPP output of the sub type check uses PUnique while the sub type check itself uses AUnique. This leads
170 // to a bad graph because the type system determines that the flat in array super klass cannot be met with the
171 // not flat in array sub klass. But the sub type check does not fold away because AUnique *could* be flat in array.
172 // Fixed with in JDK-8328480 in mainline but not yet merged in. Applied manually to make this work.
173 @Test
174 static void testSubTypeCheckNotFoldedParsingAbstractClass() {
175 Object[] arr = oArr;
176 for (int i = 0; i < 100; i++) {
177 Object arrayElement = arr[i];
178 if (arrayElement instanceof AUnique) {
179 AUnique aUnique = (AUnique)arrayElement;
180 if (aUnique == o) {
181 aUnique.foo();
182 }
183 }
184 }
185 }
186
187 // Same as testSubTypeCheckNotFoldedParsingAbstractClass() but with interfaces. This worked before because IUnique
188 // has type Object(IUnique) from which Object is a sub type of.
189 @Test
190 static void testSubTypeCheckNotFoldedParsingInterface() {
191 Object[] arr = oArr;
192 for (int i = 0; i < 100; i++) {
193 Object arrayElement = arr[i];
194 if (arrayElement instanceof IUnique) {
195 IUnique iUnique = (IUnique)arrayElement;
196 if (iUnique == o) {
197 iUnique.bar();
198 }
199 }
200 }
201 }
202
203 @Test
204 @Warmup(10000)
205 static void testJoinSameKlassDifferentFlatInArray() {
206 // Accessing the array: It could be a flat array. We therefore add a Phi to select from the normal vs.
207 // flat in array access:
208 // Phi(Object:flat_in_array, Object) -> CheckCastPP[Object:NotNull:exact]
209 for (Object o : oArrSmall) {
210 // We speculate that we always call Object::hashCode() and thus add a CheckCastPP[Object:NotNull:exact]
211 // together with a speculate_class_check trap on the failing path.
212 // We decide to unswitch the loop to get a loop version where we only have flat in array accesses. This
213 // means we can get rid of the Phi. During IGVN and folding some nodes we eventually end up with:
214 // CheckCastPP[Object:NotNull (flat_in_array)] -> CheckCastPP[Object:NotNull:exact]
215 //
216 // We know that we have some kind of Value class that needs to be joined with an exact Object that is not
217 // a value class. Thus, the result in an empty set. But this is missing in the type system. We fail with
218 // an assertion. This is fixed with 8348961.
219 //
220 // Pre-8332406:
221 // To make this type system change work, we require that the TypeInstKlassPtr::not_flat_in_array() takes
222 // exactness information into account to also fold the corresponding control path. This requires another
223 // follow up fix: The super class of a sub type check is always an exact class, i.e. "o instanceof Super".
224 // We need a version of TypeInstKlassPtr::not_flat_in_array() that treats "Super" as inexact. Failing to do
225 // so will erroneously fold a sub type check away (covered by testSubTypeCheckForObjectReceiver()).
226 //
227 // Post-8332406:
228 // We now directly cast the klass pointer in SubTypeCheckNode::sub() to inexact which triggers a
229 // recomputation of the flat in array property. This will turn an exact TypeInstKlassPtr such as Object,
230 // which is not flat in array, into an inexact maybe flat in array TypeInstKlassPtr.
231 o.hashCode();
232 }
233 }
234
235 @Test
236 @Warmup(10000)
237 static void testSubTypeCheckForObjectReceiver() {
238 for (int i = 0; i < limit; i++) {
239 // We perform a sub type check that V is a sub type of Object. This is obviously true
240 vArr[i].hashCode();
241 }
242 }
243
244 interface IUnique {
245 abstract void bar();
246 }
247
248 static abstract value class AUnique implements IUnique {
249 abstract void foo();
250 }
251
252 @LooselyConsistentValue
253 static value class PUnique extends AUnique {
254 int x;
255 int y;
256 PUnique(int x) {
257 this.x = x;
258 this.y = 34;
259 }
260
261 public void foo() {}
262 public void bar() {}
263 }
264
265 interface I {
266 void bar();
267 }
268
269 static abstract value class A implements I {
270 abstract void foo();
271 }
272
273 @LooselyConsistentValue
274 static value class FlatInArray extends A implements I {
275 int x;
276 FlatInArray(int x) {
277 this.x = x;
278 }
279
280 public void foo() {}
281 public void bar() {}
282 }
283
284 // TODO 8350865 FlatArrayElementMaxSize does not exist anymore
285 // Not flat in array with -XX:FlatArrayElementMaxSize=4
286 static value class NotFlatInArray extends A implements I {
287 int x;
288 int y;
289 NotFlatInArray(int x) {
290 this.x = x;
291 this.y = 34;
292 }
293
294 public void foo() {}
295 public void bar() {}
296 }
297
298 static value class V {}
299
300 static final Object OBJ = new Object();
301
302 @Test
303 static Object testEqualMeet1(boolean b, Object[] array, int i) {
304 return b ? array[i] : OBJ;
305 }
306
307 @Run(test = "testEqualMeet1")
308 public void testEqualMeet1_verifier() {
309 testEqualMeet1(true, new Object[1], 0);
310 testEqualMeet1(false, new Object[1], 0);
311 }
312
313 @Test
314 static Object testEqualMeet2(boolean b, Object[] array, int i) {
315 return b ? OBJ : array[i];
316 }
317
318 @Run(test = "testEqualMeet2")
319 public void testEqualMeet2_verifier() {
320 testEqualMeet2(true, new Object[1], 0);
321 testEqualMeet2(false, new Object[1], 0);
322 }
323
324 @Test
325 public static Object test8332406(boolean b, Object[] array, int i) {
326 return b ? array[i] : OBJ;
327 }
328
329 @Run(test = "test8332406")
330 public static void runTest8332406() {
331 test8332406(true, new Object[1], 0);
332 test8332406(false, new Object[1], 0);
333 }
334 }