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
 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
 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");
 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         for (int i = 0; i < 100; i++) {
122             Object arrayElement = oArrArr[i];
123             if (arrayElement == oArr) {
124                 iFld = 34;
125             }
126         }
127     }
128 
129     // Type system does not correctly handle the case that a super klass is flat in array while the sub klass is
130     // maybe flat in array. This leads to a bad graph.
131     @Test
132     static void testUnswitchingAbstractClass() {
133         Object[] arr = oArr;
134         for (int i = 0; i < 100; i++) {
135             Object arrayElement = arr[i];
136             if (arrayElement instanceof A) {
137                 A a = (A)arrayElement;
138                 if (a == o) {
139                     a.foo();
140                 }
141             }
142         }
143     }
144 
145     // Same as testUnswitchingAbstractClass() but with interfaces. This worked before because I has type Object(I)
146     // from which Object is a sub type of.
147     @Test
148     static void testUnswitchingInterface() {
149         Object[] arr = oArr;
150         for (int i = 0; i < 100; i++) {
151             Object arrayElement = arr[i];
152             if (arrayElement instanceof I) {
153                 I iVar = (I)arrayElement;
154                 if (iVar == o) {
155                     iVar.bar();
156                 }
157             }
158         }
159     }
160 
161     // TODO 8350865 FlatArrayElementMaxSize does not exist anymore
162     // PUnique is the unique concrete sub class of AUnique and is not flat in array (with FlatArrayElementMaxSize=4).
163     // The CheckCastPP output of the sub type check uses PUnique while the sub type check itself uses AUnique. This leads
164     // to a bad graph because the type system determines that the flat in array super klass cannot be met with the
165     // not flat in array sub klass. But the sub type check does not fold away because AUnique *could* be flat in array.
166     // Fixed with in JDK-8328480 in mainline but not yet merged in. Applied manually to make this work.
167     @Test
168     static void testSubTypeCheckNotFoldedParsingAbstractClass() {
169         Object[] arr = oArr;
170         for (int i = 0; i < 100; i++) {
171             Object arrayElement = arr[i];
172             if (arrayElement instanceof AUnique) {
173                 AUnique aUnique = (AUnique)arrayElement;
174                 if (aUnique == o) {
175                     aUnique.foo();
176                 }
177             }
178         }
179     }
180 
181     // Same as testSubTypeCheckNotFoldedParsingAbstractClass() but with interfaces. This worked before because IUnique
182     // has type Object(IUnique) from which Object is a sub type of.
183     @Test
184     static void testSubTypeCheckNotFoldedParsingInterface() {
185         Object[] arr = oArr;
186         for (int i = 0; i < 100; i++) {
187             Object arrayElement = arr[i];
188             if (arrayElement instanceof IUnique) {
189                 IUnique iUnique = (IUnique)arrayElement;
190                 if (iUnique == o) {
191                     iUnique.bar();
192                 }
193             }
194         }
195     }
196 
197     @Test
198     @Warmup(10000)
199     static void testJoinSameKlassDifferentFlatInArray() {
200         // Accessing the array: It could be a flat array. We therefore add a Phi to select from the normal vs.
201         // flat in array access:
202         //     Phi(Object:flat_in_array, Object) -> CheckCastPP[Object:NotNull:exact]
203         for (Object o : oArrSmall) {
204             // We speculate that we always call Object::hashCode() and thus add a CheckCastPP[Object:NotNull:exact]
205             // together with a speculate_class_check trap on the failing path.
206             // We decide to unswitch the loop to get a loop version where we only have flat in array accesses. This
207             // means we can get rid of the Phi. During IGVN and folding some nodes we eventually end up with:
208             //    CheckCastPP[Object:NotNull (flat_in_array)] -> CheckCastPP[Object:NotNull:exact]
209             //
210             // We know that we have some kind of Value class that needs to be joined with an exact Object that is not
211             // a value class. Thus, the result in an empty set. But this is missing in the type system. We fail with
212             // an assertion. This is fixed with 8348961.
213             //
214             // To make this type system change work, we require that the TypeInstKlassPtr::not_flat_in_array() takes
215             // exactness information into account to also fold the corresponding control path. This requires another
216             // follow up fix: The super class of a sub type check is always an exact class, i.e. "o instanceof Super".
217             // We need a version of TypeInstKlassPtr::not_flat_in_array() that treats "Super" as inexact. Failing to do
218             // so will erroneously fold a sub type check away (covered by testSubTypeCheckForObjectReceiver()).
219             o.hashCode();
220         }
221     }
222 
223     @Test
224     @Warmup(10000)
225     static void testSubTypeCheckForObjectReceiver() {
226         for (int i = 0; i < limit; i++) {
227             // We perform a sub type check that V is a sub type of Object. This is obviously true
228             vArr[i].hashCode();
229         }
230     }
231 
232     interface IUnique {
233         abstract void bar();
234     }
235 
236     static abstract value class AUnique implements IUnique {
237         abstract void foo();
238     }
239 
240     @LooselyConsistentValue
241     static value class PUnique extends AUnique {
242         int x;
243         int y;
244         PUnique(int x) {
245             this.x = x;
246             this.y = 34;
247         }
248 
249         public void foo() {}
250         public void bar() {}
251     }
252 
253     interface I {
254         void bar();
255     }
256 
257     static abstract value class A implements I {
258         abstract void foo();
259     }
260 
261     @LooselyConsistentValue
262     static value class FlatInArray extends A implements I {
263         int x;
264         FlatInArray(int x) {
265             this.x = x;
266         }
267 
268         public void foo() {}
269         public void bar() {}
270     }
271 
272     // TODO 8350865 FlatArrayElementMaxSize does not exist anymore
273     // Not flat in array with -XX:FlatArrayElementMaxSize=4
274     static value class NotFlatInArray extends A implements I {
275         int x;
276         int y;
277         NotFlatInArray(int x) {
278             this.x = x;
279             this.y = 34;
280         }
281 
282         public void foo() {}
283         public void bar() {}
284     }
285 
286     static value class V {}
287 }