1 /*
2 * Copyright (c) 2020, 2026, 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 package compiler.valhalla.inlinetypes;
25
26 import compiler.lib.ir_framework.CompLevel;
27 import compiler.lib.ir_framework.Run;
28 import compiler.lib.ir_framework.Scenario;
29 import compiler.lib.ir_framework.Test;
30
31 import jdk.internal.value.ValueClass;
32 import jdk.internal.vm.annotation.LooselyConsistentValue;
33 import jdk.internal.vm.annotation.NullRestricted;
34
35 import jdk.test.lib.Asserts;
36
37 /*
38 * @test
39 * @key randomness
40 * @summary Verify that chains of getfields on flat fields are correctly optimized.
41 * @library /test/lib /
42 * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64")
43 * @enablePreview
44 * @modules java.base/jdk.internal.value
45 * java.base/jdk.internal.vm.annotation
46 * @compile GetfieldChains.jcod
47 * @run main/timeout=300 compiler.valhalla.inlinetypes.TestGetfieldChains
48 */
49
50 @LooselyConsistentValue
51 value class Point {
52 int x = 4;
53 int y = 7;
54 }
55
56 @LooselyConsistentValue
57 value class Rectangle {
58 @NullRestricted
59 Point p0 = new Point();
60 @NullRestricted
61 Point p1 = new Point();
62 }
63
64 class NamedRectangle {
65 @NullRestricted
66 Rectangle rect;
67 String name = "";
68
69 NamedRectangle() {
70 rect = new Rectangle();
71 super();
72 }
73
74 static int getP1X(NamedRectangle nr) {
75 return nr.rect
76 .p1
77 .x;
78 }
79
80 static Point getP1(NamedRectangle nr) {
81 return nr.rect
82 .p1;
83 }
84 }
85
86 public class TestGetfieldChains {
87
88 public static void main(String[] args) {
89
90 final Scenario[] scenarios = {
91 new Scenario(0,
92 // C1 only
93 "-XX:TieredStopAtLevel=1",
94 "-XX:+TieredCompilation"),
95 new Scenario(1,
96 // C2 only. (Make sure the tests are correctly written)
97 "-XX:TieredStopAtLevel=4",
98 "-XX:-TieredCompilation",
99 "-XX:-OmitStackTraceInFastThrow"),
100 new Scenario(2,
101 // interpreter only
102 "-Xint"),
103 new Scenario(3,
104 // Xcomp Only C1
105 "-XX:TieredStopAtLevel=1",
106 "-XX:+TieredCompilation",
107 "-Xcomp"),
108 new Scenario(4,
109 // Xcomp Only C2
110 "-XX:TieredStopAtLevel=4",
111 "-XX:-TieredCompilation",
112 "-XX:-OmitStackTraceInFastThrow",
113 "-Xcomp")
114 };
115
116 InlineTypes.getFramework()
117 .addScenarios(scenarios)
118 .addFlags("--enable-preview",
119 "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED",
120 "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED")
121 .start();
122 }
123
124
125 // Simple chain of getfields ending with value type field
126 @Test(compLevel = CompLevel.C1_SIMPLE)
127 public int test1() {
128 return NamedRectangle.getP1X(new NamedRectangle());
129 }
130
131 @Run(test = "test1")
132 public void test1_verifier() {
133 int res = test1();
134 Asserts.assertEQ(res, 4);
135 }
136
137 // Simple chain of getfields ending with a flat field
138 @Test(compLevel = CompLevel.C1_SIMPLE)
139 public Point test2() {
140 return NamedRectangle.getP1(new NamedRectangle());
141 }
142
143 @Run(test = "test2")
144 public void test2_verifier() {
145 Point p = test2();
146 Asserts.assertEQ(p.x, 4);
147 Asserts.assertEQ(p.y, 7);
148 }
149
150 // Chain of getfields but the initial receiver is null
151 @Test(compLevel = CompLevel.C1_SIMPLE)
152 public NullPointerException test3() {
153 NullPointerException npe = null;
154 try {
155 NamedRectangle.getP1X(null);
156 throw new RuntimeException("No NullPointerException thrown");
157 } catch (NullPointerException e) {
158 npe = e;
159 }
160 return npe;
161 }
162
163 @Run(test = "test3")
164 public void test3_verifier() {
165 NullPointerException npe = test3();
166 Asserts.assertNE(npe, null);
167 StackTraceElement st = npe.getStackTrace()[0];
168 Asserts.assertEQ(st.getMethodName(), "getP1X");
169 }
170
171 // Chain of getfields but one getfield in the middle of the chain triggers an illegal access
172 @Test(compLevel = CompLevel.C1_SIMPLE)
173 public IllegalAccessError test4() {
174 IllegalAccessError iae = null;
175 try {
176 int i = NamedRectangleP.getP1Y(new NamedRectangleP());
177 throw new RuntimeException("No IllegalAccessError thrown");
178 } catch (IllegalAccessError e) {
179 iae = e;
180 }
181 return iae;
182 }
183
184 @Run(test = "test4")
185 public void test4_verifier() {
186 IllegalAccessError iae = test4();
187 Asserts.assertNE(iae, null);
188 StackTraceElement st = iae.getStackTrace()[0];
189 Asserts.assertEQ(st.getMethodName(), "getP1Y");
190 Asserts.assertTrue(iae.getMessage().contains("class compiler.valhalla.inlinetypes.NamedRectangleP tried to access private field compiler.valhalla.inlinetypes.RectangleP.p1"));
191 }
192
193 // Chain of getfields but the last getfield triggers a NoSuchFieldError
194 @Test(compLevel = CompLevel.C1_SIMPLE)
195 public NoSuchFieldError test5() {
196 NoSuchFieldError nsfe = null;
197 try {
198 int i = NamedRectangleN.getP1X(new NamedRectangleN());
199 throw new RuntimeException("No NoSuchFieldError thrown");
200 } catch (NoSuchFieldError e) {
201 nsfe = e;
202 }
203 return nsfe;
204 }
205
206 @Run(test = "test5")
207 public void test5_verifier() {
208 NoSuchFieldError nsfe = test5();
209 Asserts.assertNE(nsfe, null);
210 StackTraceElement st = nsfe.getStackTrace()[0];
211 Asserts.assertEQ(st.getMethodName(), "getP1X");
212 Asserts.assertEQ(nsfe.getMessage(), "Class compiler.valhalla.inlinetypes.PointN does not have member field 'int x'");
213 }
214
215 @LooselyConsistentValue
216 static value class EmptyType1 { }
217
218 @LooselyConsistentValue
219 static value class EmptyContainer1 {
220 int i = 0;
221 @NullRestricted
222 EmptyType1 et = new EmptyType1();
223 }
224
225 @LooselyConsistentValue
226 static value class Container1 {
227 @NullRestricted
228 EmptyContainer1 container0 = new EmptyContainer1();
229 @NullRestricted
230 EmptyContainer1 container1 = new EmptyContainer1();
231 }
232
233 @Test(compLevel = CompLevel.C1_SIMPLE)
234 public EmptyType1 test6() {
235 Container1 c = new Container1();
236 return c.container1.et;
237 }
238
239 @Run(test = "test6")
240 public void test6_verifier() {
241 EmptyType1 et = test6();
242 Asserts.assertEQ(et, new EmptyType1());
243 }
244
245 @Test(compLevel = CompLevel.C1_SIMPLE)
246 public EmptyType1 test7() {
247 Container1[] ca = (Container1[])ValueClass.newNullRestrictedNonAtomicArray(Container1.class, 10, new Container1());
248 return ca[3].container0.et;
249 }
250
251 @Run(test = "test7")
252 public void test7_verifier() {
253 EmptyType1 et = test7();
254 Asserts.assertEQ(et, new EmptyType1());
255 }
256
257 // Same as test6/test7 but not null-free and EmptyContainer2 with only one field
258
259 static value class EmptyType2 { }
260
261 // TODO 8376254: C1 bailouts if the type of the nullable flat field is uninitialized
262 static final EmptyType2 LOAD_EMPTY_TYPE_2 = new EmptyType2();
263 static final EmptyContainer2 LOAD_EMPTY_CONTAINER_2 = new EmptyContainer2();
264
265 static value class EmptyContainer2 {
266 EmptyType2 et = null;
267 }
268
269 static value class Container2 {
270 EmptyContainer2 container0 = new EmptyContainer2();
271 EmptyContainer2 container1 = new EmptyContainer2();
272 }
273
274 @Test(compLevel = CompLevel.C1_SIMPLE)
275 public EmptyType2 test8() {
276 Container2 c = new Container2();
277 return c.container1.et;
278 }
279
280 @Run(test = "test8")
281 public void test8_verifier() {
282 EmptyType2 et = test8();
283 Asserts.assertEQ(et, null);
284 }
285
286 @Test(compLevel = CompLevel.C1_SIMPLE)
287 public EmptyType2 test9() {
288 Container2[] ca = new Container2[10];
289 ca[3] = new Container2();
290 return ca[3].container0.et;
291 }
292
293 @Run(test = "test9")
294 public void test9_verifier() {
295 EmptyType2 et = test9();
296 Asserts.assertEQ(et, null);
297 }
298 }