diff --git a/asm/lumbda.s b/asm/lumbda.s index b66b39c..c258ead 100644 --- a/asm/lumbda.s +++ b/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