diff --git a/asm/lumbda b/asm/lumbda index e1634d6..dc8edcc 100755 Binary files a/asm/lumbda and b/asm/lumbda differ diff --git a/asm/lumbda-full b/asm/lumbda-full index a059cb1..5b18819 100755 Binary files a/asm/lumbda-full and b/asm/lumbda-full differ diff --git a/asm/lumbda-full.o b/asm/lumbda-full.o index a82c17b..259807e 100644 Binary files a/asm/lumbda-full.o and b/asm/lumbda-full.o differ diff --git a/asm/lumbda-gc b/asm/lumbda-gc index 6f79623..be080c2 100755 Binary files a/asm/lumbda-gc and b/asm/lumbda-gc differ diff --git a/asm/lumbda-gc.o b/asm/lumbda-gc.o index f7e4e29..00bd74f 100644 Binary files a/asm/lumbda-gc.o and b/asm/lumbda-gc.o differ diff --git a/asm/lumbda.o b/asm/lumbda.o index e674896..3735d50 100644 Binary files a/asm/lumbda.o and b/asm/lumbda.o differ diff --git a/asm/lumbda.s b/asm/lumbda.s index 957ed3e..cb7302f 100644 --- a/asm/lumbda.s +++ b/asm/lumbda.s @@ -388,6 +388,7 @@ err_ht_miss: .ascii "Error: hash-table-ref: missing key\n" cl_full_prelude: .ascii "(define (caar x) (car (car x)))\n" .ascii "(define (cdar x) (cdr (car x)))\n" + .ascii "(define (cadar x) (car (cdr (car x))))\n" .ascii "(define (caddr x) (car (cdr (cdr x))))\n" .ascii "(define (cadddr x) (car (cdr (cdr (cdr x)))))\n" .ascii "(define (cddr x) (cdr (cdr x)))\n" @@ -1049,6 +1050,15 @@ gc_collect: movq %r14, %rdi call gc_mark_env +.ifdef CL_FULL + # Root 1b: macro_env_head — same (sym, val, next) shape as the + # regular env. Without this, GC collects the macro table and the + # next macro lookup fails with "unbound: " after any + # collection cycle. + movq macro_env_head(%rip), %rdi + call gc_mark_env +.endif + # Root 2: cached else sym. movq sym_else_val(%rip), %rdi call gc_push_if_heap @@ -4950,17 +4960,15 @@ bi_foreach: RET_VAL bi_apply: - # (apply f args-list) - GETARG %rbx # f - GETARG %rdi # args-list (already a proper list) - movq %rbx, %rdi - # Need to set up %rbx=proc, %r12=args then jump to apply path - # Actually just call apply_proc_raw - pushq %rbx - movq %rbx, %rdi - movq %r12, %rsi # remaining args = the list + # (apply f args-list) — args-list is a proper list of values that + # becomes f's argument list. Previous impl clobbered the list + # with %rbx and passed %r12 (which is nil after two GETARGs) + # as the apply args, so (apply f '(1 2 3)) silently reduced to + # (f). + GETARG %rbx # f (proc) + GETARG %rsi # args-list + movq %rbx, %rdi # proc call apply_proc_raw - popq %rbx RET_VAL bi_member: @@ -5332,6 +5340,13 @@ bi_expt: GETARG %rcx sarq $3, %rcx popq %rax # restore untagged base + # Guard negative exponent — asm is integer-only; return 0 rather + # than loop forever. Scheme's (expt N -1) would yield 1/N, which + # this tier can't represent. cl-loop's look-ahead termination + # only evaluates the step if the next iter is valid, but we + # have to stay safe against eager step evaluation by the emit. + testq %rcx, %rcx + js .bexpt_done_zero # base^exp by repeated multiplication movq $1, %rdx .bexpt_loop: @@ -5344,6 +5359,10 @@ bi_expt: movq %rdx, %rdi call make_int RET_VAL +.bexpt_done_zero: + xorq %rdi, %rdi + call make_int + RET_VAL bi_gcd: GETARG %rax diff --git a/cl-compat.lsp b/cl-compat.lsp index f25801e..7e4bc70 100644 --- a/cl-compat.lsp +++ b/cl-compat.lsp @@ -166,6 +166,15 @@ (else (cons (list (car as) (car bs)) (cl-zip (cdr as) (cdr bs)))))) +;; N-list append: (cl-append '(a) '(b) '(c)) → (a b c). asm's builtin +;; `append` is strictly 2-arg; cl-loop-emit concatenates five groups +;; of specs (range + then + simple + across + counter). Reducing with +;; the two-arg `append` works on every impl. +(define (cl-append . lsts) + (cond ((null? lsts) '()) + ((null? (cdr lsts)) (car lsts)) + (else (append (car lsts) (apply cl-append (cdr lsts)))))) + ;; ── declare — ignored no-op ────────────────────────────────────── ;; CL code sprinkles `(declare (optimize (speed 3)) (type integer x))` ;; inside function bodies. These are compile-time directives in SBCL. @@ -395,7 +404,7 @@ ;; kept in parallel lists by position below. ;; For parity with downstream expectations, flatten to (var init step). (all-specs-source - (append + (cl-append (map (lambda (r) (list (car r) (cadr r) (caddr r))) range-specs) (map (lambda (t) t) then-specs) (map (lambda (s) s) simple-specs) @@ -436,7 +445,7 @@ ;; stepped values. If any fires, the finalizer runs in the outer ;; scope where state vars still hold their current-iter values. (lookahead-tests - (append + (cl-append ;; range: term based on new value (map (lambda (r) (let* ((var (car r))