jit: fix compile-let/compile-letrec stack residue (non-tail miscompile)
Both compile-let (regular + dict-destructure bindings) and compile-letrec
(named-let / letrec slot-init + value-assign) emitted `<value>; LOCAL_SET slot`
with NO POP. Slots are pre-allocated and LOCAL_SET peeks (doesn't pop), so each
binding value was left as stack residue. In TAIL position this was masked
(OP_TAIL_CALL resets sp to frame.base); in NON-TAIL position the residue leaked
to the enclosing frame, so the caller popped the wrong value ("not callable: 7",
"rest: 1 list arg", "first: expected list, got true") under the serving JIT.
Fix: emit POP (op 5) after each binding's LOCAL_SET. Broad correctness win for
any let/letrec in non-tail position under JIT (and the precondition for landing
the HO-loop desugar, which builds on named-let recur).
Ported from loops/sx-vm-extensions 3126d728 + 41d46f1b (compiler.sx hunks only;
spike commit and worktree .mcp.json/lock noise excluded). --jit conformance in
the source branch: 4842/1102 -> 4847/1097 (+5 fixed, zero regressions).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -602,6 +602,7 @@
|
||||
((temp-slot (scope-define-local let-scope "__dict_src")))
|
||||
(emit-op em 17)
|
||||
(emit-byte em temp-slot)
|
||||
(emit-op em 5)
|
||||
(for-each
|
||||
(fn
|
||||
(k)
|
||||
@@ -623,7 +624,8 @@
|
||||
(let
|
||||
((slot (scope-define-local let-scope (if (= (type-of var-name) "symbol") (symbol-name var-name) var-name))))
|
||||
(emit-op em 17)
|
||||
(emit-byte em slot))))
|
||||
(emit-byte em slot)
|
||||
(emit-op em 5))))
|
||||
(keys pattern))
|
||||
(compile-begin em body let-scope tail?)))
|
||||
(let
|
||||
@@ -641,7 +643,8 @@
|
||||
(let
|
||||
((slot (scope-define-local let-scope (symbol-name name))))
|
||||
(emit-op em 17)
|
||||
(emit-byte em slot))))
|
||||
(emit-byte em slot)
|
||||
(emit-op em 5))))
|
||||
bindings)
|
||||
(compile-begin em body let-scope tail?))))))
|
||||
(define
|
||||
@@ -655,7 +658,7 @@
|
||||
(let-scope (make-scope scope)))
|
||||
(dict-set! let-scope "next-slot" (get scope "next-slot"))
|
||||
(let
|
||||
((slots (map (fn (binding) (let ((name (if (= (type-of (first binding)) "symbol") (symbol-name (first binding)) (first binding)))) (let ((slot (scope-define-local let-scope name))) (emit-op em 2) (emit-op em 17) (emit-byte em slot) slot))) bindings)))
|
||||
((slots (map (fn (binding) (let ((name (if (= (type-of (first binding)) "symbol") (symbol-name (first binding)) (first binding)))) (let ((slot (scope-define-local let-scope name))) (emit-op em 2) (emit-op em 17) (emit-byte em slot) (emit-op em 5) slot))) bindings)))
|
||||
(for-each
|
||||
(fn
|
||||
(pair)
|
||||
@@ -663,7 +666,8 @@
|
||||
((binding (first pair)) (slot (nth pair 1)))
|
||||
(compile-expr em (nth binding 1) let-scope false)
|
||||
(emit-op em 17)
|
||||
(emit-byte em slot)))
|
||||
(emit-byte em slot)
|
||||
(emit-op em 5)))
|
||||
(map
|
||||
(fn (i) (list (nth bindings i) (nth slots i)))
|
||||
(range 0 (len bindings)))))
|
||||
|
||||
Reference in New Issue
Block a user