1 /*
2 * Copyright (c) 2023, 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 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 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
255 static void testAllocateThenLoad() {
256 Outer o1 = new Outer();
257 Common.blackhole(o1);
258 // This load is directly optimized away by C2.
259 Common.blackhole(o1.field1);
260 Common.blackhole(o1.field1);
261 }
262
263 @Test
264 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
265 static void testAllocateThenStore(Inner i) {
266 Outer o1 = new Outer();
267 Common.blackhole(o1);
268 o1.field1 = i;
269 }
270
271 @Test
272 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
273 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
274 static void testLoadThenLoad(Outer o) {
275 Common.blackhole(o.field1);
276 Common.blackhole(o.field1);
277 }
278
279 @Test
280 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
281 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
282 static void testStoreThenStore(Outer o, Inner i) {
283 o.field1 = i;
284 o.field1 = i;
285 }
286
287 @Test
288 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
289 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
290 static void testStoreThenLoad(Outer o, Inner i) {
291 o.field1 = i;
292 Common.blackhole(o.field1);
293 }
294
295 @Run(test = {"testAllocateThenLoad",
296 "testAllocateThenStore",
297 "testLoadThenLoad",
298 "testStoreThenStore",
299 "testStoreThenLoad"})
300 void runBasicTests() {
301 testAllocateThenLoad();
302 testAllocateThenStore(Common.inner);
303 testLoadThenLoad(Common.outer);
304 testStoreThenStore(Common.outer, Common.inner);
305 testStoreThenLoad(Common.outer, Common.inner);
306 }
307
308 @Test
309 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
310 static void testAllocateArrayThenStoreAtKnownIndex(Outer o) {
311 Outer[] a = new Outer[42];
312 Common.blackhole(a);
313 Common.outerArrayVarHandle.setVolatile(a, 0, o);
314 }
315
316 @Test
317 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
318 static void testAllocateArrayThenStoreAtUnknownIndex(Outer o, int index) {
319 Outer[] a = new Outer[42];
320 Common.blackhole(a);
321 Common.outerArrayVarHandle.setVolatile(a, index, o);
322 }
323
324 @Test
325 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
326 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
327 static void testArrayLoadThenLoad(Outer[] a) {
328 Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 0));
329 Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 0));
330 }
331
332 @Test
333 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
334 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
335 static void testArrayStoreThenStore(Outer[] a, Outer o) {
336 Common.outerArrayVarHandle.setVolatile(a, 0, o);
337 Common.outerArrayVarHandle.setVolatile(a, 0, o);
338 }
339
340 @Test
341 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
342 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
343 static void testArrayStoreThenLoad(Outer[] a, Outer o) {
344 Common.outerArrayVarHandle.setVolatile(a, 0, o);
345 Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 0));
346 }
347
348 @Run(test = {"testAllocateArrayThenStoreAtKnownIndex",
349 "testAllocateArrayThenStoreAtUnknownIndex",
350 "testArrayLoadThenLoad",
351 "testArrayStoreThenStore",
352 "testArrayStoreThenLoad"})
353 void runArrayTests() {
354 testAllocateArrayThenStoreAtKnownIndex(Common.outer);
355 testAllocateArrayThenStoreAtUnknownIndex(Common.outer, 10);
356 testArrayLoadThenLoad(Common.outerArray);
357 testArrayStoreThenStore(Common.outerArray, Common.outer);
358 testArrayStoreThenLoad(Common.outerArray, Common.outer);
359 }
360
361 @Test
362 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
363 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
364 static void testStoreThenConditionalStore(Outer o, Inner i, int value) {
365 o.field1 = i;
366 if (value % 2 == 0) {
367 o.field1 = i;
368 }
369 }
370
371 @Test
372 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
373 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
374 static void testStoreThenStoreInLoop(Outer o, Inner i) {
375 o.field1 = i;
376 for (int j = 0; j < 100; j++) {
377 o.field1 = i;
378 }
379 }
380
381 @Run(test = {"testStoreThenConditionalStore",
382 "testStoreThenStoreInLoop"})
383 void runControlFlowTests() {
384 testStoreThenConditionalStore(Common.outer, Common.inner, ThreadLocalRandom.current().nextInt(0, 100));
385 testStoreThenStoreInLoop(Common.outer, Common.inner);
386 }
387
388 @Test
389 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
390 @IR(counts = { IRNode.Z_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
391 static void testStoreThenAtomic(Outer o, Inner i) {
392 o.field1 = i;
393 Common.field1VarHandle.getAndSet(o, i);
394 }
395
396 @Test
397 @IR(counts = { IRNode.Z_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
398 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
399 static void testAtomicThenLoad(Outer o, Inner i) {
400 Common.field1VarHandle.getAndSet(o, i);
401 Common.blackhole(o.field1);
402 }
403
404 @Test
405 @IR(counts = { IRNode.Z_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
406 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
407 static void testAtomicThenStore(Outer o, Inner i) {
408 Common.field1VarHandle.getAndSet(o, i);
409 o.field1 = i;
410 }
411
412 @Test
413 @IR(counts = { IRNode.Z_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
414 //@IR(counts = { IRNode.Z_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
415 static void testAtomicThenAtomic(Outer o, Inner i) {
416 Common.field1VarHandle.getAndSet(o, i);
417 Common.field1VarHandle.getAndSet(o, i);
418 }
419
420 @Test
421 @IR(counts = { IRNode.Z_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
422 @IR(counts = { IRNode.Z_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
423 static void testArrayAtomicThenAtomic(Outer[] a, Outer o) {
424 Common.outerArrayVarHandle.getAndSet(a, 0, o);
425 Common.outerArrayVarHandle.getAndSet(a, 0, o);
426 }
427
428 @Run(test = {"testStoreThenAtomic",
429 "testAtomicThenLoad",
430 "testAtomicThenStore",
431 "testAtomicThenAtomic",
432 "testArrayAtomicThenAtomic"})
433 void runAtomicOperationTests() {
434 testStoreThenAtomic(Common.outer, Common.inner);
435 testAtomicThenLoad(Common.outer, Common.inner);
436 testAtomicThenStore(Common.outer, Common.inner);
437 testAtomicThenAtomic(Common.outer, Common.inner);
438 testArrayAtomicThenAtomic(Common.outerArray, Common.outer);
439 }
440 }