Skip to content

Commit 6623ca0

Browse files
committed
[GR-59329] Add option to outline SerialGC post-write barrier snippets
PullRequest: graal/19148
2 parents 7d8c121 + 04d3fa0 commit 6623ca0

File tree

7 files changed

+62
-3
lines changed

7 files changed

+62
-3
lines changed

substratevm/mx.substratevm/mx_substratevm.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1568,6 +1568,10 @@ def prevent_build_path_in_libgraal():
15681568

15691569
# No need for container support in libgraal as HotSpot already takes care of it
15701570
'-H:-UseContainerSupport',
1571+
1572+
# Reduce image size by outlining all write barriers.
1573+
# Benchmarking showed no performance degradation.
1574+
'-H:+OutlineWriteBarriers',
15711575
] + ([
15721576
# Force page size to support libgraal on AArch64 machines with a page size up to 64K.
15731577
'-H:PageSize=64K'

substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/SerialGCOptions.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ public Integer getValue(OptionValues values) {
113113
@Option(help = "Ignore the maximum heap size while in VM-internal code.", type = OptionType.Expert)//
114114
public static final HostedOptionKey<Boolean> IgnoreMaxHeapSizeWhileInVMOperation = new HostedOptionKey<>(false, SerialGCOptions::serialGCOnly);
115115

116+
@Option(help = "Determines whether to always (if true) or never (if false) outline write barrier code to a separate function, " +
117+
"trading reduced image size for (potentially) worse performance. Serial GC only.", type = OptionType.Expert) //
118+
public static final HostedOptionKey<Boolean> OutlineWriteBarriers = new HostedOptionKey<>(null, SerialGCOptions::serialGCOnly);
119+
116120
/** Query these options only through an appropriate method. */
117121
public static class ConcealedOptions {
118122
@Option(help = "Collect old generation by compacting in-place instead of copying.", type = OptionType.Expert) //

substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/BarrierSnippets.java

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,26 +24,35 @@
2424
*/
2525
package com.oracle.svm.core.genscavenge.graal;
2626

27+
import static jdk.graal.compiler.core.common.spi.ForeignCallDescriptor.CallSideEffect.NO_SIDE_EFFECT;
28+
2729
import java.util.Map;
2830

2931
import org.graalvm.word.LocationIdentity;
3032
import org.graalvm.word.UnsignedWord;
3133

34+
import com.oracle.svm.core.Uninterruptible;
3235
import com.oracle.svm.core.genscavenge.ObjectHeaderImpl;
3336
import com.oracle.svm.core.genscavenge.SerialGCOptions;
3437
import com.oracle.svm.core.genscavenge.remset.RememberedSet;
38+
import com.oracle.svm.core.graal.meta.SubstrateForeignCallsProvider;
3539
import com.oracle.svm.core.graal.snippets.NodeLoweringProvider;
3640
import com.oracle.svm.core.graal.snippets.SubstrateTemplates;
3741
import com.oracle.svm.core.heap.ObjectHeader;
3842
import com.oracle.svm.core.heap.StoredContinuation;
43+
import com.oracle.svm.core.snippets.SnippetRuntime;
44+
import com.oracle.svm.core.snippets.SubstrateForeignCallTarget;
3945

4046
import jdk.graal.compiler.api.replacements.Snippet;
4147
import jdk.graal.compiler.api.replacements.Snippet.ConstantParameter;
48+
import jdk.graal.compiler.core.common.GraalOptions;
49+
import jdk.graal.compiler.core.common.spi.ForeignCallDescriptor;
4250
import jdk.graal.compiler.graph.Node;
4351
import jdk.graal.compiler.nodes.BreakpointNode;
4452
import jdk.graal.compiler.nodes.NamedLocationIdentity;
4553
import jdk.graal.compiler.nodes.extended.BranchProbabilityNode;
4654
import jdk.graal.compiler.nodes.extended.FixedValueAnchorNode;
55+
import jdk.graal.compiler.nodes.extended.ForeignCallNode;
4756
import jdk.graal.compiler.nodes.gc.SerialArrayRangeWriteBarrierNode;
4857
import jdk.graal.compiler.nodes.gc.SerialWriteBarrierNode;
4958
import jdk.graal.compiler.nodes.gc.WriteBarrierNode;
@@ -63,6 +72,10 @@ public class BarrierSnippets extends SubstrateTemplates implements Snippets {
6372
/** A LocationIdentity to distinguish card locations from other locations. */
6473
public static final LocationIdentity CARD_REMEMBERED_SET_LOCATION = NamedLocationIdentity.mutable("CardRememberedSet");
6574

75+
private static final SnippetRuntime.SubstrateForeignCallDescriptor POST_WRITE_BARRIER = SnippetRuntime.findForeignCall(BarrierSnippets.class, "postWriteBarrierStub",
76+
NO_SIDE_EFFECT,
77+
CARD_REMEMBERED_SET_LOCATION);
78+
6679
private final SnippetInfo postWriteBarrierSnippet;
6780

6881
BarrierSnippets(OptionValues options, Providers providers) {
@@ -78,8 +91,26 @@ public void registerLowerings(MetaAccessProvider metaAccess, Map<Class<? extends
7891
lowerings.put(SerialArrayRangeWriteBarrierNode.class, lowering);
7992
}
8093

94+
public static void registerForeignCalls(SubstrateForeignCallsProvider provider) {
95+
provider.register(POST_WRITE_BARRIER);
96+
}
97+
98+
@SubstrateForeignCallTarget(stubCallingConvention = false, fullyUninterruptible = true)
99+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
100+
public static void postWriteBarrierStub(Object object) {
101+
UnsignedWord objectHeader = ObjectHeader.readHeaderFromObject(object);
102+
if (ObjectHeaderImpl.isUnalignedHeader(objectHeader)) {
103+
RememberedSet.get().dirtyCardForUnalignedObject(object, false);
104+
} else {
105+
RememberedSet.get().dirtyCardForAlignedObject(object, false);
106+
}
107+
}
108+
109+
@Node.NodeIntrinsic(ForeignCallNode.class)
110+
private static native void callPostWriteBarrierStub(@Node.ConstantNodeParameter ForeignCallDescriptor descriptor, Object object);
111+
81112
@Snippet
82-
public static void postWriteBarrierSnippet(Object object, @ConstantParameter boolean alwaysAlignedChunk, @ConstantParameter boolean verifyOnly) {
113+
public static void postWriteBarrierSnippet(Object object, @ConstantParameter boolean shouldOutline, @ConstantParameter boolean alwaysAlignedChunk, @ConstantParameter boolean verifyOnly) {
83114
Object fixedObject = FixedValueAnchorNode.getObject(object);
84115
UnsignedWord objectHeader = ObjectHeader.readHeaderFromObject(fixedObject);
85116

@@ -94,10 +125,10 @@ public static void postWriteBarrierSnippet(Object object, @ConstantParameter boo
94125
if (BranchProbabilityNode.probability(BranchProbabilityNode.SLOW_PATH_PROBABILITY, ObjectHeaderImpl.isUnalignedHeader(objectHeader))) {
95126
BreakpointNode.breakpoint();
96127
}
97-
if (BranchProbabilityNode.probability(BranchProbabilityNode.SLOW_PATH_PROBABILITY, object == null)) {
128+
if (BranchProbabilityNode.probability(BranchProbabilityNode.SLOW_PATH_PROBABILITY, fixedObject == null)) {
98129
BreakpointNode.breakpoint();
99130
}
100-
if (BranchProbabilityNode.probability(BranchProbabilityNode.SLOW_PATH_PROBABILITY, object.getClass().isArray())) {
131+
if (BranchProbabilityNode.probability(BranchProbabilityNode.SLOW_PATH_PROBABILITY, fixedObject.getClass().isArray())) {
101132
BreakpointNode.breakpoint();
102133
}
103134
}
@@ -107,6 +138,11 @@ public static void postWriteBarrierSnippet(Object object, @ConstantParameter boo
107138
return;
108139
}
109140

141+
if (shouldOutline && !verifyOnly) {
142+
callPostWriteBarrierStub(POST_WRITE_BARRIER, fixedObject);
143+
return;
144+
}
145+
110146
if (!alwaysAlignedChunk) {
111147
boolean unaligned = ObjectHeaderImpl.isUnalignedHeader(objectHeader);
112148
if (BranchProbabilityNode.probability(BranchProbabilityNode.NOT_LIKELY_PROBABILITY, unaligned)) {
@@ -143,12 +179,20 @@ public void lower(WriteBarrierNode barrier, LoweringTool tool) {
143179
boolean alwaysAlignedChunk = baseType != null && !baseType.isArray() && !baseType.isJavaLangObject() && !baseType.isInterface();
144180

145181
args.add("object", address.getBase());
182+
args.addConst("shouldOutline", shouldOutline(barrier));
146183
args.addConst("alwaysAlignedChunk", alwaysAlignedChunk);
147184
args.addConst("verifyOnly", getVerifyOnly(barrier));
148185

149186
template(tool, barrier, args).instantiate(tool.getMetaAccess(), barrier, SnippetTemplate.DEFAULT_REPLACER, args);
150187
}
151188

189+
private static boolean shouldOutline(WriteBarrierNode barrier) {
190+
if (SerialGCOptions.OutlineWriteBarriers.getValue() != null) {
191+
return SerialGCOptions.OutlineWriteBarriers.getValue();
192+
}
193+
return GraalOptions.ReduceCodeSize.getValue(barrier.getOptions());
194+
}
195+
152196
private static boolean getVerifyOnly(WriteBarrierNode barrier) {
153197
if (barrier instanceof SerialWriteBarrierNode) {
154198
return ((SerialWriteBarrierNode) barrier).getVerifyOnly();

substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/GenScavengeGCFeature.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ public void beforeCompilation(BeforeCompilationAccess access) {
142142

143143
@Override
144144
public void registerForeignCalls(SubstrateForeignCallsProvider foreignCalls) {
145+
BarrierSnippets.registerForeignCalls(foreignCalls);
145146
GenScavengeAllocationSupport.registerForeignCalls(foreignCalls);
146147
}
147148

substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/remset/CardTableBasedRememberedSet.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,14 @@ public boolean hasRememberedSet(UnsignedWord header) {
128128

129129
@Override
130130
@AlwaysInline("GC performance")
131+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
131132
public void dirtyCardForAlignedObject(Object object, boolean verifyOnly) {
132133
AlignedChunkRememberedSet.dirtyCardForObject(object, verifyOnly);
133134
}
134135

135136
@Override
136137
@AlwaysInline("GC performance")
138+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
137139
public void dirtyCardForUnalignedObject(Object object, boolean verifyOnly) {
138140
UnalignedChunkRememberedSet.dirtyCardForObject(object, verifyOnly);
139141
}

substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/remset/NoRememberedSet.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,13 @@ public boolean hasRememberedSet(UnsignedWord header) {
124124
}
125125

126126
@Override
127+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
127128
public void dirtyCardForAlignedObject(Object object, boolean verifyOnly) {
128129
throw VMError.shouldNotReachHereAtRuntime(); // ExcludeFromJacocoGeneratedReport
129130
}
130131

131132
@Override
133+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
132134
public void dirtyCardForUnalignedObject(Object object, boolean verifyOnly) {
133135
throw VMError.shouldNotReachHereAtRuntime(); // ExcludeFromJacocoGeneratedReport
134136
}

substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/remset/RememberedSet.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ static RememberedSet get() {
115115
* (from old generation to young generation, or from image heap to runtime heap).
116116
*/
117117
@AlwaysInline("GC performance")
118+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
118119
void dirtyCardForAlignedObject(Object object, boolean verifyOnly);
119120

120121
/**
@@ -123,6 +124,7 @@ static RememberedSet get() {
123124
* (from old generation to young generation, or from image heap to runtime heap).
124125
*/
125126
@AlwaysInline("GC performance")
127+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
126128
void dirtyCardForUnalignedObject(Object object, boolean verifyOnly);
127129

128130
/**

0 commit comments

Comments
 (0)