jdk-24/test/hotspot/jtreg/compiler/gcbarriers/TestZGCBarrierElision.java

441 lines
18 KiB
Java
Raw Normal View History

/*
* 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);
}
}