Expand description
The compiler-managed shadow stack (§12.3, ADR-019, ADR-101).
§12.3 offers “compiler-managed shadow-stack frames or explicit root
frames,” and the runtime has both: explicit root frames
(RootScope, ADR-012) for the host, and this
compiler-managed shadow stack that JIT-generated code spills into. At every
GC safepoint (allocation / call that may allocate), the Cranelift backend
stores the live GcRef locals into this function’s slots before the
safepoint and reloads them after.
A frame is not an object. The runtime owns one contiguous region of
slots for the whole program (SlotStack); a function’s frame is the run
of slots between the top it found on entry and the top it left behind.
Generated code claims a run by bump-allocating inline — load top, zero
slot_count slots, store top + slot_count*8 back — and reclaims it by
storing the saved base into top again. No allocation, no free, no call
(ADR-101).
Slots are raw *mut GcHeader (not GcRef) because a slot is null until
the backend writes a value into it: a local may be live across a safepoint
(so it must be in the root set) before it has ever been assigned at runtime.
GcRef is NonNull by construction, so it cannot represent that state; the
raw pointer can, and push_roots skips nulls.
The collector reaches the whole stack through one RootSet impl on
SlotStackHeader, which scans [base, top) in a single linear pass. That
is exactly every live frame’s roots, because each frame occupies exactly its
own slot run and the runs partition [base, top).
The header is #[repr(C)] and publishes SlotStackHeader::TOP_OFFSET so
the backend emits a compile-time-derived displacement rather than a literal
(Appendix B).
Structs§
- Debug
Slot Count - A count of debug value slots the debug value stack can actually hold: a
u32proven<=MAX_DEBUG_VALUE_SLOTSat construction (ADR-128 decision 3). - Shadow
Frame Guard - A frame claimed from Rust, released when dropped.
- Slot
Count - A frame width the shadow stack can actually hold: a
u32proven<=MAX_SHADOW_SLOTSat construction. - Slot
Stack - The owner of one slot reservation and its header.
- Slot
Stack Header - The three-word header generated code bump-allocates against.
Constants§
- MAX_
DEBUG_ VALUE_ SLOTS - The maximum
Gclocals a single JIT’d function may have — the bound on the dense index space the crash debugger reads (ADR-128 decision 3). - MAX_
SHADOW_ SLOTS - The maximum
Gcroots a single JIT’d function may spill. The backend rejects (at compile time) any function exceeding this, throughSlotCount; real Praxis functions have small root sets. - SHADOW_
STACK_ SLOTS - The size of the one shadow-stack reservation, in slots.
Functions§
- push_
frame ⚠ - Claim
countzeroed slots onctx’s shadow stack, the way a generated prologue does.
Type Aliases§
- Shadow
Stack - The whole shadow stack, as the runtime owns it.
- Shadow
Stack Header - The header generated code bump-allocates against.