1 /*
2 * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3 * Copyright (c) 2025, Red Hat, Inc. All rights reserved.
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This code is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 only, as
8 * published by the Free Software Foundation.
9 *
10 * This code is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * version 2 for more details (a copy is included in the LICENSE file that
14 * accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License version
17 * 2 along with this work; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21 * or visit www.oracle.com if you need additional information or have any
22 * questions.
23 */
24
25 import jdk.internal.misc.Unsafe;
26 import jdk.test.whitebox.WhiteBox;
27
28 import java.lang.reflect.Field;
29 import java.util.ArrayList;
30 import java.util.List;
31
32 /*
33 * @test id=no_coops_no_ccptr_no_coh
34 * @library /test/lib
35 * @modules java.base/jdk.internal.misc
36 * java.management
37 * @build jdk.test.whitebox.WhiteBox
38 * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
39 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UnlockExperimentalVMOptions -XX:-UseCompressedOops -XX:-UseCompressedClassPointers -XX:-UseCompactObjectHeaders TestOopMapSizeMinimal
40 */
41
42 /*
43 * @test id=coops_no_ccptr_no_coh
44 * @library /test/lib
45 * @modules java.base/jdk.internal.misc
46 * java.management
47 * @build jdk.test.whitebox.WhiteBox
48 * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
49 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UnlockExperimentalVMOptions -XX:+UseCompressedOops -XX:-UseCompressedClassPointers -XX:-UseCompactObjectHeaders TestOopMapSizeMinimal
50 */
51
52 /*
53 * @test id=no_coops_ccptr_no_coh
54 * @library /test/lib
55 * @modules java.base/jdk.internal.misc
56 * java.management
57 * @build jdk.test.whitebox.WhiteBox
58 * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
59 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UnlockExperimentalVMOptions -XX:-UseCompressedOops -XX:+UseCompressedClassPointers -XX:-UseCompactObjectHeaders TestOopMapSizeMinimal
60 */
61
62 /*
63 * @test id=coops_ccptr_no_coh
64 * @library /test/lib
65 * @modules java.base/jdk.internal.misc
66 * java.management
67 * @build jdk.test.whitebox.WhiteBox
68 * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
69 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UnlockExperimentalVMOptions -XX:+UseCompressedOops -XX:+UseCompressedClassPointers -XX:-UseCompactObjectHeaders TestOopMapSizeMinimal
70 */
71
72 /*
73 * @test id=no_coops_ccptr_coh
74 * @library /test/lib
75 * @modules java.base/jdk.internal.misc
76 * java.management
77 * @build jdk.test.whitebox.WhiteBox
78 * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
79 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UnlockExperimentalVMOptions -XX:-UseCompressedOops -XX:+UseCompactObjectHeaders TestOopMapSizeMinimal
80 */
81
82 public class TestOopMapSizeMinimal {
83
84 public static int OOP_SIZE_IN_BYTES = -1;
85 public static int HEADER_SIZE_IN_BYTES = -1;
86
87 static {
88 WhiteBox WB = WhiteBox.getWhiteBox();
89 boolean is_64_bit = System.getProperty("sun.arch.data.model").equals("64");
90 if (is_64_bit) {
91 if (System.getProperty("java.vm.compressedOopsMode") == null) {
92 OOP_SIZE_IN_BYTES = 8;
93 } else {
94 OOP_SIZE_IN_BYTES = 4;
95 }
96 } else {
97 OOP_SIZE_IN_BYTES = 4;
98 }
99 if (is_64_bit) {
100 if (WB.getBooleanVMFlag("UseCompactObjectHeaders")) {
101 HEADER_SIZE_IN_BYTES = 4;
102 } else if (WB.getBooleanVMFlag("UseCompressedClassPointers")) {
103 HEADER_SIZE_IN_BYTES = 12;
104 } else {
105 HEADER_SIZE_IN_BYTES = 16;
106 }
107 } else {
108 HEADER_SIZE_IN_BYTES = 8;
109 }
110 }
111
112 public static long alignUp(long value, long alignment) {
113 return (value + alignment - 1) & ~(alignment - 1);
114 }
115
116 public static long alignForOop(long position) {
117 return alignUp(position, OOP_SIZE_IN_BYTES);
118 }
119
120 private static final Unsafe U = Unsafe.getUnsafe();
121
122 public static class BASE {
123 int i1;
124 Object o1;
125 }
126
127 public static class DERIVED1 extends BASE {
128 int i2;
129 Object o2;
130 }
131
132 public static class DERIVED2 extends DERIVED1 {
133 int i3;
134 Object o3;
135 }
136
137 public static class DERIVED3 extends DERIVED2 {
138 int i4;
139 Object o4;
140 }
141
142 static boolean mismatch = false;
143
144 private static void checkOffset(Field f, long expectedOffset) {
145 long realOffset = U.objectFieldOffset(f);
146 System.out.println("Offset for field " + f.getName() +
147 ": expected " + expectedOffset + ", got " + realOffset + ".");
148 if (U.objectFieldOffset(f) != expectedOffset) {
149 mismatch = true;
150 System.out.println(" ... mimatch");
151 }
152 }
153
154 private static List<Field> allFieldsOf(Class c) {
155 ArrayList<Field> l = new ArrayList<>();
156 while (c != null) {
157 for (Field f : c.getDeclaredFields()) {
158 l.add(f);
159 }
160 c = c.getSuperclass();
161 }
162 return l;
163 }
164
165 public static void main(String[] args) throws Exception {
166
167 System.out.println("HEADER_SIZE_IN_BYTES " + HEADER_SIZE_IN_BYTES + ", OOP_SIZE_IN_BYTES " + OOP_SIZE_IN_BYTES);
168
169 long i1_loc_expected;
170 long o1_loc_expected;
171 long o2_loc_expected;
172 long i2_loc_expected;
173 long i3_loc_expected;
174 long o3_loc_expected;
175 long o4_loc_expected;
176 long i4_loc_expected;
177
178 // We expect the layouter to reverse order of oop- and non-oop fields
179 // when it is useful to minimize the number of oop map entries.
180 //
181 // If we have no gaps, this should be the layout:
182 // BASE i1
183 // o1 oopmap entry 1
184 // DERIVED1 o2 oopmap entry 1 (reversed order)
185 // i2
186 // DERIVED3 i3
187 // o3 oopmap entry 2
188 // DERIVED4 o4 oopmap entry 2 (reversed order)
189 // i4
190
191 // There are two combinations that have gaps:
192 // -UseCompressedOops + +COH, and -UseCompressedOops + -UseCompressedClassPointers.
193 // In both cases there is a gap following i1, and i2 will therefore nestle into that gap.
194 // Otherwise the same logic applies.
195
196 if (OOP_SIZE_IN_BYTES == 4 || // oop size == int size
197 (OOP_SIZE_IN_BYTES == 8 && (HEADER_SIZE_IN_BYTES == 12 || HEADER_SIZE_IN_BYTES == 4))
198 ) {
199 // No gaps
200
201 // Expected layout for BASE: int, object
202 i1_loc_expected = HEADER_SIZE_IN_BYTES;
203 o1_loc_expected = i1_loc_expected + 4;
204
205 // Expected layout for DERIVED1: object, int (to make o2 border o1)
206 o2_loc_expected = o1_loc_expected + OOP_SIZE_IN_BYTES;
207 i2_loc_expected = o2_loc_expected + OOP_SIZE_IN_BYTES;
208
209 // Expected layout for DERIVED2: int, object (to trail with oops, for derived classes to nestle against)
210 i3_loc_expected = i2_loc_expected + 4;
211 o3_loc_expected = i3_loc_expected + 4;
212
213 // Expected layout for DERIVED3: object, int (to make o4 border o3)
214 o4_loc_expected = o3_loc_expected + OOP_SIZE_IN_BYTES;
215 i4_loc_expected = o4_loc_expected + OOP_SIZE_IN_BYTES;
216
217 } else if (OOP_SIZE_IN_BYTES == 8) {
218
219 // gap after i1
220
221 i1_loc_expected = HEADER_SIZE_IN_BYTES;
222 o1_loc_expected = i1_loc_expected + 4 + 4; // + alignment gap
223
224 o2_loc_expected = o1_loc_expected + OOP_SIZE_IN_BYTES;
225 i2_loc_expected = i1_loc_expected + 4; // into gap following i1
226
227 o3_loc_expected = o2_loc_expected + OOP_SIZE_IN_BYTES;
228 i3_loc_expected = o3_loc_expected + OOP_SIZE_IN_BYTES;
229
230 i4_loc_expected = i3_loc_expected + 4;
231 o4_loc_expected = i4_loc_expected + 4;
232 } else {
233 throw new RuntimeException("Unexpected");
234 }
235
236 List<Field> l = allFieldsOf(DERIVED3.class);
237 for (Field f : l) {
238 switch (f.getName()) {
239 case "i1" : checkOffset(f, i1_loc_expected); break;
240 case "o1" : checkOffset(f, o1_loc_expected); break;
241 case "i2" : checkOffset(f, i2_loc_expected); break;
242 case "o2" : checkOffset(f, o2_loc_expected); break;
243 case "i3" : checkOffset(f, i3_loc_expected); break;
244 case "o3" : checkOffset(f, o3_loc_expected); break;
245 case "i4" : checkOffset(f, i4_loc_expected); break;
246 case "o4" : checkOffset(f, o4_loc_expected); break;
247 default: throw new RuntimeException("Unexpected");
248 }
249 }
250 if (mismatch) {
251 throw new RuntimeException("Mismatch!");
252 }
253 System.out.println("All good.");
254 }
255
256 }
257