# HG changeset patch # User Christian Haeubl # Date 1372067784 -7200 # Node ID 329c22feda1ff7e2f00a5e9a1f495d62906cc1c3 # Parent 7344fa3e8833b9e0e1ffc6033da8393a5238fe1a# Parent 5db21405c6a49f63f33425f64be4977c6182ccc7 Merge diff -r 7344fa3e8833 -r 329c22feda1f graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/AheadOfTimeCompilationTest.java --- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/AheadOfTimeCompilationTest.java Mon Jun 24 11:43:48 2013 +0200 +++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/AheadOfTimeCompilationTest.java Mon Jun 24 11:56:24 2013 +0200 @@ -170,6 +170,7 @@ } @Test + @Ignore public void testBoxedBooleanAOT() { StructuredGraph result = compile("getBoxedBoolean", true); diff -r 7344fa3e8833 -r 329c22feda1f graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/WriteBarrierAdditionTest.java --- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/WriteBarrierAdditionTest.java Mon Jun 24 11:43:48 2013 +0200 +++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/WriteBarrierAdditionTest.java Mon Jun 24 11:56:24 2013 +0200 @@ -22,27 +22,60 @@ */ package com.oracle.graal.hotspot.test; +import java.lang.ref.*; +import java.lang.reflect.*; + import org.junit.*; import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.api.runtime.*; import com.oracle.graal.compiler.test.*; import com.oracle.graal.debug.*; +import com.oracle.graal.hotspot.meta.*; import com.oracle.graal.hotspot.phases.*; +import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.HeapAccess.WriteBarrierType; -import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.spi.Lowerable.LoweringType; +import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.InliningUtil.InlineInfo; +import com.oracle.graal.phases.common.InliningUtil.InliningPolicy; import com.oracle.graal.phases.tiers.*; +/** + * The following unit tests assert the presence of write barriers for both Serial and G1 GCs. + * Normally, the tests check for compile time inserted barriers. However, there are the cases of + * unsafe loads of the java.lang.ref.Reference.referent field where runtime checks have to be + * performed also. For those cases, the unit tests check the presence of the compile-time inserted + * barriers. Concerning the runtime checks, the results of variable inputs (object types and + * offsets) passed as input parameters can be checked against printed output from the G1 write + * barrier snippets. The runtime checks have been validated offline. + */ + public class WriteBarrierAdditionTest extends GraalCompilerTest { + private final MetaAccessProvider metaAccessProvider; + + public WriteBarrierAdditionTest() { + this.metaAccessProvider = Graal.getRequiredCapability(MetaAccessProvider.class); + } + public static class Container { public Container a; public Container b; } + /** + * Expected 2 barriers for the Serial GC and 4 for G1 (2 pre + 2 post). + */ + @Test + public void test1() throws Exception { + test("test1Snippet", ((HotSpotRuntime) runtime()).config.useG1GC ? 4 : 2); + } + public static void test1Snippet() { Container main = new Container(); Container temp1 = new Container(); @@ -51,6 +84,14 @@ main.b = temp2; } + /** + * Expected 4 barriers for the Serial GC and 8 for G1 (4 pre + 4 post). + */ + @Test + public void test2() throws Exception { + test("test2Snippet", ((HotSpotRuntime) runtime()).config.useG1GC ? 8 : 4); + } + public static void test2Snippet(boolean test) { Container main = new Container(); Container temp1 = new Container(); @@ -66,6 +107,14 @@ } } + /** + * Expected 4 barriers for the Serial GC and 8 for G1 (4 pre + 4 post). + */ + @Test + public void test3() throws Exception { + test("test3Snippet", ((HotSpotRuntime) runtime()).config.useG1GC ? 8 : 4); + } + public static void test3Snippet() { Container[] main = new Container[10]; Container temp1 = new Container(); @@ -77,42 +126,144 @@ for (int i = 0; i < 10; i++) { main[i].a = main[i].b = temp2; } + } + /** + * Expected 2 barriers for the Serial GC and 5 for G1 (3 pre + 2 post) The (2 or 4) barriers are + * emitted while initializing the fields of the WeakReference instance. The extra pre barrier of + * G1 concerns the read of the referent field. + */ + @Test + public void test4() throws Exception { + test("test4Snippet", ((HotSpotRuntime) runtime()).config.useG1GC ? 5 : 2); } + public static Object test4Snippet() { + WeakReference weakRef = new WeakReference<>(new Object()); + return weakRef.get(); + } + + static WeakReference wr = new WeakReference<>(new Object()); + static Container con = new Container(); + + /** + * Expected 4 barriers for the Serial GC and 9 for G1 (5 pre + 4 post). In this test, we load + * the correct offset of the WeakReference object so naturally we assert the presence of the pre + * barrier. + */ @Test - public void test1() { - test("test1Snippet", 2); + public void test5() throws Exception { + test("test5Snippet", ((HotSpotRuntime) runtime()).config.useG1GC ? 9 : 4); + } + + public static Object test5Snippet() throws Exception { + return UnsafeLoadNode.load(wr, 0, 16, Kind.Object); } + /** + * The following test concern the runtime checks of the unsafe loads. In this test, we unsafely + * load the java.lang.ref.Reference.referent field so the pre barier has to be executed. + */ @Test - public void test2() { - test("test2Snippet", 4); + public void test6() throws Exception { + test2("test6Snippet", wr, new Long(HotSpotRuntime.referentOffset()), null); + } + + /** + * The following test concern the runtime checks of the unsafe loads. In this test, we unsafely + * load a matching offset of a wrong object so the pre barier must not be executed. + */ + @Test + public void test7() throws Exception { + test2("test6Snippet", con, new Long(HotSpotRuntime.referentOffset()), null); } + /** + * The following test concern the runtime checks of the unsafe loads. In this test, we unsafely + * load a non-matching offset field of the java.lang.ref.Reference object so the pre barier must + * not be executed. + */ @Test - public void test3() { - test("test3Snippet", 4); + public void test8() throws Exception { + test2("test6Snippet", wr, new Long(32), null); } - private void test(final String snippet, final int expectedBarriers) { + @SuppressWarnings("unused") + public static Object test6Snippet(Object a, Object b, Object c) throws Exception { + return UnsafeLoadNode.load(a, 0, ((Long) b).longValue(), Kind.Object); + } + + private HotSpotInstalledCode getInstalledCode(String name) throws Exception { + final Method method = WriteBarrierAdditionTest.class.getMethod(name, Object.class, Object.class, Object.class); + final HotSpotResolvedJavaMethod javaMethod = (HotSpotResolvedJavaMethod) metaAccessProvider.lookupJavaMethod(method); + final HotSpotInstalledCode installedBenchmarkCode = (HotSpotInstalledCode) getCode(javaMethod, parse(method)); + return installedBenchmarkCode; + } + + private void test(final String snippet, final int expectedBarriers) throws Exception, SecurityException { Debug.scope("WriteBarrierAditionTest", new DebugDumpScope(snippet), new Runnable() { public void run() { StructuredGraph graph = parse(snippet); HighTierContext context = new HighTierContext(runtime(), new Assumptions(false), replacements); + new InliningPhase(runtime(), replacements, context.getAssumptions(), null, getDefaultPhasePlan(), OptimisticOptimizations.ALL, new InlineAllPolicy()).apply(graph); new LoweringPhase(LoweringType.BEFORE_GUARDS).apply(graph, context); new WriteBarrierAdditionPhase().apply(graph); Debug.dump(graph, "After Write Barrier Addition"); - final int barriers = graph.getNodes(SerialWriteBarrier.class).count(); + + int barriers = 0; + if (((HotSpotRuntime) runtime()).config.useG1GC) { + barriers = graph.getNodes(G1PreWriteBarrier.class).count() + graph.getNodes(G1PostWriteBarrier.class).count(); + } else { + barriers = graph.getNodes(SerialWriteBarrier.class).count(); + } Assert.assertTrue(barriers == expectedBarriers); for (WriteNode write : graph.getNodes(WriteNode.class)) { - if (write.getWriteBarrierType() != WriteBarrierType.NONE) { - Assert.assertTrue(write.successors().count() == 1); - Assert.assertTrue(write.next() instanceof SerialWriteBarrier); + if (((HotSpotRuntime) runtime()).config.useG1GC) { + if (write.getWriteBarrierType() != WriteBarrierType.NONE) { + Assert.assertTrue(write.successors().count() == 1); + Assert.assertTrue(write.next() instanceof G1PostWriteBarrier); + Assert.assertTrue(write.predecessor() instanceof G1PreWriteBarrier); + } + } else { + if (write.getWriteBarrierType() != WriteBarrierType.NONE) { + Assert.assertTrue(write.successors().count() == 1); + Assert.assertTrue(write.next() instanceof SerialWriteBarrier); + } + } + } + + for (ReadNode read : graph.getNodes(ReadNode.class)) { + if (read.getWriteBarrierType() != WriteBarrierType.NONE) { + if (read.location() instanceof ConstantLocationNode) { + Assert.assertTrue(((ConstantLocationNode) (read.location())).getDisplacement() == HotSpotRuntime.referentOffset()); + } else { + Assert.assertTrue(((IndexedLocationNode) (read.location())).getDisplacement() == HotSpotRuntime.referentOffset()); + } + Assert.assertTrue(((HotSpotRuntime) runtime()).config.useG1GC); + Assert.assertTrue(read.getWriteBarrierType() == WriteBarrierType.PRECISE); + Assert.assertTrue(read.next() instanceof G1PreWriteBarrier); } } } }); } + + private void test2(final String snippet, Object a, Object b, Object c) throws Exception { + HotSpotInstalledCode code = getInstalledCode(snippet); + code.execute(a, b, c); + } + + final class InlineAllPolicy implements InliningPolicy { + + @Override + public boolean continueInlining(StructuredGraph graph) { + return true; + } + + @Override + public boolean isWorthInlining(InlineInfo info, int inliningDepth, double probability, double relevance, boolean fullyProcessed) { + return true; + } + } } diff -r 7344fa3e8833 -r 329c22feda1f graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java Mon Jun 24 11:43:48 2013 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java Mon Jun 24 11:56:24 2013 +0200 @@ -32,6 +32,8 @@ import static com.oracle.graal.hotspot.HotSpotForeignCallLinkage.RegisterEffect.*; import static com.oracle.graal.hotspot.HotSpotForeignCallLinkage.Transition.*; import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*; +import static com.oracle.graal.hotspot.nodes.G1PostWriteBarrierStubCall.*; +import static com.oracle.graal.hotspot.nodes.G1PreWriteBarrierStubCall.*; import static com.oracle.graal.hotspot.nodes.MonitorExitStubCall.*; import static com.oracle.graal.hotspot.nodes.NewArrayStubCall.*; import static com.oracle.graal.hotspot.nodes.NewInstanceStubCall.*; @@ -51,8 +53,6 @@ import static com.oracle.graal.phases.GraalOptions.*; import static com.oracle.graal.replacements.Log.*; import static com.oracle.graal.replacements.MathSubstitutionsX86.*; -import static com.oracle.graal.hotspot.nodes.G1PostWriteBarrierStubCall.*; -import static com.oracle.graal.hotspot.nodes.G1PreWriteBarrierStubCall.*; import java.lang.reflect.*; import java.util.*; @@ -534,7 +534,8 @@ HotSpotResolvedJavaField field = (HotSpotResolvedJavaField) loadField.field(); ValueNode object = loadField.isStatic() ? ConstantNode.forObject(field.getDeclaringClass().mirror(), this, graph) : loadField.object(); assert loadField.kind() != Kind.Illegal; - ReadNode memoryRead = graph.add(new ReadNode(object, createFieldLocation(graph, field), loadField.stamp(), WriteBarrierType.NONE, (loadField.kind() == Kind.Object))); + WriteBarrierType barrierType = getFieldLoadBarrierType(field); + ReadNode memoryRead = graph.add(new ReadNode(object, createFieldLocation(graph, field), loadField.stamp(), barrierType, (loadField.kind() == Kind.Object))); tool.createNullCheckGuard(memoryRead, object); graph.replaceFixedWithFixed(loadField, memoryRead); @@ -616,15 +617,7 @@ } else if (n instanceof UnsafeLoadNode) { UnsafeLoadNode load = (UnsafeLoadNode) n; assert load.kind() != Kind.Illegal; - IndexedLocationNode location = IndexedLocationNode.create(ANY_LOCATION, load.accessKind(), load.displacement(), load.offset(), graph, 1); - // Unsafe Accesses to the metaspace or to any - // absolute address do not perform uncompression. - boolean compress = (!load.object().isNullConstant() && load.accessKind() == Kind.Object); - ReadNode memoryRead = graph.add(new ReadNode(load.object(), location, load.stamp(), WriteBarrierType.NONE, compress)); - // An unsafe read must not floating outside its block as may float above an explicit - // null check on its object. - memoryRead.setGuard(AbstractBeginNode.prevBegin(load)); - graph.replaceFixedWithFixed(load, memoryRead); + lowerUnsafeLoad(load); } else if (n instanceof UnsafeStoreNode) { UnsafeStoreNode store = (UnsafeStoreNode) n; IndexedLocationNode location = IndexedLocationNode.create(ANY_LOCATION, store.accessKind(), store.displacement(), store.offset(), graph, 1); @@ -781,6 +774,10 @@ if (tool.getLoweringType() == LoweringType.AFTER_GUARDS) { monitorSnippets.lower((MonitorExitNode) n, tool); } + } else if (n instanceof G1PreWriteBarrier) { + writeBarrierSnippets.lower((G1PreWriteBarrier) n, tool); + } else if (n instanceof G1PostWriteBarrier) { + writeBarrierSnippets.lower((G1PostWriteBarrier) n, tool); } else if (n instanceof SerialWriteBarrier) { writeBarrierSnippets.lower((SerialWriteBarrier) n, tool); } else if (n instanceof SerialArrayRangeWriteBarrier) { @@ -828,6 +825,107 @@ return hub; } + public static long referentOffset() { + try { + return unsafe.objectFieldOffset(java.lang.ref.Reference.class.getDeclaredField("referent")); + } catch (Exception e) { + throw new GraalInternalError(e); + } + } + + /** + * The following method lowers the unsafe load node. If any GC besides G1 is used, the unsafe + * load is lowered normally to a read node. However, if the G1 is used and the unsafe load could + * not be canonicalized to a load field, a runtime check has to be inserted in order to a add a + * g1-pre barrier if the loaded field is the referent field of the java.lang.ref.Reference + * class. The following code constructs the runtime check: + * + *
+     * if (offset == referentOffset() && type == java.lang.ref.Reference) {
+     *     read;
+     *     G1PreWriteBarrier(read);
+     * } else {
+     *     read;
+     * }
+     * 
+     * 
+ * + * TODO (ck): Replace the code below with a snippet. + * + */ + private void lowerUnsafeLoad(UnsafeLoadNode load) { + StructuredGraph graph = load.graph(); + boolean compress = (!load.object().isNullConstant() && load.accessKind() == Kind.Object); + if (config().useG1GC && load.object().kind() == Kind.Object && load.accessKind() == Kind.Object && !load.object().objectStamp().alwaysNull() && load.object().objectStamp().type() != null && + !(load.object().objectStamp().type().isArray())) { + IndexedLocationNode location = IndexedLocationNode.create(ANY_LOCATION, load.accessKind(), load.displacement(), load.offset(), graph, 1); + // Calculate offset+displacement + IntegerAddNode addNode = graph.add(new IntegerAddNode(Kind.Long, load.offset(), ConstantNode.forInt(load.displacement(), graph))); + // Compare previous result with referent offset (16) + CompareNode offsetCondition = CompareNode.createCompareNode(Condition.EQ, addNode, ConstantNode.forLong(referentOffset(), graph)); + // Instance of unsafe load is java.lang.ref.Reference + InstanceOfNode instanceOfNode = graph.add(new InstanceOfNode(lookupJavaType(java.lang.ref.Reference.class), load.object(), null)); + // The two barriers + ReadNode memoryReadNoBarrier = graph.add(new ReadNode(load.object(), location, load.stamp(), WriteBarrierType.NONE, compress)); + ReadNode memoryReadBarrier = graph.add(new ReadNode(load.object(), location, load.stamp(), WriteBarrierType.PRECISE, compress)); + + // EndNodes + EndNode leftTrue = graph.add(new EndNode()); + EndNode leftFalse = graph.add(new EndNode()); + EndNode rightFirst = graph.add(new EndNode()); + EndNode rightSecond = graph.add(new EndNode()); + + // MergeNodes + MergeNode mergeNoBarrier = graph.add(new MergeNode()); + MergeNode mergeFinal = graph.add(new MergeNode()); + + // IfNodes + IfNode ifNodeType = graph.add(new IfNode(instanceOfNode, memoryReadBarrier, leftFalse, 0.1)); + IfNode ifNodeOffset = graph.add(new IfNode(offsetCondition, ifNodeType, rightFirst, 0.1)); + + // Both branches are true (i.e. Add the barrier) + memoryReadBarrier.setNext(leftTrue); + mergeNoBarrier.addForwardEnd(rightFirst); + mergeNoBarrier.addForwardEnd(leftFalse); + mergeNoBarrier.setNext(memoryReadNoBarrier); + memoryReadNoBarrier.setNext(rightSecond); + mergeFinal.addForwardEnd(leftTrue); + mergeFinal.addForwardEnd(rightSecond); + + PhiNode phiNode = graph.add(new PhiNode(load.accessKind(), mergeFinal)); + phiNode.addInput(memoryReadBarrier); + phiNode.addInput(memoryReadNoBarrier); + + // An unsafe read must not floating outside its block as may float above an explicit + // null check on its object. + memoryReadNoBarrier.setGuard(AbstractBeginNode.prevBegin(memoryReadNoBarrier)); + memoryReadBarrier.setGuard(AbstractBeginNode.prevBegin(memoryReadBarrier)); + + assert load.successors().count() == 1; + Node next = load.successors().first(); + load.replaceAndDelete(ifNodeOffset); + mergeFinal.setNext((FixedNode) next); + ifNodeOffset.replaceAtUsages(phiNode); + } else { + IndexedLocationNode location = IndexedLocationNode.create(ANY_LOCATION, load.accessKind(), load.displacement(), load.offset(), graph, 1); + // Unsafe Accesses to the metaspace or to any + // absolute address do not perform uncompression. + ReadNode memoryRead = graph.add(new ReadNode(load.object(), location, load.stamp(), WriteBarrierType.NONE, compress)); + // An unsafe read must not floating outside its block as may float above an explicit + // null check on its object. + memoryRead.setGuard(AbstractBeginNode.prevBegin(load)); + graph.replaceFixedWithFixed(load, memoryRead); + } + } + + private static WriteBarrierType getFieldLoadBarrierType(HotSpotResolvedJavaField loadField) { + WriteBarrierType barrierType = WriteBarrierType.NONE; + if (config().useG1GC && loadField.getKind() == Kind.Object && loadField.getDeclaringClass().mirror() == java.lang.ref.Reference.class && loadField.getName().equals("referent")) { + barrierType = WriteBarrierType.PRECISE; + } + return barrierType; + } + private static WriteBarrierType getFieldStoreBarrierType(StoreFieldNode storeField) { WriteBarrierType barrierType = WriteBarrierType.NONE; if (storeField.field().getKind() == Kind.Object && !storeField.value().objectStamp().alwaysNull()) { diff -r 7344fa3e8833 -r 329c22feda1f graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/phases/WriteBarrierAdditionPhase.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/phases/WriteBarrierAdditionPhase.java Mon Jun 24 11:43:48 2013 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/phases/WriteBarrierAdditionPhase.java Mon Jun 24 11:56:24 2013 +0200 @@ -22,6 +22,8 @@ */ package com.oracle.graal.hotspot.phases; +import com.oracle.graal.graph.*; +import com.oracle.graal.hotspot.replacements.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.HeapAccess.WriteBarrierType; import com.oracle.graal.nodes.extended.*; @@ -35,6 +37,11 @@ @Override protected void run(StructuredGraph graph) { + for (ReadNode node : graph.getNodes(ReadNode.class)) { + if (node.getWriteBarrierType() == WriteBarrierType.PRECISE) { + addReadNodeBarriers(node, graph); + } + } for (WriteNode node : graph.getNodes(WriteNode.class)) { addWriteNodeBarriers(node, graph); } @@ -48,12 +55,27 @@ } } + private static void addReadNodeBarriers(ReadNode node, StructuredGraph graph) { + assert HotSpotReplacementsUtil.useG1GC(); + graph.addAfterFixed(node, graph.add(new G1PreWriteBarrier(node.object(), node, node.location(), false))); + } + private static void addWriteNodeBarriers(WriteNode node, StructuredGraph graph) { WriteBarrierType barrierType = node.getWriteBarrierType(); if (barrierType == WriteBarrierType.PRECISE) { - graph.addAfterFixed(node, graph.add(new SerialWriteBarrier(node.object(), node.location(), true))); + if (HotSpotReplacementsUtil.useG1GC()) { + graph.addBeforeFixed(node, graph.add(new G1PreWriteBarrier(node.object(), null, node.location(), true))); + graph.addAfterFixed(node, graph.add(new G1PostWriteBarrier(node.object(), node.value(), node.location(), true))); + } else { + graph.addAfterFixed(node, graph.add(new SerialWriteBarrier(node.object(), node.location(), true))); + } } else if (barrierType == WriteBarrierType.IMPRECISE) { - graph.addAfterFixed(node, graph.add(new SerialWriteBarrier(node.object(), node.location(), false))); + if (HotSpotReplacementsUtil.useG1GC()) { + graph.addBeforeFixed(node, graph.add(new G1PreWriteBarrier(node.object(), null, node.location(), true))); + graph.addAfterFixed(node, graph.add(new G1PostWriteBarrier(node.object(), node.value(), node.location(), false))); + } else { + graph.addAfterFixed(node, graph.add(new SerialWriteBarrier(node.object(), node.location(), false))); + } } else { assert barrierType == WriteBarrierType.NONE; } @@ -63,17 +85,31 @@ private static void addCASBarriers(CompareAndSwapNode node, StructuredGraph graph) { WriteBarrierType barrierType = node.getWriteBarrierType(); if (barrierType == WriteBarrierType.PRECISE) { - graph.addAfterFixed(node, graph.add(new SerialWriteBarrier(node.object(), node.getLocation(), true))); + if (HotSpotReplacementsUtil.useG1GC()) { + graph.addBeforeFixed(node, graph.add(new G1PreWriteBarrier(node.object(), node.expected(), node.getLocation(), false))); + graph.addAfterFixed(node, graph.add(new G1PostWriteBarrier(node.object(), node.newValue(), node.getLocation(), true))); + } else { + graph.addAfterFixed(node, graph.add(new SerialWriteBarrier(node.object(), node.getLocation(), true))); + } } else if (barrierType == WriteBarrierType.IMPRECISE) { - graph.addAfterFixed(node, graph.add(new SerialWriteBarrier(node.object(), node.getLocation(), false))); + if (HotSpotReplacementsUtil.useG1GC()) { + graph.addBeforeFixed(node, graph.add(new G1PreWriteBarrier(node.object(), node.expected(), node.getLocation(), false))); + graph.addAfterFixed(node, graph.add(new G1PostWriteBarrier(node.object(), node.newValue(), node.getLocation(), false))); + } else { + graph.addAfterFixed(node, graph.add(new SerialWriteBarrier(node.object(), node.getLocation(), false))); + } } else { assert barrierType == WriteBarrierType.NONE; } } private static void addArrayRangeBarriers(ArrayRangeWriteNode node, StructuredGraph graph) { - SerialArrayRangeWriteBarrier serialArrayRangeWriteBarrier = graph.add(new SerialArrayRangeWriteBarrier(node.getArray(), node.getIndex(), node.getLength())); - graph.addAfterFixed(node, serialArrayRangeWriteBarrier); + if (HotSpotReplacementsUtil.useG1GC()) { + throw new GraalInternalError("G1 does not yet suppot barriers for ArrayCopy Intrinsics. Run with -G:-IntrinsifyArrayCopy"); + } else { + SerialArrayRangeWriteBarrier serialArrayRangeWriteBarrier = graph.add(new SerialArrayRangeWriteBarrier(node.getArray(), node.getIndex(), node.getLength())); + graph.addAfterFixed(node, serialArrayRangeWriteBarrier); + } } } diff -r 7344fa3e8833 -r 329c22feda1f graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/WriteBarrierSnippets.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/WriteBarrierSnippets.java Mon Jun 24 11:43:48 2013 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/WriteBarrierSnippets.java Mon Jun 24 11:56:24 2013 +0200 @@ -83,10 +83,114 @@ } } + /** + * Log method of debugging purposes. + */ + static void log(boolean enabled, String format, WordBase value) { + if (enabled) { + Log.printf(format, value.rawValue()); + } + } + + @Snippet + public static void g1PreWriteBarrier(Object object, Object expectedObject, Object location, @ConstantParameter boolean doLoad) { + Word thread = thread(); + Object fixedObject = FixedValueAnchorNode.getObject(object); + Object fixedExpectedObject = FixedValueAnchorNode.getObject(expectedObject); + Word field = (Word) Word.fromArray(fixedObject, location); + Word previousOop = (Word) Word.fromObject(fixedExpectedObject); + byte markingValue = thread.readByte(HotSpotReplacementsUtil.g1SATBQueueMarkingOffset()); + Word bufferAddress = thread.readWord(HotSpotReplacementsUtil.g1SATBQueueBufferOffset()); + Word indexAddress = thread.add(HotSpotReplacementsUtil.g1SATBQueueIndexOffset()); + Word indexValue = indexAddress.readWord(0); + + // If the concurrent marker is enabled, the barrier is issued. + if (markingValue != (byte) 0) { + // If the previous value has to be loaded (before the write), the load is issued. + // The load is always issued except the cases of CAS and referent field. + if (doLoad) { + previousOop = (Word) Word.fromObject(field.readObject(0)); + } + // If the previous value is null the barrier should not be issued. + if (previousOop.notEqual(0)) { + // If the thread-local SATB buffer is full issue a native call which will + // initialize a new one and add the entry. + if (indexValue.notEqual(0)) { + Word nextIndex = indexValue.subtract(HotSpotReplacementsUtil.wordSize()); + Word logAddress = bufferAddress.add(nextIndex); + // Log the object to be marked as well as update the SATB's buffer next index. + logAddress.writeWord(0, previousOop); + indexAddress.writeWord(0, nextIndex); + } else { + G1PreWriteBarrierStubCall.call(previousOop.toObject()); + } + } + } + } + + @Snippet + public static void g1PostWriteBarrier(Object object, Object value, Object location, @ConstantParameter boolean usePrecise) { + Word thread = thread(); + Object fixedObject = FixedValueAnchorNode.getObject(object); + Object fixedValue = FixedValueAnchorNode.getObject(value); + Word oop = (Word) Word.fromObject(fixedObject); + Word field; + if (usePrecise) { + field = (Word) Word.fromArray(fixedObject, location); + } else { + field = oop; + } + + Word writtenValue = (Word) Word.fromObject(fixedValue); + Word bufferAddress = thread.readWord(HotSpotReplacementsUtil.g1CardQueueBufferOffset()); + Word indexAddress = thread.add(HotSpotReplacementsUtil.g1CardQueueIndexOffset()); + Word indexValue = thread.readWord(HotSpotReplacementsUtil.g1CardQueueIndexOffset()); + // The result of the xor reveals whether the installed pointer crosses heap regions. + // In case it does the write barrier has to be issued. + Word xorResult = (field.xor(writtenValue)).unsignedShiftRight(HotSpotReplacementsUtil.logOfHRGrainBytes()); + + // Calculate the address of the card to be enqueued to the + // thread local card queue. + Word cardBase = field.unsignedShiftRight(cardTableShift()); + long startAddress = cardTableStart(); + int displacement = 0; + if (((int) startAddress) == startAddress) { + displacement = (int) startAddress; + } else { + cardBase = cardBase.add(Word.unsigned(cardTableStart())); + } + Word cardAddress = cardBase.add(displacement); + + if (xorResult.notEqual(0)) { + // If the written value is not null continue with the barrier addition. + if (writtenValue.notEqual(0)) { + byte cardByte = cardAddress.readByte(0); + // If the card is already dirty, (hence already enqueued) skip the insertion. + if (cardByte != (byte) 0) { + cardAddress.writeByte(0, (byte) 0); + // If the thread local card queue is full, issue a native call which will + // initialize a new one and add the card entry. + if (indexValue.notEqual(0)) { + Word nextIndex = indexValue.subtract(HotSpotReplacementsUtil.wordSize()); + Word logAddress = bufferAddress.add(nextIndex); + // Log the object to be scanned as well as update + // the card queue's next index. + logAddress.writeWord(0, cardAddress); + indexAddress.writeWord(0, nextIndex); + } else { + G1PostWriteBarrierStubCall.call(cardAddress); + } + } + } + } + } + public static class Templates extends AbstractTemplates { private final SnippetInfo serialArrayWriteBarrier = snippet(WriteBarrierSnippets.class, "serialArrayWriteBarrier"); private final SnippetInfo serialArrayRangeWriteBarrier = snippet(WriteBarrierSnippets.class, "serialArrayRangeWriteBarrier"); + private final SnippetInfo g1PreWriteBarrier = snippet(WriteBarrierSnippets.class, "g1PreWriteBarrier"); + private final SnippetInfo g1PostWriteBarrier = snippet(WriteBarrierSnippets.class, "g1PostWriteBarrier"); public Templates(CodeCacheProvider runtime, Replacements replacements, TargetDescription target) { super(runtime, replacements, target); @@ -107,5 +211,23 @@ args.add("length", arrayRangeWriteBarrier.getLength()); template(args).instantiate(runtime, arrayRangeWriteBarrier, DEFAULT_REPLACER, args); } + + public void lower(G1PreWriteBarrier writeBarrierPre, @SuppressWarnings("unused") LoweringTool tool) { + Arguments args = new Arguments(g1PreWriteBarrier); + args.add("object", writeBarrierPre.getObject()); + args.add("expectedObject", writeBarrierPre.getExpectedObject()); + args.add("location", writeBarrierPre.getLocation()); + args.addConst("doLoad", writeBarrierPre.doLoad()); + template(args).instantiate(runtime, writeBarrierPre, DEFAULT_REPLACER, args); + } + + public void lower(G1PostWriteBarrier writeBarrierPost, @SuppressWarnings("unused") LoweringTool tool) { + Arguments args = new Arguments(g1PostWriteBarrier); + args.add("object", writeBarrierPost.getObject()); + args.add("value", writeBarrierPost.getValue()); + args.add("location", writeBarrierPost.getLocation()); + args.addConst("usePrecise", writeBarrierPost.usePrecise()); + template(args).instantiate(runtime, writeBarrierPost, DEFAULT_REPLACER, args); + } } } diff -r 7344fa3e8833 -r 329c22feda1f graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/NewInstanceStub.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/NewInstanceStub.java Mon Jun 24 11:43:48 2013 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/NewInstanceStub.java Mon Jun 24 11:56:24 2013 +0200 @@ -121,6 +121,11 @@ * operation was unsuccessful */ static Word refillAllocate(Word intArrayHub, int sizeInBytes, boolean log) { + // If G1 is enabled, the "eden" allocation space is not the same always + // and therefore we have to go to slowpath to allocate a new TLAB. + if (HotSpotReplacementsUtil.useG1GC()) { + return Word.zero(); + } if (!useTLAB()) { return edenAllocate(Word.unsigned(sizeInBytes), log); } diff -r 7344fa3e8833 -r 329c22feda1f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/FloatableAccessNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/FloatableAccessNode.java Mon Jun 24 11:43:48 2013 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/FloatableAccessNode.java Mon Jun 24 11:56:24 2013 +0200 @@ -22,6 +22,7 @@ */ package com.oracle.graal.nodes.extended; +import com.oracle.graal.api.meta.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.type.*; @@ -43,4 +44,13 @@ } public abstract FloatingAccessNode asFloatingNode(ValueNode lastLocationAccess); + + /** + * AccessNodes can float only if their location identities are not ANY_LOCATION. Furthermore, in + * case G1 is enabled any access (read) to the java.lang.ref.Reference.referent field which has + * an attached write barrier with pre-semantics can not also float. + */ + public boolean canFloat() { + return location().getLocationIdentity() != LocationIdentity.ANY_LOCATION && getWriteBarrierType() == WriteBarrierType.NONE; + } } diff -r 7344fa3e8833 -r 329c22feda1f graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FloatingReadPhase.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FloatingReadPhase.java Mon Jun 24 11:43:48 2013 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FloatingReadPhase.java Mon Jun 24 11:56:24 2013 +0200 @@ -175,7 +175,7 @@ StructuredGraph graph = accessNode.graph(); assert accessNode.getNullCheck() == false; LocationIdentity locationIdentity = accessNode.location().getLocationIdentity(); - if (locationIdentity != ANY_LOCATION) { + if (accessNode.canFloat()) { ValueNode lastLocationAccess = state.getLastLocationAccess(locationIdentity); FloatingAccessNode floatingNode = accessNode.asFloatingNode(lastLocationAccess); floatingNode.setNullCheck(accessNode.getNullCheck());