Skip to content
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
9 changes: 7 additions & 2 deletions docs/design/datacontracts/GCInfo.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ The fat header is used for methods that cannot be encoded using the compact slim
| ReversePInvokeFrameSlot | `REVERSE_PINVOKE_FRAME_ENCBASE` | Normalized reverse P/Invoke frame slot | If GC_INFO_REVERSE_PINVOKE_FRAME |
| SizeOfStackOutgoingAndScratchArea | `SIZE_OF_STACK_AREA_ENCBASE` | Size of stack parameter area | Platform dependent |
| NumSafePoints | `NUM_SAFE_POINTS_ENCBASE` | Number of safe points/callsites | #ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED |
| NumInterruptibleRanges | `NUM_INTERRUPTIBLE_RANGES_ENCBASE` | Number of interruptible ranges | |
| NumInterruptibleRanges | `NUM_INTERRUPTIBLE_RANGES_ENCBASE` | Number of interruptible ranges | If `HAS_INTERRUPTIBLE_RANGES` (omitted for Wasm R2R) |

##### Header Flags

Expand Down Expand Up @@ -517,7 +517,10 @@ if HAS_FIXED_STACK_PARAMETER_SCRATCH_AREA: // platform-dependent
fixedStackParameterScratchArea = DenormalizeSizeOfStackArea(DecodeVarLengthUnsigned(...))

numSafePoints = DecodeVarLengthUnsigned(NUM_SAFE_POINTS_ENCBASE)
numInterruptibleRanges = DecodeVarLengthUnsigned(NUM_INTERRUPTIBLE_RANGES_ENCBASE)
if HAS_INTERRUPTIBLE_RANGES: // false for Wasm R2R, which never has interruptible ranges
numInterruptibleRanges = DecodeVarLengthUnsigned(NUM_INTERRUPTIBLE_RANGES_ENCBASE)
else:
numInterruptibleRanges = 0
```

#### Body Decoding
Expand Down Expand Up @@ -647,6 +650,8 @@ For each chunk, the encoding stores:

To determine liveness at the target offset: start from the chunk's final state, then apply any transitions that occur *after* the target offset (toggling the state backwards). A slot is live if its final state (after toggle adjustment) is 1.

For encodings without interruptible ranges (`HAS_INTERRUPTIBLE_RANGES` is false, as for Wasm R2R), this step is replaced by reporting only untracked slots, including for `ExecutionAborted` frames. An aborted funclet shares those slots with parent frames that are skipped.

**Step 4 — Report untracked slots**: Untracked slots are always live (they represent stack locations the JIT doesn't track at each safe point). They are reported unconditionally unless `ParentOfFuncletStackFrame` or `NoReportUntracked` flags are set. Untracked slots are reported with `reportScratchSlots=true` since the JIT may produce untracked scratch register slots for interior pointers.

**Slot filtering**: Before reporting any slot, the algorithm checks:
Expand Down
4 changes: 3 additions & 1 deletion src/coreclr/gcinfo/gcinfoencoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,8 @@ template <typename GcInfoEncoding> void TGcInfoEncoder<GcInfoEncoding>::WriteSlo

template <typename GcInfoEncoding> void TGcInfoEncoder<GcInfoEncoding>::DefineInterruptibleRange( UINT32 startInstructionOffset, UINT32 length )
{
_ASSERTE(GcInfoEncoding::HAS_INTERRUPTIBLE_RANGES);

UINT32 stopInstructionOffset = startInstructionOffset + length;

UINT32 normStartOffset = GcInfoEncoding::NORMALIZE_CODE_OFFSET(startInstructionOffset);
Expand Down Expand Up @@ -1108,7 +1110,7 @@ template <typename GcInfoEncoding> void TGcInfoEncoder<GcInfoEncoding>::Build()
m_NumCallSites = numCallSites;
#endif // PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED

if (slimHeader)
if (slimHeader || !GcInfoEncoding::HAS_INTERRUPTIBLE_RANGES)
{
_ASSERTE(numInterruptibleRanges == 0);
}
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/inc/gcinfoencoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
- ReversePInvokeFrameSlot (if any)
- SizeOfStackOutgoingAndScratchArea (#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA)
- NumCallSites (#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED)
- NumInterruptibleRanges
- NumInterruptibleRanges (fat header only; omitted if !HAS_INTERRUPTIBLE_RANGES)

2. Call sites offsets (#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED)
3. Fully-interruptible ranges
Expand Down
8 changes: 8 additions & 0 deletions src/coreclr/inc/gcinfotypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,7 @@ struct AMD64GcInfoEncoding {
static const int LIVESTATE_RLE_RUN_ENCBASE = 2;
static const int LIVESTATE_RLE_SKIP_ENCBASE = 4;
static const bool HAS_FIXED_STACK_PARAMETER_SCRATCH_AREA = true;
static const bool HAS_INTERRUPTIBLE_RANGES = true;
};

#elif defined(TARGET_ARM)
Expand Down Expand Up @@ -676,6 +677,7 @@ struct ARM32GcInfoEncoding {
static const int LIVESTATE_RLE_RUN_ENCBASE = 2;
static const int LIVESTATE_RLE_SKIP_ENCBASE = 4;
static const bool HAS_FIXED_STACK_PARAMETER_SCRATCH_AREA = true;
static const bool HAS_INTERRUPTIBLE_RANGES = true;
};

#elif defined(TARGET_ARM64)
Expand Down Expand Up @@ -737,6 +739,7 @@ struct ARM64GcInfoEncoding {
static const int LIVESTATE_RLE_RUN_ENCBASE = 2;
static const int LIVESTATE_RLE_SKIP_ENCBASE = 4;
static const bool HAS_FIXED_STACK_PARAMETER_SCRATCH_AREA = true;
static const bool HAS_INTERRUPTIBLE_RANGES = true;
};

#elif defined(TARGET_LOONGARCH64)
Expand Down Expand Up @@ -796,6 +799,7 @@ struct LoongArch64GcInfoEncoding {
static const int LIVESTATE_RLE_RUN_ENCBASE = 2;
static const int LIVESTATE_RLE_SKIP_ENCBASE = 4;
static const bool HAS_FIXED_STACK_PARAMETER_SCRATCH_AREA = true;
static const bool HAS_INTERRUPTIBLE_RANGES = true;
};

#elif defined(TARGET_RISCV64)
Expand Down Expand Up @@ -856,6 +860,7 @@ struct RISCV64GcInfoEncoding {
static const int LIVESTATE_RLE_RUN_ENCBASE = 2;
static const int LIVESTATE_RLE_SKIP_ENCBASE = 4;
static const bool HAS_FIXED_STACK_PARAMETER_SCRATCH_AREA = true;
static const bool HAS_INTERRUPTIBLE_RANGES = true;
};

#elif defined(TARGET_X86)
Expand Down Expand Up @@ -911,6 +916,7 @@ struct X86GcInfoEncoding {
static const int LIVESTATE_RLE_RUN_ENCBASE = 2;
static const int LIVESTATE_RLE_SKIP_ENCBASE = 4;
static const bool HAS_FIXED_STACK_PARAMETER_SCRATCH_AREA = true;
static const bool HAS_INTERRUPTIBLE_RANGES = true;
};

#elif defined(TARGET_WASM) && !defined(TARGET_64BIT)
Expand Down Expand Up @@ -968,6 +974,7 @@ struct Wasm32GcInfoEncoding {
static const int LIVESTATE_RLE_RUN_ENCBASE = 2;
static const int LIVESTATE_RLE_SKIP_ENCBASE = 4;
static const bool HAS_FIXED_STACK_PARAMETER_SCRATCH_AREA = false;
static const bool HAS_INTERRUPTIBLE_RANGES = false;
};

#else // No target defined
Expand Down Expand Up @@ -1031,6 +1038,7 @@ struct InterpreterGcInfoEncoding {
static const int LIVESTATE_RLE_RUN_ENCBASE = 2;
static const int LIVESTATE_RLE_SKIP_ENCBASE = 4;
static const bool HAS_FIXED_STACK_PARAMETER_SCRATCH_AREA = false;
static const bool HAS_INTERRUPTIBLE_RANGES = true;
};

#endif // FEATURE_INTERPRETER
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ public GcInfo(NativeReader imageReader, int offset, Machine machine, int version
// PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED (this macro is always defined in _gcInfoTypes.h)
NumSafePoints = imageReader.DecodeVarLengthUnsigned(_gcInfoTypes.NUM_SAFE_POINTS_ENCBASE, ref bitOffset);

if (!_slimHeader)
if (!_slimHeader && _gcInfoTypes.HAS_INTERRUPTIBLE_RANGES)
{
NumInterruptibleRanges = imageReader.DecodeVarLengthUnsigned(_gcInfoTypes.NUM_INTERRUPTIBLE_RANGES_ENCBASE, ref bitOffset);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ public class GcInfoTypes
internal int LIVESTATE_RLE_SKIP_ENCBASE { get; } = 4;
internal int NUM_NORM_CODE_OFFSETS_PER_CHUNK_LOG2 { get; } = 6;
internal bool HAS_FIXED_STACK_PARAMETER_SCRATCH_AREA { get; } = true;
internal bool HAS_INTERRUPTIBLE_RANGES { get; } = true;

internal GcInfoTypes(Machine machine, bool denormalizeCodeOffsets)
{
Expand Down Expand Up @@ -180,6 +181,7 @@ internal GcInfoTypes(Machine machine, bool denormalizeCodeOffsets)
LIVESTATE_RLE_RUN_ENCBASE = 2;
LIVESTATE_RLE_SKIP_ENCBASE = 4;
HAS_FIXED_STACK_PARAMETER_SCRATCH_AREA = false;
HAS_INTERRUPTIBLE_RANGES = false;
break;

case Machine.I386:
Expand Down
12 changes: 11 additions & 1 deletion src/coreclr/vm/gcinfodecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ TGcInfoDecoder<GcInfoEncoding>::TGcInfoDecoder(
m_SafePointIndex = m_NumSafePoints;
#endif

if (slimHeader)
if (slimHeader || !GcInfoEncoding::HAS_INTERRUPTIBLE_RANGES)
{
m_NumInterruptibleRanges = 0;
}
Expand Down Expand Up @@ -736,6 +736,7 @@ template <typename GcInfoEncoding> bool TGcInfoDecoder<GcInfoEncoding>::Enumerat
{

unsigned executionAborted = (inputFlags & ExecutionAborted);
bool reportUntrackedOnly = false;

// In order to make ARM more x86-like we only ever report the leaf frame
// of any given function. We accomplish this by having the stackwalker
Expand Down Expand Up @@ -777,6 +778,12 @@ template <typename GcInfoEncoding> bool TGcInfoDecoder<GcInfoEncoding>::Enumerat
m_Reader.DecodeVarLengthUnsigned( GcInfoEncoding::INTERRUPTIBLE_RANGE_DELTA2_ENCBASE );
}
}
else if constexpr (!GcInfoEncoding::HAS_INTERRUPTIBLE_RANGES)
{
// Outside of safe points only untracked slots can be reported. Report them for aborted
// frames too: an aborted funclet shares them with parent frames that are skipped.
reportUntrackedOnly = true;
}
else
{
//
Expand Down Expand Up @@ -829,6 +836,9 @@ template <typename GcInfoEncoding> bool TGcInfoDecoder<GcInfoEncoding>::Enumerat

slotDecoder.DecodeSlotTable(m_Reader);

if (reportUntrackedOnly)
goto ReportUntracked;

{
UINT32 numSlots = slotDecoder.GetNumTracked();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,9 @@ private IEnumerable<DecodePoints> DecodeFatHeader()
_numSafePoints = _reader.DecodeVarLengthUnsigned(TTraits.NUM_SAFE_POINTS_ENCBASE, ref _bitOffset);
}

_numInterruptibleRanges = _reader.DecodeVarLengthUnsigned(TTraits.NUM_INTERRUPTIBLE_RANGES_ENCBASE, ref _bitOffset);
_numInterruptibleRanges = TTraits.HAS_INTERRUPTIBLE_RANGES
? _reader.DecodeVarLengthUnsigned(TTraits.NUM_INTERRUPTIBLE_RANGES_ENCBASE, ref _bitOffset)
: 0;
}

private void EnsureDecodedTo(DecodePoints point)
Expand Down Expand Up @@ -976,6 +978,12 @@ private bool EnumerateLiveSlots(
{
// We have a safe point match — skip interruptible range computation
}
else if (!TTraits.HAS_INTERRUPTIBLE_RANGES)
{
// Outside of safe points only untracked slots can be reported. Report them for aborted
// frames too: an aborted funclet shares them with parent frames that are skipped.
return ReportUntrackedAndSucceed();
}
else
{
// Compute pseudoBreakOffset from interruptible ranges
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ internal interface IGCInfoTraits

static abstract bool HAS_FIXED_STACK_PARAMETER_SCRATCH_AREA { get; }

// False for encodings whose code is never interruptible (Wasm R2R).
static virtual bool HAS_INTERRUPTIBLE_RANGES => true;

/// <summary>
/// Returns true if the given register is a scratch (volatile) register.
/// Scratch register slots should only be reported for the active (leaf) stack frame.
Expand Down
Loading