boot2

Playing with the boostrap
git clone https://git.ryansepassi.com/git/boot2.git
Log | Files | Refs | README

commit 0b61df9e1743dabbe4d1912d22c97edbebd95d9b
parent 1c28efab79a61a5ce7f7191f55dd4fca7357a597
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Sun, 26 Apr 2026 16:31:07 -0700

P1pp: add %fn2 / %stl / %ldl + init_arenas; adopt in scheme1

Function definitions with locals previously required an out-of-line
%struct NAME_LOCALS and a hand-rolled %st(reg, sp, %NAME_LOCALS.field).
%fn2(name, {a b c}, body) synthesizes the struct, opens an m1pp frame,
and sizes %enter from it; %stl/%ldl take the slot name directly.

Move the BSS arena pointer-init walk (formerly scheme1's local
bss_init) into libp1pp as init_arenas + %arena_entry. The new layout
threads a running offset, so each arena_table row is (slot, size)
rather than (slot, precomputed offset).

Convert all 53 scheme1 functions with locals to %fn2; swap bss_init
for init_arenas at startup.

Diffstat:
MP1/P1pp.P1pp | 178+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Mscheme1/scheme1.P1pp | 1713++++++++++++++++++++++++++++++++++++++-----------------------------------------
2 files changed, 953 insertions(+), 938 deletions(-)

diff --git a/P1/P1pp.P1pp b/P1/P1pp.P1pp @@ -469,6 +469,44 @@ %endm # ========================================================================= +# %fn2 -- function with named locals +# ========================================================================= +# +# Like %fn, but the second argument is a braced list of local names +# instead of a byte frame size. Synthesizes a `name_FRAME` %struct +# (one 8-byte slot per local), opens both an m1pp scope and an m1pp +# frame named after the function, and sizes the stack frame from +# %name_FRAME.SIZE. +# +# Inside the body these helpers resolve against the enclosing frame: +# %local(slot) byte offset of local `slot` +# %stl(reg, slot) store reg into local `slot` +# %ldl(reg, slot) load local `slot` into reg +# +# Because m1pp tracks frames in a single slot independent of the +# %scope stack, %local / %stl / %ldl keep resolving against the +# function even when the body opens nested %scope blocks (e.g. from +# a control-flow macro). +# +# Locals follow the same braces convention as `body`: a multi-local +# list must be braced (`{a, b, c}`); a zero-local function uses `{}`. + +%macro fn2(name, locals, body) + %struct name ## _FRAME { locals } + : ## name + %scope name + %frame name + %enter(% ## name ## _FRAME.SIZE) + body + %eret + %endframe + %endscope +%endm + +%macro stl(reg, slot) %st(reg, sp, %local(slot)) %endm +%macro ldl(reg, slot) %ld(reg, sp, %local(slot)) %endm + +# ========================================================================= # %assert_<cc> macros # ========================================================================= # @@ -946,39 +984,39 @@ # sys_read(fd=a0, buf=a1, len=a2) -> n (a0) :sys_read -%mov(a3, a2) -%mov(a2, a1) -%mov(a1, a0) -%li(a0, %p1_sys_read) -%syscall -%ret + %mov(a3, a2) + %mov(a2, a1) + %mov(a1, a0) + %li(a0, %p1_sys_read) + %syscall + %ret # sys_write(fd=a0, buf=a1, len=a2) -> n (a0) :sys_write -%mov(a3, a2) -%mov(a2, a1) -%mov(a1, a0) -%li(a0, %p1_sys_write) -%syscall -%ret + %mov(a3, a2) + %mov(a2, a1) + %mov(a1, a0) + %li(a0, %p1_sys_write) + %syscall + %ret # sys_open(path=a0, flags=a1, mode=a2) -> fd (a0) # Implemented as openat(AT_FDCWD, path, flags, mode). AT_FDCWD = -100. :sys_open -%mov(t0, a2) -%mov(a3, a1) -%mov(a2, a0) -%li(a1, -100) -%li(a0, %p1_sys_openat) -%syscall -%ret + %mov(t0, a2) + %mov(a3, a1) + %mov(a2, a0) + %li(a1, -100) + %li(a0, %p1_sys_openat) + %syscall + %ret # sys_close(fd=a0) -> r (a0) :sys_close -%mov(a1, a0) -%li(a0, %p1_sys_close) -%syscall -%ret + %mov(a1, a0) + %li(a0, %p1_sys_close) + %syscall + %ret # sys_exit(code=a0) -> never returns :sys_exit @@ -1244,6 +1282,43 @@ }) # ========================================================================= +# BSS arena pointer-init table +# ========================================================================= +# +# Pattern: a program reserves a stretch of memory past :ELF_end (or any +# base) and wants to carve it into N fixed-size arenas, each anchored +# by a pointer slot in the data section. The table emits one +# (slot, size) row per arena via %arena_entry; init_arenas walks the +# table once at startup and writes base + sum of prior sizes into each +# slot, so arena[k] starts where arena[k-1] ended. + +# %arena_entry(slot, size) -- one 16-byte row: 4-byte label ref + 4 +# bytes zero pad + 8-byte size. `slot` is passed as a label ref (`&foo`). +%macro arena_entry(slot, size) slot %(0) $(size) %endm + +# init_arenas(base=a0, tbl=a1, tbl_end=a2) -> 0 +# +# Walks (slot, size) pairs from `tbl` to `tbl_end`, threading a running +# offset starting at 0. For each entry: *slot = base + offset, then +# offset += size. Leaf. +:init_arenas +%scope init_arenas + %li(t0, 0) + ::loop + %beq(a1, a2, &::done) + %ld(t1, a1, 0) + %ld(t2, a1, 8) + %add(a3, a0, t0) + %st(a3, t1, 0) + %add(t0, t0, t2) + %addi(a1, a1, 16) + %b(&::loop) + ::done + %li(a0, 0) + %ret +%endscope + +# ========================================================================= # Bump allocator # ========================================================================= # @@ -1252,14 +1327,14 @@ # bump_init(base=a0, cap=a1) -> 0 :bump_init -%la(t0, &libp1pp__bump_base) -%st(a0, t0, 0) -%la(t0, &libp1pp__bump_cursor) -%st(a0, t0, 0) -%la(t0, &libp1pp__bump_cap) -%st(a1, t0, 0) -%li(a0, 0) -%ret + %la(t0, &libp1pp__bump_base) + %st(a0, t0, 0) + %la(t0, &libp1pp__bump_cursor) + %st(a0, t0, 0) + %la(t0, &libp1pp__bump_cap) + %st(a1, t0, 0) + %li(a0, 0) + %ret # bump_alloc(n=a0) -> ptr (0 on exhaustion) # @@ -1290,25 +1365,25 @@ # bump_mark() -> saved :bump_mark -%la(t0, &libp1pp__bump_cursor) -%ld(a0, t0, 0) -%ret + %la(t0, &libp1pp__bump_cursor) + %ld(a0, t0, 0) + %ret # bump_release(saved=a0) -> 0 :bump_release -%la(t0, &libp1pp__bump_cursor) -%st(a0, t0, 0) -%li(a0, 0) -%ret + %la(t0, &libp1pp__bump_cursor) + %st(a0, t0, 0) + %li(a0, 0) + %ret # bump_reset() -> 0 :bump_reset -%la(t0, &libp1pp__bump_base) -%ld(t1, t0, 0) -%la(t0, &libp1pp__bump_cursor) -%st(t1, t0, 0) -%li(a0, 0) -%ret + %la(t0, &libp1pp__bump_base) + %ld(t1, t0, 0) + %la(t0, &libp1pp__bump_cursor) + %st(t1, t0, 0) + %li(a0, 0) + %ret # ========================================================================= # Panic @@ -1334,20 +1409,15 @@ # 8-byte word (0x0A in the low byte, zeros above) so the following # buffers and the user source that comes after libp1pp stay 8-byte # aligned. sys_write reads only the one byte callers request. -:libp1pp__newline -$(10) +:libp1pp__newline $(10) # Scratch buffer used by print_int / print_hex. fmt_dec writes at most # 20 bytes, fmt_hex at most 16, so 32 bytes with word alignment is # comfortably above both. -:libp1pp__num_buf -$(0) $(0) $(0) $(0) +:libp1pp__num_buf $(0) $(0) $(0) $(0) # Bump-allocator state. Zero-initialized so bump_alloc returns 0 until # bump_init installs an arena. -:libp1pp__bump_base -$(0) -:libp1pp__bump_cursor -$(0) -:libp1pp__bump_cap -$(0) +:libp1pp__bump_base $(0) +:libp1pp__bump_cursor $(0) +:libp1pp__bump_cap $(0) diff --git a/scheme1/scheme1.P1pp b/scheme1/scheme1.P1pp @@ -30,13 +30,13 @@ %struct REC { hdr td } # .SIZE = 16 (header) # Records are variable width: header + td slot + N field slots. -# BSS arena offsets from :ELF_end. readbuf is 256 KiB (sized to fit +# BSS arenas anchored past :ELF_end. readbuf is 256 KiB (sized to fit # the catm'd cc compiler source incl. prelude — see READBUF_CAP_BYTES), # then the heap (HEAP_CAP_BYTES, currently 16 MiB), then symtab. The three are packed # back-to-back; everything lives within the ELF p_memsz reservation -# (currently 32 MiB) declared in vendor/seed/<arch>/ELF.hex2. p1_main's -# startup loop materializes &ELF_end + OFF_X into the matching pointer -# slot. The offsets are emitted directly in bss_init_tbl via $(). +# (currently 32 MiB) declared in vendor/seed/<arch>/ELF.hex2. p1_main +# calls libp1pp's init_arenas, which walks arena_table and writes +# &ELF_end + sum of prior sizes into each pointer slot. %macro SYMTAB_CAP_SLOTS() 8192 %endm %macro READBUF_CAP_BYTES() 262144 %endm @@ -209,7 +209,10 @@ %bltu(a0, t0, &::usage) # Initialize - %call(&bss_init) + %la(a0, &ELF_end) + %la(a1, &arena_table) + %la(a2, &arena_table_end) + %call(&init_arenas) %call(&heap_init) %call(&intern_special_forms) %call(&register_primitives) @@ -457,14 +460,13 @@ # parse_list() -> tagged list value in a0. Cursor sits past '(' on entry; # returns once ')' is consumed. # -# Frame: 16 bytes -# +0 head (NIL until first item) -# +8 tail (most recent cons; set-cdr! target) -%struct PARSE_LIST_LOCALS { head tail } # .SIZE = 16 -%fn(parse_list, %PARSE_LIST_LOCALS.SIZE, { +# Locals: +# head NIL until first item +# tail most recent cons (set-cdr! target) +%fn2(parse_list, {head tail}, { %li(t0, %imm_val(%IMM.NIL)) - %st(t0, sp, %PARSE_LIST_LOCALS.head) - %st(t0, sp, %PARSE_LIST_LOCALS.tail) + %stl(t0, head) + %stl(t0, tail) ::loop %call(&skip_ws) @@ -496,18 +498,18 @@ %call(&cons) # If head is NIL, both head and tail = new cons; else set-cdr! tail = new. - %ld(t0, sp, %PARSE_LIST_LOCALS.head) + %ldl(t0, head) %li(t1, %imm_val(%IMM.NIL)) %bne(t0, t1, &::link) - %st(a0, sp, %PARSE_LIST_LOCALS.head) - %st(a0, sp, %PARSE_LIST_LOCALS.tail) + %stl(a0, head) + %stl(a0, tail) %b(&::loop) ::link - %ld(t0, sp, %PARSE_LIST_LOCALS.tail) + %ldl(t0, tail) # set-cdr! tail = a0 -> store a0 at [tail + 7] (raw + 8) %set_cdr(a0, t0) - %st(a0, sp, %PARSE_LIST_LOCALS.tail) + %stl(a0, tail) %b(&::loop) ::do_dot @@ -517,7 +519,7 @@ %addi(t0, t0, 1) %st(t0, t1, 0) %call(&parse_one) - %ld(t0, sp, %PARSE_LIST_LOCALS.tail) + %ldl(t0, tail) %set_cdr(a0, t0) %call(&skip_ws) %bnez(a0, &::eof) @@ -527,7 +529,7 @@ %bnez(a1, &::eof) %addi(t0, t0, 1) %st(t0, t1, 0) - %ld(a0, sp, %PARSE_LIST_LOCALS.head) + %ldl(a0, head) %eret ::close @@ -535,7 +537,7 @@ %lda_global(t1, t0, &readbuf_pos) %addi(t1, t1, 1) %st(t1, t0, 0) - %ld(a0, sp, %PARSE_LIST_LOCALS.head) + %ldl(a0, head) %eret ::eof @@ -547,20 +549,19 @@ # range is unchecked, matching make-bytevector's lax stance) and packs # them into a fresh bytevector. # -# Frame: 16 bytes -# +0 list head (cursor during fill pass) -# +8 result bv -%struct PARSE_U8_BODY_LOCALS { list result } # .SIZE = 16 -%fn(parse_u8_body, %PARSE_U8_BODY_LOCALS.SIZE, { +# Locals: +# list parsed element list (cursor during fill pass) +# result freshly allocated bv +%fn2(parse_u8_body, {list result}, { %call(&parse_list) - %st(a0, sp, %PARSE_U8_BODY_LOCALS.list) + %stl(a0, list) %call(&list_length) ; clobbers a0 -> count %call(&bv_alloc) ; a0 = bv - %st(a0, sp, %PARSE_U8_BODY_LOCALS.result) + %stl(a0, result) %heap_ld(t0, a0, %BV.data) - %ld(a0, sp, %PARSE_U8_BODY_LOCALS.list) ; list cursor + %ldl(a0, list) ; list cursor ::loop %if_nil(t1, a0, &::done) @@ -571,7 +572,7 @@ %cdr(a0, a0) %b(&::loop) ::done - %ld(a0, sp, %PARSE_U8_BODY_LOCALS.result) + %ldl(a0, result) }) # is_ident_byte(c=a0) -> a1 (1 if c is a valid identifier byte, else 0). @@ -637,14 +638,13 @@ # parse_dec and any non-numeric byte aborts; otherwise the token is a # symbol and every byte is checked against is_ident_byte before intern. # -# Frame: 24 bytes -# +0 start cursor (byte offset) -# +8 end cursor (byte offset) -# +16 cursor (scratch slot for the symbol-validation loop) -%struct PARSE_ATOM_LOCALS { start end cursor } # .SIZE = 24 -%fn(parse_atom, %PARSE_ATOM_LOCALS.SIZE, { +# Locals: +# start cursor (byte offset) +# end cursor (byte offset) +# cursor (scratch slot for the symbol-validation loop) +%fn2(parse_atom, {start end cursor}, { %lda_global(t1, t0, &readbuf_pos) - %st(t1, sp, %PARSE_ATOM_LOCALS.start) + %stl(t1, start) %ld_global(t2, &readbuf_len) @@ -663,11 +663,11 @@ %b(&::scan) ::end - %st(t1, sp, %PARSE_ATOM_LOCALS.end) + %stl(t1, end) %st(t1, t0, 0) # Dispatch on the first byte. - %ld(t0, sp, %PARSE_ATOM_LOCALS.start) + %ldl(t0, start) %ld_global(a0, &readbuf_buf_ptr) %add(a0, a0, t0) %lb(t1, a0, 0) @@ -684,7 +684,7 @@ %beqz(a1, &::sign) %b(&::is_sym) ::sign - %ld(t2, sp, %PARSE_ATOM_LOCALS.end) + %ldl(t2, end) %addi(t0, t0, 1) %beq(t0, t2, &::is_sym) %readbuf_byte(a0, t0) @@ -694,35 +694,35 @@ ::is_sym # Validate every byte; abort on the first non-ident byte. - %ld(t0, sp, %PARSE_ATOM_LOCALS.start) - %st(t0, sp, %PARSE_ATOM_LOCALS.cursor) + %ldl(t0, start) + %stl(t0, cursor) ::sym_loop - %ld(t0, sp, %PARSE_ATOM_LOCALS.cursor) - %ld(t1, sp, %PARSE_ATOM_LOCALS.end) + %ldl(t0, cursor) + %ldl(t1, end) %beq(t0, t1, &::sym_intern) %readbuf_byte(a0, t0) %call(&is_ident_byte) %beqz(a1, &::sym_bad) - %ld(t0, sp, %PARSE_ATOM_LOCALS.cursor) + %ldl(t0, cursor) %addi(t0, t0, 1) - %st(t0, sp, %PARSE_ATOM_LOCALS.cursor) + %stl(t0, cursor) %b(&::sym_loop) ::sym_bad %die(msg_bad_ident) ::sym_intern - %ld(a0, sp, %PARSE_ATOM_LOCALS.start) + %ldl(a0, start) %ld_global(t0, &readbuf_buf_ptr) %add(a0, t0, a0) - %ld(t1, sp, %PARSE_ATOM_LOCALS.end) - %ld(t2, sp, %PARSE_ATOM_LOCALS.start) + %ldl(t1, end) + %ldl(t2, start) %sub(a1, t1, t2) %tail(&intern) ::is_int - %ld(t0, sp, %PARSE_ATOM_LOCALS.start) ; start_off - %ld(t1, sp, %PARSE_ATOM_LOCALS.end) ; end_off + %ldl(t0, start) ; start_off + %ldl(t1, end) ; end_off %ld_global(a0, &readbuf_buf_ptr) %add(a0, a0, t0) ; ptr = base + start_off %sub(a1, t1, t0) ; len = end_off - start_off @@ -740,15 +740,14 @@ # (\n \t \r \\ \") yields one byte; an inline-hex escape \xHEX; (1+ # hex digits, value 0..255, terminated by ';') also yields one byte. # -# Frame: 32 bytes -# +0 start cursor (first content byte) -# +8 end cursor (closing '"' position) -# +16 bv wrapper (saved across the data fill loop) -# +24 spill slot (write ptr saved across parse_hex in \x escape) -%struct PARSE_STRING_LOCALS { start end bv spill } # .SIZE = 32 -%fn(parse_string, %PARSE_STRING_LOCALS.SIZE, { +# Locals: +# start cursor (first content byte) +# end cursor (closing '"' position) +# bv wrapper (saved across the data fill loop) +# spill slot (write ptr saved across parse_hex in \x escape) +%fn2(parse_string, {start end bv spill}, { %ld_global(t1, &readbuf_pos) - %st(t1, sp, %PARSE_STRING_LOCALS.start) + %stl(t1, start) %ld_global(t2, &readbuf_len) @@ -795,13 +794,13 @@ %b(&::scan) ::scan_done - %st(t1, sp, %PARSE_STRING_LOCALS.end) + %stl(t1, end) %call(&bv_alloc) - %st(a0, sp, %PARSE_STRING_LOCALS.bv) + %stl(a0, bv) # Pass 2: decode into the freshly allocated data buffer. - %ld(t1, sp, %PARSE_STRING_LOCALS.start) ; start - %ld(t2, sp, %PARSE_STRING_LOCALS.end) ; end + %ldl(t1, start) ; start + %ldl(t2, end) ; end %heap_ld(a3, a0, %BV.data) ::fill @@ -851,8 +850,8 @@ # t0/t1/t2 and a2/a3, so spill the cursor (t1) and write ptr (a3) # across the call. sp+0 is free once pass 1 finishes. %addi(t1, t1, 1) ; t1 -> first hex digit - %st(t1, sp, %PARSE_STRING_LOCALS.start) - %st(a3, sp, %PARSE_STRING_LOCALS.spill) + %stl(t1, start) + %stl(a3, spill) %ld_global(t0, &readbuf_buf_ptr) %add(a0, t0, t1) ; ptr to first hex digit %sub(a1, t2, t1) ; max len (bytes left in body) @@ -860,15 +859,15 @@ %beqz(a1, &::hex_bad) %li(t0, 255) %bltu(t0, a0, &::hex_bad) - %ld(t1, sp, %PARSE_STRING_LOCALS.start) + %ldl(t1, start) %add(t1, t1, a1) ; t1 = position of expected ';' - %ld(t2, sp, %PARSE_STRING_LOCALS.end) + %ldl(t2, end) %beq(t1, t2, &::hex_bad) %readbuf_byte(t0, t1) %addi(t0, t0, -59) ; ';' %bnez(t0, &::hex_bad) %addi(t1, t1, 1) ; consume ';' - %ld(a3, sp, %PARSE_STRING_LOCALS.spill) + %ldl(a3, spill) %sb(a0, a3, 0) %addi(a3, a3, 1) %b(&::fill) @@ -879,7 +878,7 @@ ::fill_done %addi(t1, t1, 1) ; consume closing '"' %st_global(t1, &readbuf_pos, t0) - %ld(a0, sp, %PARSE_STRING_LOCALS.bv) + %ldl(a0, bv) %eret ::eof @@ -892,13 +891,12 @@ # bodies yield that byte; multi-byte bodies dispatch to hex (#\xNN) or # named (#\space, #\newline, #\tab, #\return, #\null) forms. # -# Frame: 16 bytes -# +0 start cursor -# +8 end cursor -%struct PARSE_CHAR_LOCALS { start end } # .SIZE = 16 -%fn(parse_char, %PARSE_CHAR_LOCALS.SIZE, { +# Locals: +# start cursor +# end cursor +%fn2(parse_char, {start end}, { %lda_global(t1, t0, &readbuf_pos) - %st(t1, sp, %PARSE_CHAR_LOCALS.start) + %stl(t1, start) %ld_global(t2, &readbuf_len) @@ -920,11 +918,11 @@ %b(&::scan) ::scan_done - %st(t1, sp, %PARSE_CHAR_LOCALS.end) + %stl(t1, end) %st(t1, t0, 0) - %ld(t0, sp, %PARSE_CHAR_LOCALS.start) - %ld(t1, sp, %PARSE_CHAR_LOCALS.end) + %ldl(t0, start) + %ldl(t1, end) %sub(a2, t1, t0) ; length %li(a3, 1) @@ -1103,14 +1101,14 @@ # eval(expr=a0, env=a1) -> value (a0) # -# Frame: 32 bytes -# +0 expr -# +8 env -# +16 fn (head value, while args are being evaluated) -%struct EVAL_LOCALS { expr env fn pad } # .SIZE = 32 -%fn(eval, %EVAL_LOCALS.SIZE, { - %st(a0, sp, %EVAL_LOCALS.expr) - %st(a1, sp, %EVAL_LOCALS.env) +# Locals: +# expr +# env +# fn (head value, while args are being evaluated) +# pad +%fn2(eval, {expr env fn pad}, { + %stl(a0, expr) + %stl(a1, env) %tagof(t0, a0) %li(t1, %TAG.PAIR) @@ -1150,7 +1148,7 @@ # Special-form dispatch: pointer-compare head against the cached # special-form symbol values. SYM is a distinct tag, so a head that # isn't a symbol cannot collide with any sym_* slot. - %ld(t0, sp, %EVAL_LOCALS.expr) + %ldl(t0, expr) %car(t0, t0) ; t0 = head %dispatch_form(&sym_quote, &::do_quote) %dispatch_form(&sym_if, &::do_if) @@ -1168,26 +1166,26 @@ %dispatch_form(&sym_pmatch, &::do_pmatch) # head = eval(car(expr), env) - %ld(a0, sp, %EVAL_LOCALS.expr) + %ldl(a0, expr) %car(a0, a0) - %ld(a1, sp, %EVAL_LOCALS.env) + %ldl(a1, env) %call(&eval) - %st(a0, sp, %EVAL_LOCALS.fn) + %stl(a0, fn) # args = eval_args(cdr(expr), env) - %ld(a0, sp, %EVAL_LOCALS.expr) + %ldl(a0, expr) %cdr(a0, a0) - %ld(a1, sp, %EVAL_LOCALS.env) + %ldl(a1, env) %call(&eval_args) # apply(fn, args) -- tail call %mov(a1, a0) - %ld(a0, sp, %EVAL_LOCALS.fn) + %ldl(a0, fn) %tail(&apply) ::do_quote # (quote datum) -> car(cdr(expr)) - %ld(a0, sp, %EVAL_LOCALS.expr) + %ldl(a0, expr) %cdr(a0, a0) %car(a0, a0) %eret @@ -1226,59 +1224,57 @@ # previous tail. Host stack stays O(1) regardless of arg-list length; # eval order is left-to-right. # -# Frame: 32 bytes -# +0 args (advances) -# +8 env -# +16 head (NIL until first val is appended) -# +24 tail (most recent cell; set-cdr! target) -%struct EVAL_ARGS_LOCALS { args env head tail } # .SIZE = 32 -%fn(eval_args, %EVAL_ARGS_LOCALS.SIZE, { - %st(a0, sp, %EVAL_ARGS_LOCALS.args) - %st(a1, sp, %EVAL_ARGS_LOCALS.env) +# Locals: +# args (advances) +# env +# head (NIL until first val is appended) +# tail (most recent cell; set-cdr! target) +%fn2(eval_args, {args env head tail}, { + %stl(a0, args) + %stl(a1, env) %li(t0, %imm_val(%IMM.NIL)) - %st(t0, sp, %EVAL_ARGS_LOCALS.head) - %st(t0, sp, %EVAL_ARGS_LOCALS.tail) + %stl(t0, head) + %stl(t0, tail) ::loop - %ld(t0, sp, %EVAL_ARGS_LOCALS.args) + %ldl(t0, args) %if_nil(t1, t0, &::done) # val = eval(car(args), env) %car(a0, t0) - %ld(a1, sp, %EVAL_ARGS_LOCALS.env) + %ldl(a1, env) %call(&eval) # cell = cons(val, NIL); append to head/tail. %li(a1, %imm_val(%IMM.NIL)) %call(&cons) - %ld(t0, sp, %EVAL_ARGS_LOCALS.head) + %ldl(t0, head) %if_nil(t1, t0, &::first) - %ld(t0, sp, %EVAL_ARGS_LOCALS.tail) + %ldl(t0, tail) %set_cdr(a0, t0) - %st(a0, sp, %EVAL_ARGS_LOCALS.tail) + %stl(a0, tail) %b(&::advance) ::first - %st(a0, sp, %EVAL_ARGS_LOCALS.head) - %st(a0, sp, %EVAL_ARGS_LOCALS.tail) + %stl(a0, head) + %stl(a0, tail) ::advance %advance_walk(0) %b(&::loop) ::done - %ld(a0, sp, %EVAL_ARGS_LOCALS.head) + %ldl(a0, head) }) # apply(fn=a0, args=a1) -> result (a0) # -# Frame: 16 bytes -# +0 args -# +8 body (saved across bind_params for the closure path) -%struct APPLY_LOCALS { args body } # .SIZE = 16 -%fn(apply, %APPLY_LOCALS.SIZE, { - %st(a1, sp, %APPLY_LOCALS.args) +# Locals: +# args +# body (saved across bind_params for the closure path) +%fn2(apply, {args body}, { + %stl(a1, args) # Only HEAP-tagged values can be applicable. %tagof(t0, a0) @@ -1307,25 +1303,25 @@ # tail-calls back into apply. %mov(a1, a0) %heap_ld(t0, a0, %PRIM.entry_w) - %ld(a0, sp, %APPLY_LOCALS.args) + %ldl(a0, args) %tailr(t0) ::closure # Closure layout (HEAP-tagged): [hdr][params][body][env] # field offsets from tagged ptr: params=5, body=13, env=21. %heap_ld(t1, a0, %CLOSURE.body) ; must survive bind_params - %st(t1, sp, %APPLY_LOCALS.body) + %stl(t1, body) %heap_ld(t2, a0, %CLOSURE.env) %heap_ld(t0, a0, %CLOSURE.params) %mov(a0, t0) ; bind_params(params, args, env) - %ld(a1, sp, %APPLY_LOCALS.args) + %ldl(a1, args) %mov(a2, t2) %call(&bind_params) # eval_body(body, new_env) -- tail call %mov(a1, a0) - %ld(a0, sp, %APPLY_LOCALS.body) + %ldl(a0, body) %tail(&eval_body) }) @@ -1364,13 +1360,12 @@ # eval_if(rest=a0, env=a1) -> value (a0). `rest` is (test then else). # No arity check here -- spec policy: malformed special forms are UB. # -# Frame: 16 bytes -# +0 rest -# +8 env -%struct EVAL_IF_LOCALS { rest env } # .SIZE = 16 -%fn(eval_if, %EVAL_IF_LOCALS.SIZE, { - %st(a0, sp, %EVAL_IF_LOCALS.rest) - %st(a1, sp, %EVAL_IF_LOCALS.env) +# Locals: +# rest +# env +%fn2(eval_if, {rest env}, { + %stl(a0, rest) + %stl(a1, env) # val = eval(car(rest), env) %car(a0, a0) @@ -1380,19 +1375,19 @@ %beq(a0, t0, &::else_branch) # then-branch: tail-eval(cadr(rest), env) - %ld(a0, sp, %EVAL_IF_LOCALS.rest) + %ldl(a0, rest) %cdr(a0, a0) %car(a0, a0) - %ld(a1, sp, %EVAL_IF_LOCALS.env) + %ldl(a1, env) %tail(&eval) ::else_branch # else-branch: tail-eval(caddr(rest), env) - %ld(a0, sp, %EVAL_IF_LOCALS.rest) + %ldl(a0, rest) %cdr(a0, a0) %cdr(a0, a0) %car(a0, a0) - %ld(a1, sp, %EVAL_IF_LOCALS.env) + %ldl(a1, env) %tail(&eval) }) @@ -1400,36 +1395,35 @@ # rest is (params . body). Allocates a 32-byte CLOSURE on the heap # and stores params, body, and the captured env directly. # -# Frame: 24 bytes -# +0 rest -# +8 env -# +16 closure ptr (HEAP-tagged) -%struct EVAL_LAMBDA_LOCALS { rest env closure } # .SIZE = 24 -%fn(eval_lambda, %EVAL_LAMBDA_LOCALS.SIZE, { - %st(a0, sp, %EVAL_LAMBDA_LOCALS.rest) - %st(a1, sp, %EVAL_LAMBDA_LOCALS.env) +# Locals: +# rest +# env +# closure ptr (HEAP-tagged) +%fn2(eval_lambda, {rest env closure}, { + %stl(a0, rest) + %stl(a1, env) %li(a0, 32) %li(a1, %HDR.CLOSURE) %call(&alloc_hdr) - %st(a0, sp, %EVAL_LAMBDA_LOCALS.closure) + %stl(a0, closure) # closure[params] = car(rest) - %ld(t0, sp, %EVAL_LAMBDA_LOCALS.rest) + %ldl(t0, rest) %car(t1, t0) - %ld(t0, sp, %EVAL_LAMBDA_LOCALS.closure) + %ldl(t0, closure) %heap_st(t1, t0, %CLOSURE.params) # closure[body] = cdr(rest) - %ld(t1, sp, %EVAL_LAMBDA_LOCALS.rest) + %ldl(t1, rest) %cdr(t1, t1) %heap_st(t1, t0, %CLOSURE.body) # closure[env] = captured env - %ld(t1, sp, %EVAL_LAMBDA_LOCALS.env) + %ldl(t1, env) %heap_st(t1, t0, %CLOSURE.env) - %ld(a0, sp, %EVAL_LAMBDA_LOCALS.closure) + %ldl(a0, closure) }) # eval_define(rest=a0, env=a1) -> UNSPEC (a0). @@ -1441,13 +1435,12 @@ # (every internal body context routes through eval_body); see the check # at the head of eval_body's loop. # -# Frame: 16 bytes -# +0 rest -# +8 env -%struct EVAL_DEFINE_LOCALS { rest env } # .SIZE = 16 -%fn(eval_define, %EVAL_DEFINE_LOCALS.SIZE, { - %st(a0, sp, %EVAL_DEFINE_LOCALS.rest) - %st(a1, sp, %EVAL_DEFINE_LOCALS.env) +# Locals: +# rest +# env +%fn2(eval_define, {rest env}, { + %stl(a0, rest) + %stl(a1, env) # If car(rest) is a pair, this is the lambda-sugar form. %car(t0, a0) @@ -1456,13 +1449,13 @@ %beq(t1, t2, &::sugar) # Plain define: value = eval(car(cdr(rest)), env) - %ld(t0, sp, %EVAL_DEFINE_LOCALS.rest) + %ldl(t0, rest) %cdr(a0, t0) %car(a0, a0) - %ld(a1, sp, %EVAL_DEFINE_LOCALS.env) + %ldl(a1, env) %call(&eval) - %ld(t0, sp, %EVAL_DEFINE_LOCALS.rest) + %ldl(t0, rest) %car(t0, t0) %bind_global_from_t0() %li(a0, %imm_val(%IMM.UNSPEC)) @@ -1470,16 +1463,16 @@ ::sugar # rest = ((name . params) . body); build (params . body) for eval_lambda. - %ld(t0, sp, %EVAL_DEFINE_LOCALS.rest) + %ldl(t0, rest) %car(t0, t0) %cdr(a0, t0) ; params - %ld(t0, sp, %EVAL_DEFINE_LOCALS.rest) + %ldl(t0, rest) %cdr(a1, t0) ; body %call(&cons) - %ld(a1, sp, %EVAL_DEFINE_LOCALS.env) + %ldl(a1, env) %call(&eval_lambda) - %ld(t0, sp, %EVAL_DEFINE_LOCALS.rest) + %ldl(t0, rest) %car(t0, t0) %car(t0, t0) ; name %bind_global_from_t0() @@ -1494,48 +1487,47 @@ # by define for top-level rebind. Spec: behavior on a truly unbound # name follows the primitive-failure policy. # -# Frame: 24 bytes -# +0 rest (sym . (value-expr . ())) -# +8 env -# +16 saved value (eval'd value-expr) -%struct EVAL_SETBANG_LOCALS { rest env saved } # .SIZE = 24 -%fn(eval_setbang, %EVAL_SETBANG_LOCALS.SIZE, { - %st(a0, sp, %EVAL_SETBANG_LOCALS.rest) - %st(a1, sp, %EVAL_SETBANG_LOCALS.env) +# Locals: +# rest (sym . (value-expr . ())) +# env +# saved value (eval'd value-expr) +%fn2(eval_setbang, {rest env saved}, { + %stl(a0, rest) + %stl(a1, env) # value = eval(cadr(rest), env) %cdr(a0, a0) %car(a0, a0) - %ld(a1, sp, %EVAL_SETBANG_LOCALS.env) + %ldl(a1, env) %call(&eval) - %st(a0, sp, %EVAL_SETBANG_LOCALS.saved) + %stl(a0, saved) # Walk env looking for a binding cell whose car == target sym. # Only t0..t2 are available: t0 scratch, t1 target sym, t2 env cursor. - %ld(t1, sp, %EVAL_SETBANG_LOCALS.rest) + %ldl(t1, rest) %car(t1, t1) ; target sym ::lp - %ld(t2, sp, %EVAL_SETBANG_LOCALS.env) + %ldl(t2, env) %if_nil(t0, t2, &::ms) %car(t0, t2) %car(t0, t0) ; cell sym %beq(t0, t1, &::ht) %cdr(t2, t2) - %st(t2, sp, %EVAL_SETBANG_LOCALS.env) + %stl(t2, env) %b(&::lp) ::ht %car(t0, t2) ; re-fetch binding cell - %ld(a0, sp, %EVAL_SETBANG_LOCALS.saved) + %ldl(a0, saved) %set_cdr(a0, t0) ; mutate cell's cdr %li(a0, %imm_val(%IMM.UNSPEC)) %eret ::ms # Miss: rebind global. - %ld(a0, sp, %EVAL_SETBANG_LOCALS.saved) - %ld(t0, sp, %EVAL_SETBANG_LOCALS.rest) + %ldl(a0, saved) + %ldl(t0, rest) %car(t0, t0) %bind_global_from_t0() %li(a0, %imm_val(%IMM.UNSPEC)) @@ -1547,18 +1539,17 @@ # arrow is only recognized in non-else clauses; an empty body after a # truthy test returns UNSPEC (spec policy: malformed-form UB). # -# Frame: 32 bytes -# +0 clauses (advances) -# +8 env -# +16 test value (live across the => eval/cons calls) -# +24 proc (live across the => cons call) -%struct EVAL_COND_LOCALS { clauses env test proc } # .SIZE = 32 -%fn(eval_cond, %EVAL_COND_LOCALS.SIZE, { - %st(a0, sp, %EVAL_COND_LOCALS.clauses) - %st(a1, sp, %EVAL_COND_LOCALS.env) +# Locals: +# clauses (advances) +# env +# test value (live across the => eval/cons calls) +# proc (live across the => cons call) +%fn2(eval_cond, {clauses env test proc}, { + %stl(a0, clauses) + %stl(a1, env) ::loop - %ld(t0, sp, %EVAL_COND_LOCALS.clauses) + %ldl(t0, clauses) %if_nil(t1, t0, &::nm) %car(t1, t0) ; clause @@ -1568,15 +1559,15 @@ %beq(t2, a0, &::dm) %mov(a0, t2) - %ld(a1, sp, %EVAL_COND_LOCALS.env) + %ldl(a1, env) %call(&eval) %li(t0, %imm_val(%IMM.FALSE)) %beq(a0, t0, &::nx) # Truthy. Spill test value and inspect cdr(clause): empty -> UNSPEC, # car == => -> arrow path, else regular body. - %st(a0, sp, %EVAL_COND_LOCALS.test) - %ld(t0, sp, %EVAL_COND_LOCALS.clauses) + %stl(a0, test) + %ldl(t0, clauses) %car(t0, t0) %cdr(t0, t0) %if_nil(t1, t0, &::nm) @@ -1585,27 +1576,27 @@ %beq(t1, t2, &::ar) %mov(a0, t0) ; regular body - %ld(a1, sp, %EVAL_COND_LOCALS.env) + %ldl(a1, env) %tail(&eval_body) ::ar %cdr(t0, t0) %car(a0, t0) ; proc-expr - %ld(a1, sp, %EVAL_COND_LOCALS.env) + %ldl(a1, env) %call(&eval) - %st(a0, sp, %EVAL_COND_LOCALS.proc) - %ld(a0, sp, %EVAL_COND_LOCALS.test) + %stl(a0, proc) + %ldl(a0, test) %li(a1, %imm_val(%IMM.NIL)) %call(&cons) %mov(a1, a0) - %ld(a0, sp, %EVAL_COND_LOCALS.proc) + %ldl(a0, proc) %tail(&apply) ::dm - %ld(t0, sp, %EVAL_COND_LOCALS.clauses) + %ldl(t0, clauses) %car(t0, t0) %cdr(a0, t0) - %ld(a1, sp, %EVAL_COND_LOCALS.env) + %ldl(a1, env) %tail(&eval_body) ::nx @@ -1623,15 +1614,14 @@ # Standard `let` evaluates every init in `env`, then extends env with all # bindings simultaneously and tail-evaluates the body. # -# Frame: 32 bytes -# +0 rest -# +8 env (original) -# +16 walk (bindings, advances) -# +24 new_env (built up) -%struct EVAL_LET_LOCALS { rest env walk new_env } # .SIZE = 32 -%fn(eval_let, %EVAL_LET_LOCALS.SIZE, { - %st(a0, sp, %EVAL_LET_LOCALS.rest) - %st(a1, sp, %EVAL_LET_LOCALS.env) +# Locals: +# rest +# env (original) +# walk (bindings, advances) +# new_env (built up) +%fn2(eval_let, {rest env walk new_env}, { + %stl(a0, rest) + %stl(a1, env) # Named let? %car(t0, a0) @@ -1639,14 +1629,14 @@ %li(t2, %TAG.SYM) %beq(t1, t2, &::named) - %ld(t0, sp, %EVAL_LET_LOCALS.rest) + %ldl(t0, rest) %car(t0, t0) ; bindings - %st(t0, sp, %EVAL_LET_LOCALS.walk) - %ld(t0, sp, %EVAL_LET_LOCALS.env) - %st(t0, sp, %EVAL_LET_LOCALS.new_env) ; new_env = env + %stl(t0, walk) + %ldl(t0, env) + %stl(t0, new_env) ; new_env = env ::loop - %ld(t0, sp, %EVAL_LET_LOCALS.walk) + %ldl(t0, walk) %if_nil(t1, t0, &::done) %car(t1, t0) ; pair = (name init) @@ -1655,11 +1645,11 @@ # val = eval(init, env_orig) %mov(a0, t2) - %ld(a1, sp, %EVAL_LET_LOCALS.env) + %ldl(a1, env) %call(&eval) # binding = cons(name, val) - %ld(t0, sp, %EVAL_LET_LOCALS.walk) + %ldl(t0, walk) %car(t1, t0) %car(t2, t1) %mov(a1, a0) @@ -1667,22 +1657,22 @@ %call(&cons) # new_env = cons(binding, new_env) - %ld(a1, sp, %EVAL_LET_LOCALS.new_env) + %ldl(a1, new_env) %call(&cons) - %st(a0, sp, %EVAL_LET_LOCALS.new_env) + %stl(a0, new_env) %advance_walk(16) %b(&::loop) ::done - %ld(a0, sp, %EVAL_LET_LOCALS.rest) + %ldl(a0, rest) %cdr(a0, a0) ; body - %ld(a1, sp, %EVAL_LET_LOCALS.new_env) + %ldl(a1, new_env) %tail(&eval_body) ::named - %ld(a0, sp, %EVAL_LET_LOCALS.rest) - %ld(a1, sp, %EVAL_LET_LOCALS.env) + %ldl(a0, rest) + %ldl(a1, env) %tail(&eval_let_named) }) @@ -1690,20 +1680,23 @@ # Like let, but each init is evaluated in the env extended by all prior # bindings of the same let* form (left-to-right shadowing). # -# Frame: 32 bytes (same layout as eval_let) -%struct EVAL_LETSTAR_LOCALS { rest env walk new_env } # .SIZE = 32 -%fn(eval_letstar, %EVAL_LETSTAR_LOCALS.SIZE, { - %st(a0, sp, %EVAL_LETSTAR_LOCALS.rest) - %st(a1, sp, %EVAL_LETSTAR_LOCALS.env) - - %ld(t0, sp, %EVAL_LETSTAR_LOCALS.rest) +# Locals: +# rest +# env +# walk +# new_env +%fn2(eval_letstar, {rest env walk new_env}, { + %stl(a0, rest) + %stl(a1, env) + + %ldl(t0, rest) %car(t0, t0) - %st(t0, sp, %EVAL_LETSTAR_LOCALS.walk) - %ld(t0, sp, %EVAL_LETSTAR_LOCALS.env) - %st(t0, sp, %EVAL_LETSTAR_LOCALS.new_env) + %stl(t0, walk) + %ldl(t0, env) + %stl(t0, new_env) ::loop - %ld(t0, sp, %EVAL_LETSTAR_LOCALS.walk) + %ldl(t0, walk) %if_nil(t1, t0, &::done) %car(t1, t0) @@ -1712,27 +1705,27 @@ # val = eval(init, new_env) %mov(a0, t2) - %ld(a1, sp, %EVAL_LETSTAR_LOCALS.new_env) + %ldl(a1, new_env) %call(&eval) - %ld(t0, sp, %EVAL_LETSTAR_LOCALS.walk) + %ldl(t0, walk) %car(t1, t0) %car(t2, t1) %mov(a1, a0) %mov(a0, t2) %call(&cons) - %ld(a1, sp, %EVAL_LETSTAR_LOCALS.new_env) + %ldl(a1, new_env) %call(&cons) - %st(a0, sp, %EVAL_LETSTAR_LOCALS.new_env) + %stl(a0, new_env) %advance_walk(16) %b(&::loop) ::done - %ld(a0, sp, %EVAL_LETSTAR_LOCALS.rest) + %ldl(a0, rest) %cdr(a0, a0) - %ld(a1, sp, %EVAL_LETSTAR_LOCALS.new_env) + %ldl(a1, new_env) %tail(&eval_body) }) @@ -1742,24 +1735,23 @@ # env and patch the matching binding's cdr (linear-scan lookup is fine # here -- bindings are typically ≤ a handful). # -# Frame: 32 bytes -# +0 rest -# +8 env_orig -# +16 walk -# +24 new_env -%struct EVAL_LETREC_LOCALS { rest env_orig walk new_env } # .SIZE = 32 -%fn(eval_letrec, %EVAL_LETREC_LOCALS.SIZE, { - %st(a0, sp, %EVAL_LETREC_LOCALS.rest) - %st(a1, sp, %EVAL_LETREC_LOCALS.env_orig) - - %ld(t0, sp, %EVAL_LETREC_LOCALS.rest) +# Locals: +# rest +# env_orig +# walk +# new_env +%fn2(eval_letrec, {rest env_orig walk new_env}, { + %stl(a0, rest) + %stl(a1, env_orig) + + %ldl(t0, rest) %car(t0, t0) - %st(t0, sp, %EVAL_LETREC_LOCALS.walk) - %ld(t0, sp, %EVAL_LETREC_LOCALS.env_orig) - %st(t0, sp, %EVAL_LETREC_LOCALS.new_env) + %stl(t0, walk) + %ldl(t0, env_orig) + %stl(t0, new_env) ::phase1 - %ld(t0, sp, %EVAL_LETREC_LOCALS.walk) + %ldl(t0, walk) %if_nil(t1, t0, &::p1_done) %car(t1, t0) @@ -1767,34 +1759,34 @@ %mov(a0, t2) %li(a1, %imm_val(%IMM.UNSPEC)) %call(&cons) - %ld(a1, sp, %EVAL_LETREC_LOCALS.new_env) + %ldl(a1, new_env) %call(&cons) - %st(a0, sp, %EVAL_LETREC_LOCALS.new_env) + %stl(a0, new_env) %advance_walk(16) %b(&::phase1) ::p1_done - %ld(t0, sp, %EVAL_LETREC_LOCALS.rest) + %ldl(t0, rest) %car(t0, t0) - %st(t0, sp, %EVAL_LETREC_LOCALS.walk) ; reset walk + %stl(t0, walk) ; reset walk ::phase2 - %ld(t0, sp, %EVAL_LETREC_LOCALS.walk) + %ldl(t0, walk) %if_nil(t1, t0, &::p2_done) %car(t1, t0) %cdr(t2, t1) %car(t2, t2) ; init %mov(a0, t2) - %ld(a1, sp, %EVAL_LETREC_LOCALS.new_env) + %ldl(a1, new_env) %call(&eval) ; val in a0 - %ld(t0, sp, %EVAL_LETREC_LOCALS.walk) + %ldl(t0, walk) %car(t1, t0) %car(t2, t1) ; name (in t2) - %ld(t1, sp, %EVAL_LETREC_LOCALS.new_env) + %ldl(t1, new_env) ::scan %car(a1, t1) %car(a2, a1) @@ -1810,9 +1802,9 @@ %b(&::phase2) ::p2_done - %ld(a0, sp, %EVAL_LETREC_LOCALS.rest) + %ldl(a0, rest) %cdr(a0, a0) - %ld(a1, sp, %EVAL_LETREC_LOCALS.new_env) + %ldl(a1, new_env) %tail(&eval_body) }) @@ -1821,17 +1813,16 @@ # the moment one yields #f. The last form is tail-evaluated so a tail call # inside `and` doesn't grow the host stack. # -# Frame: 16 bytes -# +0 rest -# +8 env -%struct EVAL_AND_LOCALS { rest env } # .SIZE = 16 -%fn(eval_and, %EVAL_AND_LOCALS.SIZE, { +# Locals: +# rest +# env +%fn2(eval_and, {rest env}, { %li(t0, %imm_val(%IMM.TRUE)) %if_nil(t1, a0, &::done_imm) ::loop - %st(a0, sp, %EVAL_AND_LOCALS.rest) - %st(a1, sp, %EVAL_AND_LOCALS.env) + %stl(a0, rest) + %stl(a1, env) # If cdr(rest) is NIL, the head is the last form -> tail-eval. %cdr(t0, a0) @@ -1842,15 +1833,15 @@ %call(&eval) %li(t0, %imm_val(%IMM.FALSE)) %beq(a0, t0, &::done) - %ld(a0, sp, %EVAL_AND_LOCALS.rest) + %ldl(a0, rest) %cdr(a0, a0) - %ld(a1, sp, %EVAL_AND_LOCALS.env) + %ldl(a1, env) %b(&::loop) ::last - %ld(a0, sp, %EVAL_AND_LOCALS.rest) + %ldl(a0, rest) %car(a0, a0) - %ld(a1, sp, %EVAL_AND_LOCALS.env) + %ldl(a1, env) %tail(&eval) ::done @@ -1865,17 +1856,16 @@ # non-#f value; if every form was #f, return #f. The last form is # tail-evaluated. # -# Frame: 16 bytes -# +0 rest -# +8 env -%struct EVAL_OR_LOCALS { rest env } # .SIZE = 16 -%fn(eval_or, %EVAL_OR_LOCALS.SIZE, { +# Locals: +# rest +# env +%fn2(eval_or, {rest env}, { %li(t0, %imm_val(%IMM.FALSE)) %if_nil(t1, a0, &::done_imm) ::loop - %st(a0, sp, %EVAL_OR_LOCALS.rest) - %st(a1, sp, %EVAL_OR_LOCALS.env) + %stl(a0, rest) + %stl(a1, env) %cdr(t0, a0) %if_nil(t1, t0, &::last) @@ -1884,15 +1874,15 @@ %call(&eval) %li(t0, %imm_val(%IMM.FALSE)) %bne(a0, t0, &::done) - %ld(a0, sp, %EVAL_OR_LOCALS.rest) + %ldl(a0, rest) %cdr(a0, a0) - %ld(a1, sp, %EVAL_OR_LOCALS.env) + %ldl(a1, env) %b(&::loop) ::last - %ld(a0, sp, %EVAL_OR_LOCALS.rest) + %ldl(a0, rest) %car(a0, a0) - %ld(a1, sp, %EVAL_OR_LOCALS.env) + %ldl(a1, env) %tail(&eval) ::done @@ -1914,28 +1904,27 @@ # clause's body is tail-evaluated via eval_body so the last form keeps # tail position. No-match (and no else) dies via runtime_error. # -# Frame: 48 bytes -# +0 subject -# +8 env_outer (per-clause restart point) -# +16 clauses (current cursor; advances on miss / failed guard) -# +24 env_ext (env extended with the matched clause's bindings) -# +32 guard cursor (advances during the guard AND-fold) -# +40 body (saved across guard evals, tail-evaluated on success) -%struct EVAL_PMATCH_LOCALS { subject env_outer clauses env_ext guard body } # .SIZE = 48 -%fn(eval_pmatch, %EVAL_PMATCH_LOCALS.SIZE, { - %st(a1, sp, %EVAL_PMATCH_LOCALS.env_outer) +# Locals: +# subject +# env_outer (per-clause restart point) +# clauses (current cursor; advances on miss / failed guard) +# env_ext (env extended with the matched clause's bindings) +# guard cursor (advances during the guard AND-fold) +# body (saved across guard evals, tail-evaluated on success) +%fn2(eval_pmatch, {subject env_outer clauses env_ext guard body}, { + %stl(a1, env_outer) # subject = eval(car(rest), env_outer); clauses = cdr(rest). %mov(t0, a0) %cdr(t1, t0) - %st(t1, sp, %EVAL_PMATCH_LOCALS.clauses) + %stl(t1, clauses) %car(a0, t0) - %ld(a1, sp, %EVAL_PMATCH_LOCALS.env_outer) + %ldl(a1, env_outer) %call(&eval) - %st(a0, sp, %EVAL_PMATCH_LOCALS.subject) + %stl(a0, subject) ::loop - %ld(t0, sp, %EVAL_PMATCH_LOCALS.clauses) + %ldl(t0, clauses) %if_nil(t1, t0, &::no_match) %car(t1, t0) ; clause @@ -1946,15 +1935,15 @@ # pmatch_match(pat, subject, env_outer) -> (a0=env_ext, a1=ok) %mov(a0, t2) - %ld(a1, sp, %EVAL_PMATCH_LOCALS.subject) - %ld(a2, sp, %EVAL_PMATCH_LOCALS.env_outer) + %ldl(a1, subject) + %ldl(a2, env_outer) %call(&pmatch_match) %beqz(a1, &::next) - %st(a0, sp, %EVAL_PMATCH_LOCALS.env_ext) ; env_ext + %stl(a0, env_ext) ; env_ext # tail = cdr(clause) - %ld(t0, sp, %EVAL_PMATCH_LOCALS.clauses) + %ldl(t0, clauses) %car(t0, t0) %cdr(t0, t0) ; tail = (body...) or ((guard ...) body...) @@ -1973,28 +1962,28 @@ # Guard clause. guards = cdr(car(tail)); body = cdr(tail). %cdr(a0, t1) - %st(a0, sp, %EVAL_PMATCH_LOCALS.guard) + %stl(a0, guard) %cdr(t0, t0) - %st(t0, sp, %EVAL_PMATCH_LOCALS.body) + %stl(t0, body) ::g_loop - %ld(t0, sp, %EVAL_PMATCH_LOCALS.guard) + %ldl(t0, guard) %if_nil(t1, t0, &::body_run) %car(a0, t0) ; guard expr - %ld(a1, sp, %EVAL_PMATCH_LOCALS.env_ext) ; env_ext + %ldl(a1, env_ext) ; env_ext %call(&eval) %li(t0, %imm_val(%IMM.FALSE)) %beq(a0, t0, &::next) - %ld(t0, sp, %EVAL_PMATCH_LOCALS.guard) + %ldl(t0, guard) %cdr(t0, t0) - %st(t0, sp, %EVAL_PMATCH_LOCALS.guard) + %stl(t0, guard) %b(&::g_loop) ::body_run - %ld(a0, sp, %EVAL_PMATCH_LOCALS.body) - %ld(a1, sp, %EVAL_PMATCH_LOCALS.env_ext) + %ldl(a0, body) + %ldl(a1, env_ext) %tail(&eval_body) ::body_simple @@ -2002,20 +1991,20 @@ # with the extended env; tail position of the matched clause's body # is preserved. %mov(a0, t0) - %ld(a1, sp, %EVAL_PMATCH_LOCALS.env_ext) + %ldl(a1, env_ext) %tail(&eval_body) ::do_else - %ld(t0, sp, %EVAL_PMATCH_LOCALS.clauses) + %ldl(t0, clauses) %car(t0, t0) %cdr(a0, t0) ; body - %ld(a1, sp, %EVAL_PMATCH_LOCALS.env_outer) ; env_outer (no bindings introduced) + %ldl(a1, env_outer) ; env_outer (no bindings introduced) %tail(&eval_body) ::next - %ld(t0, sp, %EVAL_PMATCH_LOCALS.clauses) + %ldl(t0, clauses) %cdr(t0, t0) - %st(t0, sp, %EVAL_PMATCH_LOCALS.clauses) + %stl(t0, clauses) %b(&::loop) ::no_match @@ -2039,15 +2028,14 @@ # - HEAP-tagged HDR.BV: structural byte-for-byte equality via # bv_equal_check; only when both pat and subj are HDR.BV. # -# Frame: 24 bytes -# +0 pat -# +8 subj -# +16 env -%struct PMATCH_MATCH_LOCALS { pat subj env } # .SIZE = 24 -%fn(pmatch_match, %PMATCH_MATCH_LOCALS.SIZE, { - %st(a0, sp, %PMATCH_MATCH_LOCALS.pat) - %st(a1, sp, %PMATCH_MATCH_LOCALS.subj) - %st(a2, sp, %PMATCH_MATCH_LOCALS.env) +# Locals: +# pat +# subj +# env +%fn2(pmatch_match, {pat subj env}, { + %stl(a0, pat) + %stl(a1, subj) + %stl(a2, env) %tagof(t0, a0) %li(t1, %TAG.PAIR) @@ -2085,25 +2073,25 @@ %bne(t0, t1, &::no) # Recurse on the cars; on success, recurse on the cdrs as a tail call. - %ld(t0, sp, %PMATCH_MATCH_LOCALS.pat) + %ldl(t0, pat) %car(a0, t0) - %ld(t0, sp, %PMATCH_MATCH_LOCALS.subj) + %ldl(t0, subj) %car(a1, t0) - %ld(a2, sp, %PMATCH_MATCH_LOCALS.env) + %ldl(a2, env) %call(&pmatch_match) %beqz(a1, &::no) %mov(a2, a0) ; env_after_car - %ld(t0, sp, %PMATCH_MATCH_LOCALS.pat) + %ldl(t0, pat) %cdr(a0, t0) - %ld(t0, sp, %PMATCH_MATCH_LOCALS.subj) + %ldl(t0, subj) %cdr(a1, t0) %tail(&pmatch_match) ::binder # Validate (unquote <sym>): cdr(pat) is a pair, cdr(cdr(pat)) is NIL, # car(cdr(pat)) is a symbol. - %ld(t0, sp, %PMATCH_MATCH_LOCALS.pat) + %ldl(t0, pat) %cdr(t1, t0) ; cdr(pat) %tagof(t0, t1) %li(t2, %TAG.PAIR) @@ -2123,15 +2111,15 @@ # Bind: env' = cons(cons(pident, subj), env). pident lives in t0; # cons clobbers t0..t2, so move it into a0 right away. %mov(a0, t0) - %ld(a1, sp, %PMATCH_MATCH_LOCALS.subj) + %ldl(a1, subj) %call(&cons) - %ld(a1, sp, %PMATCH_MATCH_LOCALS.env) + %ldl(a1, env) %call(&cons) %li(a1, 1) %eret ::ok - %ld(a0, sp, %PMATCH_MATCH_LOCALS.env) + %ldl(a0, env) %li(a1, 1) %eret @@ -2150,41 +2138,40 @@ # pair). Inits are evaluated in the *original* env (matches let # semantics), then we apply the closure. # -# Frame: 64 bytes -# +0 rest -# +8 env_orig -# +16 self_binding (the (name . UNSPEC) placeholder, patched at the end) -# +24 self_env (cons(self_binding, env_orig)) -# +32 walk (advances; reset between passes) -# +40 head (current pass's list head — params, then args) -# +48 tail (current pass's list tail) -# +56 params (saved between passes) -%struct EVAL_LET_NAMED_LOCALS { rest env_orig self_binding self_env walk head tail params } # .SIZE = 64 -%fn(eval_let_named, %EVAL_LET_NAMED_LOCALS.SIZE, { - %st(a0, sp, %EVAL_LET_NAMED_LOCALS.rest) - %st(a1, sp, %EVAL_LET_NAMED_LOCALS.env_orig) +# Locals: +# rest +# env_orig +# self_binding (the (name . UNSPEC) placeholder, patched at the end) +# self_env (cons(self_binding, env_orig)) +# walk (advances; reset between passes) +# head (current pass's list head — params, then args) +# tail (current pass's list tail) +# params (saved between passes) +%fn2(eval_let_named, {rest env_orig self_binding self_env walk head tail params}, { + %stl(a0, rest) + %stl(a1, env_orig) # 1. self_binding = (name . UNSPEC); self_env = cons(self_binding, env) %car(t0, a0) %mov(a0, t0) %li(a1, %imm_val(%IMM.UNSPEC)) %call(&cons) - %st(a0, sp, %EVAL_LET_NAMED_LOCALS.self_binding) - %ld(a1, sp, %EVAL_LET_NAMED_LOCALS.env_orig) + %stl(a0, self_binding) + %ldl(a1, env_orig) %call(&cons) - %st(a0, sp, %EVAL_LET_NAMED_LOCALS.self_env) + %stl(a0, self_env) # 2. Pass 1: build params list (cdr-tail trick) by walking bindings. %li(t0, %imm_val(%IMM.NIL)) - %st(t0, sp, %EVAL_LET_NAMED_LOCALS.head) - %st(t0, sp, %EVAL_LET_NAMED_LOCALS.tail) - %ld(t0, sp, %EVAL_LET_NAMED_LOCALS.rest) + %stl(t0, head) + %stl(t0, tail) + %ldl(t0, rest) %cdr(t0, t0) %car(t0, t0) ; bindings - %st(t0, sp, %EVAL_LET_NAMED_LOCALS.walk) + %stl(t0, walk) ::p1_loop - %ld(t0, sp, %EVAL_LET_NAMED_LOCALS.walk) + %ldl(t0, walk) %if_nil(t1, t0, &::p1_done) %car(t1, t0) @@ -2193,58 +2180,58 @@ %li(a1, %imm_val(%IMM.NIL)) %call(&cons) ; cell = (name . NIL) - %ld(t0, sp, %EVAL_LET_NAMED_LOCALS.head) + %ldl(t0, head) %if_nil(t1, t0, &::p1_first) - %ld(t0, sp, %EVAL_LET_NAMED_LOCALS.tail) + %ldl(t0, tail) %set_cdr(a0, t0) - %st(a0, sp, %EVAL_LET_NAMED_LOCALS.tail) + %stl(a0, tail) %b(&::p1_advance) ::p1_first - %st(a0, sp, %EVAL_LET_NAMED_LOCALS.head) - %st(a0, sp, %EVAL_LET_NAMED_LOCALS.tail) + %stl(a0, head) + %stl(a0, tail) ::p1_advance %advance_walk(32) %b(&::p1_loop) ::p1_done - %ld(t0, sp, %EVAL_LET_NAMED_LOCALS.head) - %st(t0, sp, %EVAL_LET_NAMED_LOCALS.params) ; save params + %ldl(t0, head) + %stl(t0, params) ; save params # 3. Pass 2: build args list (eval inits in env_orig). %li(t0, %imm_val(%IMM.NIL)) - %st(t0, sp, %EVAL_LET_NAMED_LOCALS.head) - %st(t0, sp, %EVAL_LET_NAMED_LOCALS.tail) - %ld(t0, sp, %EVAL_LET_NAMED_LOCALS.rest) + %stl(t0, head) + %stl(t0, tail) + %ldl(t0, rest) %cdr(t0, t0) %car(t0, t0) - %st(t0, sp, %EVAL_LET_NAMED_LOCALS.walk) + %stl(t0, walk) ::p2_loop - %ld(t0, sp, %EVAL_LET_NAMED_LOCALS.walk) + %ldl(t0, walk) %if_nil(t1, t0, &::p2_done) %car(t1, t0) %cdr(t2, t1) %car(t2, t2) ; init %mov(a0, t2) - %ld(a1, sp, %EVAL_LET_NAMED_LOCALS.env_orig) + %ldl(a1, env_orig) %call(&eval) ; val %li(a1, %imm_val(%IMM.NIL)) %call(&cons) ; cell = (val . NIL) - %ld(t0, sp, %EVAL_LET_NAMED_LOCALS.head) + %ldl(t0, head) %if_nil(t1, t0, &::p2_first) - %ld(t0, sp, %EVAL_LET_NAMED_LOCALS.tail) + %ldl(t0, tail) %set_cdr(a0, t0) - %st(a0, sp, %EVAL_LET_NAMED_LOCALS.tail) + %stl(a0, tail) %b(&::p2_advance) ::p2_first - %st(a0, sp, %EVAL_LET_NAMED_LOCALS.head) - %st(a0, sp, %EVAL_LET_NAMED_LOCALS.tail) + %stl(a0, head) + %stl(a0, tail) ::p2_advance %advance_walk(32) @@ -2252,20 +2239,20 @@ ::p2_done # 4. Closure: eval_lambda((params . body), self_env). - %ld(a0, sp, %EVAL_LET_NAMED_LOCALS.params) ; params - %ld(t0, sp, %EVAL_LET_NAMED_LOCALS.rest) + %ldl(a0, params) ; params + %ldl(t0, rest) %cdr(t0, t0) %cdr(a1, t0) ; body %call(&cons) - %ld(a1, sp, %EVAL_LET_NAMED_LOCALS.self_env) + %ldl(a1, self_env) %call(&eval_lambda) # 5. Patch self_binding cdr to closure. - %ld(t0, sp, %EVAL_LET_NAMED_LOCALS.self_binding) + %ldl(t0, self_binding) %set_cdr(a0, t0) # 6. apply(closure, args). - %ld(a1, sp, %EVAL_LET_NAMED_LOCALS.head) + %ldl(a1, head) %tail(&apply) }) @@ -2274,18 +2261,17 @@ # Variadic `.`-tail: when params terminates with a SYM (rather than NIL), # bind it to the remaining args list and stop. # -# Frame: 24 bytes -# +0 params (advanced each iteration) -# +8 args (advanced each iteration) -# +16 env (extended each iteration) -%struct BIND_PARAMS_LOCALS { params args env } # .SIZE = 24 -%fn(bind_params, %BIND_PARAMS_LOCALS.SIZE, { - %st(a0, sp, %BIND_PARAMS_LOCALS.params) - %st(a1, sp, %BIND_PARAMS_LOCALS.args) - %st(a2, sp, %BIND_PARAMS_LOCALS.env) +# Locals: +# params (advanced each iteration) +# args (advanced each iteration) +# env (extended each iteration) +%fn2(bind_params, {params args env}, { + %stl(a0, params) + %stl(a1, args) + %stl(a2, env) ::loop - %ld(t0, sp, %BIND_PARAMS_LOCALS.params) + %ldl(t0, params) %tagof(t1, t0) %li(t2, %TAG.PAIR) %beq(t1, t2, &::pair) @@ -2295,16 +2281,16 @@ ::pair # binding = cons(car(params), car(args)) - %ld(t0, sp, %BIND_PARAMS_LOCALS.params) + %ldl(t0, params) %car(a0, t0) - %ld(t0, sp, %BIND_PARAMS_LOCALS.args) + %ldl(t0, args) %car(a1, t0) %call(&cons) # env = cons(binding, env) - %ld(a1, sp, %BIND_PARAMS_LOCALS.env) + %ldl(a1, env) %call(&cons) - %st(a0, sp, %BIND_PARAMS_LOCALS.env) + %stl(a0, env) # advance params and args %advance_walk(0) @@ -2313,15 +2299,15 @@ ::rest_bind # binding = cons(params_sym, args_list); env = cons(binding, env) - %ld(a0, sp, %BIND_PARAMS_LOCALS.params) - %ld(a1, sp, %BIND_PARAMS_LOCALS.args) + %ldl(a0, params) + %ldl(a1, args) %call(&cons) - %ld(a1, sp, %BIND_PARAMS_LOCALS.env) + %ldl(a1, env) %call(&cons) - %st(a0, sp, %BIND_PARAMS_LOCALS.env) + %stl(a0, env) ::done - %ld(a0, sp, %BIND_PARAMS_LOCALS.env) + %ldl(a0, env) }) # eval_body(body=a0, env=a1) -> value of last form (a0). @@ -2333,14 +2319,13 @@ # bodies, cond clause bodies, begin's body). Per-form check is one # tagof + one symbol compare, regardless of body length. # -# Frame: 16 bytes -# +0 body -# +8 env -%struct EVAL_BODY_LOCALS { body env } # .SIZE = 16 -%fn(eval_body, %EVAL_BODY_LOCALS.SIZE, { +# Locals: +# body +# env +%fn2(eval_body, {body env}, { ::loop - %st(a0, sp, %EVAL_BODY_LOCALS.body) - %st(a1, sp, %EVAL_BODY_LOCALS.env) + %stl(a0, body) + %stl(a1, env) # Reject internal `define`. Detect (define ...) at the head of any # form before dispatching it to eval. @@ -2354,23 +2339,23 @@ ::not_define # If cdr(body) is NIL, body's car is the last form. - %ld(a0, sp, %EVAL_BODY_LOCALS.body) + %ldl(a0, body) %cdr(t0, a0) %if_nil(t1, t0, &::last) # Non-last form: eval and discard, advance. %car(a0, a0) - %ld(a1, sp, %EVAL_BODY_LOCALS.env) + %ldl(a1, env) %call(&eval) - %ld(a0, sp, %EVAL_BODY_LOCALS.body) + %ldl(a0, body) %cdr(a0, a0) - %ld(a1, sp, %EVAL_BODY_LOCALS.env) + %ldl(a1, env) %b(&::loop) ::last - %ld(a0, sp, %EVAL_BODY_LOCALS.body) + %ldl(a0, body) %car(a0, a0) - %ld(a1, sp, %EVAL_BODY_LOCALS.env) + %ldl(a1, env) %tail(&eval) ::internal_define @@ -2480,51 +2465,50 @@ # Symbol intern -- linear scan, append on miss # ========================================================================= # -# Frame: 32 bytes -# +0 name_ptr (input) -# +8 name_len (input) -# +16 idx (loop counter / found index) -# +24 entry_ptr (spilled across memcmp) -%struct INTERN_LOCALS { name_ptr name_len idx entry_ptr } # .SIZE = 32 -%fn(intern, %INTERN_LOCALS.SIZE, { - %st(a0, sp, %INTERN_LOCALS.name_ptr) - %st(a1, sp, %INTERN_LOCALS.name_len) +# Locals: +# name_ptr (input) +# name_len (input) +# idx (loop counter / found index) +# entry_ptr (spilled across memcmp) +%fn2(intern, {name_ptr name_len idx entry_ptr}, { + %stl(a0, name_ptr) + %stl(a1, name_len) %li(t0, 0) - %st(t0, sp, %INTERN_LOCALS.idx) + %stl(t0, idx) ::scan # idx >= count? -> append - %ld(t0, sp, %INTERN_LOCALS.idx) + %ldl(t0, idx) %ld_global(t1, &symtab_count) %bltu(t0, t1, &::probe) %b(&::append) ::probe %symtab_entry(t1, t0, t2) - %st(t1, sp, %INTERN_LOCALS.entry_ptr) + %stl(t1, entry_ptr) # entry.name_len == name_len ? %ld(t2, t1, %SYMENT.name_len) - %ld(a2, sp, %INTERN_LOCALS.name_len) + %ldl(a2, name_len) %bne(t2, a2, &::next) # memcmp(entry.name_ptr, name_ptr, len) %ld(a0, t1, %SYMENT.name_ptr) - %ld(a1, sp, %INTERN_LOCALS.name_ptr) - %ld(a2, sp, %INTERN_LOCALS.name_len) + %ldl(a1, name_ptr) + %ldl(a2, name_len) %call(&memcmp) %beqz(a0, &::found) ::next - %ld(t0, sp, %INTERN_LOCALS.idx) + %ldl(t0, idx) %addi(t0, t0, 1) - %st(t0, sp, %INTERN_LOCALS.idx) + %stl(t0, idx) %b(&::scan) ::append # Bounds check; on overflow exit 5 with a message. - %ld(t0, sp, %INTERN_LOCALS.idx) + %ldl(t0, idx) %li(t1, %SYMTAB_CAP_SLOTS) %bltu(t0, t1, &::append_ok) %die(msg_symtab_full) @@ -2533,16 +2517,16 @@ # Copy the name into a stable heap buffer. The caller-provided ptr # may live in readbuf_buf (parse_atom), which gets overwritten when # the next source is loaded; symtab entries must outlive that. - %ld(a0, sp, %INTERN_LOCALS.name_len) + %ldl(a0, name_len) %call(&alloc_bytes) - %ld(a1, sp, %INTERN_LOCALS.name_ptr) - %ld(a2, sp, %INTERN_LOCALS.name_len) + %ldl(a1, name_ptr) + %ldl(a2, name_len) %call(&memcpy) ; returns dst in a0 = stable copy - %ld(t0, sp, %INTERN_LOCALS.idx) + %ldl(t0, idx) %symtab_entry(t1, t0, t2) %st(a0, t1, %SYMENT.name_ptr) ; stable copy - %ld(a0, sp, %INTERN_LOCALS.name_len) + %ldl(a0, name_len) %st(a0, t1, %SYMENT.name_len) %li(a0, %imm_val(%IMM.UNBOUND)) %st(a0, t1, %SYMENT.global_val) @@ -2556,7 +2540,7 @@ # fall through with idx in t0 = sp[16] ::found - %ld(t0, sp, %INTERN_LOCALS.idx) + %ldl(t0, idx) %shli(a0, t0, 3) %ori(a0, a0, %TAG.SYM) }) @@ -2591,21 +2575,19 @@ # the surface name, and bind the symbol's global slot to the HEAP-tagged # prim pointer. # -# Frame: 24 bytes -# +0 prim ptr (HEAP-tagged; spilled across intern + sym_set_global) -# +8 walk (current table cursor) -# +16 end (table_end) - -%struct REGISTER_PRIMITIVES_LOCALS { prim walk end } # .SIZE = 24 -%fn(register_primitives, %REGISTER_PRIMITIVES_LOCALS.SIZE, { +# Locals: +# prim ptr (HEAP-tagged; spilled across intern + sym_set_global) +# walk (current table cursor) +# end (table_end) +%fn2(register_primitives, {prim walk end}, { %la(t0, &prim_table) - %st(t0, sp, %REGISTER_PRIMITIVES_LOCALS.walk) + %stl(t0, walk) %la(t0, &prim_table_end) - %st(t0, sp, %REGISTER_PRIMITIVES_LOCALS.end) + %stl(t0, end) ::loop - %ld(t0, sp, %REGISTER_PRIMITIVES_LOCALS.walk) - %ld(t1, sp, %REGISTER_PRIMITIVES_LOCALS.end) + %ldl(t0, walk) + %ldl(t1, end) %beq(t0, t1, &::done) # alloc_hdr(24, HDR.PRIM) -> HEAP-tagged a0. The third slot (offset 13 @@ -2615,26 +2597,26 @@ %li(a0, 24) %li(a1, %HDR.PRIM) %call(&alloc_hdr) - %st(a0, sp, %REGISTER_PRIMITIVES_LOCALS.prim) + %stl(a0, prim) # Write entry-label into prim's entry slot. - %ld(t0, sp, %REGISTER_PRIMITIVES_LOCALS.walk) + %ldl(t0, walk) %ld(t1, t0, 16) - %ld(t2, sp, %REGISTER_PRIMITIVES_LOCALS.prim) + %ldl(t2, prim) %heap_st(t1, t2, %PRIM.entry_w) # Intern surface name; bind global to prim ptr. - %ld(t0, sp, %REGISTER_PRIMITIVES_LOCALS.walk) + %ldl(t0, walk) %ld(a0, t0, 0) %ld(a1, t0, 8) %call(&intern) %untag_sym(a0, a0) - %ld(a1, sp, %REGISTER_PRIMITIVES_LOCALS.prim) + %ldl(a1, prim) %call(&sym_set_global) - %ld(t0, sp, %REGISTER_PRIMITIVES_LOCALS.walk) + %ldl(t0, walk) %addi(t0, t0, 24) - %st(t0, sp, %REGISTER_PRIMITIVES_LOCALS.walk) + %stl(t0, walk) %b(&::loop) ::done @@ -2768,14 +2750,13 @@ # t-regs, so the running write offset and remaining-args cursor live # in the frame across each call. # -# Frame: 32 bytes -# +0 args list head (re-read for pass 2; cursor during pass 2) -# +8 total length (raw) -# +16 result bv -# +24 write offset (raw, into result.data) -%struct PRIM_BV_APPEND_ENTRY_LOCALS { args total result write } # .SIZE = 32 -%fn(prim_bv_append_entry, %PRIM_BV_APPEND_ENTRY_LOCALS.SIZE, { - %st(a0, sp, %PRIM_BV_APPEND_ENTRY_LOCALS.args) +# Locals: +# args list head (re-read for pass 2; cursor during pass 2) +# total length (raw) +# result bv +# write offset (raw, into result.data) +%fn2(prim_bv_append_entry, {args total result write}, { + %stl(a0, args) %li(t0, 0) %mov(t1, a0) @@ -2788,39 +2769,39 @@ %cdr(t1, t1) %b(&::sum_loop) ::sum_done - %st(t0, sp, %PRIM_BV_APPEND_ENTRY_LOCALS.total) + %stl(t0, total) %mov(a0, t0) %call(&bv_alloc) - %st(a0, sp, %PRIM_BV_APPEND_ENTRY_LOCALS.result) + %stl(a0, result) %li(t0, 0) - %st(t0, sp, %PRIM_BV_APPEND_ENTRY_LOCALS.write) + %stl(t0, write) ::copy_loop - %ld(t0, sp, %PRIM_BV_APPEND_ENTRY_LOCALS.args) + %ldl(t0, args) %if_nil(t1, t0, &::copy_done) %car(t1, t0) ; src bv %heap_ld(t2, t1, %BV.hdr) %shri(t2, t2, 8) ; src length - %ld(a0, sp, %PRIM_BV_APPEND_ENTRY_LOCALS.result) + %ldl(a0, result) %heap_ld(a0, a0, %BV.data) ; result.data - %ld(a3, sp, %PRIM_BV_APPEND_ENTRY_LOCALS.write) + %ldl(a3, write) %add(a0, a0, a3) ; dst = result.data + offset %heap_ld(a1, t1, %BV.data) ; src.data %mov(a2, t2) ; count %add(a3, a3, t2) - %st(a3, sp, %PRIM_BV_APPEND_ENTRY_LOCALS.write) + %stl(a3, write) %cdr(t0, t0) - %st(t0, sp, %PRIM_BV_APPEND_ENTRY_LOCALS.args) + %stl(t0, args) %call(&memcpy) %b(&::copy_loop) ::copy_done - %ld(a0, sp, %PRIM_BV_APPEND_ENTRY_LOCALS.result) + %ldl(a0, result) }) # (string->symbol bv) -- intern the bytes and return the SYM-tagged @@ -2837,22 +2818,22 @@ # returns (ptr, len); bv_alloc gives us a clean wrapper; memcpy fills # the data. Frame holds the (ptr, len) pair across bv_alloc and the # resulting bv across memcpy. -%struct PRIM_SYMBOL_TO_STRING_ENTRY_LOCALS { ptr len bv } # .SIZE = 24 -%fn(prim_symbol_to_string_entry, %PRIM_SYMBOL_TO_STRING_ENTRY_LOCALS.SIZE, { + +%fn2(prim_symbol_to_string_entry, {ptr len bv}, { %car(a0, a0) %sari(a0, a0, 3) ; raw sym idx %call(&sym_name) ; -> ptr (a0), len (a1) - %st(a0, sp, %PRIM_SYMBOL_TO_STRING_ENTRY_LOCALS.ptr) - %st(a1, sp, %PRIM_SYMBOL_TO_STRING_ENTRY_LOCALS.len) + %stl(a0, ptr) + %stl(a1, len) %mov(a0, a1) %call(&bv_alloc) ; tagged bv in a0 - %st(a0, sp, %PRIM_SYMBOL_TO_STRING_ENTRY_LOCALS.bv) - %ld(a1, sp, %PRIM_SYMBOL_TO_STRING_ENTRY_LOCALS.ptr) ; src ptr - %ld(a2, sp, %PRIM_SYMBOL_TO_STRING_ENTRY_LOCALS.len) ; len + %stl(a0, bv) + %ldl(a1, ptr) ; src ptr + %ldl(a2, len) ; len %heap_ld(t0, a0, %BV.data) ; dst = bv.data %mov(a0, t0) %call(&memcpy) - %ld(a0, sp, %PRIM_SYMBOL_TO_STRING_ENTRY_LOCALS.bv) + %ldl(a0, bv) }) # (number->string n [radix]) -- decimal repr in a fresh bv. The radix @@ -2861,14 +2842,14 @@ # is silently ignored. bv_putint takes the raw value, so untag first; # bv_alloc(0) gives an empty wrapper that bv_putint grows in place. # +0 holds the raw value across bv_alloc. -%struct PRIM_NUMBER_TO_STRING_ENTRY_LOCALS { value pad } # .SIZE = 16 -%fn(prim_number_to_string_entry, %PRIM_NUMBER_TO_STRING_ENTRY_LOCALS.SIZE, { + +%fn2(prim_number_to_string_entry, {value pad}, { %car(t0, a0) %sari(t0, t0, 3) ; raw value - %st(t0, sp, %PRIM_NUMBER_TO_STRING_ENTRY_LOCALS.value) + %stl(t0, value) %li(a0, 0) %call(&bv_alloc) - %ld(a1, sp, %PRIM_NUMBER_TO_STRING_ENTRY_LOCALS.value) + %ldl(a1, value) %tail(&bv_putint) }) @@ -3171,27 +3152,26 @@ # bv_capacity_for, data buffer uninitialized. data_ptr lives in a frame # slot because alloc_hdr's alignup clobbers t-regs. # -# Frame: 24 bytes -# +0 raw_len -# +8 capacity -# +16 data_ptr (raw) -%struct BV_ALLOC_LOCALS { raw_len capacity data_ptr } # .SIZE = 24 -%fn(bv_alloc, %BV_ALLOC_LOCALS.SIZE, { - %st(a0, sp, %BV_ALLOC_LOCALS.raw_len) +# Locals: +# raw_len +# capacity +# data_ptr (raw) +%fn2(bv_alloc, {raw_len capacity data_ptr}, { + %stl(a0, raw_len) %call(&bv_capacity_for) - %st(a0, sp, %BV_ALLOC_LOCALS.capacity) + %stl(a0, capacity) %call(&alloc_bytes) - %st(a0, sp, %BV_ALLOC_LOCALS.data_ptr) + %stl(a0, data_ptr) - %ld(a1, sp, %BV_ALLOC_LOCALS.raw_len) + %ldl(a1, raw_len) %shli(a1, a1, 8) ; hdr = (raw_len << 8) | HDR.BV (BV == 0) %li(a0, 24) %call(&alloc_hdr) - %ld(t0, sp, %BV_ALLOC_LOCALS.data_ptr) + %ldl(t0, data_ptr) %heap_st(t0, a0, %BV.data) - %ld(t1, sp, %BV_ALLOC_LOCALS.capacity) + %ldl(t1, capacity) %st(t1, a0, 13) ; bv.cap (raw offset 16; not in BV struct) }) @@ -3200,56 +3180,52 @@ # capacity), and patches the wrapper's data_ptr/capacity slots in place. # A no-op when current capacity already satisfies min_cap. # -# Frame: 32 bytes -# +0 bv -# +8 min_cap (input) / new_cap (during loop) -# +16 new_data_ptr -# +24 raw length -%struct BV_GROW_LOCALS { bv min_cap new_data_ptr raw } # .SIZE = 32 -%fn(bv_grow, %BV_GROW_LOCALS.SIZE, { - %st(a0, sp, %BV_GROW_LOCALS.bv) - %st(a1, sp, %BV_GROW_LOCALS.min_cap) +# Locals: +# bv +# min_cap (input) / new_cap (during loop) +# new_data_ptr +# raw length +%fn2(bv_grow, {bv min_cap new_data_ptr raw}, { + %stl(a0, bv) + %stl(a1, min_cap) %ld(t0, a0, 13) ; bv.cap (raw offset 16; not in BV struct) %bltu(t0, a1, &::need) - %ld(a0, sp, %BV_GROW_LOCALS.bv) + %ldl(a0, bv) %eret ::need ::loop %shli(t0, t0, 1) - %ld(t1, sp, %BV_GROW_LOCALS.min_cap) + %ldl(t1, min_cap) %bltu(t0, t1, &::loop) - %st(t0, sp, %BV_GROW_LOCALS.min_cap) + %stl(t0, min_cap) %mov(a0, t0) %call(&alloc_bytes) - %st(a0, sp, %BV_GROW_LOCALS.new_data_ptr) + %stl(a0, new_data_ptr) - %ld(t0, sp, %BV_GROW_LOCALS.bv) + %ldl(t0, bv) %heap_ld(t1, t0, %BV.hdr) %shri(t1, t1, 8) ; raw length - %st(t1, sp, %BV_GROW_LOCALS.raw) - %ld(a0, sp, %BV_GROW_LOCALS.new_data_ptr) + %stl(t1, raw) + %ldl(a0, new_data_ptr) %heap_ld(a1, t0, %BV.data) ; old data ptr - %ld(a2, sp, %BV_GROW_LOCALS.raw) + %ldl(a2, raw) %call(&memcpy) - %ld(t0, sp, %BV_GROW_LOCALS.bv) - %ld(t1, sp, %BV_GROW_LOCALS.new_data_ptr) + %ldl(t0, bv) + %ldl(t1, new_data_ptr) %heap_st(t1, t0, %BV.data) - %ld(t1, sp, %BV_GROW_LOCALS.min_cap) + %ldl(t1, min_cap) %st(t1, t0, 13) ; bv.cap (raw offset 16; not in BV struct) - %ld(a0, sp, %BV_GROW_LOCALS.bv) + %ldl(a0, bv) }) # (make-bytevector len) or (make-bytevector len fill) -%struct PRIM_MAKE_BYTEVECTOR_ENTRY_LOCALS { args fill wrapper } # .SIZE = 24 -%fn(prim_make_bytevector_entry, %PRIM_MAKE_BYTEVECTOR_ENTRY_LOCALS.SIZE, { - # +0 args - # +8 fill (raw byte) - # +16 wrapper (saved across fill loop) - %st(a0, sp, %PRIM_MAKE_BYTEVECTOR_ENTRY_LOCALS.args) + +%fn2(prim_make_bytevector_entry, {args fill wrapper}, { + %stl(a0, args) %li(t2, 0) %cdr(t0, a0) @@ -3257,18 +3233,18 @@ %car(t0, t0) %sari(t2, t0, 3) ::no_fill - %st(t2, sp, %PRIM_MAKE_BYTEVECTOR_ENTRY_LOCALS.fill) + %stl(t2, fill) - %ld(a0, sp, %PRIM_MAKE_BYTEVECTOR_ENTRY_LOCALS.args) + %ldl(a0, args) %car_fix(a0, a0) %bltz(a0, &::bad_len) %call(&bv_alloc) - %st(a0, sp, %PRIM_MAKE_BYTEVECTOR_ENTRY_LOCALS.wrapper) + %stl(a0, wrapper) - %ld(t0, sp, %PRIM_MAKE_BYTEVECTOR_ENTRY_LOCALS.args) + %ldl(t0, args) %car_fix(t0, t0) ; raw_len - %ld(t1, sp, %PRIM_MAKE_BYTEVECTOR_ENTRY_LOCALS.fill) ; fill - %ld(a1, sp, %PRIM_MAKE_BYTEVECTOR_ENTRY_LOCALS.wrapper) + %ldl(t1, fill) ; fill + %ldl(a1, wrapper) %heap_ld(t2, a1, %BV.data) %li(a1, 0) @@ -3280,7 +3256,7 @@ %b(&::fill_loop) ::fill_done - %ld(a0, sp, %PRIM_MAKE_BYTEVECTOR_ENTRY_LOCALS.wrapper) + %ldl(a0, wrapper) %eret ::bad_len @@ -3333,16 +3309,15 @@ # (bytevector-copy src start end) -> fresh bv of length end-start. # Bounds: 0 <= start <= end <= src.length. # -# Frame: 24 bytes -# +0 args -# +8 src tagged -# +16 wrapper (saved after bv_alloc) -%struct PRIM_BV_COPY_ENTRY_LOCALS { args src wrapper } # .SIZE = 24 -%fn(prim_bv_copy_entry, %PRIM_BV_COPY_ENTRY_LOCALS.SIZE, { - %st(a0, sp, %PRIM_BV_COPY_ENTRY_LOCALS.args) +# Locals: +# args +# src tagged +# wrapper (saved after bv_alloc) +%fn2(prim_bv_copy_entry, {args src wrapper}, { + %stl(a0, args) %args3(t0, t2, t1, a0) ; src, start, end - %st(t0, sp, %PRIM_BV_COPY_ENTRY_LOCALS.src) + %stl(t0, src) %sari(t2, t2, 3) ; raw start %sari(t1, t1, 3) ; raw end @@ -3356,16 +3331,16 @@ %sub(a0, t1, t2) ; count %call(&bv_alloc) - %st(a0, sp, %PRIM_BV_COPY_ENTRY_LOCALS.wrapper) + %stl(a0, wrapper) # Recompute src ptr at start; dst ptr at 0; count from new bv's hdr. - %ld(t0, sp, %PRIM_BV_COPY_ENTRY_LOCALS.args) + %ldl(t0, args) %cdr(t0, t0) %car_fix(t0, t0) ; raw start - %ld(t1, sp, %PRIM_BV_COPY_ENTRY_LOCALS.src) + %ldl(t1, src) %heap_ld(t2, t1, %BV.data) %add(t2, t2, t0) ; src ptr - %ld(a3, sp, %PRIM_BV_COPY_ENTRY_LOCALS.wrapper) + %ldl(a3, wrapper) %heap_ld(a2, a3, %BV.data) ; dst ptr %heap_ld(a1, a3, %BV.hdr) %shri(a1, a1, 8) ; count @@ -3380,7 +3355,7 @@ %b(&::copy_loop) ::copy_done - %ld(a0, sp, %PRIM_BV_COPY_ENTRY_LOCALS.wrapper) + %ldl(a0, wrapper) %eret ::oob @@ -3391,65 +3366,64 @@ # 0 <= src-start <= src-end <= src.length and # 0 <= dst-start && dst-start + (src-end-src-start) <= dst.length. # -# Frame: 40 bytes (five raw args parked across the bounds checks). -# +0 dst (tagged) -# +8 dst-start (raw) -# +16 src (tagged) -# +24 src-start (raw) -# +32 src-end (raw) -%struct PRIM_BV_COPY_BANG_ENTRY_LOCALS { dst dst_start src src_start src_end } # .SIZE = 40 -%fn(prim_bv_copy_bang_entry, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.SIZE, { +# Locals: +# dst -start (raw) +# dst_start +# src -end (raw) +# src_start +# src_end +%fn2(prim_bv_copy_bang_entry, {dst dst_start src src_start src_end}, { %car(t0, a0) - %st(t0, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.dst) ; dst + %stl(t0, dst) ; dst %cdr(a0, a0) %car(t0, a0) %sari(t0, t0, 3) - %st(t0, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.dst_start) ; dst-start + %stl(t0, dst_start) ; dst-start %cdr(a0, a0) %car(t0, a0) - %st(t0, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.src) ; src + %stl(t0, src) ; src %cdr(a0, a0) %car(t0, a0) %sari(t0, t0, 3) - %st(t0, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.src_start) ; src-start + %stl(t0, src_start) ; src-start %cdr(a0, a0) %car(t0, a0) %sari(t0, t0, 3) - %st(t0, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.src_end) ; src-end + %stl(t0, src_end) ; src-end # src-start >= 0 - %ld(t0, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.src_start) + %ldl(t0, src_start) %bltz(t0, &::oob) # src-end >= src-start (signed catches negative src-end) - %ld(t1, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.src_end) + %ldl(t1, src_end) %blt(t1, t0, &::oob) # src-end <= src.length - %ld(t2, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.src) + %ldl(t2, src) %heap_ld(a0, t2, %BV.hdr) %shri(a0, a0, 8) %blt(a0, t1, &::oob) # dst-start >= 0 - %ld(t2, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.dst_start) + %ldl(t2, dst_start) %bltz(t2, &::oob) # dst-start + count <= dst.length %sub(a0, t1, t0) ; count = src-end - src-start %add(a0, a0, t2) ; dst-start + count - %ld(t1, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.dst) + %ldl(t1, dst) %heap_ld(t2, t1, %BV.hdr) %shri(t2, t2, 8) %blt(t2, a0, &::oob) # Set up copy. dst ptr = dst.data + dst-start; src ptr = src.data + # src-start; count = src-end - src-start. - %ld(t0, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.dst) + %ldl(t0, dst) %heap_ld(t0, t0, %BV.data) - %ld(a1, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.dst_start) + %ldl(a1, dst_start) %add(t0, t0, a1) ; dst ptr - %ld(a1, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.src) + %ldl(a1, src) %heap_ld(a1, a1, %BV.data) - %ld(a2, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.src_start) + %ldl(a2, src_start) %add(a1, a1, a2) ; src ptr - %ld(a3, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.src_end) + %ldl(a3, src_end) %sub(a3, a3, a2) ; count ::loop @@ -3535,11 +3509,12 @@ # only when both are HDR.BV (closures, prims, records, and TDs are # identity-only). Tail-calls the cdr-side recursion and the BV check. # -# Frame: 16 bytes (a, b spilled across each %call). -%struct EQUAL_RECURSE_LOCALS { a b } # .SIZE = 16 -%fn(equal_recurse, %EQUAL_RECURSE_LOCALS.SIZE, { - %st(a0, sp, %EQUAL_RECURSE_LOCALS.a) - %st(a1, sp, %EQUAL_RECURSE_LOCALS.b) +# Locals: +# a +# b +%fn2(equal_recurse, {a b}, { + %stl(a0, a) + %stl(a1, b) %beq(a0, a1, &::true) @@ -3554,22 +3529,22 @@ %b(&::false) ::pair - %ld(t0, sp, %EQUAL_RECURSE_LOCALS.a) - %ld(t1, sp, %EQUAL_RECURSE_LOCALS.b) + %ldl(t0, a) + %ldl(t1, b) %car(a0, t0) %car(a1, t1) %call(&equal_recurse) %li(t0, %imm_val(%IMM.FALSE)) %beq(a0, t0, &::done) - %ld(t0, sp, %EQUAL_RECURSE_LOCALS.a) - %ld(t1, sp, %EQUAL_RECURSE_LOCALS.b) + %ldl(t0, a) + %ldl(t1, b) %cdr(a0, t0) %cdr(a1, t1) %tail(&equal_recurse) ::heap - %ld(t0, sp, %EQUAL_RECURSE_LOCALS.a) - %ld(t1, sp, %EQUAL_RECURSE_LOCALS.b) + %ldl(t0, a) + %ldl(t1, b) %hdr_type(t2, t0) %hdr_type(a0, t1) %bne(t2, a0, &::false) ; differing heap classes -> #f @@ -3604,45 +3579,44 @@ # every field is equal? (recursing through equal_recurse). Field i sits # at tagged + 13 + 8*i; nfields lives at the TD's offset 13 (raw). # -# Frame: 32 bytes -# +0 a (rec, tagged) -# +8 b (rec, tagged) -# +16 i (raw counter) -# +24 nfields (raw) -%struct REC_EQUAL_CHECK_LOCALS { a b i nfields } # .SIZE = 32 -%fn(rec_equal_check, %REC_EQUAL_CHECK_LOCALS.SIZE, { - %st(a0, sp, %REC_EQUAL_CHECK_LOCALS.a) - %st(a1, sp, %REC_EQUAL_CHECK_LOCALS.b) +# Locals: +# a (rec, tagged) +# b (rec, tagged) +# i (raw counter) +# nfields (raw) +%fn2(rec_equal_check, {a b i nfields}, { + %stl(a0, a) + %stl(a1, b) %heap_ld(t0, a0, %REC.td) ; td_a %heap_ld(t1, a1, %REC.td) ; td_b %bne(t0, t1, &::false) %heap_ld(t1, t0, %TD.nfields) - %st(t1, sp, %REC_EQUAL_CHECK_LOCALS.nfields) + %stl(t1, nfields) %li(t0, 0) - %st(t0, sp, %REC_EQUAL_CHECK_LOCALS.i) ; i = 0 + %stl(t0, i) ; i = 0 ::loop - %ld(t0, sp, %REC_EQUAL_CHECK_LOCALS.i) - %ld(t1, sp, %REC_EQUAL_CHECK_LOCALS.nfields) + %ldl(t0, i) + %ldl(t1, nfields) %beq(t0, t1, &::true) %shli(t2, t0, 3) %addi(t2, t2, 13) ; field offset = 13 + 8*i - %ld(t1, sp, %REC_EQUAL_CHECK_LOCALS.a) + %ldl(t1, a) %add(t1, t1, t2) %ld(a0, t1, 0) ; a's field i - %ld(t1, sp, %REC_EQUAL_CHECK_LOCALS.b) + %ldl(t1, b) %add(t1, t1, t2) %ld(a1, t1, 0) ; b's field i %call(&equal_recurse) %li(t0, %imm_val(%IMM.FALSE)) %beq(a0, t0, &::done) - %ld(t0, sp, %REC_EQUAL_CHECK_LOCALS.i) + %ldl(t0, i) %addi(t0, t0, 1) - %st(t0, sp, %REC_EQUAL_CHECK_LOCALS.i) + %stl(t0, i) %b(&::loop) ::true @@ -3674,13 +3648,13 @@ # clobber it freely while assembling args, then tail-call apply, which # re-derives a1 from the callee fn it dispatches on. Outer convention # stays intact end-to-end. -%struct PRIM_APPLY_ENTRY_LOCALS { args pad } # .SIZE = 16 -%fn(prim_apply_entry, %PRIM_APPLY_ENTRY_LOCALS.SIZE, { - %st(a0, sp, %PRIM_APPLY_ENTRY_LOCALS.args) + +%fn2(prim_apply_entry, {args pad}, { + %stl(a0, args) %cdr(a0, a0) %call(&apply_build_args) %mov(t0, a0) - %ld(a0, sp, %PRIM_APPLY_ENTRY_LOCALS.args) + %ldl(a0, args) %car(a0, a0) %mov(a1, t0) %tail(&apply) @@ -3693,19 +3667,18 @@ # (aₖ . NIL) cons; the final element (the trailing list) becomes the # tail's cdr (or the result itself if there are no leading elements). # -# Frame: 24 bytes -# +0 walk (advances; current cell of rest) -# +8 head (NIL until first leading arg appended) -# +16 tail (most recent cell; set-cdr! target) -%struct APPLY_BUILD_ARGS_LOCALS { walk head tail } # .SIZE = 24 -%fn(apply_build_args, %APPLY_BUILD_ARGS_LOCALS.SIZE, { - %st(a0, sp, %APPLY_BUILD_ARGS_LOCALS.walk) +# Locals: +# walk (advances; current cell of rest) +# head (NIL until first leading arg appended) +# tail (most recent cell; set-cdr! target) +%fn2(apply_build_args, {walk head tail}, { + %stl(a0, walk) %li(t0, %imm_val(%IMM.NIL)) - %st(t0, sp, %APPLY_BUILD_ARGS_LOCALS.head) - %st(t0, sp, %APPLY_BUILD_ARGS_LOCALS.tail) + %stl(t0, head) + %stl(t0, tail) ::loop - %ld(t0, sp, %APPLY_BUILD_ARGS_LOCALS.walk) + %ldl(t0, walk) %cdr(t1, t0) %if_nil(t2, t1, &::last) @@ -3714,16 +3687,16 @@ %li(a1, %imm_val(%IMM.NIL)) %call(&cons) - %ld(t0, sp, %APPLY_BUILD_ARGS_LOCALS.head) + %ldl(t0, head) %if_nil(t1, t0, &::first) - %ld(t0, sp, %APPLY_BUILD_ARGS_LOCALS.tail) + %ldl(t0, tail) %set_cdr(a0, t0) - %st(a0, sp, %APPLY_BUILD_ARGS_LOCALS.tail) + %stl(a0, tail) %b(&::advance) ::first - %st(a0, sp, %APPLY_BUILD_ARGS_LOCALS.head) - %st(a0, sp, %APPLY_BUILD_ARGS_LOCALS.tail) + %stl(a0, head) + %stl(a0, tail) ::advance %advance_walk(0) @@ -3734,11 +3707,11 @@ # args -- return the trailing list directly. Otherwise splice it onto # the tail and return head. %car(a0, t0) - %ld(t1, sp, %APPLY_BUILD_ARGS_LOCALS.head) + %ldl(t1, head) %if_nil(t2, t1, &::done) - %ld(t1, sp, %APPLY_BUILD_ARGS_LOCALS.tail) + %ldl(t1, tail) %set_cdr(a0, t1) - %ld(a0, sp, %APPLY_BUILD_ARGS_LOCALS.head) + %ldl(a0, head) ::done }) @@ -3752,18 +3725,18 @@ # make_param_prim(entry=a0, data=a1) -> prim (a0). Allocates a 24-byte # PRIM, sets the entry label and data word. -%struct MAKE_PARAM_PRIM_LOCALS { entry data } # .SIZE = 16 -%fn(make_param_prim, %MAKE_PARAM_PRIM_LOCALS.SIZE, { - %st(a0, sp, %MAKE_PARAM_PRIM_LOCALS.entry) - %st(a1, sp, %MAKE_PARAM_PRIM_LOCALS.data) + +%fn2(make_param_prim, {entry data}, { + %stl(a0, entry) + %stl(a1, data) %li(a0, 24) %li(a1, %HDR.PRIM) %call(&alloc_hdr) - %ld(t0, sp, %MAKE_PARAM_PRIM_LOCALS.entry) + %ldl(t0, entry) %heap_st(t0, a0, %PRIM.entry_w) - %ld(t1, sp, %MAKE_PARAM_PRIM_LOCALS.data) + %ldl(t1, data) %heap_st(t1, a0, %PRIM.data) }) @@ -3778,14 +3751,11 @@ # ctor: prim.data = TD (HEAP); args = (f0 f1 ...). Inlines the # %make-record body so we don't have to cons (TD . args) first. -%struct PRIM_CTOR_ENTRY_LOCALS { args td record } # .SIZE = 24 -%fn(prim_ctor_entry, %PRIM_CTOR_ENTRY_LOCALS.SIZE, { - # +0 args - # +8 td (from prim.data) - # +16 record - %st(a0, sp, %PRIM_CTOR_ENTRY_LOCALS.args) + +%fn2(prim_ctor_entry, {args td record}, { + %stl(a0, args) %heap_ld(t0, a1, %PRIM.data) - %st(t0, sp, %PRIM_CTOR_ENTRY_LOCALS.td) + %stl(t0, td) # Count = length(args). %call(&list_length) @@ -3793,12 +3763,12 @@ %addi(a0, a0, 16) %li(a1, %HDR.REC) %call(&alloc_hdr) - %st(a0, sp, %PRIM_CTOR_ENTRY_LOCALS.record) + %stl(a0, record) - %ld(t0, sp, %PRIM_CTOR_ENTRY_LOCALS.td) + %ldl(t0, td) %heap_st(t0, a0, %REC.td) - %ld(t0, sp, %PRIM_CTOR_ENTRY_LOCALS.args) + %ldl(t0, args) %addi(t1, a0, 13) ::fill_loop @@ -3810,7 +3780,7 @@ %b(&::fill_loop) ::fill_done - %ld(a0, sp, %PRIM_CTOR_ENTRY_LOCALS.record) + %ldl(a0, record) }) # predicate: prim.data = TD; args = (rec). @@ -3861,43 +3831,43 @@ # Allocates one TD + one parameterized PRIM per name introduced (ctor, # predicate, accessor, mutator) and binds each to the symbol's global. # -# Frame: 56 bytes -# +0 rest -# +8 env (unused, but the dispatcher passes it) -# +16 td -# +24 walk (clauses, advancing) -# +32 idx (raw counter) -# +40 nfields -%struct EVAL_DEFINE_RECORD_TYPE_LOCALS { rest env td walk idx nfields pad } # .SIZE = 56 -%fn(eval_define_record_type, %EVAL_DEFINE_RECORD_TYPE_LOCALS.SIZE, { - %st(a0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.rest) - %st(a1, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.env) +# Locals: +# rest +# env (unused, but the dispatcher passes it) +# td +# walk (clauses, advancing) +# idx (raw counter) +# nfields +# pad +%fn2(eval_define_record_type, {rest env td walk idx nfields pad}, { + %stl(a0, rest) + %stl(a1, env) # clauses = cdddr(rest); count them via list_length. - %ld(a0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.rest) + %ldl(a0, rest) %cdr(a0, a0) %cdr(a0, a0) %cdr(a0, a0) - %st(a0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.walk) + %stl(a0, walk) %call(&list_length) - %st(a0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.nfields) + %stl(a0, nfields) # td = alloc_hdr(24, HDR.TD); td.name = type-name; td.nfields = nfields. %li(a0, 24) %li(a1, %HDR.TD) %call(&alloc_hdr) - %st(a0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.td) - %ld(t0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.rest) + %stl(a0, td) + %ldl(t0, rest) %car(t0, t0) %heap_st(t0, a0, %TD.name) - %ld(t1, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.nfields) + %ldl(t1, nfields) %heap_st(t1, a0, %TD.nfields) # ctor-prim = make_param_prim(prim_ctor_entry, td); bind ctor-name. %la(a0, &prim_ctor_entry) - %ld(a1, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.td) + %ldl(a1, td) %call(&make_param_prim) - %ld(t0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.rest) + %ldl(t0, rest) %cdr(t0, t0) %car(t0, t0) %car(t0, t0) @@ -3905,9 +3875,9 @@ # pred-prim = make_param_prim(prim_predicate_entry, td); bind pred. %la(a0, &prim_predicate_entry) - %ld(a1, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.td) + %ldl(a1, td) %call(&make_param_prim) - %ld(t0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.rest) + %ldl(t0, rest) %cdr(t0, t0) %cdr(t0, t0) %car(t0, t0) @@ -3915,37 +3885,37 @@ # Iterate clauses: bind accessor + optional mutator per clause. %li(t0, 0) - %st(t0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.idx) + %stl(t0, idx) ::clause_loop - %ld(t0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.walk) + %ldl(t0, walk) %if_nil(t1, t0, &::done) # accessor-prim with data = tagged idx; bind cadr(clause). - %ld(a1, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.idx) + %ldl(a1, idx) %shli(a1, a1, 3) %la(a0, &prim_accessor_entry) %call(&make_param_prim) - %ld(t0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.walk) + %ldl(t0, walk) %car(t0, t0) %cdr(t0, t0) %car(t0, t0) %bind_global_from_t0() # Mutator? If cddr(clause) is a pair, bind it. - %ld(t0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.walk) + %ldl(t0, walk) %car(t0, t0) %cdr(t0, t0) %cdr(t0, t0) %if_nil(t1, t0, &::no_mutator) - %ld(a1, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.idx) + %ldl(a1, idx) %shli(a1, a1, 3) %la(a0, &prim_mutator_entry) %call(&make_param_prim) - %ld(t0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.walk) + %ldl(t0, walk) %car(t0, t0) %cdr(t0, t0) %cdr(t0, t0) @@ -3954,9 +3924,9 @@ ::no_mutator %advance_walk(24) - %ld(t0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.idx) + %ldl(t0, idx) %addi(t0, t0, 1) - %st(t0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.idx) + %stl(t0, idx) %b(&::clause_loop) ::done @@ -3990,80 +3960,80 @@ # `length` after append is left zero (preserved by the cap > length # invariant from bv_capacity_for + the BSS-zero heap), so syscalls that # read the data_ptr as a C string still see a NUL terminator. -%struct BV_PUTN_LOCALS { bv src n old_len } # .SIZE = 32 -%fn(bv_putn, %BV_PUTN_LOCALS.SIZE, { - %st(a0, sp, %BV_PUTN_LOCALS.bv) - %st(a1, sp, %BV_PUTN_LOCALS.src) - %st(a2, sp, %BV_PUTN_LOCALS.n) + +%fn2(bv_putn, {bv src n old_len}, { + %stl(a0, bv) + %stl(a1, src) + %stl(a2, n) %heap_ld(t0, a0, %BV.hdr) %shri(t0, t0, 8) ; old_len - %st(t0, sp, %BV_PUTN_LOCALS.old_len) + %stl(t0, old_len) # bv_grow ensures cap >= old_len + n + 1, so cap > new_len. %add(a1, t0, a2) %addi(a1, a1, 1) %call(&bv_grow) - %ld(t0, sp, %BV_PUTN_LOCALS.bv) + %ldl(t0, bv) %heap_ld(a0, t0, %BV.data) - %ld(t1, sp, %BV_PUTN_LOCALS.old_len) + %ldl(t1, old_len) %add(a0, a0, t1) ; dst = data + old_len - %ld(a1, sp, %BV_PUTN_LOCALS.src) - %ld(a2, sp, %BV_PUTN_LOCALS.n) + %ldl(a1, src) + %ldl(a2, n) %call(&memcpy) # hdr = (old_len + n) << 8 | HDR.BV. HDR.BV is 0. - %ld(t0, sp, %BV_PUTN_LOCALS.old_len) - %ld(t1, sp, %BV_PUTN_LOCALS.n) + %ldl(t0, old_len) + %ldl(t1, n) %add(t0, t0, t1) %shli(t0, t0, 8) - %ld(t1, sp, %BV_PUTN_LOCALS.bv) + %ldl(t1, bv) %heap_st(t0, t1, %BV.hdr) - %ld(a0, sp, %BV_PUTN_LOCALS.bv) + %ldl(a0, bv) }) # bv_putc(bv=a0, byte=a1) -> bv (a0). Append a single byte (low 8 bits # of a1). Same growth + length-update protocol as bv_putn. -%struct BV_PUTC_LOCALS { bv byte } # .SIZE = 16 -%fn(bv_putc, %BV_PUTC_LOCALS.SIZE, { - %st(a0, sp, %BV_PUTC_LOCALS.bv) - %st(a1, sp, %BV_PUTC_LOCALS.byte) + +%fn2(bv_putc, {bv byte}, { + %stl(a0, bv) + %stl(a1, byte) %heap_ld(t0, a0, %BV.hdr) %shri(t0, t0, 8) ; old_len %addi(a1, t0, 2) ; min_cap = old_len + 2 %call(&bv_grow) - %ld(t0, sp, %BV_PUTC_LOCALS.bv) + %ldl(t0, bv) %heap_ld(t1, t0, %BV.hdr) %shri(t1, t1, 8) ; old_len (re-read after grow) %heap_ld(t2, t0, %BV.data) %add(t2, t2, t1) - %ld(a0, sp, %BV_PUTC_LOCALS.byte) + %ldl(a0, byte) %sb(a0, t2, 0) %addi(t1, t1, 1) %shli(t1, t1, 8) %heap_st(t1, t0, %BV.hdr) - %ld(a0, sp, %BV_PUTC_LOCALS.bv) + %ldl(a0, bv) }) # bv_putint(bv=a0, value=a1) -> bv (a0). Append decimal repr of (raw, # untagged) value. Uses :writer_num_buf as a 24-byte scratch buffer # (fmt_dec writes at most 20 bytes for a 64-bit signed integer). -%struct BV_PUTINT_LOCALS { bv pad } # .SIZE = 16 -%fn(bv_putint, %BV_PUTINT_LOCALS.SIZE, { - %st(a0, sp, %BV_PUTINT_LOCALS.bv) + +%fn2(bv_putint, {bv pad}, { + %stl(a0, bv) %la(a0, &writer_num_buf) %call(&fmt_dec) ; n_bytes (a0) %mov(a2, a0) %la(a1, &writer_num_buf) - %ld(a0, sp, %BV_PUTINT_LOCALS.bv) + %ldl(a0, bv) %tail(&bv_putn) }) @@ -4080,11 +4050,11 @@ # bytes (display); mode = 1 emits them as `"..."` (write). Pairs are # delegated to write_pair_to_bv so the recursion through PAIR has its # own frame. -%struct WRITE_TO_BV_LOCALS { val bv mode pad } # .SIZE = 32 -%fn(write_to_bv, %WRITE_TO_BV_LOCALS.SIZE, { - %st(a0, sp, %WRITE_TO_BV_LOCALS.val) - %st(a1, sp, %WRITE_TO_BV_LOCALS.bv) - %st(a2, sp, %WRITE_TO_BV_LOCALS.mode) + +%fn2(write_to_bv, {val bv mode pad}, { + %stl(a0, val) + %stl(a1, bv) + %stl(a2, mode) %tagof(t0, a0) %li(t1, %TAG.PAIR) @@ -4097,24 +4067,24 @@ %beq(t0, t1, &::imm) # Fall-through: FIXNUM (the only remaining tag). - %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) - %ld(a1, sp, %WRITE_TO_BV_LOCALS.val) + %ldl(a0, bv) + %ldl(a1, val) %sari(a1, a1, 3) %tail(&bv_putint) ::sym - %ld(a0, sp, %WRITE_TO_BV_LOCALS.val) + %ldl(a0, val) %sari(a0, a0, 3) %call(&sym_name) %mov(a2, a1) %mov(a1, a0) - %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) + %ldl(a0, bv) %tail(&bv_putn) ::pair - %ld(a0, sp, %WRITE_TO_BV_LOCALS.val) - %ld(a1, sp, %WRITE_TO_BV_LOCALS.bv) - %ld(a2, sp, %WRITE_TO_BV_LOCALS.mode) + %ldl(a0, val) + %ldl(a1, bv) + %ldl(a2, mode) %tail(&write_pair_to_bv) ::heap @@ -4132,13 +4102,13 @@ %b(&::heap_unknown) ::heap_bv - %ld(t0, sp, %WRITE_TO_BV_LOCALS.mode) + %ldl(t0, mode) %beqz(t0, &::heap_bv_raw) # write mode: emit `"`, then the raw bytes, then `"`. - %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) + %ldl(a0, bv) %li(a1, 34) %call(&bv_putc) - %ld(t0, sp, %WRITE_TO_BV_LOCALS.val) + %ldl(t0, val) %heap_ld(a1, t0, %BV.data) %heap_ld(a2, t0, %BV.hdr) %shri(a2, a2, 8) @@ -4147,45 +4117,45 @@ %tail(&bv_putc) ::heap_bv_raw - %ld(t0, sp, %WRITE_TO_BV_LOCALS.val) + %ldl(t0, val) %heap_ld(a1, t0, %BV.data) %heap_ld(a2, t0, %BV.hdr) %shri(a2, a2, 8) - %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) + %ldl(a0, bv) %tail(&bv_putn) ::heap_closure %la(a1, &str_closure) %li(a2, 10) - %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) + %ldl(a0, bv) %tail(&bv_putn) ::heap_prim %la(a1, &str_prim) %li(a2, 7) - %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) + %ldl(a0, bv) %tail(&bv_putn) ::heap_td %la(a1, &str_td) %li(a2, 11) - %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) + %ldl(a0, bv) %tail(&bv_putn) ::heap_rec %la(a1, &str_rec) %li(a2, 9) - %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) + %ldl(a0, bv) %tail(&bv_putn) ::heap_unknown %la(a1, &str_unknown) %li(a2, 10) - %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) + %ldl(a0, bv) %tail(&bv_putn) ::imm - %ld(a0, sp, %WRITE_TO_BV_LOCALS.val) + %ldl(a0, val) %sari(a0, a0, 3) %beqz(a0, &::imm_false) %addi(t0, a0, -1) @@ -4199,37 +4169,37 @@ # EOF (idx == 5) is the only remaining IMM. %la(a1, &str_eof) %li(a2, 5) - %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) + %ldl(a0, bv) %tail(&bv_putn) ::imm_false %la(a1, &str_false) %li(a2, 2) - %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) + %ldl(a0, bv) %tail(&bv_putn) ::imm_true %la(a1, &str_true) %li(a2, 2) - %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) + %ldl(a0, bv) %tail(&bv_putn) ::imm_nil %la(a1, &str_nil) %li(a2, 2) - %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) + %ldl(a0, bv) %tail(&bv_putn) ::imm_unspec %la(a1, &str_unspec) %li(a2, 8) - %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) + %ldl(a0, bv) %tail(&bv_putn) ::imm_unbound %la(a1, &str_unbound) %li(a2, 9) - %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) + %ldl(a0, bv) %tail(&bv_putn) }) @@ -4239,30 +4209,30 @@ # a separator and continue, emit ` . val)` for a dotted tail, or just # emit `)` for a proper-list NIL. # -# Frame: 32 bytes -# +0 pair walk -# +8 bv (stable wrapper; reused across recursive calls) -# +16 mode -%struct WRITE_PAIR_TO_BV_LOCALS { pair bv mode pad } # .SIZE = 32 -%fn(write_pair_to_bv, %WRITE_PAIR_TO_BV_LOCALS.SIZE, { - %st(a0, sp, %WRITE_PAIR_TO_BV_LOCALS.pair) - %st(a1, sp, %WRITE_PAIR_TO_BV_LOCALS.bv) - %st(a2, sp, %WRITE_PAIR_TO_BV_LOCALS.mode) - - %ld(a0, sp, %WRITE_PAIR_TO_BV_LOCALS.bv) +# Locals: +# pair walk +# bv (stable wrapper; reused across recursive calls) +# mode +# pad +%fn2(write_pair_to_bv, {pair bv mode pad}, { + %stl(a0, pair) + %stl(a1, bv) + %stl(a2, mode) + + %ldl(a0, bv) %li(a1, 40) %call(&bv_putc) ::loop - %ld(t0, sp, %WRITE_PAIR_TO_BV_LOCALS.pair) + %ldl(t0, pair) %car(a0, t0) - %ld(a1, sp, %WRITE_PAIR_TO_BV_LOCALS.bv) - %ld(a2, sp, %WRITE_PAIR_TO_BV_LOCALS.mode) + %ldl(a1, bv) + %ldl(a2, mode) %call(&write_to_bv) - %ld(t0, sp, %WRITE_PAIR_TO_BV_LOCALS.pair) + %ldl(t0, pair) %cdr(t0, t0) - %st(t0, sp, %WRITE_PAIR_TO_BV_LOCALS.pair) + %stl(t0, pair) %if_nil(t1, t0, &::done) %tagof(t1, t0) @@ -4270,29 +4240,29 @@ %beq(t1, t2, &::cont) # Dotted tail: emit ` . ` then write_to_bv(cdr). - %ld(a0, sp, %WRITE_PAIR_TO_BV_LOCALS.bv) + %ldl(a0, bv) %li(a1, 32) %call(&bv_putc) - %ld(a0, sp, %WRITE_PAIR_TO_BV_LOCALS.bv) + %ldl(a0, bv) %li(a1, 46) %call(&bv_putc) - %ld(a0, sp, %WRITE_PAIR_TO_BV_LOCALS.bv) + %ldl(a0, bv) %li(a1, 32) %call(&bv_putc) - %ld(a0, sp, %WRITE_PAIR_TO_BV_LOCALS.pair) - %ld(a1, sp, %WRITE_PAIR_TO_BV_LOCALS.bv) - %ld(a2, sp, %WRITE_PAIR_TO_BV_LOCALS.mode) + %ldl(a0, pair) + %ldl(a1, bv) + %ldl(a2, mode) %call(&write_to_bv) %b(&::done) ::cont - %ld(a0, sp, %WRITE_PAIR_TO_BV_LOCALS.bv) + %ldl(a0, bv) %li(a1, 32) %call(&bv_putc) %b(&::loop) ::done - %ld(a0, sp, %WRITE_PAIR_TO_BV_LOCALS.bv) + %ldl(a0, bv) %li(a1, 41) %tail(&bv_putc) }) @@ -4301,15 +4271,15 @@ # delegate to write_to_bv; helper for display / write. The 16-byte # starting capacity is the floor from bv_capacity_for; bv_putn / # bv_putc grow as needed. -%struct VALUE_TO_BV_LOCALS { val mode } # .SIZE = 16 -%fn(value_to_bv, %VALUE_TO_BV_LOCALS.SIZE, { - %st(a0, sp, %VALUE_TO_BV_LOCALS.val) - %st(a1, sp, %VALUE_TO_BV_LOCALS.mode) + +%fn2(value_to_bv, {val mode}, { + %stl(a0, val) + %stl(a1, mode) %li(a0, 0) %call(&bv_alloc) %mov(a1, a0) - %ld(a0, sp, %VALUE_TO_BV_LOCALS.val) - %ld(a2, sp, %VALUE_TO_BV_LOCALS.mode) + %ldl(a0, val) + %ldl(a2, mode) %tail(&write_to_bv) }) @@ -4348,54 +4318,53 @@ # the byte at `length` is the BSS-zero NUL terminator, making the bv's # data_ptr a valid C string for panic's eprint_cstr. # -# Frame: 16 bytes -# +0 walk (initially args; advances over irritants) -# +8 bv -%struct PRIM_ERROR_ENTRY_LOCALS { walk bv } # .SIZE = 16 -%fn(prim_error_entry, %PRIM_ERROR_ENTRY_LOCALS.SIZE, { - %st(a0, sp, %PRIM_ERROR_ENTRY_LOCALS.walk) +# Locals: +# walk (initially args; advances over irritants) +# bv +%fn2(prim_error_entry, {walk bv}, { + %stl(a0, walk) %li(a0, 0) %call(&bv_alloc) - %st(a0, sp, %PRIM_ERROR_ENTRY_LOCALS.bv) + %stl(a0, bv) %la(a1, &str_error_prefix) %li(a2, 16) - %ld(a0, sp, %PRIM_ERROR_ENTRY_LOCALS.bv) + %ldl(a0, bv) %call(&bv_putn) # First arg (the message) goes through write_to_bv with display mode. - %ld(t0, sp, %PRIM_ERROR_ENTRY_LOCALS.walk) + %ldl(t0, walk) %car(a0, t0) - %ld(a1, sp, %PRIM_ERROR_ENTRY_LOCALS.bv) + %ldl(a1, bv) %li(a2, 0) %call(&write_to_bv) - %ld(t0, sp, %PRIM_ERROR_ENTRY_LOCALS.walk) + %ldl(t0, walk) %cdr(t0, t0) - %st(t0, sp, %PRIM_ERROR_ENTRY_LOCALS.walk) + %stl(t0, walk) ::loop - %ld(t0, sp, %PRIM_ERROR_ENTRY_LOCALS.walk) + %ldl(t0, walk) %if_nil(t1, t0, &::done) - %ld(a0, sp, %PRIM_ERROR_ENTRY_LOCALS.bv) + %ldl(a0, bv) %li(a1, 32) %call(&bv_putc) - %ld(t0, sp, %PRIM_ERROR_ENTRY_LOCALS.walk) + %ldl(t0, walk) %car(a0, t0) - %ld(a1, sp, %PRIM_ERROR_ENTRY_LOCALS.bv) + %ldl(a1, bv) %li(a2, 0) %call(&write_to_bv) - %ld(t0, sp, %PRIM_ERROR_ENTRY_LOCALS.walk) + %ldl(t0, walk) %cdr(t0, t0) - %st(t0, sp, %PRIM_ERROR_ENTRY_LOCALS.walk) + %stl(t0, walk) %b(&::loop) ::done - %ld(t0, sp, %PRIM_ERROR_ENTRY_LOCALS.bv) + %ldl(t0, bv) %heap_ld(a0, t0, %BV.data) %tail(&runtime_error) }) @@ -4406,33 +4375,32 @@ # pass through verbatim. Returns the assembled bv; the caller decides # how to consume it (e.g. (display (format ...))). # -# Frame: 32 bytes -# +0 out bv -# +8 template bv -# +16 args walk -# +24 idx (current byte offset into template) -%struct PRIM_FORMAT_ENTRY_LOCALS { out template args idx } # .SIZE = 32 -%fn(prim_format_entry, %PRIM_FORMAT_ENTRY_LOCALS.SIZE, { - %st(a0, sp, %PRIM_FORMAT_ENTRY_LOCALS.args) ; spill incoming args while we set up +# Locals: +# out bv +# template bv +# args walk +# idx (current byte offset into template) +%fn2(prim_format_entry, {out template args idx}, { + %stl(a0, args) ; spill incoming args while we set up %li(a0, 0) %call(&bv_alloc) - %st(a0, sp, %PRIM_FORMAT_ENTRY_LOCALS.out) + %stl(a0, out) - %ld(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.args) + %ldl(t0, args) %car(t1, t0) - %st(t1, sp, %PRIM_FORMAT_ENTRY_LOCALS.template) + %stl(t1, template) %cdr(t0, t0) - %st(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.args) + %stl(t0, args) %li(t0, 0) - %st(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.idx) + %stl(t0, idx) ::loop - %ld(t1, sp, %PRIM_FORMAT_ENTRY_LOCALS.template) + %ldl(t1, template) %heap_ld(t2, t1, %BV.hdr) %shri(t2, t2, 8) ; template length - %ld(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.idx) + %ldl(t0, idx) %beq(t0, t2, &::done) %heap_ld(a3, t1, %BV.data) @@ -4443,18 +4411,18 @@ %beqz(t1, &::tilde) # Plain byte: emit and advance. - %ld(a0, sp, %PRIM_FORMAT_ENTRY_LOCALS.out) + %ldl(a0, out) %mov(a1, a3) %call(&bv_putc) - %ld(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.idx) + %ldl(t0, idx) %addi(t0, t0, 1) - %st(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.idx) + %stl(t0, idx) %b(&::loop) ::tilde - %ld(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.idx) + %ldl(t0, idx) %addi(t0, t0, 1) - %ld(t1, sp, %PRIM_FORMAT_ENTRY_LOCALS.template) + %ldl(t1, template) %heap_ld(t2, t1, %BV.hdr) %shri(t2, t2, 8) %beq(t0, t2, &::tilde_lit) @@ -4464,7 +4432,7 @@ %lb(a3, t1, 0) ; spec %addi(t0, t0, 1) ; advance past spec - %st(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.idx) + %stl(t0, idx) %addi(t1, a3, -97) ; 'a' %beqz(t1, &::spec_a) @@ -4479,73 +4447,73 @@ # Unknown directive: emit `~` then the spec byte verbatim. Re-read # the spec byte from the template since bv_putc may clobber a3. - %ld(a0, sp, %PRIM_FORMAT_ENTRY_LOCALS.out) + %ldl(a0, out) %li(a1, 126) %call(&bv_putc) - %ld(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.template) + %ldl(t0, template) %heap_ld(t1, t0, %BV.data) - %ld(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.idx) + %ldl(t0, idx) %addi(t0, t0, -1) %add(t1, t1, t0) %lb(a1, t1, 0) - %ld(a0, sp, %PRIM_FORMAT_ENTRY_LOCALS.out) + %ldl(a0, out) %call(&bv_putc) %b(&::loop) ::tilde_lit # `~` at end of template: emit literal `~` and finish next iter. - %ld(a0, sp, %PRIM_FORMAT_ENTRY_LOCALS.out) + %ldl(a0, out) %li(a1, 126) %call(&bv_putc) - %ld(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.idx) + %ldl(t0, idx) %addi(t0, t0, 1) - %st(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.idx) + %stl(t0, idx) %b(&::loop) ::spec_a - %ld(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.args) + %ldl(t0, args) %car(a0, t0) %cdr(t0, t0) - %st(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.args) - %ld(a1, sp, %PRIM_FORMAT_ENTRY_LOCALS.out) + %stl(t0, args) + %ldl(a1, out) %li(a2, 0) %call(&write_to_bv) %b(&::loop) ::spec_s - %ld(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.args) + %ldl(t0, args) %car(a0, t0) %cdr(t0, t0) - %st(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.args) - %ld(a1, sp, %PRIM_FORMAT_ENTRY_LOCALS.out) + %stl(t0, args) + %ldl(a1, out) %li(a2, 1) %call(&write_to_bv) %b(&::loop) ::spec_d - %ld(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.args) + %ldl(t0, args) %car(t1, t0) %cdr(t0, t0) - %st(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.args) + %stl(t0, args) %sari(a1, t1, 3) - %ld(a0, sp, %PRIM_FORMAT_ENTRY_LOCALS.out) + %ldl(a0, out) %call(&bv_putint) %b(&::loop) ::spec_pct - %ld(a0, sp, %PRIM_FORMAT_ENTRY_LOCALS.out) + %ldl(a0, out) %li(a1, 10) %call(&bv_putc) %b(&::loop) ::spec_tilde - %ld(a0, sp, %PRIM_FORMAT_ENTRY_LOCALS.out) + %ldl(a0, out) %li(a1, 126) %call(&bv_putc) %b(&::loop) ::done - %ld(a0, sp, %PRIM_FORMAT_ENTRY_LOCALS.out) + %ldl(a0, out) }) # ========================================================================= @@ -4562,16 +4530,16 @@ # to syscalls expecting a C string. # wrap_syscall_result(raw=a0) -> (#t . r) or (#f . errno). -%struct WRAP_SYSCALL_RESULT_LOCALS { raw pad } # .SIZE = 16 -%fn(wrap_syscall_result, %WRAP_SYSCALL_RESULT_LOCALS.SIZE, { - %st(a0, sp, %WRAP_SYSCALL_RESULT_LOCALS.raw) + +%fn2(wrap_syscall_result, {raw pad}, { + %stl(a0, raw) %bltz(a0, &::err) %shli(a1, a0, 3) %li(a0, %imm_val(%IMM.TRUE)) %tail(&cons) ::err - %ld(t0, sp, %WRAP_SYSCALL_RESULT_LOCALS.raw) + %ldl(t0, raw) %li(t1, 0) %sub(t0, t1, t0) %shli(a1, t0, 3) @@ -4592,9 +4560,9 @@ # sys_clone() -> r (a0). Linux clone(SIGCHLD, 0, 0, 0, 0) -- fork-style. # Saves and restores s0 around the syscall because %p1_syscall reads s0 # as the 5th OS-syscall argument. -%struct SYS_CLONE_LOCALS { saved_s0 pad } # .SIZE = 16 -%fn(sys_clone, %SYS_CLONE_LOCALS.SIZE, { - %st(s0, sp, %SYS_CLONE_LOCALS.saved_s0) + +%fn2(sys_clone, {saved_s0 pad}, { + %stl(s0, saved_s0) %li(s0, 0) %li(a1, 17) @@ -4604,7 +4572,7 @@ %li(a0, %p1_sys_clone) %syscall - %ld(s0, sp, %SYS_CLONE_LOCALS.saved_s0) + %ldl(s0, saved_s0) }) # sys_execve(path=a0, argv=a1, envp=a2) -> -errno (a0). Only returns on @@ -4631,23 +4599,22 @@ # Walks `list` (cons-list of bytevectors), allocates (count+1)*8 bytes, # writes each bv's data_ptr, terminates with NULL. # -# Frame: 24 bytes -# +0 list -# +8 count -# +16 array ptr (raw) -%struct BUILD_EXECVE_ARGV_LOCALS { list count array } # .SIZE = 24 -%fn(build_execve_argv, %BUILD_EXECVE_ARGV_LOCALS.SIZE, { - %st(a0, sp, %BUILD_EXECVE_ARGV_LOCALS.list) +# Locals: +# list +# count +# array ptr (raw) +%fn2(build_execve_argv, {list count array}, { + %stl(a0, list) %call(&list_length) ; clobbers a0 -> count - %st(a0, sp, %BUILD_EXECVE_ARGV_LOCALS.count) + %stl(a0, count) %addi(a0, a0, 1) %shli(a0, a0, 3) %call(&alloc_bytes) - %st(a0, sp, %BUILD_EXECVE_ARGV_LOCALS.array) + %stl(a0, array) - %ld(t0, sp, %BUILD_EXECVE_ARGV_LOCALS.list) - %ld(t1, sp, %BUILD_EXECVE_ARGV_LOCALS.array) + %ldl(t0, list) + %ldl(t1, array) ::fill_loop %if_nil(t2, t0, &::fill_done) @@ -4662,7 +4629,7 @@ %li(t2, 0) %st(t2, t1, 0) - %ld(a0, sp, %BUILD_EXECVE_ARGV_LOCALS.array) + %ldl(a0, array) }) # (sys-read fd buf count) @@ -4719,13 +4686,13 @@ }) # (sys-execve path argv-list) -%struct PRIM_SYS_EXECVE_ENTRY_LOCALS { path pad } # .SIZE = 16 -%fn(prim_sys_execve_entry, %PRIM_SYS_EXECVE_ENTRY_LOCALS.SIZE, { + +%fn2(prim_sys_execve_entry, {path pad}, { %args2(t0, a0, a0) ; t0 = path bv, a0 = argv-list - %st(t0, sp, %PRIM_SYS_EXECVE_ENTRY_LOCALS.path) + %stl(t0, path) %call(&build_execve_argv) %mov(a1, a0) - %ld(a0, sp, %PRIM_SYS_EXECVE_ENTRY_LOCALS.path) + %ldl(a0, path) %heap_ld(a0, a0, %BV.data) ; path data ptr %li(a2, 0) %call(&sys_execve) @@ -4750,71 +4717,70 @@ # NUL-terminated entry into a fresh bytevector and consing them in order # via the head/tail trick. # -# Frame: 40 bytes -# +0 argv ptr (advancing 8 bytes per iteration) -# +8 count remaining (decrementing from saved_argc) -# +16 list head -# +24 list tail -# +32 current bv (across memcpy) -%struct PRIM_SYS_ARGV_ENTRY_LOCALS { argv count head tail bv } # .SIZE = 40 -%fn(prim_sys_argv_entry, %PRIM_SYS_ARGV_ENTRY_LOCALS.SIZE, { +# Locals: +# argv ptr (advancing 8 bytes per iteration) +# count remaining (decrementing from saved_argc) +# head +# tail +# bv +%fn2(prim_sys_argv_entry, {argv count head tail bv}, { %ld_global(t0, &saved_argv) - %st(t0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.argv) + %stl(t0, argv) %ld_global(t0, &saved_argc) - %st(t0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.count) + %stl(t0, count) %li(t0, %imm_val(%IMM.NIL)) - %st(t0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.head) - %st(t0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.tail) + %stl(t0, head) + %stl(t0, tail) ::loop - %ld(t0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.count) + %ldl(t0, count) %beqz(t0, &::done) # len = strlen(*argv) - %ld(t0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.argv) + %ldl(t0, argv) %ld(a0, t0, 0) %call(&strlen) # bv = bv_alloc(len) %call(&bv_alloc) - %st(a0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.bv) + %stl(a0, bv) # memcpy(bv.data_ptr, *argv, len-from-bv-hdr). - %ld(t0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.bv) + %ldl(t0, bv) %heap_ld(a0, t0, %BV.data) - %ld(t1, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.argv) + %ldl(t1, argv) %ld(a1, t1, 0) %heap_ld(t1, t0, %BV.hdr) %shri(a2, t1, 8) %call(&memcpy) # cell = cons(bv, NIL); append to list head/tail. - %ld(a0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.bv) + %ldl(a0, bv) %li(a1, %imm_val(%IMM.NIL)) %call(&cons) - %ld(t0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.head) + %ldl(t0, head) %if_nil(t1, t0, &::first) - %ld(t0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.tail) + %ldl(t0, tail) %set_cdr(a0, t0) - %st(a0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.tail) + %stl(a0, tail) %b(&::advance) ::first - %st(a0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.head) - %st(a0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.tail) + %stl(a0, head) + %stl(a0, tail) ::advance - %ld(t0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.argv) + %ldl(t0, argv) %addi(t0, t0, 8) - %st(t0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.argv) - %ld(t0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.count) + %stl(t0, argv) + %ldl(t0, count) %addi(t0, t0, -1) - %st(t0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.count) + %stl(t0, count) %b(&::loop) ::done - %ld(a0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.head) + %ldl(a0, head) }) # (eof-object) and (eof-object? x). @@ -5026,31 +4992,9 @@ :name_ch_newline "newline" # ========================================================================= -# Startup -- bss_init / heap_init +# Startup -- heap_init # ========================================================================= -# bss_init() -> none. Walks bss_init_tbl once and writes &ELF_end + OFF_* -# into each pointer slot. Same idiom as M1pp.P1: a tiny init table walked -# once. Leaf. -:bss_init -%scope bss_init - %la(t0, &ELF_end) - %la(t1, &bss_init_tbl) - %la(t2, &bss_init_tbl_end) - - ::loop - %beq(t1, t2, &::done) - %ld(a0, t1, 0) - %ld(a2, t1, 8) - %add(a2, a2, t0) - %st(a2, a0, 0) - %addi(t1, t1, 16) - %b(&::loop) - ::done - - %ret -%endscope - # heap_init() -> none. Sets heap_next (&heap_buf rounded up to 8-byte # alignment) and heap_end (heap_buf + HEAP_CAP_BYTES). cons assumes # 8-byte-aligned heap_next so every pair pointer's low 3 bits are exactly @@ -5070,16 +5014,17 @@ %ret # ========================================================================= -# BSS pointer-init table +# BSS arena table # ========================================================================= # -# Each entry: 8-byte slot pointer (4-byte label ref + 4 bytes pad) + -# 8-byte offset constant. p1_main walks this once at startup. -:bss_init_tbl -&readbuf_buf_ptr %(0) $(0) -&heap_buf_ptr %(0) $(%READBUF_CAP_BYTES) -&symtab_buf_ptr %(0) $((+ %READBUF_CAP_BYTES %HEAP_CAP_BYTES)) -:bss_init_tbl_end +# (slot, size) rows for libp1pp's init_arenas, walked once at startup. +# init_arenas threads a running offset, so each arena starts where the +# previous one ended. +:arena_table +%arena_entry(&readbuf_buf_ptr, %READBUF_CAP_BYTES) +%arena_entry(&heap_buf_ptr, %HEAP_CAP_BYTES) +%arena_entry(&symtab_buf_ptr, (* %SYMTAB_CAP_SLOTS %SYMENT.SIZE)) +:arena_table_end # ========================================================================= # Scalar BSS (file-resident, zero-initialized)