1 /*
  2  * Copyright (c) 2024, 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
 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
 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.ImplicitlyConstructible;
 56 import jdk.internal.vm.annotation.LooselyConsistentValue;
 57 
 58 public class TestFlatInArraysFolding {
 59     static Object[] oArrArr = new Object[100][100];
 60     static Object[] oArr = new Object[100];
 61     static Object o = new Object();
 62 
 63     // Make sure these are loaded such that A has a flat in array and a not flat in array sub class.
 64     static FlatInArray flat = new FlatInArray(34);
 65     static NotFlatInArray notFlat = new NotFlatInArray(34);
 66 
 67     // Make sure PUnique is the unique concrete sub class loaded from AUnique.
 68     static PUnique pUnique = new PUnique(34);
 69 
 70     static int iFld;
 71 
 72     public static void main(String[] args) {
 73         Scenario flatArrayElementMaxSize1Scenario = new Scenario(1, "-XX:FlatArrayElementMaxSize=1");
 74         Scenario flatArrayElementMaxSize4Scenario = new Scenario(2, "-XX:FlatArrayElementMaxSize=4");
 75         Scenario noFlagsScenario = new Scenario(3);
 76         TestFramework testFramework = new TestFramework();
 77         testFramework.setDefaultWarmup(0)
 78                 .addFlags("--enable-preview",
 79                           "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED",
 80                           "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED")
 81                 .addScenarios(flatArrayElementMaxSize1Scenario,
 82                               flatArrayElementMaxSize4Scenario, noFlagsScenario);
 83 
 84         if (args.length > 0) {
 85             // Disable Loop Unrolling for IR matching in testCmpP().
 86             // Use IgnoreUnrecognizedVMOptions since LoopMaxUnroll is a C2 flag.
 87             // testSubTypeCheck() only triggers with SerialGC.
 88             Scenario serialGCScenario = new Scenario(4, "-XX:+UseSerialGC", "-XX:+IgnoreUnrecognizedVMOptions",
 89                                                      "-XX:LoopMaxUnroll=0");
 90             testFramework.addScenarios(serialGCScenario);
 91         }
 92         testFramework.start();
 93     }
 94 
 95     // SubTypeCheck is not folded while CheckCastPPNode is replaced by top which results in a bad graph (data dies while
 96     // control does not).
 97     @Test
 98     static void testSubTypeCheck() {
 99         for (int i = 0; i < 100; i++) {
100             Object arrayElement = oArrArr[i];
101             oArr = (Object[])arrayElement;
102         }
103     }
104 
105     @Test
106     @IR(counts = {IRNode.COUNTED_LOOP, "2", // Loop Unswitching done?
107                   IRNode.STORE_I, "1"}, // CmpP folded in unswitched loop version with flat in array?
108         applyIf = {"LoopMaxUnroll", "0"})
109     static void testCmpP() {
110         for (int i = 0; i < 100; i++) {
111             Object arrayElement = oArrArr[i];
112             if (arrayElement == oArr) {
113                 iFld = 34;
114             }
115         }
116     }
117 
118     // Type system does not correctly handle the case that a super klass is flat in array while the sub klass is
119     // maybe flat in array. This leads to a bad graph.
120     @Test
121     static void testUnswitchingAbstractClass() {
122         Object[] arr = oArr;
123         for (int i = 0; i < 100; i++) {
124             Object arrayElement = arr[i];
125             if (arrayElement instanceof A) {
126                 A a = (A)arrayElement;
127                 if (a == o) {
128                     a.foo();
129                 }
130             }
131         }
132     }
133 
134     // Same as testUnswitchingAbstractClass() but with interfaces. This worked before because I has type Object(I)
135     // from which Object is a sub type of.
136     @Test
137     static void testUnswitchingInterface() {
138         Object[] arr = oArr;
139         for (int i = 0; i < 100; i++) {
140             Object arrayElement = arr[i];
141             if (arrayElement instanceof I) {
142                 I iVar = (I)arrayElement;
143                 if (iVar == o) {
144                     iVar.bar();
145                 }
146             }
147         }
148     }
149 
150     // PUnique is the unique concrete sub class of AUnique and is not flat in array (with FlatArrayElementMaxSize=4).
151     // The CheckCastPP output of the sub type check uses PUnique while the sub type check itself uses AUnique. This leads
152     // to a bad graph because the type system determines that the flat in array super klass cannot be met with the
153     // not flat in array sub klass. But the sub type check does not fold away because AUnique *could* be flat in array.
154     // Fixed with in JDK-8328480 in mainline but not yet merged in. Applied manually to make this work.
155     @Test
156     static void testSubTypeCheckNotFoldedParsingAbstractClass() {
157         Object[] arr = oArr;
158         for (int i = 0; i < 100; i++) {
159             Object arrayElement = arr[i];
160             if (arrayElement instanceof AUnique) {
161                 AUnique aUnique = (AUnique)arrayElement;
162                 if (aUnique == o) {
163                     aUnique.foo();
164                 }
165             }
166         }
167     }
168 
169     // Same as testSubTypeCheckNotFoldedParsingAbstractClass() but with interfaces. This worked before because IUnique
170     // has type Object(IUnique) from which Object is a sub type of.
171     @Test
172     static void testSubTypeCheckNotFoldedParsingInterface() {
173         Object[] arr = oArr;
174         for (int i = 0; i < 100; i++) {
175             Object arrayElement = arr[i];
176             if (arrayElement instanceof IUnique) {
177                 IUnique iUnique = (IUnique)arrayElement;
178                 if (iUnique == o) {
179                     iUnique.bar();
180                 }
181             }
182         }
183     }
184 
185     interface IUnique {
186         abstract void bar();
187     }
188 
189     static abstract value class AUnique implements IUnique {
190         abstract void foo();
191     }
192 
193     @ImplicitlyConstructible
194     @LooselyConsistentValue
195     static value class PUnique extends AUnique {
196         int x;
197         int y;
198         PUnique(int x) {
199             this.x = x;
200             this.y = 34;
201         }
202 
203         public void foo() {}
204         public void bar() {}
205     }
206 
207     interface I {
208         void bar();
209     }
210 
211     static abstract value class A implements I {
212         abstract void foo();
213     }
214 
215     @ImplicitlyConstructible
216     @LooselyConsistentValue
217     static value class FlatInArray extends A implements I {
218         int x;
219         FlatInArray(int x) {
220             this.x = x;
221         }
222 
223         public void foo() {}
224         public void bar() {}
225     }
226 
227     // Not flat in array with -XX:FlatArrayElementMaxSize=4
228     static value class NotFlatInArray extends A implements I {
229         int x;
230         int y;
231         NotFlatInArray(int x) {
232             this.x = x;
233             this.y = 34;
234         }
235 
236         public void foo() {}
237         public void bar() {}
238     }
239 }