/* * 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 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 org.graalvm.util.EconomicMap; 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 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 closure = new NodeIteratorClosure() { @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 EconomicMap processLoop(LoopBeginNode loop, Boolean initialState) { return ReentrantNodeIterator.processLoop(this, loop, initialState).exitStates; } @Override protected Boolean merge(AbstractMergeNode merge, List states) { return false; } @Override protected Boolean afterSplit(AbstractBeginNode node, Boolean oldState) { return false; } }; DebugConfig debugConfig = DebugScope.getConfig(); DebugConfig fixedConfig = debugConfig == null ? null : Debug.fixedConfig(debugConfig.getOptions(), 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); } } }