asm: values + call-with-values + #(...) reader + exit + vector equal?
Closes the remaining asm-side gaps from ticket 0005's follow-up
discussion. Every test in tests/cl-compat.lsp and tests/ursa.lsp
now runs unmodified on default asm (Scheme port) and asm-full (full
CL path) — no more commented-out tests or shim syntax.
Landed (all in default asm — useful beyond cl-compat):
* (values . xs) / (call-with-values producer consumer). values
packs a tagged pair (mval_marker . xs) when multiple; a lone arg
passes through unchanged so legacy single-value code is
undisturbed. call-with-values invokes the producer, destructures
the multi-value packet if present, applies consumer positionally.
The marker is a gensymed symbol interned once at init, so no
user-constructed pair can masquerade as a multi-value packet.
* (exit [code]) builtin. Default code is 0 when called with no
args. Passes through to the SYS_EXIT syscall.
* #(...) vector literal in the reader. .sr_hash now dispatches on
'(' as a vector literal alongside 't' and 'f'. list_to_vector_
reader is a standalone helper callable from the reader (separate
from bi_listtovec which uses the GETARG builtin convention).
Matches R7RS vector literal syntax. Existing vector builtins
already handled construction; this just teaches the reader.
* deep_equal extended to vectors. equal? now descends into vectors
(length + elementwise recursive compare), matching R7RS.
Previously only strings and pairs were handled; vectors fell
through to shallow pointer compare which only matched identical
heap objects.
Test file reverts (picking up the new capabilities):
* tests/cl-compat.lsp — multiple-value-bind test restored
(previously commented out because asm lacked values /
call-with-values).
* tests/ursa-scheme.lsp — #(1 0 1 0 1 0) literal restored
(previously worked around with (vector->list (digits ...)));
(exit 1) failure trailer restored (previously removed because
asm had no exit builtin).
* tests/ursa.lsp — same digits literal restoration.
Verified:
* asm regression: 158/158.
* asm-full regression: 158/158.
* Zoë-favorites across Python + C + asm + asm-full: all suites
green with native reader syntax and multi-value tests.
* make test-all stays green.
This commit is contained in:
parent
2061cb169a
commit
99b0622520
10 changed files with 202 additions and 23 deletions
BIN
asm/lumbda
BIN
asm/lumbda
Binary file not shown.
BIN
asm/lumbda-full
BIN
asm/lumbda-full
Binary file not shown.
Binary file not shown.
BIN
asm/lumbda-gc
BIN
asm/lumbda-gc
Binary file not shown.
BIN
asm/lumbda-gc.o
BIN
asm/lumbda-gc.o
Binary file not shown.
BIN
asm/lumbda.o
BIN
asm/lumbda.o
Binary file not shown.
204
asm/lumbda.s
204
asm/lumbda.s
|
|
@ -203,15 +203,18 @@
|
|||
.equ BI_CADR, 115
|
||||
.equ BI_SORT, 116
|
||||
.equ BI_GENSYM, 117
|
||||
.equ BI_EXIT, 118
|
||||
.equ BI_VALUES, 119
|
||||
.equ BI_CWV, 120
|
||||
.ifdef GC_NAIVE
|
||||
.equ BI_GC_COLLECT, 118
|
||||
.equ BI_GC_STATS, 119
|
||||
.equ BI_WITH_ARENA, 120
|
||||
.equ BI_ARENA_STATS, 121
|
||||
.equ BI_ARENA_SET_MODE, 122
|
||||
.equ BI_COUNT, 123
|
||||
.equ BI_GC_COLLECT, 121
|
||||
.equ BI_GC_STATS, 122
|
||||
.equ BI_WITH_ARENA, 123
|
||||
.equ BI_ARENA_STATS, 124
|
||||
.equ BI_ARENA_SET_MODE, 125
|
||||
.equ BI_COUNT, 126
|
||||
.else
|
||||
.equ BI_COUNT, 118
|
||||
.equ BI_COUNT, 121
|
||||
.endif
|
||||
|
||||
# ============================================================
|
||||
|
|
@ -363,6 +366,9 @@ bn_hslist: .byte 14; .ascii "hash-set->list"
|
|||
bn_cadr: .byte 4; .ascii "cadr"
|
||||
bn_sort: .byte 4; .ascii "sort"
|
||||
bn_gensym: .byte 6; .ascii "gensym"
|
||||
bn_exit: .byte 4; .ascii "exit"
|
||||
bn_values: .byte 6; .ascii "values"
|
||||
bn_cwv: .byte 16; .ascii "call-with-values"
|
||||
.ifdef GC_NAIVE
|
||||
bn_gccollect: .byte 10; .ascii "gc-collect"
|
||||
bn_gcstats: .byte 8; .ascii "gc-stats"
|
||||
|
|
@ -423,6 +429,8 @@ cl_full_prelude:
|
|||
.equ cl_full_prelude_len, . - cl_full_prelude
|
||||
.endif
|
||||
|
||||
bn_mval_tag: .byte 17; .ascii "__values-marker__"
|
||||
|
||||
portal_magic: .ascii "LUMBDAB2"
|
||||
.equ PORTAL_MAGIC_LEN, 8
|
||||
# magic(8) + heap_size(8) + heap_base(8) + r14(8) + r15(8)
|
||||
|
|
@ -464,7 +472,7 @@ bi_names:
|
|||
.quad bn_isqrt
|
||||
.quad bn_randomseed, bn_randomint, bn_randomstate, bn_randomstateset
|
||||
.quad bn_randomseedfromos
|
||||
.quad bn_cadr, bn_sort, bn_gensym
|
||||
.quad bn_cadr, bn_sort, bn_gensym, bn_exit, bn_values, bn_cwv
|
||||
.ifdef GC_NAIVE
|
||||
.quad bn_gccollect, bn_gcstats, bn_witharena, bn_arenastats, bn_arenamode
|
||||
.endif
|
||||
|
|
@ -565,6 +573,7 @@ sym_hash_buckets: .skip 8192 # 1024 * 8 bytes
|
|||
|
||||
gensym_counter: .skip 8 # bumped each call to (gensym)
|
||||
num_buf_lp: .skip 64 # scratch [len:byte][chars...] for intern
|
||||
mval_marker: .skip 8 # interned symbol; marks multi-value packets
|
||||
|
||||
.ifdef CL_FULL
|
||||
# macro_env_head — linked list of (sym, closure, next) 24-byte nodes.
|
||||
|
|
@ -1945,10 +1954,65 @@ scheme_read:
|
|||
je .sr_true
|
||||
cmpb $'f', %al
|
||||
je .sr_false
|
||||
cmpb $'(', %al
|
||||
je .sr_hash_vector
|
||||
movq $VAL_VOID, %rax
|
||||
popq %r12
|
||||
popq %rbx
|
||||
ret
|
||||
|
||||
.sr_hash_vector:
|
||||
# `(` already consumed. Read list of elements, convert to vector.
|
||||
call .read_list_elems
|
||||
movq %rax, %rdi # list (tagged)
|
||||
call list_to_vector_reader
|
||||
popq %r12
|
||||
popq %rbx
|
||||
ret
|
||||
|
||||
# list_to_vector_reader: %rdi = tagged list -> %rax = tagged vector.
|
||||
# Separate from bi_listtovec (which uses GETARG) so the reader can
|
||||
# call it directly.
|
||||
list_to_vector_reader:
|
||||
pushq %rbx
|
||||
movq %rdi, %rbx # save list
|
||||
xorq %rcx, %rcx
|
||||
movq %rdi, %rax
|
||||
.l2vr_count:
|
||||
cmpq $VAL_NIL, %rax
|
||||
je .l2vr_alloc
|
||||
incq %rcx
|
||||
movq %rax, %rdx
|
||||
andq $-8, %rdx
|
||||
movq 8(%rdx), %rax
|
||||
jmp .l2vr_count
|
||||
.l2vr_alloc:
|
||||
pushq %rbx
|
||||
pushq %rcx
|
||||
leaq 8(,%rcx,8), %rdi
|
||||
call heap_alloc
|
||||
.ifdef GC_NAIVE
|
||||
movb $HT_VECTOR, -7(%rax)
|
||||
.endif
|
||||
popq %rcx
|
||||
popq %rbx
|
||||
movq %rcx, (%rax)
|
||||
leaq 8(%rax), %rdi
|
||||
movq %rbx, %rdx
|
||||
.l2vr_fill:
|
||||
cmpq $VAL_NIL, %rdx
|
||||
je .l2vr_done
|
||||
movq %rdx, %rcx
|
||||
andq $-8, %rcx
|
||||
movq (%rcx), %rsi
|
||||
movq %rsi, (%rdi)
|
||||
addq $8, %rdi
|
||||
movq 8(%rcx), %rdx
|
||||
jmp .l2vr_fill
|
||||
.l2vr_done:
|
||||
orq $7, %rax
|
||||
popq %rbx
|
||||
ret
|
||||
.sr_true:
|
||||
movq $VAL_TRUE, %rax
|
||||
popq %r12
|
||||
|
|
@ -3901,6 +3965,12 @@ eval_list:
|
|||
je bi_sort
|
||||
cmpq $BI_GENSYM, %rax
|
||||
je bi_gensym
|
||||
cmpq $BI_EXIT, %rax
|
||||
je bi_exit
|
||||
cmpq $BI_VALUES, %rax
|
||||
je bi_values
|
||||
cmpq $BI_CWV, %rax
|
||||
je bi_call_with_values
|
||||
cmpq $BI_INTEGERP, %rax
|
||||
je bi_integerp
|
||||
cmpq $BI_PORTALSAVE, %rax
|
||||
|
|
@ -4297,6 +4367,8 @@ deep_equal:
|
|||
jne .deq_false
|
||||
cmpq $TAG_STRING, %rax
|
||||
je .deq_string
|
||||
cmpq $7, %rax # vector tag
|
||||
je .deq_vector
|
||||
cmpq $TAG_PAIR, %rax
|
||||
jne .deq_false
|
||||
# Both pairs — compare car then cdr
|
||||
|
|
@ -4350,6 +4422,36 @@ deep_equal:
|
|||
decq %rdx
|
||||
jmp .deq_str_loop
|
||||
|
||||
.deq_vector:
|
||||
# Both vectors. Compare lengths, then recurse elementwise.
|
||||
movq %rdi, %rax
|
||||
andq $-8, %rax
|
||||
movq %rsi, %rcx
|
||||
andq $-8, %rcx
|
||||
movq (%rax), %rdx # len a
|
||||
cmpq (%rcx), %rdx # len a vs len b
|
||||
jne .deq_false
|
||||
leaq 8(%rax), %r8 # a elements
|
||||
leaq 8(%rcx), %r9 # b elements
|
||||
.deq_vec_loop:
|
||||
testq %rdx, %rdx
|
||||
jz .deq_true
|
||||
pushq %r8
|
||||
pushq %r9
|
||||
pushq %rdx
|
||||
movq (%r8), %rdi
|
||||
movq (%r9), %rsi
|
||||
call deep_equal
|
||||
popq %rdx
|
||||
popq %r9
|
||||
popq %r8
|
||||
cmpq $VAL_FALSE, %rax
|
||||
je .deq_false
|
||||
addq $8, %r8
|
||||
addq $8, %r9
|
||||
decq %rdx
|
||||
jmp .deq_vec_loop
|
||||
|
||||
bi_abs:
|
||||
GETARG %rax
|
||||
sarq $3, %rax
|
||||
|
|
@ -4729,6 +4831,86 @@ bi_gensym:
|
|||
call intern_static
|
||||
RET_VAL
|
||||
|
||||
# (exit [code]) — graceful termination. Missing arg → exit 0.
|
||||
bi_exit:
|
||||
cmpq $VAL_NIL, %r12
|
||||
je .bex_zero
|
||||
GETARG %rax
|
||||
sarq $3, %rax
|
||||
movq %rax, %rdi
|
||||
movq $SYS_EXIT, %rax
|
||||
syscall
|
||||
.bex_zero:
|
||||
xorq %rdi, %rdi
|
||||
movq $SYS_EXIT, %rax
|
||||
syscall
|
||||
|
||||
# (values . xs) — packs its args as a multi-value packet.
|
||||
# Calling convention: if xs has exactly one element, that element is
|
||||
# returned directly (so code that ignores multi-value semantics still
|
||||
# works). Otherwise we build a tagged pair (mval_marker . xs) that
|
||||
# call-with-values destructures. mval_marker is a gensymed symbol set
|
||||
# at init, so no user-written pair can masquerade as a packet.
|
||||
bi_values:
|
||||
cmpq $VAL_NIL, %r12
|
||||
je .bv_empty
|
||||
movq %r12, %rax
|
||||
andq $-8, %rax
|
||||
movq 8(%rax), %rcx # cdr
|
||||
cmpq $VAL_NIL, %rcx
|
||||
je .bv_single
|
||||
# multi: build pair (mval_marker . args)
|
||||
movq mval_marker(%rip), %rdi
|
||||
movq %r12, %rsi
|
||||
call make_pair
|
||||
RET_VAL
|
||||
.bv_single:
|
||||
movq (%rax), %rax # car
|
||||
RET_VAL
|
||||
.bv_empty:
|
||||
movq mval_marker(%rip), %rdi
|
||||
movq $VAL_NIL, %rsi
|
||||
call make_pair
|
||||
RET_VAL
|
||||
|
||||
# (call-with-values producer consumer) — invokes producer with no
|
||||
# args, destructures the multi-value packet (if any), passes the
|
||||
# values positionally to consumer.
|
||||
bi_call_with_values:
|
||||
GETARG %rbx # producer
|
||||
GETARG %rcx # consumer (save in %rcx via stack)
|
||||
pushq %rcx
|
||||
movq %rbx, %rdi # producer
|
||||
movq $VAL_NIL, %rsi # empty arg list
|
||||
call apply_proc_raw
|
||||
popq %rcx # consumer
|
||||
# Is %rax a multi-value packet? (pair whose car = mval_marker)
|
||||
movq %rax, %rdx
|
||||
andq $TAG_MASK, %rdx
|
||||
cmpq $TAG_PAIR, %rdx
|
||||
jne .bcwv_single
|
||||
movq %rax, %rdx
|
||||
andq $-8, %rdx
|
||||
movq (%rdx), %r8
|
||||
cmpq mval_marker(%rip), %r8
|
||||
jne .bcwv_single
|
||||
# Multi-value — args = cdr of packet
|
||||
movq 8(%rdx), %rsi
|
||||
movq %rcx, %rdi # consumer
|
||||
call apply_proc_raw
|
||||
RET_VAL
|
||||
.bcwv_single:
|
||||
# Single value — wrap in a 1-element list and apply
|
||||
pushq %rcx
|
||||
movq %rax, %rdi
|
||||
movq $VAL_NIL, %rsi
|
||||
call make_pair
|
||||
popq %rcx
|
||||
movq %rax, %rsi # (result)
|
||||
movq %rcx, %rdi # consumer
|
||||
call apply_proc_raw
|
||||
RET_VAL
|
||||
|
||||
# (cadr x) — second element. Shortcut: (car (cdr x)).
|
||||
bi_cadr:
|
||||
GETARG %rdi
|
||||
|
|
@ -8269,6 +8451,12 @@ init_special_forms:
|
|||
call intern_static
|
||||
movq %rax, sym_else_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
|
||||
call intern_static
|
||||
movq %rax, mval_marker(%rip)
|
||||
|
||||
.ifdef CL_FULL
|
||||
leaq sf_quasiquote(%rip), %rdi
|
||||
call intern_static
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue