001/*
002 * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.hotspot.test;
024
025import static jdk.internal.jvmci.common.UnsafeAccess.*;
026
027import java.lang.ref.*;
028
029import com.oracle.graal.debug.*;
030import com.oracle.graal.debug.Debug.*;
031import jdk.internal.jvmci.hotspot.*;
032import jdk.internal.jvmci.meta.*;
033
034import org.junit.*;
035
036import sun.misc.*;
037
038import com.oracle.graal.compiler.test.*;
039import com.oracle.graal.hotspot.*;
040import com.oracle.graal.hotspot.nodes.*;
041import com.oracle.graal.hotspot.phases.*;
042import com.oracle.graal.hotspot.replacements.*;
043import com.oracle.graal.nodes.*;
044import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions;
045import com.oracle.graal.nodes.memory.HeapAccess.BarrierType;
046import com.oracle.graal.nodes.memory.*;
047import com.oracle.graal.nodes.memory.address.*;
048import com.oracle.graal.nodes.spi.*;
049import com.oracle.graal.phases.*;
050import com.oracle.graal.phases.common.*;
051import com.oracle.graal.phases.common.inlining.*;
052import com.oracle.graal.phases.common.inlining.policy.*;
053import com.oracle.graal.phases.tiers.*;
054
055/**
056 * The following unit tests assert the presence of write barriers for both Serial and G1 GCs.
057 * Normally, the tests check for compile time inserted barriers. However, there are the cases of
058 * unsafe loads of the java.lang.ref.Reference.referent field where runtime checks have to be
059 * performed also. For those cases, the unit tests check the presence of the compile-time inserted
060 * barriers. Concerning the runtime checks, the results of variable inputs (object types and
061 * offsets) passed as input parameters can be checked against printed output from the G1 write
062 * barrier snippets. The runtime checks have been validated offline.
063 */
064public class WriteBarrierAdditionTest extends GraalCompilerTest {
065
066    private static final HotSpotVMConfig config = HotSpotGraalRuntime.runtime().getConfig();
067    private static final long referentOffset = HotSpotReplacementsUtil.referentOffset();
068
069    public static class Container {
070
071        public Container a;
072        public Container b;
073    }
074
075    /**
076     * Expected 2 barriers for the Serial GC and 4 for G1 (2 pre + 2 post).
077     */
078    @Test
079    public void test1() throws Exception {
080        testHelper("test1Snippet", (config.useG1GC) ? 4 : 2);
081    }
082
083    public static void test1Snippet() {
084        Container main = new Container();
085        Container temp1 = new Container();
086        Container temp2 = new Container();
087        main.a = temp1;
088        main.b = temp2;
089    }
090
091    /**
092     * Expected 4 barriers for the Serial GC and 8 for G1 (4 pre + 4 post).
093     */
094    @Test
095    public void test2() throws Exception {
096        testHelper("test2Snippet", config.useG1GC ? 8 : 4);
097    }
098
099    public static void test2Snippet(boolean test) {
100        Container main = new Container();
101        Container temp1 = new Container();
102        Container temp2 = new Container();
103        for (int i = 0; i < 10; i++) {
104            if (test) {
105                main.a = temp1;
106                main.b = temp2;
107            } else {
108                main.a = temp2;
109                main.b = temp1;
110            }
111        }
112    }
113
114    /**
115     * Expected 4 barriers for the Serial GC and 8 for G1 (4 pre + 4 post).
116     */
117    @Test
118    public void test3() throws Exception {
119        testHelper("test3Snippet", config.useG1GC ? 8 : 4);
120    }
121
122    public static void test3Snippet() {
123        Container[] main = new Container[10];
124        Container temp1 = new Container();
125        Container temp2 = new Container();
126        for (int i = 0; i < 10; i++) {
127            main[i].a = main[i].b = temp1;
128        }
129
130        for (int i = 0; i < 10; i++) {
131            main[i].a = main[i].b = temp2;
132        }
133    }
134
135    /**
136     * Expected 2 barriers for the Serial GC and 5 for G1 (3 pre + 2 post) The (2 or 4) barriers are
137     * emitted while initializing the fields of the WeakReference instance. The extra pre barrier of
138     * G1 concerns the read of the referent field.
139     */
140    @Test
141    public void test4() throws Exception {
142        testHelper("test4Snippet", config.useG1GC ? 5 : 2);
143    }
144
145    public static Object test4Snippet() {
146        WeakReference<Object> weakRef = new WeakReference<>(new Object());
147        return weakRef.get();
148    }
149
150    static WeakReference<Object> wr = new WeakReference<>(new Object());
151    static Container con = new Container();
152
153    /**
154     * Expected 4 barriers for the Serial GC and 9 for G1 (1 ref + 4 pre + 4 post). In this test, we
155     * load the correct offset of the WeakReference object so naturally we assert the presence of
156     * the pre barrier.
157     */
158    @Test
159    public void test5() throws Exception {
160        testHelper("test5Snippet", config.useG1GC ? 1 : 0);
161    }
162
163    public static Object test5Snippet() throws Exception {
164        return unsafe.getObject(wr, config.useCompressedOops ? 12L : 16L);
165    }
166
167    /**
168     * The following test concerns the runtime checks of the unsafe loads. In this test, we unsafely
169     * load the java.lang.ref.Reference.referent field so the pre barier has to be executed.
170     */
171    @Test
172    public void test6() throws Exception {
173        test2("testUnsafeLoad", unsafe, wr, new Long(referentOffset), null);
174    }
175
176    /**
177     * The following test concerns the runtime checks of the unsafe loads. In this test, we unsafely
178     * load a matching offset of a wrong object so the pre barier must not be executed.
179     */
180    @Test
181    public void test7() throws Exception {
182        test2("testUnsafeLoad", unsafe, con, new Long(referentOffset), null);
183    }
184
185    /**
186     * The following test concerns the runtime checks of the unsafe loads. In this test, we unsafely
187     * load a non-matching offset field of the java.lang.ref.Reference object so the pre barier must
188     * not be executed.
189     */
190    @Test
191    public void test8() throws Exception {
192        test2("testUnsafeLoad", unsafe, wr, new Long(config.useCompressedOops ? 20 : 32), null);
193    }
194
195    /**
196     * The following test concerns the runtime checks of the unsafe loads. In this test, we unsafely
197     * load a matching offset+disp field of the java.lang.ref.Reference object so the pre barier
198     * must be executed.
199     */
200    @Test
201    public void test10() throws Exception {
202        test2("testUnsafeLoad", unsafe, wr, new Long(config.useCompressedOops ? 6 : 8), new Integer(config.useCompressedOops ? 6 : 8));
203    }
204
205    /**
206     * The following test concerns the runtime checks of the unsafe loads. In this test, we unsafely
207     * load a non-matching offset+disp field of the java.lang.ref.Reference object so the pre barier
208     * must not be executed.
209     */
210    @Test
211    public void test9() throws Exception {
212        test2("testUnsafeLoad", unsafe, wr, new Long(config.useCompressedOops ? 10 : 16), new Integer(config.useCompressedOops ? 10 : 16));
213    }
214
215    static Object[] src = new Object[1];
216    static Object[] dst = new Object[1];
217
218    static {
219        for (int i = 0; i < src.length; i++) {
220            src[i] = new Object();
221        }
222        for (int i = 0; i < dst.length; i++) {
223            dst[i] = new Object();
224        }
225    }
226
227    public static void testArrayCopy(Object a, Object b, Object c) throws Exception {
228        System.arraycopy(a, 0, b, 0, (int) c);
229    }
230
231    @Test
232    public void test11() throws Exception {
233        test2("testArrayCopy", src, dst, dst.length);
234    }
235
236    public static Object testUnsafeLoad(Unsafe theUnsafe, Object a, Object b, Object c) throws Exception {
237        final int offset = (c == null ? 0 : ((Integer) c).intValue());
238        final long displacement = (b == null ? 0 : ((Long) b).longValue());
239        return theUnsafe.getObject(a, offset + displacement);
240    }
241
242    private HotSpotInstalledCode getInstalledCode(String name, boolean withUnsafePrefix) throws Exception {
243        final ResolvedJavaMethod javaMethod = withUnsafePrefix ? getResolvedJavaMethod(WriteBarrierAdditionTest.class, name, Unsafe.class, Object.class, Object.class, Object.class)
244                        : getResolvedJavaMethod(WriteBarrierAdditionTest.class, name, Object.class, Object.class, Object.class);
245        final HotSpotInstalledCode installedCode = (HotSpotInstalledCode) getCode(javaMethod);
246        return installedCode;
247    }
248
249    private void testHelper(final String snippetName, final int expectedBarriers) throws Exception, SecurityException {
250        ResolvedJavaMethod snippet = getResolvedJavaMethod(snippetName);
251        try (Scope s = Debug.scope("WriteBarrierAdditionTest", snippet)) {
252            StructuredGraph graph = parseEager(snippet, AllowAssumptions.NO);
253            HighTierContext highContext = getDefaultHighTierContext();
254            MidTierContext midContext = new MidTierContext(getProviders(), getCodeCache().getTarget(), OptimisticOptimizations.ALL, graph.method().getProfilingInfo());
255            new InliningPhase(new InlineEverythingPolicy(), new CanonicalizerPhase()).apply(graph, highContext);
256            new CanonicalizerPhase().apply(graph, highContext);
257            new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, highContext);
258            new GuardLoweringPhase().apply(graph, midContext);
259            new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.MID_TIER).apply(graph, midContext);
260            new WriteBarrierAdditionPhase(config).apply(graph);
261            Debug.dump(graph, "After Write Barrier Addition");
262
263            int barriers = 0;
264            if (config.useG1GC) {
265                barriers = graph.getNodes().filter(G1ReferentFieldReadBarrier.class).count() + graph.getNodes().filter(G1PreWriteBarrier.class).count() +
266                                graph.getNodes().filter(G1PostWriteBarrier.class).count();
267            } else {
268                barriers = graph.getNodes().filter(SerialWriteBarrier.class).count();
269            }
270            Assert.assertEquals(expectedBarriers, barriers);
271            for (WriteNode write : graph.getNodes().filter(WriteNode.class)) {
272                if (config.useG1GC) {
273                    if (write.getBarrierType() != BarrierType.NONE) {
274                        Assert.assertEquals(1, write.successors().count());
275                        Assert.assertTrue(write.next() instanceof G1PostWriteBarrier);
276                        Assert.assertTrue(write.predecessor() instanceof G1PreWriteBarrier);
277                    }
278                } else {
279                    if (write.getBarrierType() != BarrierType.NONE) {
280                        Assert.assertEquals(1, write.successors().count());
281                        Assert.assertTrue(write.next() instanceof SerialWriteBarrier);
282                    }
283                }
284            }
285
286            for (ReadNode read : graph.getNodes().filter(ReadNode.class)) {
287                if (read.getBarrierType() != BarrierType.NONE) {
288                    Assert.assertTrue(read.getAddress() instanceof OffsetAddressNode);
289                    JavaConstant constDisp = ((OffsetAddressNode) read.getAddress()).getOffset().asJavaConstant();
290                    Assert.assertNotNull(constDisp);
291                    Assert.assertEquals(referentOffset, constDisp.asLong());
292                    Assert.assertTrue(config.useG1GC);
293                    Assert.assertEquals(BarrierType.PRECISE, read.getBarrierType());
294                    Assert.assertTrue(read.next() instanceof G1ReferentFieldReadBarrier);
295                }
296            }
297        } catch (Throwable e) {
298            throw Debug.handle(e);
299        }
300    }
301
302    private void test2(final String snippet, Object... args) throws Exception {
303        HotSpotInstalledCode code = getInstalledCode(snippet, args[0] instanceof Unsafe);
304        code.executeVarargs(args);
305    }
306}