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         // TODO 8366668 Re-enable IR verification
103         test.addFlags("-DVerifyIR=false", "-XX:+UseZGC", "-XX:+UnlockExperimentalVMOptions",
104                       "-XX:CompileCommand=blackhole," + commonName + "::blackhole",
105                       "-XX:CompileCommand=dontinline," + commonName + "::nonInlinedMethod",
106                       "-XX:LoopMaxUnroll=0");
107         test.start();
108     }
109 }
110 
111 class TestZGCCorrectBarrierElision {
112 
113     @Test
114     @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
115     @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" },  phase = CompilePhase.FINAL_CODE)
116     static void testLoadThenStore(Outer o, Inner i) {
117         Common.blackhole(o.field1);
118         o.field1 = i;
119     }
120 
121     @Test
122     @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE)
123     static void testLoadThenLoadAnotherField(Outer o) {
124         Common.blackhole(o.field1);
125         Common.blackhole(o.field2);
126     }
127 
128     @Test
129     @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE)
130     static void testLoadThenLoadFromAnotherObject(Outer o1, Outer o2) {
131         Common.blackhole(o1.field1);
132         Common.blackhole(o2.field1);
133     }
134 
135     @Run(test = {"testLoadThenStore",
136                  "testLoadThenLoadAnotherField",
137                  "testLoadThenLoadFromAnotherObject"})
138     void runBasicTests() {
139         testLoadThenStore(Common.outer, Common.inner);
140         testLoadThenLoadAnotherField(Common.outer);
141         testLoadThenLoadFromAnotherObject(Common.outer, Common.outer2);
142     }
143 
144     @Test
145     @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
146     @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
147     static void testArrayLoadThenStore(Outer[] a, Outer o) {
148         Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 0));
149         Common.outerArrayVarHandle.setVolatile(a, 0, o);
150     }
151 
152     @Test
153     @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE)
154     static void testArrayLoadThenLoadAnotherElement(Outer[] a) {
155         Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 0));
156         Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 10));
157     }
158 
159     @Run(test = {"testArrayLoadThenStore",
160                  "testArrayLoadThenLoadAnotherElement"})
161     void runArrayTests() {
162         testArrayLoadThenStore(Common.outerArray, Common.outer);
163         testArrayLoadThenLoadAnotherElement(Common.outerArray);
164     }
165 
166     @Test
167     @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE)
168     static void testConditionalStoreThenStore(Outer o, Inner i, int value) {
169         if (value % 2 == 0) {
170             o.field1 = i;
171         }
172         o.field1 = i;
173     }
174 
175     @Test
176     @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE)
177     static void testStoreThenCallThenStore(Outer o, Inner i) {
178         o.field1 = i;
179         Common.nonInlinedMethod();
180         o.field1 = i;
181     }
182 
183     @Run(test = {"testConditionalStoreThenStore",
184                  "testStoreThenCallThenStore"})
185     void runControlFlowTests() {
186         testConditionalStoreThenStore(Common.outer, Common.inner, ThreadLocalRandom.current().nextInt(0, 100));
187         testStoreThenCallThenStore(Common.outer, Common.inner);
188     }
189 
190     @Test
191     @IR(counts = { IRNode.Z_COMPARE_AND_SWAP_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     @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
200     @IR(counts = { IRNode.Z_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
201     static void testLoadThenAtomic(Outer o, Inner i) {
202         Common.blackhole(o.field1);
203         Common.field1VarHandle.getAndSet(o, i);
204     }
205 
206     @Test
207     @IR(counts = { IRNode.Z_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE)
208     static void testAtomicThenAtomicAnotherField(Outer o, Inner i) {
209         Common.field1VarHandle.getAndSet(o, i);
210         Common.field2VarHandle.getAndSet(o, i);
211     }
212 
213     @Test
214     @IR(counts = { IRNode.Z_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
215     static void testAllocateArrayThenAtomicAtKnownIndex(Outer o) {
216         Outer[] a = new Outer[42];
217         Common.blackhole(a);
218         Common.outerArrayVarHandle.getAndSet(a, 2, o);
219     }
220 
221     @Test
222     @IR(counts = { IRNode.Z_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
223     static void testAllocateArrayThenAtomicAtUnknownIndex(Outer o, int index) {
224         Outer[] a = new Outer[42];
225         Common.blackhole(a);
226         Common.outerArrayVarHandle.getAndSet(a, index, o);
227     }
228 
229     @Test
230     @IR(counts = { IRNode.Z_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE)
231     static void testArrayAtomicThenAtomicAtUnknownIndices(Outer[] a, Outer o, int index1, int index2) {
232         Common.outerArrayVarHandle.getAndSet(a, index1, o);
233         Common.outerArrayVarHandle.getAndSet(a, index2, o);
234     }
235 
236     @Run(test = {"testAllocateThenAtomic",
237                  "testLoadThenAtomic",
238                  "testAtomicThenAtomicAnotherField",
239                  "testAllocateArrayThenAtomicAtKnownIndex",
240                  "testAllocateArrayThenAtomicAtUnknownIndex",
241                  "testArrayAtomicThenAtomicAtUnknownIndices"})
242     void runAtomicOperationTests() {
243         testAllocateThenAtomic(Common.inner);
244         testLoadThenAtomic(Common.outer, Common.inner);
245         testAtomicThenAtomicAnotherField(Common.outer, Common.inner);
246         testAllocateArrayThenAtomicAtKnownIndex(Common.outer);
247         testAllocateArrayThenAtomicAtUnknownIndex(Common.outer, 10);
248         testArrayAtomicThenAtomicAtUnknownIndices(Common.outerArray, Common.outer, 10, 20);
249     }
250 }
251 
252 class TestZGCEffectiveBarrierElision {
253 
254     @Test
255     @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, 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 }