jit: save/restore loop_slots across nested named-let
When inner (let loop ((i ...))) shadows an outer (let loop ((a ...) (b ...))), the inner named-let block was overwriting j->loop_slots[] without saving the outer's slot positions. After the inner block restored loop_sym / loop_nparams / loop_params, the outer's recursive call (loop new-a new-b) would write the new args into the inner's stale slot positions instead of the outer's slots, causing the outer body to see stale binding values or trigger 'set! undefined' on tail-call args. loop_slots is a member ARRAY (not pointer) — memcpy'd at line 876 when setting up loop context — so the existing pointer save/restore for loop_params didn't cover it. Verified: - jit_named_let_factorial still PASS (3628800) - new nested-shadowing test: outer 3-param + 4 inner 1-param now PASS - exact replica of squaring.lsp round84-fold structure now PASS Bug surfaced when investigating test-round84-keep-quotient-product failure in www.foxhop.net/ecdsa tests. Test still has a deeper substrate bug beyond this JIT fix (width 0 ancilla in non-fast mode), but this fix is independently correct + closes the loop_slots scope leak.
This commit is contained in:
parent
11470a5fba
commit
0cf66cc3dd
1 changed files with 16 additions and 0 deletions
16
c/jit.c
16
c/jit.c
|
|
@ -839,6 +839,18 @@ static bool emit_expr(JitCtx *j, Value expr, bool tail) {
|
|||
int saved_loop_start = j->loop_start;
|
||||
int saved_loop_nparams = j->loop_nparams;
|
||||
Value *saved_loop_params = j->loop_params;
|
||||
/* 2026-06-14: loop_slots is an ARRAY (memcpy'd in below); save it
|
||||
* for nested named-let restore. Without this, inner (let loop ...)
|
||||
* overwrites outer's slots and outer recursive call writes args to
|
||||
* inner's slots → outer's bindings appear stale → "set! undefined"
|
||||
* or wrong values at recursion. Bug surfaced in squaring.lsp's
|
||||
* round84-fold-hi-into-lo-aggregate with outer terms-keyed loop +
|
||||
* 4 inner (let loop ((i 0))). */
|
||||
int saved_loop_slots[MAX_JIT_LOCALS];
|
||||
if (saved_loop_nparams > 0 && saved_loop_nparams <= MAX_JIT_LOCALS) {
|
||||
memcpy(saved_loop_slots, j->loop_slots,
|
||||
sizeof(int) * saved_loop_nparams);
|
||||
}
|
||||
|
||||
/* Pre-compute stack slots (but don't register yet — inits use outer scope) */
|
||||
Value loop_var_syms[MAX_JIT_LOCALS];
|
||||
|
|
@ -899,6 +911,10 @@ static bool emit_expr(JitCtx *j, Value expr, bool tail) {
|
|||
j->loop_start = saved_loop_start;
|
||||
j->loop_nparams = saved_loop_nparams;
|
||||
j->loop_params = saved_loop_params;
|
||||
if (saved_loop_nparams > 0 && saved_loop_nparams <= MAX_JIT_LOCALS) {
|
||||
memcpy(j->loop_slots, saved_loop_slots,
|
||||
sizeof(int) * saved_loop_nparams);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue