jdk-24/test/hotspot/jtreg/compiler/gcbarriers/TestZGCBarrierElision.java
Axel Boldt-Christmas 821c514a13 8341692: Implement JEP 490: ZGC: Remove the Non-Generational Mode
Reviewed-by: ihse, eosterlund, stefank, prr, cjplummer, dholmes
2024-10-30 11:05:07 +00:00

441 lines
18 KiB
Java
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package compiler.gcbarriers;
import compiler.lib.ir_framework.*;
import java.lang.invoke.VarHandle;
import java.lang.invoke.MethodHandles;
import java.util.concurrent.ThreadLocalRandom;
/**
* @test
* @summary Test that the ZGC barrier elision optimization does not elide
* necessary barriers. The tests use volatile memory accesses and
* blackholes to prevent C2 from simply optimizing them away.
* @library /test/lib /
* @requires vm.gc.Z
* @run driver compiler.gcbarriers.TestZGCBarrierElision test-correctness
*/
/**
* @test
* @summary Test that the ZGC barrier elision optimization elides unnecessary
* barriers following simple allocation and domination rules.
* @library /test/lib /
* @requires vm.gc.Z & (vm.simpleArch == "x64" | vm.simpleArch == "aarch64")
* @run driver compiler.gcbarriers.TestZGCBarrierElision test-effectiveness
*/
class Inner {}
class Outer {
volatile Inner field1;
volatile Inner field2;
Outer() {}
}
class Common {
static Inner inner = new Inner();
static Outer outer = new Outer();
static Outer outer2 = new Outer();
static Outer[] outerArray = new Outer[42];
static final VarHandle field1VarHandle;
static final VarHandle field2VarHandle;
static {
MethodHandles.Lookup l = MethodHandles.lookup();
try {
field1VarHandle = l.findVarHandle(Outer.class, "field1", Inner.class);
field2VarHandle = l.findVarHandle(Outer.class, "field2", Inner.class);
} catch (Exception e) {
throw new Error(e);
}
}
static final VarHandle outerArrayVarHandle =
MethodHandles.arrayElementVarHandle(Outer[].class);
static final String REMAINING = "strong";
static final String ELIDED = "elided";
static void blackhole(Object o) {}
static void nonInlinedMethod() {}
}
public class TestZGCBarrierElision {
public static void main(String[] args) {
if (args.length != 1) {
throw new IllegalArgumentException();
}
Class testClass;
if (args[0].equals("test-correctness")) {
testClass = TestZGCCorrectBarrierElision.class;
} else if (args[0].equals("test-effectiveness")) {
testClass = TestZGCEffectiveBarrierElision.class;
} else {
throw new IllegalArgumentException();
}
String commonName = Common.class.getName();
TestFramework test = new TestFramework(testClass);
test.addFlags("-XX:+UseZGC", "-XX:+UnlockExperimentalVMOptions",
"-XX:CompileCommand=blackhole," + commonName + "::blackhole",
"-XX:CompileCommand=dontinline," + commonName + "::nonInlinedMethod",
"-XX:LoopMaxUnroll=0");
test.start();
}
}
class TestZGCCorrectBarrierElision {
@Test
@IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
@IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
static void testLoadThenStore(Outer o, Inner i) {
Common.blackhole(o.field1);
o.field1 = i;
}
@Test
@IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE)
static void testLoadThenLoadAnotherField(Outer o) {
Common.blackhole(o.field1);
Common.blackhole(o.field2);
}
@Test
@IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE)
static void testLoadThenLoadFromAnotherObject(Outer o1, Outer o2) {
Common.blackhole(o1.field1);
Common.blackhole(o2.field1);
}
@Run(test = {"testLoadThenStore",
"testLoadThenLoadAnotherField",
"testLoadThenLoadFromAnotherObject"})
void runBasicTests() {
testLoadThenStore(Common.outer, Common.inner);
testLoadThenLoadAnotherField(Common.outer);
testLoadThenLoadFromAnotherObject(Common.outer, Common.outer2);
}
@Test
@IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
@IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
static void testArrayLoadThenStore(Outer[] a, Outer o) {
Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 0));
Common.outerArrayVarHandle.setVolatile(a, 0, o);
}
@Test
@IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE)
static void testArrayLoadThenLoadAnotherElement(Outer[] a) {
Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 0));
Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 10));
}
@Run(test = {"testArrayLoadThenStore",
"testArrayLoadThenLoadAnotherElement"})
void runArrayTests() {
testArrayLoadThenStore(Common.outerArray, Common.outer);
testArrayLoadThenLoadAnotherElement(Common.outerArray);
}
@Test
@IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE)
static void testConditionalStoreThenStore(Outer o, Inner i, int value) {
if (value % 2 == 0) {
o.field1 = i;
}
o.field1 = i;
}
@Test
@IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE)
static void testStoreThenCallThenStore(Outer o, Inner i) {
o.field1 = i;
Common.nonInlinedMethod();
o.field1 = i;
}
@Run(test = {"testConditionalStoreThenStore",
"testStoreThenCallThenStore"})
void runControlFlowTests() {
testConditionalStoreThenStore(Common.outer, Common.inner, ThreadLocalRandom.current().nextInt(0, 100));
testStoreThenCallThenStore(Common.outer, Common.inner);
}
@Test
@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
static void testAllocateThenAtomic(Inner i) {
Outer o = new Outer();
Common.blackhole(o);
Common.field1VarHandle.getAndSet(o, i);
}
@Test
@IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
static void testLoadThenAtomic(Outer o, Inner i) {
Common.blackhole(o.field1);
Common.field1VarHandle.getAndSet(o, i);
}
@Test
@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE)
static void testAtomicThenAtomicAnotherField(Outer o, Inner i) {
Common.field1VarHandle.getAndSet(o, i);
Common.field2VarHandle.getAndSet(o, i);
}
@Test
@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
static void testAllocateArrayThenAtomicAtKnownIndex(Outer o) {
Outer[] a = new Outer[42];
Common.blackhole(a);
Common.outerArrayVarHandle.getAndSet(a, 2, o);
}
@Test
@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
static void testAllocateArrayThenAtomicAtUnknownIndex(Outer o, int index) {
Outer[] a = new Outer[42];
Common.blackhole(a);
Common.outerArrayVarHandle.getAndSet(a, index, o);
}
@Test
@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE)
static void testArrayAtomicThenAtomicAtUnknownIndices(Outer[] a, Outer o, int index1, int index2) {
Common.outerArrayVarHandle.getAndSet(a, index1, o);
Common.outerArrayVarHandle.getAndSet(a, index2, o);
}
@Run(test = {"testAllocateThenAtomic",
"testLoadThenAtomic",
"testAtomicThenAtomicAnotherField",
"testAllocateArrayThenAtomicAtKnownIndex",
"testAllocateArrayThenAtomicAtUnknownIndex",
"testArrayAtomicThenAtomicAtUnknownIndices"})
void runAtomicOperationTests() {
testAllocateThenAtomic(Common.inner);
testLoadThenAtomic(Common.outer, Common.inner);
testAtomicThenAtomicAnotherField(Common.outer, Common.inner);
testAllocateArrayThenAtomicAtKnownIndex(Common.outer);
testAllocateArrayThenAtomicAtUnknownIndex(Common.outer, 10);
testArrayAtomicThenAtomicAtUnknownIndices(Common.outerArray, Common.outer, 10, 20);
}
}
class TestZGCEffectiveBarrierElision {
@Test
@IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
static void testAllocateThenLoad() {
Outer o1 = new Outer();
Common.blackhole(o1);
// This load is directly optimized away by C2.
Common.blackhole(o1.field1);
Common.blackhole(o1.field1);
}
@Test
@IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
static void testAllocateThenStore(Inner i) {
Outer o1 = new Outer();
Common.blackhole(o1);
o1.field1 = i;
}
@Test
@IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
@IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
static void testLoadThenLoad(Outer o) {
Common.blackhole(o.field1);
Common.blackhole(o.field1);
}
@Test
@IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
@IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
static void testStoreThenStore(Outer o, Inner i) {
o.field1 = i;
o.field1 = i;
}
@Test
@IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
@IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
static void testStoreThenLoad(Outer o, Inner i) {
o.field1 = i;
Common.blackhole(o.field1);
}
@Run(test = {"testAllocateThenLoad",
"testAllocateThenStore",
"testLoadThenLoad",
"testStoreThenStore",
"testStoreThenLoad"})
void runBasicTests() {
testAllocateThenLoad();
testAllocateThenStore(Common.inner);
testLoadThenLoad(Common.outer);
testStoreThenStore(Common.outer, Common.inner);
testStoreThenLoad(Common.outer, Common.inner);
}
@Test
@IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
static void testAllocateArrayThenStoreAtKnownIndex(Outer o) {
Outer[] a = new Outer[42];
Common.blackhole(a);
Common.outerArrayVarHandle.setVolatile(a, 0, o);
}
@Test
@IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
static void testAllocateArrayThenStoreAtUnknownIndex(Outer o, int index) {
Outer[] a = new Outer[42];
Common.blackhole(a);
Common.outerArrayVarHandle.setVolatile(a, index, o);
}
@Test
@IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
@IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
static void testArrayLoadThenLoad(Outer[] a) {
Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 0));
Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 0));
}
@Test
@IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
@IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
static void testArrayStoreThenStore(Outer[] a, Outer o) {
Common.outerArrayVarHandle.setVolatile(a, 0, o);
Common.outerArrayVarHandle.setVolatile(a, 0, o);
}
@Test
@IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
@IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
static void testArrayStoreThenLoad(Outer[] a, Outer o) {
Common.outerArrayVarHandle.setVolatile(a, 0, o);
Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 0));
}
@Run(test = {"testAllocateArrayThenStoreAtKnownIndex",
"testAllocateArrayThenStoreAtUnknownIndex",
"testArrayLoadThenLoad",
"testArrayStoreThenStore",
"testArrayStoreThenLoad"})
void runArrayTests() {
testAllocateArrayThenStoreAtKnownIndex(Common.outer);
testAllocateArrayThenStoreAtUnknownIndex(Common.outer, 10);
testArrayLoadThenLoad(Common.outerArray);
testArrayStoreThenStore(Common.outerArray, Common.outer);
testArrayStoreThenLoad(Common.outerArray, Common.outer);
}
@Test
@IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
@IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
static void testStoreThenConditionalStore(Outer o, Inner i, int value) {
o.field1 = i;
if (value % 2 == 0) {
o.field1 = i;
}
}
@Test
@IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
@IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
static void testStoreThenStoreInLoop(Outer o, Inner i) {
o.field1 = i;
for (int j = 0; j < 100; j++) {
o.field1 = i;
}
}
@Run(test = {"testStoreThenConditionalStore",
"testStoreThenStoreInLoop"})
void runControlFlowTests() {
testStoreThenConditionalStore(Common.outer, Common.inner, ThreadLocalRandom.current().nextInt(0, 100));
testStoreThenStoreInLoop(Common.outer, Common.inner);
}
@Test
@IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
static void testStoreThenAtomic(Outer o, Inner i) {
o.field1 = i;
Common.field1VarHandle.getAndSet(o, i);
}
@Test
@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
@IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
static void testAtomicThenLoad(Outer o, Inner i) {
Common.field1VarHandle.getAndSet(o, i);
Common.blackhole(o.field1);
}
@Test
@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
@IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
static void testAtomicThenStore(Outer o, Inner i) {
Common.field1VarHandle.getAndSet(o, i);
o.field1 = i;
}
@Test
@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
static void testAtomicThenAtomic(Outer o, Inner i) {
Common.field1VarHandle.getAndSet(o, i);
Common.field1VarHandle.getAndSet(o, i);
}
@Test
@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE)
static void testArrayAtomicThenAtomic(Outer[] a, Outer o) {
Common.outerArrayVarHandle.getAndSet(a, 0, o);
Common.outerArrayVarHandle.getAndSet(a, 0, o);
}
@Run(test = {"testStoreThenAtomic",
"testAtomicThenLoad",
"testAtomicThenStore",
"testAtomicThenAtomic",
"testArrayAtomicThenAtomic"})
void runAtomicOperationTests() {
testStoreThenAtomic(Common.outer, Common.inner);
testAtomicThenLoad(Common.outer, Common.inner);
testAtomicThenStore(Common.outer, Common.inner);
testAtomicThenAtomic(Common.outer, Common.inner);
testArrayAtomicThenAtomic(Common.outerArray, Common.outer);
}
}