You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
GC (aarch64): the platform-unwinder stack-map walker lands one frame size below the correct SP #7399
Split out of #7394, which fixed the other half. The fast x29-chain walker was
decoding lsl #12 frame adjustments wrong; that is fixed and the default
configuration is correct. The platform-unwinder path is independently wrong,
by exactly one frame size.
Measured
test_gap_gc_call_argument_rooting, aarch64 macOS, RS4GC default backend, at
the commit that fixes the fast walker:
PERRY_STACKMAP_WALKER
PERRY_GC_HEAP_LIMIT=8 PERRY_GC_INCREMENTAL=0
fast (default)
bad 0 ✅
unwind
bad 1 ❌
verify
panics — slot sets differ
verify prints both sets:
fast : [0x16f625130, 0x16f625138, 0x16f625210]
unwind: [0x16f623950, 0x16f625060, 0x16f625068]
Pairing by frame rather than by sort order:
frame
fast
unwind
unwind − fast
A (2 slots)
…5130, …5138
…5060, …5068
−0xD0
B (1 slot)
…5210
…3950
−0x18C0
0x18C0 is exactly the total frame of perry_fn_test_gap_gc_call_argument_rooting_ts__run
(0x80 pre-index + 0x1000 + 0x840). The unwinder lands one whole frame
below the correct body SP, and 0xD0 is consistent with the same relationship on
the smaller frame.
Where
crates/perry-runtime/src/gc/roots/stack_maps.rs, the Itanium unwind::walk_frame
SP-relative base:
let cfa = _Unwind_GetCFA(context);
cfa.checked_sub(CFA_RETURN_ADDRESS_BYTES)// 0 on aarch64.and_then(|v| v.checked_sub(record.stack_sizeasusize))
The delta being exactlyrecord.stack_size on both frames points at a double
subtraction — i.e. _Unwind_GetCFA here already yields the value the formula is
trying to derive. That is a hypothesis, not a measurement; it needs confirming
before the formula is changed, and any change has to be checked on x86-64/Linux,
where CFA_RETURN_ADDRESS_BYTES = 8 and the current formula is what the Linux
gate runs against.
Why it is not merely a bisection knob
WalkerMode::Fastfalls back to this path whenever chain_walkable is false
or fp_chain::visit returns None on an anomaly:
So a frame the fast walker declines is handed to a walker that computes the
wrong base — and evacuation writes through the slots it is given.
It also means PERRY_STACKMAP_WALKER=verify cannot be promoted to a CI gate
until this is fixed: verify performs its real visitation through the unwinder,
so it currently both trips on the difference and does the wrong visitation.
That gate is the only check that can catch a fast walk silently skipping frames
— exactly the class of bug #7394 turned out to be — so it is worth having.
Already fixed by #7400 (d5b115df6), which landed before this was triaged — closing as a duplicate.
#7400 measured the thing this issue only hypothesised. Its probe showed _Unwind_GetCFA returns the frame's own SP, not the DWARF CFA, inside an _Unwind_Backtrace callback — on aarch64 Linux (libgcc), aarch64 macOS (Apple libunwind) and x86-64 Linux alike. So the base needed no derivation at all, and CFA_RETURN_ADDRESS_BYTES was deleted rather than corrected:
- let cfa = _Unwind_GetCFA(context);- .checked_sub(CFA_RETURN_ADDRESS_BYTES)- .and_then(|v| v.checked_sub(record.stack_size as usize))+ _Unwind_GetCFA(context)
That is exactly the double subtraction this issue reported, and it explains the measurement here precisely: the delta was record.stack_size on both frames, because stack_size was being subtracted from a value that was already the body SP.
The one thing worth carrying forward from here: #7400 also makes PERRY_STACKMAP_WALKER=verify meaningful again. With #7398 fixing the fast walk and #7400 the unwind walk, the two should now agree — and verify is the only check that can catch a fast walk silently skipping frames, which is what #7398 turned out to be. Worth a CI arm.
Activity
docs: name the changelog fragment for its real PR and link #7399
fix(gc): decode `lsl #12` frame adjustments in the aarch64 prologue w…
proggeramlug commentedon Aug 4, 2026
Already fixed by #7400 (
d5b115df6), which landed before this was triaged — closing as a duplicate.#7400 measured the thing this issue only hypothesised. Its probe showed
_Unwind_GetCFAreturns the frame's own SP, not the DWARF CFA, inside an_Unwind_Backtracecallback — on aarch64 Linux (libgcc), aarch64 macOS (Apple libunwind) and x86-64 Linux alike. So the base needed no derivation at all, andCFA_RETURN_ADDRESS_BYTESwas deleted rather than corrected:That is exactly the double subtraction this issue reported, and it explains the measurement here precisely: the delta was
record.stack_sizeon both frames, becausestack_sizewas being subtracted from a value that was already the body SP.The one thing worth carrying forward from here: #7400 also makes
PERRY_STACKMAP_WALKER=verifymeaningful again. With #7398 fixing the fast walk and #7400 the unwind walk, the two should now agree — andverifyis the only check that can catch a fast walk silently skipping frames, which is what #7398 turned out to be. Worth a CI arm.