1 /*
2 * Copyright (c) 2023, 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.gcbarriers;
25
26 import compiler.lib.ir_framework.*;
27 import java.lang.invoke.VarHandle;
28 import java.lang.invoke.MethodHandles;
29 import java.util.concurrent.ThreadLocalRandom;
30
31 /**
32 * @test id=Z
33 * @summary Test that the ZGC barrier elision optimization does not elide
34 * necessary barriers. The tests use volatile memory accesses and
35 * blackholes to prevent C2 from simply optimizing them away.
36 * @library /test/lib /
37 * @requires vm.gc.Z
38 * @run driver compiler.gcbarriers.TestZGCBarrierElision test-correctness
39 */
40
41 /**
42 * @test id=ZGen
43 * @summary Test that the ZGC barrier elision optimization elides unnecessary
44 * barriers following simple allocation and domination rules.
45 * @library /test/lib /
46 * @requires vm.gc.Z & (vm.simpleArch == "x64" | vm.simpleArch == "aarch64")
47 * @run driver compiler.gcbarriers.TestZGCBarrierElision test-effectiveness
48 */
49
50 class Inner {}
51
52 class Outer {
53 volatile Inner field1;
54 volatile Inner field2;
55 Outer() {}
56 }
57
58 class Common {
59
60 static Inner inner = new Inner();
61 static Outer outer = new Outer();
62 static Outer outer2 = new Outer();
63 static Outer[] outerArray = new Outer[42];
64
65 static final VarHandle field1VarHandle;
66 static final VarHandle field2VarHandle;
67 static {
68 MethodHandles.Lookup l = MethodHandles.lookup();
69 try {
70 field1VarHandle = l.findVarHandle(Outer.class, "field1", Inner.class);
71 field2VarHandle = l.findVarHandle(Outer.class, "field2", Inner.class);
72 } catch (Exception e) {
73 throw new Error(e);
74 }
75 }
76 static final VarHandle outerArrayVarHandle =
77 MethodHandles.arrayElementVarHandle(Outer[].class);
78
79 static final String REMAINING = "strong";
80 static final String ELIDED = "elided";
81
82 static void blackhole(Object o) {}
83 static void nonInlinedMethod() {}
84 }
85
86 public class TestZGCBarrierElision {
87
88 public static void main(String[] args) {
89 if (args.length != 1) {
90 throw new IllegalArgumentException();
91 }
92 Class testClass;
93 if (args[0].equals("test-correctness")) {
94 testClass = TestZGCCorrectBarrierElision.class;
95 } else if (args[0].equals("test-effectiveness")) {
96 testClass = TestZGCEffectiveBarrierElision.class;
97 } else {
98 throw new IllegalArgumentException();
99 }
100 String commonName = Common.class.getName();
101 TestFramework test = new TestFramework(testClass);
102 test.addFlags("-XX:+UseZGC", "-XX:+UnlockExperimentalVMOptions",
103 "-XX:CompileCommand=blackhole," + commonName + "::blackhole",
104 "-XX:CompileCommand=dontinline," + commonName + "::nonInlinedMethod",
105 "-XX:LoopMaxUnroll=0");
106 test.start();
107 }
108 }
109
110 class TestZGCCorrectBarrierElision {
111
112 @Test
113 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
114 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
115 static void testLoadThenStore(Outer o, Inner i) {
116 Common.blackhole(o.field1);
117 o.field1 = i;
118 }
119
120 @Test
121 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE)
122 static void testLoadThenLoadAnotherField(Outer o) {
123 Common.blackhole(o.field1);
124 Common.blackhole(o.field2);
125 }
126
127 @Test
128 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE)
129 static void testLoadThenLoadFromAnotherObject(Outer o1, Outer o2) {
130 Common.blackhole(o1.field1);
131 Common.blackhole(o2.field1);
132 }
133
134 @Run(test = {"testLoadThenStore",
135 "testLoadThenLoadAnotherField",
136 "testLoadThenLoadFromAnotherObject"})
137 void runBasicTests() {
138 testLoadThenStore(Common.outer, Common.inner);
139 testLoadThenLoadAnotherField(Common.outer);
140 testLoadThenLoadFromAnotherObject(Common.outer, Common.outer2);
141 }
142
143 @Test
144 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
145 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
146 static void testArrayLoadThenStore(Outer[] a, Outer o) {
147 Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 0));
148 Common.outerArrayVarHandle.setVolatile(a, 0, o);
149 }
150
151 @Test
152 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE)
153 static void testArrayLoadThenLoadAnotherElement(Outer[] a) {
154 Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 0));
155 Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 10));
156 }
157
158 @Run(test = {"testArrayLoadThenStore",
159 "testArrayLoadThenLoadAnotherElement"})
160 void runArrayTests() {
161 testArrayLoadThenStore(Common.outerArray, Common.outer);
162 testArrayLoadThenLoadAnotherElement(Common.outerArray);
163 }
164
165 @Test
166 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE)
167 static void testConditionalStoreThenStore(Outer o, Inner i, int value) {
168 if (value % 2 == 0) {
169 o.field1 = i;
170 }
171 o.field1 = i;
172 }
173
174 @Test
175 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE)
176 static void testStoreThenCallThenStore(Outer o, Inner i) {
177 o.field1 = i;
178 Common.nonInlinedMethod();
179 o.field1 = i;
180 }
181
182 @Run(test = {"testConditionalStoreThenStore",
183 "testStoreThenCallThenStore"})
184 void runControlFlowTests() {
185 testConditionalStoreThenStore(Common.outer, Common.inner, ThreadLocalRandom.current().nextInt(0, 100));
186 testStoreThenCallThenStore(Common.outer, Common.inner);
187 }
188
189 @Test
190 @IR(counts = { IRNode.Z_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
191 static void testAllocateThenAtomic(Inner i) {
192 Outer o = new Outer();
193 Common.blackhole(o);
194 Common.field1VarHandle.getAndSet(o, i);
195 }
196
197 @Test
198 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
199 @IR(counts = { IRNode.Z_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
200 static void testLoadThenAtomic(Outer o, Inner i) {
201 Common.blackhole(o.field1);
202 Common.field1VarHandle.getAndSet(o, i);
203 }
204
205 @Test
206 @IR(counts = { IRNode.Z_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE)
207 static void testAtomicThenAtomicAnotherField(Outer o, Inner i) {
208 Common.field1VarHandle.getAndSet(o, i);
209 Common.field2VarHandle.getAndSet(o, i);
210 }
211
212 @Test
213 @IR(counts = { IRNode.Z_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
214 static void testAllocateArrayThenAtomicAtKnownIndex(Outer o) {
215 Outer[] a = new Outer[42];
216 Common.blackhole(a);
217 Common.outerArrayVarHandle.getAndSet(a, 2, o);
218 }
219
220 @Test
221 @IR(counts = { IRNode.Z_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
222 static void testAllocateArrayThenAtomicAtUnknownIndex(Outer o, int index) {
223 Outer[] a = new Outer[42];
224 Common.blackhole(a);
225 Common.outerArrayVarHandle.getAndSet(a, index, o);
226 }
227
228 @Test
229 @IR(counts = { IRNode.Z_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE)
230 static void testArrayAtomicThenAtomicAtUnknownIndices(Outer[] a, Outer o, int index1, int index2) {
231 Common.outerArrayVarHandle.getAndSet(a, index1, o);
232 Common.outerArrayVarHandle.getAndSet(a, index2, o);
233 }
234
235 @Run(test = {"testAllocateThenAtomic",
236 "testLoadThenAtomic",
237 "testAtomicThenAtomicAnotherField",
238 "testAllocateArrayThenAtomicAtKnownIndex",
239 "testAllocateArrayThenAtomicAtUnknownIndex",
240 "testArrayAtomicThenAtomicAtUnknownIndices"})
241 void runAtomicOperationTests() {
242 testAllocateThenAtomic(Common.inner);
243 testLoadThenAtomic(Common.outer, Common.inner);
244 testAtomicThenAtomicAnotherField(Common.outer, Common.inner);
245 testAllocateArrayThenAtomicAtKnownIndex(Common.outer);
246 testAllocateArrayThenAtomicAtUnknownIndex(Common.outer, 10);
247 testArrayAtomicThenAtomicAtUnknownIndices(Common.outerArray, Common.outer, 10, 20);
248 }
249 }
250
251 class TestZGCEffectiveBarrierElision {
252
253 @Test
254 // C2 does not emit a field load during parsing, so it also does not emit any barriers.
255 @IR(failOn = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED }, phase = CompilePhase.FINAL_CODE)
256 static void testAllocateThenLoad() {
257 Outer o1 = new Outer();
258 Common.blackhole(o1);
259 // This load is directly optimized away by C2.
260 Common.blackhole(o1.field1);
261 Common.blackhole(o1.field1);
262 }
263
264 @Test
265 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
266 static void testAllocateThenStore(Inner i) {
267 Outer o1 = new Outer();
268 Common.blackhole(o1);
269 o1.field1 = i;
270 }
271
272 @Test
273 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
274 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
275 static void testLoadThenLoad(Outer o) {
276 Common.blackhole(o.field1);
277 Common.blackhole(o.field1);
278 }
279
280 @Test
281 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
282 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
283 static void testStoreThenStore(Outer o, Inner i) {
284 o.field1 = i;
285 o.field1 = i;
286 }
287
288 @Test
289 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
290 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
291 static void testStoreThenLoad(Outer o, Inner i) {
292 o.field1 = i;
293 Common.blackhole(o.field1);
294 }
295
296 @Run(test = {"testAllocateThenLoad",
297 "testAllocateThenStore",
298 "testLoadThenLoad",
299 "testStoreThenStore",
300 "testStoreThenLoad"})
301 void runBasicTests() {
302 testAllocateThenLoad();
303 testAllocateThenStore(Common.inner);
304 testLoadThenLoad(Common.outer);
305 testStoreThenStore(Common.outer, Common.inner);
306 testStoreThenLoad(Common.outer, Common.inner);
307 }
308
309 @Test
310 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
311 static void testAllocateArrayThenStoreAtKnownIndex(Outer o) {
312 Outer[] a = new Outer[42];
313 Common.blackhole(a);
314 Common.outerArrayVarHandle.setVolatile(a, 0, o);
315 }
316
317 @Test
318 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
319 static void testAllocateArrayThenStoreAtUnknownIndex(Outer o, int index) {
320 Outer[] a = new Outer[42];
321 Common.blackhole(a);
322 Common.outerArrayVarHandle.setVolatile(a, index, o);
323 }
324
325 @Test
326 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
327 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
328 static void testArrayLoadThenLoad(Outer[] a) {
329 Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 0));
330 Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 0));
331 }
332
333 @Test
334 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
335 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
336 static void testArrayStoreThenStore(Outer[] a, Outer o) {
337 Common.outerArrayVarHandle.setVolatile(a, 0, o);
338 Common.outerArrayVarHandle.setVolatile(a, 0, o);
339 }
340
341 @Test
342 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
343 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
344 static void testArrayStoreThenLoad(Outer[] a, Outer o) {
345 Common.outerArrayVarHandle.setVolatile(a, 0, o);
346 Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 0));
347 }
348
349 @Run(test = {"testAllocateArrayThenStoreAtKnownIndex",
350 "testAllocateArrayThenStoreAtUnknownIndex",
351 "testArrayLoadThenLoad",
352 "testArrayStoreThenStore",
353 "testArrayStoreThenLoad"})
354 void runArrayTests() {
355 testAllocateArrayThenStoreAtKnownIndex(Common.outer);
356 testAllocateArrayThenStoreAtUnknownIndex(Common.outer, 10);
357 testArrayLoadThenLoad(Common.outerArray);
358 testArrayStoreThenStore(Common.outerArray, Common.outer);
359 testArrayStoreThenLoad(Common.outerArray, Common.outer);
360 }
361
362 @Test
363 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
364 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
365 static void testStoreThenConditionalStore(Outer o, Inner i, int value) {
366 o.field1 = i;
367 if (value % 2 == 0) {
368 o.field1 = i;
369 }
370 }
371
372 @Test
373 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
374 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
375 static void testStoreThenStoreInLoop(Outer o, Inner i) {
376 o.field1 = i;
377 for (int j = 0; j < 100; j++) {
378 o.field1 = i;
379 }
380 }
381
382 @Run(test = {"testStoreThenConditionalStore",
383 "testStoreThenStoreInLoop"})
384 void runControlFlowTests() {
385 testStoreThenConditionalStore(Common.outer, Common.inner, ThreadLocalRandom.current().nextInt(0, 100));
386 testStoreThenStoreInLoop(Common.outer, Common.inner);
387 }
388
389 @Test
390 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
391 @IR(counts = { IRNode.Z_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
392 static void testStoreThenAtomic(Outer o, Inner i) {
393 o.field1 = i;
394 Common.field1VarHandle.getAndSet(o, i);
395 }
396
397 @Test
398 @IR(counts = { IRNode.Z_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
399 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
400 static void testAtomicThenLoad(Outer o, Inner i) {
401 Common.field1VarHandle.getAndSet(o, i);
402 Common.blackhole(o.field1);
403 }
404
405 @Test
406 @IR(counts = { IRNode.Z_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
407 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
408 static void testAtomicThenStore(Outer o, Inner i) {
409 Common.field1VarHandle.getAndSet(o, i);
410 o.field1 = i;
411 }
412
413 @Test
414 @IR(counts = { IRNode.Z_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
415 //@IR(counts = { IRNode.Z_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
416 static void testAtomicThenAtomic(Outer o, Inner i) {
417 Common.field1VarHandle.getAndSet(o, i);
418 Common.field1VarHandle.getAndSet(o, i);
419 }
420
421 @Test
422 @IR(counts = { IRNode.Z_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
423 @IR(counts = { IRNode.Z_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
424 static void testArrayAtomicThenAtomic(Outer[] a, Outer o) {
425 Common.outerArrayVarHandle.getAndSet(a, 0, o);
426 Common.outerArrayVarHandle.getAndSet(a, 0, o);
427 }
428
429 @Run(test = {"testStoreThenAtomic",
430 "testAtomicThenLoad",
431 "testAtomicThenStore",
432 "testAtomicThenAtomic",
433 "testArrayAtomicThenAtomic"})
434 void runAtomicOperationTests() {
435 testStoreThenAtomic(Common.outer, Common.inner);
436 testAtomicThenLoad(Common.outer, Common.inner);
437 testAtomicThenStore(Common.outer, Common.inner);
438 testAtomicThenAtomic(Common.outer, Common.inner);
439 testArrayAtomicThenAtomic(Common.outerArray, Common.outer);
440 }
441 }