package org.graalvm.compiler.nodes.gc;
import org.graalvm.compiler.core.common.type.AbstractObjectStamp;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.nodes.NodeView;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.extended.ArrayRangeWrite;
import org.graalvm.compiler.nodes.extended.RawLoadNode;
import org.graalvm.compiler.nodes.extended.RawStoreNode;
import org.graalvm.compiler.nodes.java.AbstractCompareAndSwapNode;
import org.graalvm.compiler.nodes.java.LoweredAtomicReadAndWriteNode;
import org.graalvm.compiler.nodes.memory.FixedAccessNode;
import org.graalvm.compiler.nodes.memory.OnHeapMemoryAccess.BarrierType;
import org.graalvm.compiler.nodes.memory.ReadNode;
import org.graalvm.compiler.nodes.memory.WriteNode;
import org.graalvm.compiler.nodes.memory.address.AddressNode;
import org.graalvm.compiler.nodes.type.StampTool;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.ResolvedJavaField;
import jdk.vm.ci.meta.ResolvedJavaType;
public class G1BarrierSet implements BarrierSet {
private final ResolvedJavaType objectArrayType;
private final ResolvedJavaField referentField;
public G1BarrierSet(ResolvedJavaType objectArrayType, ResolvedJavaField referentField) {
this.objectArrayType = objectArrayType;
this.referentField = referentField;
}
@Override
public BarrierType readBarrierType(RawLoadNode load) {
if (load.object().getStackKind() == JavaKind.Object &&
load.accessKind() == JavaKind.Object &&
!StampTool.isPointerAlwaysNull(load.object())) {
long referentOffset = referentField.getOffset();
assert referentOffset > 0;
if (load.offset().isJavaConstant() && referentOffset != load.offset().asJavaConstant().asLong()) {
return BarrierType.NONE;
}
ResolvedJavaType referenceType = referentField.getDeclaringClass();
ResolvedJavaType type = StampTool.typeOrNull(load.object());
if (type != null && referenceType.isAssignableFrom(type)) {
if (load.offset().isJavaConstant() && referentOffset == load.offset().asJavaConstant().asLong()) {
return BarrierType.WEAK_FIELD;
}
return BarrierType.MAYBE_WEAK_FIELD;
}
if (type == null || type.isAssignableFrom(referenceType)) {
return BarrierType.MAYBE_WEAK_FIELD;
}
}
return BarrierType.NONE;
}
@Override
public BarrierType storeBarrierType(RawStoreNode store) {
return store.needsBarrier() ? guessStoreBarrierType(store.object(), store.value()) : BarrierType.NONE;
}
@Override
public BarrierType fieldLoadBarrierType(ResolvedJavaField field, JavaKind storageKind) {
if (field.getJavaKind() == JavaKind.Object && field.equals(referentField)) {
return BarrierType.WEAK_FIELD;
}
return BarrierType.NONE;
}
@Override
public BarrierType fieldStoreBarrierType(ResolvedJavaField field, JavaKind storageKind) {
return storageKind == JavaKind.Object ? BarrierType.FIELD : BarrierType.NONE;
}
@Override
public BarrierType arrayStoreBarrierType(JavaKind storageKind) {
return storageKind == JavaKind.Object ? BarrierType.ARRAY : BarrierType.NONE;
}
@Override
public BarrierType guessStoreBarrierType(ValueNode object, ValueNode value) {
if (value.getStackKind() == JavaKind.Object && object.getStackKind() == JavaKind.Object) {
ResolvedJavaType type = StampTool.typeOrNull(object);
if (type != null && type.isArray()) {
return BarrierType.ARRAY;
} else if (type == null || type.isAssignableFrom(objectArrayType)) {
return BarrierType.UNKNOWN;
} else {
return BarrierType.FIELD;
}
}
return BarrierType.NONE;
}
@Override
public void addBarriers(FixedAccessNode n) {
if (n instanceof ReadNode) {
addReadNodeBarriers((ReadNode) n);
} else if (n instanceof WriteNode) {
WriteNode write = (WriteNode) n;
addWriteBarriers(write, write.value(), null, true, write.getNullCheck());
} else if (n instanceof LoweredAtomicReadAndWriteNode) {
LoweredAtomicReadAndWriteNode atomic = (LoweredAtomicReadAndWriteNode) n;
addWriteBarriers(atomic, atomic.getNewValue(), null, true, atomic.getNullCheck());
} else if (n instanceof AbstractCompareAndSwapNode) {
AbstractCompareAndSwapNode cmpSwap = (AbstractCompareAndSwapNode) n;
addWriteBarriers(cmpSwap, cmpSwap.getNewValue(), cmpSwap.getExpectedValue(), false, false);
} else if (n instanceof ArrayRangeWrite) {
addArrayRangeBarriers((ArrayRangeWrite) n);
} else {
GraalError.guarantee(n.getBarrierType() == BarrierType.NONE, "missed a node that requires a GC barrier: %s", n.getClass());
}
}
private static void addReadNodeBarriers(ReadNode node) {
if (node.getBarrierType() == BarrierType.WEAK_FIELD || node.getBarrierType() == BarrierType.MAYBE_WEAK_FIELD) {
StructuredGraph graph = node.graph();
G1ReferentFieldReadBarrier barrier = graph.add(new G1ReferentFieldReadBarrier(node.getAddress(), node, node.getBarrierType() == BarrierType.MAYBE_WEAK_FIELD));
graph.addAfterFixed(node, barrier);
}
}
private void addWriteBarriers(FixedAccessNode node, ValueNode writtenValue, ValueNode expectedValue, boolean doLoad, boolean nullCheck) {
BarrierType barrierType = node.getBarrierType();
switch (barrierType) {
case NONE:
break;
case FIELD:
case ARRAY:
case UNKNOWN:
if (isObjectValue(writtenValue)) {
StructuredGraph graph = node.graph();
boolean init = node.getLocationIdentity().isInit();
if (!init) {
addG1PreWriteBarrier(node, node.getAddress(), expectedValue, doLoad, nullCheck, graph);
}
if (writeRequiresPostBarrier(node, writtenValue)) {
boolean precise = barrierType != BarrierType.FIELD;
addG1PostWriteBarrier(node, node.getAddress(), writtenValue, precise, graph);
}
}
break;
default:
throw new GraalError("unexpected barrier type: " + barrierType);
}
}
@SuppressWarnings("unused")
protected boolean writeRequiresPostBarrier(FixedAccessNode node, ValueNode writtenValue) {
assert isObjectValue(writtenValue);
return !StampTool.isPointerAlwaysNull(writtenValue);
}
private static void addArrayRangeBarriers(ArrayRangeWrite write) {
if (write.writesObjectArray()) {
StructuredGraph graph = write.asNode().graph();
if (!write.isInitialization()) {
G1ArrayRangePreWriteBarrier g1ArrayRangePreWriteBarrier = graph.add(new G1ArrayRangePreWriteBarrier(write.getAddress(), write.getLength(), write.getElementStride()));
graph.addBeforeFixed(write.asNode(), g1ArrayRangePreWriteBarrier);
}
G1ArrayRangePostWriteBarrier g1ArrayRangePostWriteBarrier = graph.add(new G1ArrayRangePostWriteBarrier(write.getAddress(), write.getLength(), write.getElementStride()));
graph.addAfterFixed(write.asNode(), g1ArrayRangePostWriteBarrier);
}
}
private static void addG1PreWriteBarrier(FixedAccessNode node, AddressNode address, ValueNode value, boolean doLoad, boolean nullCheck, StructuredGraph graph) {
G1PreWriteBarrier preBarrier = graph.add(new G1PreWriteBarrier(address, value, doLoad, nullCheck));
preBarrier.setStateBefore(node.stateBefore());
node.setNullCheck(false);
node.setStateBefore(null);
graph.addBeforeFixed(node, preBarrier);
}
private static void addG1PostWriteBarrier(FixedAccessNode node, AddressNode address, ValueNode value, boolean precise, StructuredGraph graph) {
final boolean alwaysNull = StampTool.isPointerAlwaysNull(value);
graph.addAfterFixed(node, graph.add(new G1PostWriteBarrier(address, value, precise, alwaysNull)));
}
private static boolean isObjectValue(ValueNode value) {
return value.stamp(NodeView.DEFAULT) instanceof AbstractObjectStamp;
}
}