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     // TODO: 8353182
191     //@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
192     static void testAllocateThenAtomic(Inner i) {
193         Outer o = new Outer();
194         Common.blackhole(o);
195         Common.field1VarHandle.getAndSet​(o, i);
196     }
197 
198     @Test
199     // TODO: 8329234
200     //@IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
201     // TODO: 8353182
202     //@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
203     static void testLoadThenAtomic(Outer o, Inner i) {
204         Common.blackhole(o.field1);
205         Common.field1VarHandle.getAndSet​(o, i);
206     }
207 
208     @Test
209     // TODO: 8353182
210     //@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE)
211     static void testAtomicThenAtomicAnotherField(Outer o, Inner i) {
212         Common.field1VarHandle.getAndSet​(o, i);
213         Common.field2VarHandle.getAndSet​(o, i);
214     }
215 
216     @Test
217     // TODO: 8353182
218     //@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
219     static void testAllocateArrayThenAtomicAtKnownIndex(Outer o) {
220         Outer[] a = new Outer[42];
221         Common.blackhole(a);
222         Common.outerArrayVarHandle.getAndSet(a, 2, o);
223     }
224 
225     @Test
226     // TODO: 8353182
227     //@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
228     static void testAllocateArrayThenAtomicAtUnknownIndex(Outer o, int index) {
229         Outer[] a = new Outer[42];
230         Common.blackhole(a);
231         Common.outerArrayVarHandle.getAndSet(a, index, o);
232     }
233 
234     @Test
235     // TODO: 8353182
236     //@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE)
237     static void testArrayAtomicThenAtomicAtUnknownIndices(Outer[] a, Outer o, int index1, int index2) {
238         Common.outerArrayVarHandle.getAndSet(a, index1, o);
239         Common.outerArrayVarHandle.getAndSet(a, index2, o);
240     }
241 
242     @Run(test = {"testAllocateThenAtomic",
243                  "testLoadThenAtomic",
244                  "testAtomicThenAtomicAnotherField",
245                  "testAllocateArrayThenAtomicAtKnownIndex",
246                  "testAllocateArrayThenAtomicAtUnknownIndex",
247                  "testArrayAtomicThenAtomicAtUnknownIndices"})
248     void runAtomicOperationTests() {
249         testAllocateThenAtomic(Common.inner);
250         testLoadThenAtomic(Common.outer, Common.inner);
251         testAtomicThenAtomicAnotherField(Common.outer, Common.inner);
252         testAllocateArrayThenAtomicAtKnownIndex(Common.outer);
253         testAllocateArrayThenAtomicAtUnknownIndex(Common.outer, 10);
254         testArrayAtomicThenAtomicAtUnknownIndices(Common.outerArray, Common.outer, 10, 20);
255     }
256 }
257 
258 class TestZGCEffectiveBarrierElision {
259 
260     @Test
261     @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
262     static void testAllocateThenLoad() {
263         Outer o1 = new Outer();
264         Common.blackhole(o1);
265         // This load is directly optimized away by C2.
266         Common.blackhole(o1.field1);
267         Common.blackhole(o1.field1);
268     }
269 
270     @Test
271     @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
272     static void testAllocateThenStore(Inner i) {
273         Outer o1 = new Outer();
274         Common.blackhole(o1);
275         o1.field1 = i;
276     }
277 
278     @Test
279     @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
280     @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
281     static void testLoadThenLoad(Outer o) {
282         Common.blackhole(o.field1);
283         Common.blackhole(o.field1);
284     }
285 
286     @Test
287     @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
288     @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
289     static void testStoreThenStore(Outer o, Inner i) {
290         o.field1 = i;
291         o.field1 = i;
292     }
293 
294     @Test
295     @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
296     @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" },  phase = CompilePhase.FINAL_CODE)
297     static void testStoreThenLoad(Outer o, Inner i) {
298         o.field1 = i;
299         Common.blackhole(o.field1);
300     }
301 
302     @Run(test = {"testAllocateThenLoad",
303                  "testAllocateThenStore",
304                  "testLoadThenLoad",
305                  "testStoreThenStore",
306                  "testStoreThenLoad"})
307     void runBasicTests() {
308         testAllocateThenLoad();
309         testAllocateThenStore(Common.inner);
310         testLoadThenLoad(Common.outer);
311         testStoreThenStore(Common.outer, Common.inner);
312         testStoreThenLoad(Common.outer, Common.inner);
313     }
314 
315     @Test
316     @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
317     static void testAllocateArrayThenStoreAtKnownIndex(Outer o) {
318         Outer[] a = new Outer[42];
319         Common.blackhole(a);
320         Common.outerArrayVarHandle.setVolatile(a, 0, o);
321     }
322 
323     @Test
324     @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
325     static void testAllocateArrayThenStoreAtUnknownIndex(Outer o, int index) {
326         Outer[] a = new Outer[42];
327         Common.blackhole(a);
328         Common.outerArrayVarHandle.setVolatile(a, index, o);
329     }
330 
331     @Test
332     @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
333     @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
334     static void testArrayLoadThenLoad(Outer[] a) {
335         Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 0));
336         Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 0));
337     }
338 
339     @Test
340     @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
341     @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
342     static void testArrayStoreThenStore(Outer[] a, Outer o) {
343         Common.outerArrayVarHandle.setVolatile(a, 0, o);
344         Common.outerArrayVarHandle.setVolatile(a, 0, o);
345     }
346 
347     @Test
348     @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
349     @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
350     static void testArrayStoreThenLoad(Outer[] a, Outer o) {
351         Common.outerArrayVarHandle.setVolatile(a, 0, o);
352         Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 0));
353     }
354 
355     @Run(test = {"testAllocateArrayThenStoreAtKnownIndex",
356                  "testAllocateArrayThenStoreAtUnknownIndex",
357                  "testArrayLoadThenLoad",
358                  "testArrayStoreThenStore",
359                  "testArrayStoreThenLoad"})
360     void runArrayTests() {
361         testAllocateArrayThenStoreAtKnownIndex(Common.outer);
362         testAllocateArrayThenStoreAtUnknownIndex(Common.outer, 10);
363         testArrayLoadThenLoad(Common.outerArray);
364         testArrayStoreThenStore(Common.outerArray, Common.outer);
365         testArrayStoreThenLoad(Common.outerArray, Common.outer);
366     }
367 
368     @Test
369     @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
370     @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
371     static void testStoreThenConditionalStore(Outer o, Inner i, int value) {
372         o.field1 = i;
373         if (value % 2 == 0) {
374             o.field1 = i;
375         }
376     }
377 
378     @Test
379     @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
380     @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
381     static void testStoreThenStoreInLoop(Outer o, Inner i) {
382         o.field1 = i;
383         for (int j = 0; j < 100; j++) {
384             o.field1 = i;
385         }
386     }
387 
388     @Run(test = {"testStoreThenConditionalStore",
389                  "testStoreThenStoreInLoop"})
390     void runControlFlowTests() {
391         testStoreThenConditionalStore(Common.outer, Common.inner, ThreadLocalRandom.current().nextInt(0, 100));
392         testStoreThenStoreInLoop(Common.outer, Common.inner);
393     }
394 
395     @Test
396     @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
397     // TODO: 8353182
398     //@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
399     static void testStoreThenAtomic(Outer o, Inner i) {
400         o.field1 = i;
401         Common.field1VarHandle.getAndSet​(o, i);
402     }
403 
404     @Test
405     // TODO: 8353182
406     //@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
407     @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
408     static void testAtomicThenLoad(Outer o, Inner i) {
409         Common.field1VarHandle.getAndSet​(o, i);
410         Common.blackhole(o.field1);
411     }
412 
413     @Test
414     // TODO: 8353182
415     //@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
416     @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
417     static void testAtomicThenStore(Outer o, Inner i) {
418         Common.field1VarHandle.getAndSet​(o, i);
419         o.field1 = i;
420     }
421 
422     @Test
423     // TODO: 8353182
424     //@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
425     //@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
426     static void testAtomicThenAtomic(Outer o, Inner i) {
427         Common.field1VarHandle.getAndSet​(o, i);
428         Common.field1VarHandle.getAndSet​(o, i);
429     }
430 
431     @Test
432     // TODO: 8353182
433     //@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
434     //@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
435     static void testArrayAtomicThenAtomic(Outer[] a, Outer o) {
436         Common.outerArrayVarHandle.getAndSet(a, 0, o);
437         Common.outerArrayVarHandle.getAndSet(a, 0, o);
438     }
439 
440     @Run(test = {"testStoreThenAtomic",
441                  "testAtomicThenLoad",
442                  "testAtomicThenStore",
443                  "testAtomicThenAtomic",
444                  "testArrayAtomicThenAtomic"})
445     void runAtomicOperationTests() {
446         testStoreThenAtomic(Common.outer, Common.inner);
447         testAtomicThenLoad(Common.outer, Common.inner);
448         testAtomicThenStore(Common.outer, Common.inner);
449         testAtomicThenAtomic(Common.outer, Common.inner);
450         testArrayAtomicThenAtomic(Common.outerArray, Common.outer);
451     }
452 }