Skip to content

[GR-63262] SVM Serial: Emit imprecise write barriers for arrays in aligned heap chunks #11008

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
Apr 8, 2025
Merged
Show file tree
Hide file tree
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
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
Expand Down Expand Up @@ -33,6 +33,7 @@
import jdk.graal.compiler.nodes.ValueNode;
import jdk.graal.compiler.nodes.extended.RawStoreNode;
import jdk.graal.compiler.nodes.memory.FixedAccessNode;
import jdk.graal.compiler.nodes.spi.CoreProviders;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.ResolvedJavaField;

Expand All @@ -50,7 +51,7 @@ default BarrierType postAllocationInitBarrier(BarrierType original) {
return original;
}

void addBarriers(FixedAccessNode n);
void addBarriers(FixedAccessNode n, CoreProviders context);

BarrierType fieldReadBarrierType(ResolvedJavaField field, JavaKind storageKind);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
Expand Down Expand Up @@ -32,7 +32,6 @@
import jdk.graal.compiler.core.common.type.Stamp;
import jdk.graal.compiler.debug.Assertions;
import jdk.graal.compiler.debug.GraalError;
import jdk.graal.compiler.nodes.FixedWithNextNode;
import jdk.graal.compiler.nodes.NodeView;
import jdk.graal.compiler.nodes.StructuredGraph;
import jdk.graal.compiler.nodes.ValueNode;
Expand All @@ -43,8 +42,8 @@
import jdk.graal.compiler.nodes.memory.FixedAccessNode;
import jdk.graal.compiler.nodes.memory.ReadNode;
import jdk.graal.compiler.nodes.memory.WriteNode;
import jdk.graal.compiler.nodes.memory.address.AddressNode;
import jdk.graal.compiler.nodes.memory.address.OffsetAddressNode;
import jdk.graal.compiler.nodes.spi.CoreProviders;
import jdk.graal.compiler.nodes.type.StampTool;
import jdk.graal.compiler.nodes.util.GraphUtil;
import jdk.vm.ci.meta.JavaKind;
Expand Down Expand Up @@ -112,20 +111,17 @@ public boolean hasReadBarrier() {
}

@Override
public void addBarriers(FixedAccessNode n) {
public void addBarriers(FixedAccessNode n, CoreProviders context) {
if (n instanceof ReadNode) {
// nothing to do
} else if (n instanceof WriteNode) {
WriteNode write = (WriteNode) n;
addWriteBarrier(write, write.value());
} else if (n instanceof LoweredAtomicReadAndWriteNode) {
LoweredAtomicReadAndWriteNode atomic = (LoweredAtomicReadAndWriteNode) n;
addWriteBarrier(atomic, atomic.getNewValue());
} else if (n instanceof AbstractCompareAndSwapNode) {
AbstractCompareAndSwapNode cmpSwap = (AbstractCompareAndSwapNode) n;
addWriteBarrier(cmpSwap, cmpSwap.getNewValue());
} else if (n instanceof ArrayRangeWrite) {
addArrayRangeBarriers((ArrayRangeWrite) n);
} else if (n instanceof WriteNode write) {
addWriteBarrier(write, write.value(), context);
} else if (n instanceof LoweredAtomicReadAndWriteNode atomic) {
addWriteBarrier(atomic, atomic.getNewValue(), context);
} else if (n instanceof AbstractCompareAndSwapNode cmpSwap) {
addWriteBarrier(cmpSwap, cmpSwap.getNewValue(), context);
} else if (n instanceof ArrayRangeWrite rangeWrite) {
addArrayRangeBarriers(rangeWrite, context);
} else {
GraalError.guarantee(n.getBarrierType() == BarrierType.NONE, "missed a node that requires a GC barrier: %s", n.getClass());
}
Expand Down Expand Up @@ -175,25 +171,23 @@ public boolean isMatchingBarrier(FixedAccessNode n, WriteBarrierNode barrier) {
if (n instanceof ReadNode) {
return false;
} else if (n instanceof WriteNode || n instanceof LoweredAtomicReadAndWriteNode || n instanceof AbstractCompareAndSwapNode) {
return barrier instanceof SerialWriteBarrierNode && matches(n, (SerialWriteBarrierNode) barrier);
} else if (n instanceof ArrayRangeWrite) {
return barrier instanceof SerialArrayRangeWriteBarrierNode && matches((ArrayRangeWrite) n, (SerialArrayRangeWriteBarrierNode) barrier);
return barrier instanceof SerialWriteBarrierNode s && matches(n, s);
} else if (n instanceof ArrayRangeWrite w) {
return (barrier instanceof SerialArrayRangeWriteBarrierNode r && matches(w, r)) || (barrier instanceof SerialWriteBarrierNode s && !s.usePrecise() && matches(n, s));
} else {
throw GraalError.shouldNotReachHere("Unexpected node: " + n.getClass()); // ExcludeFromJacocoGeneratedReport
}
}

public void addArrayRangeBarriers(ArrayRangeWrite write) {
private void addArrayRangeBarriers(ArrayRangeWrite write, CoreProviders context) {
if (arrayRangeWriteRequiresBarrier(write)) {
StructuredGraph graph = write.asNode().graph();
SerialArrayRangeWriteBarrierNode serialArrayRangeWriteBarrier = graph.add(new SerialArrayRangeWriteBarrierNode(write.getAddress(), write.getLength(), write.getElementStride()));
graph.addAfterFixed(write.postBarrierInsertionPosition(), serialArrayRangeWriteBarrier);
addSerialPostRangeWriteBarrier(write, context);
}
}

private void addWriteBarrier(FixedAccessNode node, ValueNode writtenValue) {
private void addWriteBarrier(FixedAccessNode node, ValueNode writtenValue, CoreProviders context) {
if (needsWriteBarrier(node, writtenValue)) {
addSerialPostWriteBarrier(node, node.getAddress(), node.graph());
addSerialPostWriteBarrier(node, context);
}
}

Expand Down Expand Up @@ -228,15 +222,34 @@ private static boolean hasWriteBarrier(FixedAccessNode node) {
}

private static boolean hasWriteBarrier(ArrayRangeWrite write) {
FixedWithNextNode node = write.asFixedWithNextNode();
return node.next() instanceof SerialArrayRangeWriteBarrierNode && matches(write, (SerialArrayRangeWriteBarrierNode) node.next());
FixedAccessNode node = (FixedAccessNode) write;
return (node.next() instanceof SerialArrayRangeWriteBarrierNode r && matches(write, r)) || (node.next() instanceof SerialWriteBarrierNode s && !s.usePrecise() && matches(node, s));
}

private void addSerialPostRangeWriteBarrier(ArrayRangeWrite write, CoreProviders context) {
FixedAccessNode node = (FixedAccessNode) write;
StructuredGraph graph = node.graph();
if (!barrierPrecise(node, write.getAddress().getBase(), context)) {
SerialWriteBarrierNode barrier = graph.add(new SerialWriteBarrierNode(node.getAddress(), false));
graph.addAfterFixed(node, barrier);
return;
}

SerialArrayRangeWriteBarrierNode barrier = graph.add(new SerialArrayRangeWriteBarrierNode(write.getAddress(), write.getLength(), write.getElementStride()));
graph.addAfterFixed(write.postBarrierInsertionPosition(), barrier);
}

private void addSerialPostWriteBarrier(FixedAccessNode node, CoreProviders context) {
StructuredGraph graph = node.graph();
boolean precise = barrierPrecise(node, node.getAddress().getBase(), context);
graph.addAfterFixed(node, graph.add(new SerialWriteBarrierNode(node.getAddress(), precise)));
}

private static void addSerialPostWriteBarrier(FixedAccessNode node, AddressNode address, StructuredGraph graph) {
// Use a precise barrier for everything that might be an array write. Being too precise with
// the barriers does not cause any correctness issues.
boolean precise = node.getBarrierType() != BarrierType.FIELD && node.getBarrierType() != BarrierType.AS_NO_KEEPALIVE_WRITE;
graph.addAfterFixed(node, graph.add(new SerialWriteBarrierNode(address, precise)));
/**
* Decide if a precise barrier is needed for an access.
*/
protected boolean barrierPrecise(FixedAccessNode node, @SuppressWarnings("unused") ValueNode base, @SuppressWarnings("unused") CoreProviders context) {
return node.getBarrierType() != BarrierType.FIELD && node.getBarrierType() != BarrierType.AS_NO_KEEPALIVE_WRITE;
}

private static boolean isNonNullObjectValue(ValueNode value) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
Expand Down Expand Up @@ -42,6 +42,7 @@
import jdk.graal.compiler.nodes.memory.ReadNode;
import jdk.graal.compiler.nodes.memory.WriteNode;
import jdk.graal.compiler.nodes.memory.address.AddressNode;
import jdk.graal.compiler.nodes.spi.CoreProviders;
import jdk.graal.compiler.nodes.type.StampTool;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.ResolvedJavaField;
Expand Down Expand Up @@ -120,7 +121,7 @@ public boolean hasReadBarrier() {
}

@Override
public void addBarriers(FixedAccessNode n) {
public void addBarriers(FixedAccessNode n, CoreProviders context) {
if (n instanceof ReadNode) {
addReadNodeBarriers((ReadNode) n);
} else if (n instanceof WriteNode) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2025, 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
Expand Down Expand Up @@ -31,6 +31,7 @@
import jdk.graal.compiler.nodes.ValueNode;
import jdk.graal.compiler.nodes.extended.RawStoreNode;
import jdk.graal.compiler.nodes.memory.FixedAccessNode;
import jdk.graal.compiler.nodes.spi.CoreProviders;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.ResolvedJavaField;

Expand All @@ -49,7 +50,7 @@ public boolean hasReadBarrier() {
}

@Override
public void addBarriers(FixedAccessNode n) {
public void addBarriers(FixedAccessNode n, CoreProviders context) {
// Nothing to do.
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, 2025, 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
Expand Down Expand Up @@ -47,6 +47,7 @@
import jdk.graal.compiler.nodes.memory.ReadNode;
import jdk.graal.compiler.nodes.memory.WriteNode;
import jdk.graal.compiler.nodes.memory.address.AddressNode;
import jdk.graal.compiler.nodes.spi.CoreProviders;
import jdk.graal.compiler.nodes.type.StampTool;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.ResolvedJavaField;
Expand Down Expand Up @@ -160,7 +161,7 @@ public boolean hasReadBarrier() {
}

@Override
public void addBarriers(FixedAccessNode n) {
public void addBarriers(FixedAccessNode n, CoreProviders context) {
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2025, 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
Expand Down Expand Up @@ -52,7 +52,7 @@ protected void run(StructuredGraph graph, CoreProviders context) {
if (barrierSet.hasWriteBarrier()) {
for (FixedAccessNode n : graph.getNodes(FixedAccessNode.TYPE)) {
try (DebugCloseable scope = n.graph().withNodeSourcePosition(n)) {
barrierSet.addBarriers(n);
barrierSet.addBarriers(n, context);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2025, 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
Expand Down Expand Up @@ -46,6 +46,7 @@
import com.oracle.svm.core.config.ConfigurationValues;
import com.oracle.svm.core.genscavenge.AlignedHeapChunk.AlignedHeader;
import com.oracle.svm.core.genscavenge.UnalignedHeapChunk.UnalignedHeader;
import com.oracle.svm.core.genscavenge.graal.GenScavengeAllocationSupport;
import com.oracle.svm.core.genscavenge.graal.nodes.FormatArrayNode;
import com.oracle.svm.core.genscavenge.graal.nodes.FormatObjectNode;
import com.oracle.svm.core.genscavenge.graal.nodes.FormatPodNode;
Expand Down Expand Up @@ -273,7 +274,7 @@ private static Object slowPathNewArrayLikeObject0(DynamicHub hub, int length, Un
HeapImpl.exitIfAllocationDisallowed("ThreadLocalAllocation.slowPathNewArrayOrPodWithoutAllocating", DynamicHub.toClass(hub).getName());
GCImpl.getGCImpl().maybeCollectOnAllocation(size);

if (size.aboveOrEqual(HeapParameters.getLargeArrayThreshold())) {
if (!GenScavengeAllocationSupport.arrayAllocatedInAlignedChunk(size)) {
/* Large arrays go into their own unaligned chunk. */
boolean needsZeroing = !HeapChunkProvider.areUnalignedChunksZeroed();
UnalignedHeapChunk.UnalignedHeader newTlabChunk = HeapImpl.getChunkProvider().produceUnalignedChunk(size);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2025, 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
Expand Down Expand Up @@ -104,7 +104,7 @@ public boolean useTLAB() {

@Override
public boolean shouldAllocateInTLAB(UnsignedWord size, boolean isArray) {
return !isArray || size.belowThan(HeapParameters.getLargeArrayThreshold());
return !isArray || arrayAllocatedInAlignedChunk(size);
}

@Override
Expand All @@ -122,6 +122,10 @@ public int tlabEndOffset() {
return ThreadLocalAllocation.Descriptor.offsetOfAllocationEnd();
}

public static boolean arrayAllocatedInAlignedChunk(UnsignedWord objectSize) {
return objectSize.belowThan(HeapParameters.getLargeArrayThreshold());
}

@SubstrateForeignCallTarget(stubCallingConvention = false)
@Uninterruptible(reason = "The newly allocated object must be young or all its covered cards must be dirty.")
private static Object slowNewInstance(Word objectHeader) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2025, 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
Expand All @@ -24,11 +24,24 @@
*/
package com.oracle.svm.core.genscavenge.graal;

import jdk.graal.compiler.core.common.memory.BarrierType;
import jdk.graal.compiler.nodes.gc.CardTableBarrierSet;
import org.graalvm.word.UnsignedWord;

import com.oracle.svm.core.StaticFieldsSupport;
import com.oracle.svm.core.hub.LayoutEncoding;
import com.oracle.svm.core.meta.SharedType;

import jdk.graal.compiler.core.common.NumUtil;
import jdk.graal.compiler.core.common.memory.BarrierType;
import jdk.graal.compiler.core.common.type.IntegerStamp;
import jdk.graal.compiler.debug.GraalError;
import jdk.graal.compiler.nodes.NodeView;
import jdk.graal.compiler.nodes.ValueNode;
import jdk.graal.compiler.nodes.gc.CardTableBarrierSet;
import jdk.graal.compiler.nodes.memory.FixedAccessNode;
import jdk.graal.compiler.nodes.spi.ArrayLengthProvider;
import jdk.graal.compiler.nodes.spi.CoreProviders;
import jdk.graal.compiler.nodes.type.StampTool;
import jdk.graal.compiler.nodes.util.GraphUtil;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.ResolvedJavaField;
import jdk.vm.ci.meta.ResolvedJavaType;
Expand All @@ -50,4 +63,51 @@ public BarrierType fieldWriteBarrierType(ResolvedJavaField field, JavaKind stora
}
return super.fieldWriteBarrierType(field, storageKind);
}

/**
* On SVM, precise write barriers are only needed for object arrays that are located in
* unaligned chunks.
* <p>
* This method is conservative, that is it can return {@code true} even if in practice a precise
* barrier is not needed.
*/
@Override
protected boolean barrierPrecise(FixedAccessNode node, ValueNode base, CoreProviders context) {
if (!super.barrierPrecise(node, base, context)) {
return false;
}

ResolvedJavaType baseType = StampTool.typeOrNull(base);
if (baseType == null) {
return true;
}

/*
* We know that instances (in contrast to arrays) are always in aligned chunks, except for
* StoredContinuation objects, but these are immutable and do not need barriers.
*
* 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.
*/
if (!baseType.isArray()) {
return baseType.isInterface() || baseType.isJavaLangObject();
}

/*
* Arrays smaller than HeapParameters.getLargeArrayThreshold() are allocated in the aligned
* chunks.
*/
ValueNode length = GraphUtil.arrayLength(base, ArrayLengthProvider.FindLengthMode.SEARCH_ONLY, context.getConstantReflection());
if (length == null) {
return true;
}

IntegerStamp lengthStamp = (IntegerStamp) length.stamp(NodeView.DEFAULT);
GraalError.guarantee(lengthStamp.getBits() == Integer.SIZE, "unexpected length %s", lengthStamp);
int lengthBound = NumUtil.safeToInt(lengthStamp.upperBound());
SharedType componentType = (SharedType) baseType.getComponentType();
UnsignedWord sizeBound = LayoutEncoding.getArrayAllocationSize(componentType.getHub().getLayoutEncoding(), lengthBound);
return !GenScavengeAllocationSupport.arrayAllocatedInAlignedChunk(sizeBound);
}
}
Loading