Skip to content

[GR-33665] Optimize serial GC write barrier for non-array stores. #3771

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 11, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
import org.graalvm.compiler.debug.DebugHandlersFactory;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.nodes.BreakpointNode;
import org.graalvm.compiler.nodes.NamedLocationIdentity;
import org.graalvm.compiler.nodes.extended.BranchProbabilityNode;
import org.graalvm.compiler.nodes.extended.FixedValueAnchorNode;
Expand All @@ -42,6 +43,7 @@
import org.graalvm.compiler.nodes.gc.WriteBarrier;
import org.graalvm.compiler.nodes.memory.address.OffsetAddressNode;
import org.graalvm.compiler.nodes.spi.LoweringTool;
import org.graalvm.compiler.nodes.type.StampTool;
import org.graalvm.compiler.options.Option;
import org.graalvm.compiler.options.OptionValues;
import org.graalvm.compiler.phases.util.Providers;
Expand All @@ -64,13 +66,18 @@
import com.oracle.svm.core.util.Counter;
import com.oracle.svm.core.util.CounterFeature;

import jdk.vm.ci.meta.ResolvedJavaType;

public class BarrierSnippets extends SubstrateTemplates implements Snippets {
/** A LocationIdentity to distinguish card locations from other locations. */
public static final LocationIdentity CARD_REMEMBERED_SET_LOCATION = NamedLocationIdentity.mutable("CardRememberedSet");

public static class Options {
@Option(help = "Instrument write barriers with counters")//
public static final HostedOptionKey<Boolean> CountWriteBarriers = new HostedOptionKey<>(false);

@Option(help = "Verify write barriers")//
public static final HostedOptionKey<Boolean> VerifyWriteBarriers = new HostedOptionKey<>(false);
}

@Fold
Expand All @@ -90,23 +97,40 @@ public void registerLowerings(Map<Class<? extends Node>, NodeLoweringProvider<?>
}

@Snippet
public static void postWriteBarrierSnippet(Object object, @ConstantParameter boolean verifyOnly) {
public static void postWriteBarrierSnippet(Object object, @ConstantParameter boolean alwaysAlignedChunk, @ConstantParameter boolean verifyOnly) {
counters().postWriteBarrier.inc();

Object fixedObject = FixedValueAnchorNode.getObject(object);
UnsignedWord objectHeader = ObjectHeaderImpl.readHeaderFromObject(fixedObject);

if (Options.VerifyWriteBarriers.getValue() && alwaysAlignedChunk) {
/*
* To increase verification coverage, we do the verification before checking if a
* barrier is needed at all. And in addition to verifying that the object is in an
* aligned chunk, we also verify that it is not an array at all because most arrays are
* small and therefore in an aligned chunk.
*/
if (ObjectHeaderImpl.isUnalignedHeader(objectHeader) || object == null || object.getClass().isArray()) {
BreakpointNode.breakpoint();
}
}

boolean needsBarrier = RememberedSet.get().hasRememberedSet(objectHeader);
if (BranchProbabilityNode.probability(BranchProbabilityNode.FREQUENT_PROBABILITY, !needsBarrier)) {
return;
}
boolean aligned = ObjectHeaderImpl.isAlignedHeader(objectHeader);
if (BranchProbabilityNode.probability(BranchProbabilityNode.LIKELY_PROBABILITY, aligned)) {
counters().postWriteBarrierAligned.inc();
RememberedSet.get().dirtyCardForAlignedObject(fixedObject, verifyOnly);
return;

if (!alwaysAlignedChunk) {
boolean unaligned = ObjectHeaderImpl.isUnalignedHeader(objectHeader);
if (BranchProbabilityNode.probability(BranchProbabilityNode.NOT_LIKELY_PROBABILITY, unaligned)) {
counters().postWriteBarrierUnaligned.inc();
RememberedSet.get().dirtyCardForUnalignedObject(fixedObject, verifyOnly);
return;
}
}
counters().postWriteBarrierUnaligned.inc();
RememberedSet.get().dirtyCardForUnalignedObject(fixedObject, verifyOnly);

counters().postWriteBarrierAligned.inc();
RememberedSet.get().dirtyCardForAlignedObject(fixedObject, verifyOnly);
}

private class PostWriteBarrierLowering implements NodeLoweringProvider<WriteBarrier> {
Expand All @@ -116,7 +140,20 @@ private class PostWriteBarrierLowering implements NodeLoweringProvider<WriteBarr
public void lower(WriteBarrier barrier, LoweringTool tool) {
Arguments args = new Arguments(postWriteBarrierSnippet, barrier.graph().getGuardsStage(), tool.getLoweringStage());
OffsetAddressNode address = (OffsetAddressNode) barrier.getAddress();

/*
* We know that instances (in contrast to arrays) are always in aligned chunks. There is
* no code anywhere that would allocate an instance into an unaligned chunk.
*
* Note that arrays can be assigned to values that have the type java.lang.Object, so
* that case is excluded. Arrays can also implement some interfaces, like Serializable.
* For simplicity, we exclude all interface types.
*/
ResolvedJavaType baseType = StampTool.typeOrNull(address.getBase());
boolean alwaysAlignedChunk = baseType != null && !baseType.isArray() && !baseType.isJavaLangObject() && !baseType.isInterface();

args.add("object", address.getBase());
args.addConst("alwaysAlignedChunk", alwaysAlignedChunk);
args.addConst("verifyOnly", getVerifyOnly(barrier));

template(barrier, args).instantiate(providers.getMetaAccess(), barrier, SnippetTemplate.DEFAULT_REPLACER, args);
Expand Down