pub const SHADOW_STACK_SLOTS: usize = _; // 728_192usizeExpand description
The size of the one shadow-stack reservation, in slots.
Exhaustion is unrepresentable, not handled. One fact bounds the stack, and since ADR-105 it bounds it exactly rather than through a product of two independent worst cases:
- every generated prologue rejects
stack_left < frame_cost(slots)before it pushes anything. A context starts with at mostSTACK_BUDGET_BYTES— [StackBudget] refuses to make a larger one — and a frame spends at leastFRAME_BYTES_BASE, so there are at mostMAX_RECURSION_DEPTHlive frames; and it spendsFRAME_BYTES_PER_SLOTon every slot pastREFERENCE_FRAME_SLOTS, so those slots number at mostSTACK_BUDGET_BYTES / FRAME_BYTES_PER_SLOT. Adding the two: live slots are bounded bybudget / FRAME_BYTES_PER_SLOT + MAX_RECURSION_DEPTH × REFERENCE_FRAME_SLOTS.
Reserving that — plus one frame of headroom for the Rust-side push_frame
callers, which spend no budget and so are not covered by the argument —
means there is no inline bounds check in the prologue, because there is
nothing left to check. That is one branch removed from the hottest path in
the language.
Sizing this as MAX_RECURSION_DEPTH * MAX_SHADOW_SLOTS would multiply “the
deepest recursion” by “the widest frame” as if a program could have both at
once. It cannot, and the guard is what says so: a maximum-width frame spends
FRAME_BYTES_BASE + 2 × (MAX_SHADOW_SLOTS − REFERENCE_FRAME_SLOTS) bytes, so
a stack of them runs out of budget at 2452 frames, not 8000. The reservation
is therefore 5.56 MiB of virtual address space per
Runtime. SlotStack::new allocates it zeroed, which is
an mmap of fresh zero pages: resident memory tracks how deep the program
actually recurses, not how deep it is allowed to.