blob: ce367b0831e257282e48fbd40ef818ccaf630343 [file] [log] [blame]
/*
* Copyright (c) 2013, 2016, 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 org.graalvm.compiler.hotspot.test;
import java.util.List;
import java.util.Map;
import org.junit.Assert;
import org.junit.Test;
import org.graalvm.compiler.core.common.LocationIdentity;
import org.graalvm.compiler.debug.Debug;
import org.graalvm.compiler.debug.Debug.Scope;
import org.graalvm.compiler.debug.DebugConfig;
import org.graalvm.compiler.debug.DebugConfigScope;
import org.graalvm.compiler.debug.DebugDumpScope;
import org.graalvm.compiler.debug.internal.DebugScope;
import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
import org.graalvm.compiler.hotspot.nodes.G1ArrayRangePostWriteBarrier;
import org.graalvm.compiler.hotspot.nodes.G1ArrayRangePreWriteBarrier;
import org.graalvm.compiler.hotspot.nodes.G1PostWriteBarrier;
import org.graalvm.compiler.hotspot.nodes.G1PreWriteBarrier;
import org.graalvm.compiler.hotspot.nodes.SerialArrayRangeWriteBarrier;
import org.graalvm.compiler.hotspot.nodes.SerialWriteBarrier;
import org.graalvm.compiler.hotspot.phases.WriteBarrierAdditionPhase;
import org.graalvm.compiler.hotspot.phases.WriteBarrierVerificationPhase;
import org.graalvm.compiler.hotspot.replacements.arraycopy.UnsafeArrayCopyNode;
import org.graalvm.compiler.nodes.AbstractBeginNode;
import org.graalvm.compiler.nodes.AbstractMergeNode;
import org.graalvm.compiler.nodes.FieldLocationIdentity;
import org.graalvm.compiler.nodes.FixedNode;
import org.graalvm.compiler.nodes.FixedWithNextNode;
import org.graalvm.compiler.nodes.LoopBeginNode;
import org.graalvm.compiler.nodes.LoopExitNode;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
import org.graalvm.compiler.nodes.memory.WriteNode;
import org.graalvm.compiler.nodes.spi.LoweringTool;
import org.graalvm.compiler.phases.OptimisticOptimizations;
import org.graalvm.compiler.phases.common.CanonicalizerPhase;
import org.graalvm.compiler.phases.common.GuardLoweringPhase;
import org.graalvm.compiler.phases.common.LoopSafepointInsertionPhase;
import org.graalvm.compiler.phases.common.LoweringPhase;
import org.graalvm.compiler.phases.common.inlining.InliningPhase;
import org.graalvm.compiler.phases.graph.ReentrantNodeIterator;
import org.graalvm.compiler.phases.graph.ReentrantNodeIterator.NodeIteratorClosure;
import org.graalvm.compiler.phases.tiers.HighTierContext;
import org.graalvm.compiler.phases.tiers.MidTierContext;
import jdk.vm.ci.meta.ResolvedJavaField;
/**
* The following tests validate the write barrier verification phase. For every tested snippet, an
* array of write barrier indices and the total write barrier number are passed as parameters. The
* indices denote the barriers that will be manually removed. The write barrier verification phase
* runs after the write barrier removal and depending on the result an assertion might be generated.
* The tests anticipate the presence or not of an assertion generated by the verification phase.
*/
public class WriteBarrierVerificationTest extends HotSpotGraalCompilerTest {
public static int barrierIndex;
private final GraalHotSpotVMConfig config = runtime().getVMConfig();
public static class Container {
public Container a;
public Container b;
}
private static native void safepoint();
public static void test1Snippet(Container main) {
Container temp1 = new Container();
Container temp2 = new Container();
barrierIndex = 0;
safepoint();
barrierIndex = 1;
main.a = temp1;
safepoint();
barrierIndex = 2;
main.b = temp2;
safepoint();
}
@Test(expected = AssertionError.class)
public void test1() {
test("test1Snippet", 2, new int[]{1});
}
@Test(expected = AssertionError.class)
public void test2() {
test("test1Snippet", 2, new int[]{2});
}
public static void test2Snippet(Container main) {
Container temp1 = new Container();
Container temp2 = new Container();
barrierIndex = 0;
safepoint();
barrierIndex = 1;
main.a = temp1;
barrierIndex = 2;
main.b = temp2;
safepoint();
}
@Test(expected = AssertionError.class)
public void test3() {
test("test2Snippet", 2, new int[]{1});
}
@Test
public void test4() {
test("test2Snippet", 2, new int[]{2});
}
public static void test3Snippet(Container main, boolean test) {
Container temp1 = new Container();
Container temp2 = new Container();
barrierIndex = 0;
safepoint();
for (int i = 0; i < 10; i++) {
if (test) {
barrierIndex = 1;
main.a = temp1;
barrierIndex = 2;
main.b = temp2;
} else {
barrierIndex = 3;
main.a = temp1;
barrierIndex = 4;
main.b = temp2;
}
}
}
@Test(expected = AssertionError.class)
public void test5() {
test("test3Snippet", 4, new int[]{1, 2});
}
@Test(expected = AssertionError.class)
public void test6() {
test("test3Snippet", 4, new int[]{3, 4});
}
@Test(expected = AssertionError.class)
public void test7() {
test("test3Snippet", 4, new int[]{1});
}
@Test
public void test8() {
test("test3Snippet", 4, new int[]{2});
}
@Test(expected = AssertionError.class)
public void test9() {
test("test3Snippet", 4, new int[]{3});
}
@Test
public void test10() {
test("test3Snippet", 4, new int[]{4});
}
public static void test4Snippet(Container main, boolean test) {
Container temp1 = new Container();
Container temp2 = new Container();
safepoint();
barrierIndex = 1;
main.a = temp1;
for (int i = 0; i < 10; i++) {
if (test) {
barrierIndex = 2;
main.a = temp1;
barrierIndex = 3;
main.b = temp2;
} else {
barrierIndex = 4;
main.a = temp2;
barrierIndex = 5;
main.b = temp1;
}
}
}
@Test(expected = AssertionError.class)
public void test11() {
test("test4Snippet", 5, new int[]{2, 3});
}
@Test(expected = AssertionError.class)
public void test12() {
test("test4Snippet", 5, new int[]{4, 5});
}
@Test(expected = AssertionError.class)
public void test13() {
test("test4Snippet", 5, new int[]{1});
}
public static void test5Snippet(Container main) {
Container temp1 = new Container();
Container temp2 = new Container();
safepoint();
barrierIndex = 1;
main.a = temp1;
if (main.a == main.b) {
barrierIndex = 2;
main.a = temp1;
barrierIndex = 3;
main.b = temp2;
} else {
barrierIndex = 4;
main.a = temp2;
barrierIndex = 5;
main.b = temp1;
}
safepoint();
}
@Test(expected = AssertionError.class)
public void test14() {
test("test5Snippet", 5, new int[]{1});
}
@Test
public void test15() {
test("test5Snippet", 5, new int[]{2});
}
@Test
public void test16() {
test("test5Snippet", 5, new int[]{4});
}
@Test
public void test17() {
test("test5Snippet", 5, new int[]{3});
}
@Test
public void test18() {
test("test5Snippet", 5, new int[]{5});
}
@Test
public void test19() {
test("test5Snippet", 5, new int[]{2, 3});
}
@Test
public void test20() {
test("test5Snippet", 5, new int[]{4, 5});
}
public static void test6Snippet(Container main, boolean test) {
Container temp1 = new Container();
Container temp2 = new Container();
safepoint();
barrierIndex = 1;
main.a = temp1;
if (test) {
barrierIndex = 2;
main.a = temp1;
barrierIndex = 3;
main.b = temp1.a.a;
} else {
barrierIndex = 4;
main.a = temp2;
barrierIndex = 5;
main.b = temp2.a.a;
}
safepoint();
}
@Test(expected = AssertionError.class)
public void test21() {
test("test6Snippet", 5, new int[]{1});
}
@Test(expected = AssertionError.class)
public void test22() {
test("test6Snippet", 5, new int[]{1, 2});
}
@Test(expected = AssertionError.class)
public void test23() {
test("test6Snippet", 5, new int[]{3});
}
@Test
public void test24() {
test("test6Snippet", 5, new int[]{4});
}
public static void test7Snippet(Container main, boolean test) {
Container temp1 = new Container();
Container temp2 = new Container();
safepoint();
barrierIndex = 1;
main.a = temp1;
if (test) {
barrierIndex = 2;
main.a = temp1;
}
barrierIndex = 3;
main.b = temp2;
safepoint();
}
@Test
public void test25() {
test("test7Snippet", 3, new int[]{2});
}
@Test
public void test26() {
test("test7Snippet", 3, new int[]{3});
}
@Test
public void test27() {
test("test7Snippet", 3, new int[]{2, 3});
}
@Test(expected = AssertionError.class)
public void test28() {
test("test7Snippet", 3, new int[]{1});
}
public static void test8Snippet(Container main, boolean test) {
Container temp1 = new Container();
Container temp2 = new Container();
safepoint();
if (test) {
barrierIndex = 1;
main.a = temp1;
}
barrierIndex = 2;
main.b = temp2;
safepoint();
}
@Test(expected = AssertionError.class)
public void test29() {
test("test8Snippet", 2, new int[]{1});
}
@Test(expected = AssertionError.class)
public void test30() {
test("test8Snippet", 2, new int[]{2});
}
@Test(expected = AssertionError.class)
public void test31() {
test("test8Snippet", 2, new int[]{1, 2});
}
public static void test9Snippet(Container main1, Container main2, boolean test) {
Container temp1 = new Container();
Container temp2 = new Container();
safepoint();
if (test) {
barrierIndex = 1;
main1.a = temp1;
} else {
barrierIndex = 2;
main2.a = temp1;
}
barrierIndex = 3;
main1.b = temp2;
barrierIndex = 4;
main2.b = temp2;
safepoint();
}
@Test(expected = AssertionError.class)
public void test32() {
test("test9Snippet", 4, new int[]{1});
}
@Test(expected = AssertionError.class)
public void test33() {
test("test9Snippet", 4, new int[]{2});
}
@Test(expected = AssertionError.class)
public void test34() {
test("test9Snippet", 4, new int[]{3});
}
@Test(expected = AssertionError.class)
public void test35() {
test("test9Snippet", 4, new int[]{4});
}
@Test(expected = AssertionError.class)
public void test36() {
test("test9Snippet", 4, new int[]{1, 2});
}
@Test(expected = AssertionError.class)
public void test37() {
test("test9Snippet", 4, new int[]{3, 4});
}
public static void test10Snippet(Container main1, Container main2, boolean test) {
Container temp1 = new Container();
Container temp2 = new Container();
safepoint();
if (test) {
barrierIndex = 1;
main1.a = temp1;
barrierIndex = 2;
main2.a = temp2;
} else {
barrierIndex = 3;
main2.a = temp1;
}
barrierIndex = 4;
main1.b = temp2;
barrierIndex = 5;
main2.b = temp2;
safepoint();
}
@Test(expected = AssertionError.class)
public void test38() {
test("test10Snippet", 5, new int[]{1});
}
@Test(expected = AssertionError.class)
public void test39() {
test("test10Snippet", 5, new int[]{2});
}
@Test(expected = AssertionError.class)
public void test40() {
test("test10Snippet", 5, new int[]{3});
}
@Test(expected = AssertionError.class)
public void test41() {
test("test10Snippet", 5, new int[]{4});
}
@Test
public void test42() {
test("test10Snippet", 5, new int[]{5});
}
@Test(expected = AssertionError.class)
public void test43() {
test("test10Snippet", 5, new int[]{1, 2});
}
@Test(expected = AssertionError.class)
public void test44() {
test("test10Snippet", 5, new int[]{1, 2, 3});
}
@Test(expected = AssertionError.class)
public void test45() {
test("test10Snippet", 5, new int[]{3, 4});
}
public static void test11Snippet(Container main1, Container main2, Container main3, boolean test) {
Container temp1 = new Container();
Container temp2 = new Container();
safepoint();
if (test) {
barrierIndex = 1;
main1.a = temp1;
barrierIndex = 2;
main3.a = temp1;
if (!test) {
barrierIndex = 3;
main2.a = temp2;
} else {
barrierIndex = 4;
main1.a = temp2;
barrierIndex = 5;
main3.a = temp2;
}
} else {
barrierIndex = 6;
main1.b = temp2;
for (int i = 0; i < 10; i++) {
barrierIndex = 7;
main3.a = temp1;
}
barrierIndex = 8;
main3.b = temp2;
}
barrierIndex = 9;
main1.b = temp2;
barrierIndex = 10;
main2.b = temp2;
barrierIndex = 11;
main3.b = temp2;
safepoint();
}
@Test(expected = AssertionError.class)
public void test46() {
test("test11Snippet", 11, new int[]{1});
}
@Test(expected = AssertionError.class)
public void test47() {
test("test11Snippet", 11, new int[]{2});
}
@Test(expected = AssertionError.class)
public void test48() {
test("test11Snippet", 11, new int[]{3});
}
@Test(expected = AssertionError.class)
public void test49() {
test("test11Snippet", 11, new int[]{6});
}
@Test(expected = AssertionError.class)
public void test50() {
test("test11Snippet", 11, new int[]{7});
}
@Test(expected = AssertionError.class)
public void test51() {
test("test11Snippet", 11, new int[]{8});
}
@Test(expected = AssertionError.class)
public void test52() {
test("test11Snippet", 11, new int[]{9});
}
@Test(expected = AssertionError.class)
public void test53() {
test("test11Snippet", 11, new int[]{10});
}
@Test
public void test54() {
test("test11Snippet", 11, new int[]{4});
}
@Test
public void test55() {
test("test11Snippet", 11, new int[]{5});
}
@Test
public void test56() {
test("test11Snippet", 11, new int[]{11});
}
public static void test12Snippet(Container main, Container main1, boolean test) {
Container temp1 = new Container();
Container temp2 = new Container();
barrierIndex = 0;
safepoint();
barrierIndex = 7;
main1.a = temp1;
for (int i = 0; i < 10; i++) {
if (test) {
barrierIndex = 1;
main.a = temp1;
barrierIndex = 2;
main.b = temp2;
} else {
barrierIndex = 3;
main.a = temp1;
barrierIndex = 4;
main.b = temp2;
}
}
barrierIndex = 5;
main.a = temp1;
barrierIndex = 6;
main.b = temp1;
barrierIndex = 8;
main1.b = temp1;
safepoint();
}
@Test(expected = AssertionError.class)
public void test57() {
test("test12Snippet", 8, new int[]{5});
}
@Test
public void test58() {
test("test12Snippet", 8, new int[]{6});
}
@Test(expected = AssertionError.class)
public void test59() {
test("test12Snippet", 8, new int[]{7});
}
@Test(expected = AssertionError.class)
public void test60() {
test("test12Snippet", 8, new int[]{8});
}
public static void test13Snippet(Object[] a, Object[] b) {
System.arraycopy(a, 0, b, 0, a.length);
}
@Test
public void test61() {
GraphPredicate checkForUnsafeArrayCopy = graph -> graph.getNodes().filter(UnsafeArrayCopyNode.class).count() > 0 ? 1 : 0;
testPredicate("test13Snippet", checkForUnsafeArrayCopy, new int[]{});
}
private interface GraphPredicate {
int apply(StructuredGraph graph);
}
private void test(final String snippet, final int expectedBarriers, final int... removedBarrierIndices) {
GraphPredicate noCheck = noArg -> expectedBarriers;
testPredicate(snippet, noCheck, removedBarrierIndices);
}
@SuppressWarnings("try")
private void testPredicate(final String snippet, final GraphPredicate expectedBarriers, final int... removedBarrierIndices) {
try (Scope d = Debug.scope("WriteBarrierVerificationTest", new DebugDumpScope(snippet))) {
final StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
HighTierContext highTierContext = getDefaultHighTierContext();
new InliningPhase(new CanonicalizerPhase()).apply(graph, highTierContext);
MidTierContext midTierContext = new MidTierContext(getProviders(), getTargetProvider(), OptimisticOptimizations.ALL, graph.getProfilingInfo());
new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, highTierContext);
new GuardLoweringPhase().apply(graph, midTierContext);
new LoopSafepointInsertionPhase().apply(graph);
new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.MID_TIER).apply(graph, highTierContext);
new WriteBarrierAdditionPhase(config).apply(graph);
int barriers = 0;
// First, the total number of expected barriers is checked.
if (config.useG1GC) {
barriers = graph.getNodes().filter(G1PreWriteBarrier.class).count() + graph.getNodes().filter(G1PostWriteBarrier.class).count() +
graph.getNodes().filter(G1ArrayRangePreWriteBarrier.class).count() + graph.getNodes().filter(G1ArrayRangePostWriteBarrier.class).count();
Assert.assertTrue(expectedBarriers.apply(graph) * 2 == barriers);
} else {
barriers = graph.getNodes().filter(SerialWriteBarrier.class).count() + graph.getNodes().filter(SerialArrayRangeWriteBarrier.class).count();
Assert.assertTrue(expectedBarriers.apply(graph) == barriers);
}
ResolvedJavaField barrierIndexField = getMetaAccess().lookupJavaField(WriteBarrierVerificationTest.class.getDeclaredField("barrierIndex"));
LocationIdentity barrierIdentity = new FieldLocationIdentity(barrierIndexField);
// Iterate over all write nodes and remove barriers according to input indices.
NodeIteratorClosure<Boolean> closure = new NodeIteratorClosure<Boolean>() {
@Override
protected Boolean processNode(FixedNode node, Boolean currentState) {
if (node instanceof WriteNode) {
WriteNode write = (WriteNode) node;
LocationIdentity obj = write.getLocationIdentity();
if (obj.equals(barrierIdentity)) {
/*
* A "barrierIndex" variable was found and is checked against the input
* barrier array.
*/
if (eliminateBarrier(write.value().asJavaConstant().asInt(), removedBarrierIndices)) {
return true;
}
}
} else if (node instanceof SerialWriteBarrier || node instanceof G1PostWriteBarrier) {
// Remove flagged write barriers.
if (currentState) {
graph.removeFixed(((FixedWithNextNode) node));
return false;
}
}
return currentState;
}
private boolean eliminateBarrier(int index, int[] map) {
for (int i = 0; i < map.length; i++) {
if (map[i] == index) {
return true;
}
}
return false;
}
@Override
protected Map<LoopExitNode, Boolean> processLoop(LoopBeginNode loop, Boolean initialState) {
return ReentrantNodeIterator.processLoop(this, loop, initialState).exitStates;
}
@Override
protected Boolean merge(AbstractMergeNode merge, List<Boolean> states) {
return false;
}
@Override
protected Boolean afterSplit(AbstractBeginNode node, Boolean oldState) {
return false;
}
};
DebugConfig debugConfig = DebugScope.getConfig();
DebugConfig fixedConfig = debugConfig == null ? null
: Debug.fixedConfig(0, 0, false, false, false, false, false, debugConfig.dumpHandlers(), debugConfig.verifyHandlers(), debugConfig.output());
try (DebugConfigScope s = Debug.setConfig(fixedConfig)) {
ReentrantNodeIterator.apply(closure, graph.start(), false);
new WriteBarrierVerificationPhase(config).apply(graph);
} catch (AssertionError error) {
/*
* Catch assertion, test for expected one and re-throw in order to validate unit
* test.
*/
Assert.assertTrue(error.getMessage().contains("Write barrier must be present"));
throw error;
}
} catch (Throwable e) {
throw Debug.handle(e);
}
}
}