Skip to content

Commit 19ec24d

Browse files
committed
GR-73325: Preserve bytecode handler slot sentinels
1 parent 8b1bdc0 commit 19ec24d

4 files changed

Lines changed: 56 additions & 27 deletions

File tree

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/LIR.java

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -292,16 +292,10 @@ public static LabelRef getExceptionEdge(LIRInstruction op) {
292292
public static final int MAX_EXCEPTION_EDGE_OP_DISTANCE_FROM_END = 3;
293293

294294
/**
295-
* Multi-result operations may be followed by moves that materialize additional defined values
296-
* before the block end. Those moves are still part of the operation's normal successor value
297-
* setup, so the verifier allows them to extend the usual exception-edge distance.
295+
* Custom calling conventions may materialize additional return values with moves after the
296+
* call. Those moves are still part of the operation's normal successor value setup, so the
297+
* verifier allows them to extend the usual exception-edge distance.
298298
*/
299-
private static boolean hasMultipleDefs(LIRInstruction op) {
300-
int[] defCount = {0};
301-
op.visitEachOutput((instruction, value, mode, flags) -> defCount[0]++);
302-
return defCount[0] > 1;
303-
}
304-
305299
private static boolean hasOnlyTrailingMoves(ArrayList<LIRInstruction> ops, int exceptionEdgeIndex, int lastIndex) {
306300
for (int i = exceptionEdgeIndex + 1; i < lastIndex; i++) {
307301
if (!ops.get(i).isMoveOp()) {
@@ -327,8 +321,7 @@ public static boolean verifyBlock(LIR lir, BasicBlock<?> block) {
327321
assert opWithExceptionEdge == null : "multiple ops with an exception edge not allowed";
328322
opWithExceptionEdge = op;
329323
int distanceFromEnd = lastIndex - index;
330-
assert distanceFromEnd <= MAX_EXCEPTION_EDGE_OP_DISTANCE_FROM_END ||
331-
(hasMultipleDefs(op) && hasOnlyTrailingMoves(ops, index, lastIndex)) : distanceFromEnd;
324+
assert distanceFromEnd <= MAX_EXCEPTION_EDGE_OP_DISTANCE_FROM_END || hasOnlyTrailingMoves(ops, index, lastIndex) : distanceFromEnd;
332325
}
333326
index++;
334327
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/code/PendingExceptionStateSupport.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.graalvm.nativeimage.Platform;
3232
import org.graalvm.nativeimage.Platforms;
3333

34+
import com.oracle.svm.core.NeverInline;
3435
import com.oracle.svm.shared.BuildPhaseProvider.ReadyForCompilation;
3536
import com.oracle.svm.core.heap.UnknownPrimitiveField;
3637
import com.oracle.svm.core.thread.ThreadListener;
@@ -90,10 +91,10 @@ public void setMaxSlots(int newMaxSlots) {
9091
}
9192

9293
/**
93-
* Enables diagnostic poisoning of pending-state slots. Consumed object slots are always cleared
94-
* to {@code null} for GC. When disabled, consumed primitive slots are left unchanged. When
95-
* enabled, initially empty slots and consumed primitive slots contain recognizable sentinel
96-
* values to expose stale or duplicate reads.
94+
* Enables diagnostic poisoning of pending-state slots. When disabled, consumed object slots are
95+
* cleared to {@code null} for GC and consumed primitive slots are left unchanged. When enabled,
96+
* initially empty and consumed slots contain recognizable sentinel values to expose stale or
97+
* duplicate reads.
9798
*/
9899
@Platforms(Platform.HOSTED_ONLY.class)
99100
public void setUseSlotSentinel(boolean useSlotSentinel) {
@@ -134,6 +135,11 @@ static void initializeSlotSentinels(PendingExceptionStateHolder holder) {
134135
Arrays.fill(holder.primitiveSlots, PRIMITIVE_SLOT_SENTINEL);
135136
}
136137

138+
@NeverInline("Keep object sentinel materialization out of generated bytecode-handler unwind graphs")
139+
public static void poisonObjectSlot(Object[] objectSlots, int slotIndex) {
140+
objectSlots[slotIndex] = OBJECT_SLOT_SENTINEL;
141+
}
142+
137143
private static final class ObjectSlotSentinel {
138144
}
139145
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/BytecodeHandlerFeature.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import com.oracle.svm.shared.singletons.traits.BuiltinTraits.DisallowLayered;
5050
import com.oracle.svm.shared.singletons.traits.BuiltinTraits.NoLayeredCallbacks;
5151
import com.oracle.svm.shared.singletons.traits.SingletonTraits;
52+
import com.oracle.svm.shared.util.ReflectionUtil;
5253
import com.oracle.svm.shared.util.SubstrateUtil;
5354
import com.oracle.svm.util.OriginalClassProvider;
5455

@@ -126,6 +127,10 @@ public void afterRegistration(AfterRegistrationAccess access) {
126127
@Override
127128
public void beforeAnalysis(BeforeAnalysisAccess access) {
128129
BeforeAnalysisAccessImpl accessImpl = (BeforeAnalysisAccessImpl) access;
130+
if (Options.BytecodeHandlerSlotSentinel.getValue()) {
131+
accessImpl.registerAsRoot(ReflectionUtil.lookupMethod(PendingExceptionStateSupport.class, "poisonObjectSlot", Object[].class, int.class), false,
132+
"Object-slot sentinel support, registered in " + BytecodeHandlerFeature.class);
133+
}
129134
BytecodeInterpreterAnnotations.registerCompilerDirectives(accessImpl.getMetaAccess(), OriginalClassProvider::getOriginalType);
130135
}
131136

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SubstrateBytecodeHandlerUnwindPath.java

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
package com.oracle.svm.hosted;
2626

2727
import java.lang.reflect.Field;
28+
import java.lang.reflect.Method;
2829
import java.util.List;
2930
import java.util.function.Function;
3031

@@ -34,22 +35,27 @@
3435
import com.oracle.svm.core.graal.code.PendingExceptionStateSupport;
3536
import com.oracle.svm.core.graal.nodes.ReadReservedRegisterFloatingNode;
3637
import com.oracle.svm.core.graal.thread.LoadVMThreadLocalNode;
38+
import com.oracle.svm.core.nodes.SubstrateMethodCallTargetNode;
3739
import com.oracle.svm.core.threadlocal.VMThreadLocalInfo;
3840
import com.oracle.svm.shared.util.ReflectionUtil;
3941

4042
import jdk.graal.compiler.core.common.memory.BarrierType;
4143
import jdk.graal.compiler.core.common.memory.MemoryOrderMode;
4244
import jdk.graal.compiler.core.common.type.ObjectStamp;
45+
import jdk.graal.compiler.core.common.type.StampFactory;
46+
import jdk.graal.compiler.core.common.type.StampPair;
4347
import jdk.graal.compiler.debug.GraalError;
4448
import jdk.graal.compiler.graph.Node;
4549
import jdk.graal.compiler.nodeinfo.InputType;
4650
import jdk.graal.compiler.nodes.AbstractMergeNode;
51+
import jdk.graal.compiler.nodes.CallTargetNode.InvokeKind;
4752
import jdk.graal.compiler.nodes.ConstantNode;
4853
import jdk.graal.compiler.nodes.EndNode;
4954
import jdk.graal.compiler.nodes.FixedNode;
5055
import jdk.graal.compiler.nodes.FixedWithNextNode;
5156
import jdk.graal.compiler.nodes.FrameState;
5257
import jdk.graal.compiler.nodes.InvokeWithExceptionNode;
58+
import jdk.graal.compiler.nodes.InvokeNode;
5359
import jdk.graal.compiler.nodes.NamedLocationIdentity;
5460
import jdk.graal.compiler.nodes.NodeView;
5561
import jdk.graal.compiler.nodes.StructuredGraph;
@@ -130,6 +136,7 @@ final class SubstrateBytecodeHandlerUnwindPath {
130136

131137
private static final Field OBJECT_SLOTS_FIELD = ReflectionUtil.lookupField(PendingExceptionStateHolder.class, "objectSlots");
132138
private static final Field PRIMITIVE_SLOTS_FIELD = ReflectionUtil.lookupField(PendingExceptionStateHolder.class, "primitiveSlots");
139+
private static final Method POISON_OBJECT_SLOT_METHOD = ReflectionUtil.lookupMethod(PendingExceptionStateSupport.class, "poisonObjectSlot", Object[].class, int.class);
133140

134141
private record PendingStateRead(ValueNode value, FixedWithNextNode last) {
135142
}
@@ -547,14 +554,11 @@ private static PendingStateRead readObjectPendingStateSlot(MetaAccessProvider me
547554
true));
548555
graph.addAfterFixed(insertAfter, read);
549556
/*
550-
* Object pending-state slots are thread-local roots. Clear consumed references so they do not
551-
* keep object graphs live until a later exception overwrites this slot.
557+
* Object pending-state slots are thread-local roots. Replace consumed references with the
558+
* debug sentinel or null so they do not keep object graphs live until a later exception
559+
* overwrites this slot.
552560
*/
553-
JavaWriteNode clear = graph.add(createClearObjectSlotWrite(graph, slotAddress));
554-
graph.addAfterFixed(read, clear);
555-
if (stateAfter != null) {
556-
clear.setStateAfter(stateAfter);
557-
}
561+
FixedWithNextNode clear = appendClearObjectSlotWrite(metaAccess, graph, read, objectSlots, slotIndex, slotAddress, stateAfter);
558562
return new PendingStateRead(read, clear);
559563
}
560564

@@ -608,19 +612,40 @@ private static void clearObjectPendingStateSlot(MetaAccessProvider metaAccess, F
608612
long objectArrayBaseOffset = metaAccess.getArrayBaseOffset(JavaKind.Object);
609613
int objectArrayIndexScale = metaAccess.getArrayIndexScale(JavaKind.Object);
610614
AddressNode slotAddress = elementAddress(graph, objectSlots, objectArrayBaseOffset + (long) slotIndex * objectArrayIndexScale);
611-
JavaWriteNode clear = graph.add(createClearObjectSlotWrite(graph, slotAddress));
612-
graph.addAfterFixed(last, clear);
615+
appendClearObjectSlotWrite(metaAccess, graph, last, objectSlots, slotIndex, slotAddress, null);
613616
}
614617

615-
private static JavaWriteNode createClearObjectSlotWrite(StructuredGraph graph, AddressNode slotAddress) {
616-
return new JavaWriteNode(JavaKind.Object,
618+
private static FixedWithNextNode appendClearObjectSlotWrite(MetaAccessProvider metaAccess, StructuredGraph graph, FixedWithNextNode insertAfter,
619+
ValueNode objectSlots, int slotIndex, AddressNode slotAddress, FrameState stateAfter) {
620+
if (useSlotDebugSentinel()) {
621+
ResolvedJavaMethod poisonMethod = metaAccess.lookupJavaMethod(POISON_OBJECT_SLOT_METHOD);
622+
SubstrateMethodCallTargetNode callTarget = graph.add(new SubstrateMethodCallTargetNode(InvokeKind.Static, poisonMethod,
623+
new ValueNode[]{objectSlots, ConstantNode.forInt(slotIndex, graph)}, StampPair.createSingle(StampFactory.forVoid())));
624+
FrameState callState = stateAfter;
625+
if (callState == null) {
626+
FrameState lastFrameState = GraphUtil.findLastFrameState(insertAfter);
627+
callState = lastFrameState == null ? null : lastFrameState.duplicateWithVirtualState();
628+
}
629+
GraalError.guarantee(callState != null, "Missing frame state for object-slot sentinel write");
630+
InvokeNode poison = graph.add(new InvokeNode(callTarget, callState.bci));
631+
graph.addAfterFixed(insertAfter, poison);
632+
poison.setStateAfter(callState);
633+
poison.setStateDuring(callState.duplicateWithVirtualState());
634+
return poison;
635+
}
636+
JavaWriteNode clear = graph.add(new JavaWriteNode(JavaKind.Object,
617637
slotAddress,
618638
NamedLocationIdentity.getArrayLocation(JavaKind.Object),
619639
ConstantNode.defaultForKind(JavaKind.Object, graph),
620640
BarrierType.ARRAY,
621641
true,
622642
true,
623-
MemoryOrderMode.PLAIN);
643+
MemoryOrderMode.PLAIN));
644+
graph.addAfterFixed(insertAfter, clear);
645+
if (stateAfter != null) {
646+
clear.setStateAfter(stateAfter);
647+
}
648+
return clear;
624649
}
625650

626651
private static FixedWithNextNode appendPrimitiveSlotDebugWrite(StructuredGraph graph, FixedWithNextNode insertAfter,

0 commit comments

Comments
 (0)