asm: case as always-on special form (defect #32)
ecdsa cross-tier validation on lumbda-gc and bump-only lumbda blocked
on `case` being unbound. R7RS standard control form — present on
Python (lumbda.py) and C (c/) tiers, but on asm reachable only via
cl_full_prelude's define-macro form (carved into lumbda-full only).
Mirrors commit 865be28 (when/unless via Path B dispatch table).
Path B (special-form dispatch table extension):
- sf_case length-prefixed symbol name
- sym_case_val interned at init_special_forms
- dispatch case in .eval_top alongside .ev_when / .ev_unless
(placed before the .ifdef CL_FULL macro-lookup block so the
dispatch shadow takes precedence over the cl_full_prelude macro
on lumbda-full — no conflict, the macro just becomes dead code)
- .ev_case evaluator: eval key once, push on stack, walk clauses;
each clause's datum list compared by pointer equality (eqv? on
the asm tier — fixnums, symbols, booleans, characters, nil are
all interned/unboxed to unique values). `else` matches uncondi-
tionally. Match → .ev_begin (TCO). No match → .ev_begin_void.
Available on every asm tier (plain `lumbda`, `lumbda-gc`,
`lumbda-full`). Binary size impact:
lumbda 60768 → 61040 (+272, +0.45%)
lumbda-gc 69496 → 69768 (+272, +0.39%)
lumbda-full 72000 → 72264 (+264, +0.37%)
All 158 asm tests still pass. Sanity tests: single-datum, multi-datum,
symbol key (eqv?), else, empty body, no-match, nested case — all
correct on all three tiers.
ecdsa search.lsp now produces byte-identical winner (v3-clifford-only
score 0) across five tiers: Python, C, asm-bump, asm-gc, asm-full.
This commit is contained in:
parent
865be28091
commit
ebcf1625c7
1 changed files with 70 additions and 0 deletions
70
asm/lumbda.s
70
asm/lumbda.s
|
|
@ -248,6 +248,7 @@ sf_or: .byte 2; .ascii "or"
|
|||
sf_else: .byte 4; .ascii "else"
|
||||
sf_when: .byte 4; .ascii "when"
|
||||
sf_unless: .byte 6; .ascii "unless"
|
||||
sf_case: .byte 4; .ascii "case"
|
||||
|
||||
# Builtin names (length-prefixed)
|
||||
bn_add: .byte 1; .ascii "+"
|
||||
|
|
@ -558,6 +559,7 @@ sym_or_val: .quad 0
|
|||
sym_else_val: .quad 0
|
||||
sym_when_val: .quad 0
|
||||
sym_unless_val: .quad 0
|
||||
sym_case_val: .quad 0
|
||||
|
||||
# ============================================================
|
||||
.bss
|
||||
|
|
@ -2819,6 +2821,8 @@ eval:
|
|||
je .ev_when
|
||||
cmpq sym_unless_val(%rip), %rbx
|
||||
je .ev_unless
|
||||
cmpq sym_case_val(%rip), %rbx
|
||||
je .ev_case
|
||||
.ifdef CL_FULL
|
||||
cmpq sym_quasiquote_val(%rip), %rbx
|
||||
je .ev_quasiquote
|
||||
|
|
@ -3571,6 +3575,68 @@ env_set_both:
|
|||
.ev_unless_run:
|
||||
jmp .ev_begin
|
||||
|
||||
# ---- case ----
|
||||
# (case key clause ...) where each clause is
|
||||
# ((datum ...) body...) or (else body...)
|
||||
# Evaluate `key` once, then walk clauses; for the first clause whose
|
||||
# datum list contains a value `eqv?` to key, evaluate body via begin
|
||||
# (TCO). `else` matches unconditionally and must be last. If no clause
|
||||
# matches, return void. R7RS standard form — Path B mirrors the just-
|
||||
# shipped `when`/`unless` fix (commit 865be28); previously case lived
|
||||
# only in cl_full_prelude as a define-macro form, leaving lumbda-gc
|
||||
# and bump-only lumbda unable to run ecdsa search.lsp.
|
||||
# eqv? on the asm tier is pointer equality (fixnums, symbols, bools,
|
||||
# chars, nil are all interned/unboxed to unique values).
|
||||
.ev_case:
|
||||
# %r12 = (key . clauses)
|
||||
cmpq $VAL_NIL, %r12
|
||||
je .ev_begin_void # (case) → void (degenerate)
|
||||
movq %r12, %rax
|
||||
andq $-8, %rax
|
||||
movq (%rax), %rdi # key expr
|
||||
movq 8(%rax), %r12 # clauses
|
||||
pushq %r12 # save clauses
|
||||
movq %rbp, %rsi
|
||||
call eval
|
||||
popq %r12 # restore clauses
|
||||
pushq %rax # save key value on stack
|
||||
# Loop over clauses; key sits at (%rsp).
|
||||
.ev_case_loop:
|
||||
cmpq $VAL_NIL, %r12
|
||||
je .ev_case_void
|
||||
movq %r12, %rax
|
||||
andq $-8, %rax
|
||||
movq (%rax), %rbx # current clause
|
||||
movq 8(%rax), %r12 # rest clauses
|
||||
# Clause shape: (datums . body) or (else . body)
|
||||
movq %rbx, %rax
|
||||
andq $-8, %rax
|
||||
movq (%rax), %rdx # datums (or `else` symbol)
|
||||
movq 8(%rax), %rcx # body forms
|
||||
# else clause matches unconditionally
|
||||
cmpq sym_else_val(%rip), %rdx
|
||||
je .ev_case_match
|
||||
# Walk datum list; key is at (%rsp)
|
||||
movq (%rsp), %rdi
|
||||
.ev_case_dloop:
|
||||
cmpq $VAL_NIL, %rdx
|
||||
je .ev_case_loop # exhausted datums → next clause
|
||||
movq %rdx, %rax
|
||||
andq $-8, %rax
|
||||
movq (%rax), %rsi # datum
|
||||
movq 8(%rax), %rdx # rest datums
|
||||
cmpq %rdi, %rsi # eqv? — pointer compare
|
||||
je .ev_case_match
|
||||
jmp .ev_case_dloop
|
||||
.ev_case_match:
|
||||
# %rcx = body forms. Discard saved key, run body via begin (TCO).
|
||||
addq $8, %rsp
|
||||
movq %rcx, %r12
|
||||
jmp .ev_begin
|
||||
.ev_case_void:
|
||||
addq $8, %rsp # discard saved key
|
||||
jmp .ev_begin_void
|
||||
|
||||
.ifdef CL_FULL
|
||||
# ---- quasiquote (CL_FULL only) ----
|
||||
# (quasiquote template) walks `template`:
|
||||
|
|
@ -8668,6 +8734,10 @@ init_special_forms:
|
|||
call intern_static
|
||||
movq %rax, sym_unless_val(%rip)
|
||||
|
||||
leaq sf_case(%rip), %rdi
|
||||
call intern_static
|
||||
movq %rax, sym_case_val(%rip)
|
||||
|
||||
# Intern the multi-value marker symbol used by (values) and
|
||||
# (call-with-values). Stored once at init.
|
||||
leaq bn_mval_tag(%rip), %rdi
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue