boot2

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

commit 65fc09c8eabc994c0c5b77384b896cdbb9d5aaf2
parent ad86871713a85088c3cb1d3aa5f9f0eb383cd665
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Sun, 26 Apr 2026 15:14:14 -0700

scheme1: adopt libp1pp global/array helpers; add pair + heap field macros

Replace open-coded la+ld(_,0) and li+mul+add+ld sequences with the
new ld_global / st_global / ld_array / st_array helpers. Add
scheme1-local set_car / set_cdr for tagged-pair stores and
heap_ld / heap_st for tagged-heap-pointer field access via a
constant field offset; declare BV and REC structs to back them.
Trim the now-stale file-header narrative.

Diffstat:
Mscheme1/scheme1.P1pp | 2522+++++++++++++++++++++++++++++++++++++++----------------------------------------
1 file changed, 1231 insertions(+), 1291 deletions(-)

diff --git a/scheme1/scheme1.P1pp b/scheme1/scheme1.P1pp @@ -1,36 +1,11 @@ # scheme1.P1pp -- Phase 1 minimal Scheme interpreter on P1. # -# The full target is described in docs/LISP-C.md and docs/LISP.md. This -# file is the spine: enough infrastructure to read a single source file, -# parse one s-expression, and evaluate it via tag-dispatched eval/apply -# with a single primitive (`sys-exit`). Every later piece (more -# primitives, special forms, closures, pmatch, records, prelude, repl -# loop) hooks onto these anchors without restructuring. -# -# What's wired up now: -# - Tag layout per LISP-C.md: fixnums, pairs, symbols, headered objects, -# immediate singletons. mkimm composes IMM constants at M1pp time. -# - Bump heap allocator (cons + alloc_hdr) over a BSS-past-ELF_end arena. -# - Linear-scan symbol intern table; entries store (name_ptr, name_len, -# global_val) borrowed directly from readbuf. -# - Reader: '(', ')', fixnums (decimal, optional leading `-`), bare -# symbols. No strings/hex/chars/quote/dotted yet, no `;` comments. -# - eval: tag dispatch -> self-eval / symbol lookup / pair application. -# - apply: HDR.PRIM dispatch only; closures/specials come later. -# - One primitive: sys-exit. Exits with the raw fixnum. -# -# Build chain (P1pp.P1pp = libp1pp must be in the catm sequence): +# Build chain: # catm P1-<arch>.M1pp P1.M1pp P1pp.P1pp scheme1/scheme1.P1pp \ # | m1pp -> M0 -> hex2 -> ELF # -# At run time, scripts/boot-run-scheme1.sh catm's scheme1/prelude.scm -# in front of the user .scm and passes the combined file to the binary, -# so the interpreter itself has no embedded prelude. -# -# Memory model: the ELF's ph_memsz is 8 MB (boot2 default), so all -# zero-initialized arenas live past :ELF_end and cost zero file bytes. -# p1_main writes their absolute addresses into pointer slots once at -# startup; every later access goes through one extra load. +# Run chain: +# catm scheme1/prelude.scm prog.scm | scheme1 # ========================================================================= # Constants @@ -43,9 +18,7 @@ # imm_val(idx) -> integer-expression for the tagged immediate at IMM index # `idx`. Used both at %li sites (loaded into a register) and at $() emission # sites (baked into a static word). -%macro imm_val(idx) -(| (<< idx 3) %TAG.IMM) -%endm +%macro imm_val(idx) (| (<< idx 3) %TAG.IMM) %endm # Layout helpers. %struct stride is 8 bytes per field. %struct PAIR { car cdr } # .SIZE = 16 @@ -53,6 +26,8 @@ %struct PRIM { hdr entry_w data } # .SIZE = 24 %struct CLOSURE { hdr params body env } # .SIZE = 32 %struct TD { hdr name nfields } # .SIZE = 24 +%struct BV { hdr data } # .SIZE = 16 +%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 @@ -63,49 +38,29 @@ # startup loop materializes &ELF_end + OFF_X into the matching pointer # slot. The offsets are emitted directly in bss_init_tbl via $(). -%macro SYMTAB_CAP_SLOTS() -8192 -%endm - -%macro READBUF_CAP_BYTES() -262144 -%endm - -%macro HEAP_CAP_BYTES() -0x1000000 -%endm +%macro SYMTAB_CAP_SLOTS() 8192 %endm +%macro READBUF_CAP_BYTES() 262144 %endm +%macro HEAP_CAP_BYTES() 0x1000000 %endm # ========================================================================= # Tag idioms # ========================================================================= -%macro tagof(rd, rs) -%andi(rd, rs, 7) -%endm - -%macro mkfix(rd, rs) -%shli(rd, rs, 3) -%endm - -%macro untag_fix(rd, rs) -%sari(rd, rs, 3) -%endm - -%macro untag_sym(rd, rs) -%sari(rd, rs, 3) -%endm - -%macro car(rd, rs) -%ld(rd, rs, -1) -%endm - -%macro cdr(rd, rs) -%ld(rd, rs, 7) -%endm - -%macro hdr_type(rd, rs) -%lb(rd, rs, -3) -%endm +%macro tagof(rd, rs) %andi(rd, rs, 7) %endm +%macro mkfix(rd, rs) %shli(rd, rs, 3) %endm +%macro untag_fix(rd, rs) %sari(rd, rs, 3) %endm +%macro untag_sym(rd, rs) %sari(rd, rs, 3) %endm +%macro car(rd, rs) %ld(rd, rs, -1) %endm +%macro cdr(rd, rs) %ld(rd, rs, 7) %endm +%macro set_car(rs, pair_tagged) %st(rs, pair_tagged, -1) %endm +%macro set_cdr(rs, pair_tagged) %st(rs, pair_tagged, 7) %endm +%macro hdr_type(rd, rs) %lb(rd, rs, -3) %endm + +# Field access through a tagged HEAP pointer (tag = 3). `field` is a +# constant byte offset from the underlying raw object (e.g. %PRIM.data, +# %CLOSURE.env). Reader is %ld; writer is %heap_st. +%macro heap_ld(rd, rs, field) %ld(rd, rs, (- field 3)) %endm +%macro heap_st(rs, rt, field) %st(rs, rt, (- field 3)) %endm # ========================================================================= # Scheme1-local helpers @@ -115,217 +70,169 @@ # be the destination register; the macro reuses it as a scratch pointer # during the la / ld / add chain before the final lb writes the byte. %macro readbuf_byte(rd, off_reg) -%la(rd, &readbuf_buf_ptr) -%ld(rd, rd, 0) -%add(rd, rd, off_reg) -%lb(rd, rd, 0) + %ld_global(rd, &readbuf_buf_ptr) + %add(rd, rd, off_reg) + %lb(rd, rd, 0) %endm # Branch to `target` if `ch_reg` holds an ASCII whitespace byte (space, # tab, LF, CR). `scratch` is clobbered. %macro is_ws_branch(scratch, ch_reg, target) -%addi(scratch, ch_reg, -32) -%beqz(scratch, target) -%addi(scratch, ch_reg, -9) -%beqz(scratch, target) -%addi(scratch, ch_reg, -10) -%beqz(scratch, target) -%addi(scratch, ch_reg, -13) -%beqz(scratch, target) + %addi(scratch, ch_reg, -32) + %beqz(scratch, target) + %addi(scratch, ch_reg, -9) + %beqz(scratch, target) + %addi(scratch, ch_reg, -10) + %beqz(scratch, target) + %addi(scratch, ch_reg, -13) + %beqz(scratch, target) %endm # Compute &symtab_buf + idx_reg * SYMENT.SIZE into rd. `scratch` is # clobbered. %macro symtab_entry(rd, idx_reg, scratch) -%la(rd, &symtab_buf_ptr) -%ld(rd, rd, 0) -%shli(scratch, idx_reg, 5) -%add(rd, rd, scratch) + %ld_global(rd, &symtab_buf_ptr) + %shli(scratch, idx_reg, 5) + %add(rd, rd, scratch) %endm # Print msg_label and abort. Never returns. Routes through runtime_error # so every error path lands in one place (stderr + exit 1). %macro die(msg) -%la(a0, & ## msg) -%call(&runtime_error) + %la(a0, & ## msg) + %call(&runtime_error) %endm # Intern a special-form name and stash the tagged-symbol value in a # labeled slot. `name` and `slot` are written as full label refs # (`&foo`) so the macro can substitute them verbatim into %la sites. %macro intern_form(name, len, slot) -%la(a0, name) -%li(a1, len) -%call(&intern) -%la(t0, slot) -%st(a0, t0, 0) + %la(a0, name) + %li(a1, len) + %call(&intern) + %st_global(a0, slot, t0) %endm # Special-form dispatch: pointer-compare the head symbol against `slot`'s # cached value (in t0) and branch to `target` on hit. Caller has already # loaded head into t0. %macro dispatch_form(slot, target) -%la(t1, slot) -%ld(t1, t1, 0) -%beq(t0, t1, target) + %ld_global(t1, slot) + %beq(t0, t1, target) %endm # Tail-jump from a special-form dispatch label to its handler. Handlers # uniformly take (rest=cdr(expr), env) -> value; expr lives at sp[0], # env at sp[8] in eval's frame. %macro tail_to_handler(handler) -%ld(a0, sp, 0) -%cdr(a0, a0) -%ld(a1, sp, 8) -%tail(handler) + %ld(a0, sp, 0) + %cdr(a0, a0) + %ld(a1, sp, 8) + %tail(handler) %endm # Branch to `target` if `val` holds the NIL immediate. `scratch` is # clobbered. %macro if_nil(scratch, val, target) -%li(scratch, %imm_val(%IMM.NIL)) -%beq(val, scratch, target) + %li(scratch, %imm_val(%IMM.NIL)) + %beq(val, scratch, target) %endm # Advance a list cursor parked at sp[slot] to its cdr. t0 is the implicit # scratch register; callers must ensure it's free. %macro advance_walk(slot) -%ld(t0, sp, slot) -%cdr(t0, t0) -%st(t0, sp, slot) + %ld(t0, sp, slot) + %cdr(t0, t0) + %st(t0, sp, slot) %endm # Bind a global from registers a0 (value) and t0 (tagged sym). Untags t0, # rearranges into the (idx, val) ABI, and calls sym_set_global. Used at # the tail of every define-style binder. %macro bind_global_from_t0() -%untag_sym(t0, t0) -%mov(a1, a0) -%mov(a0, t0) -%call(&sym_set_global) + %untag_sym(t0, t0) + %mov(a1, a0) + %mov(a0, t0) + %call(&sym_set_global) %endm # car-and-untag-fixnum: rd = car(list) >> 3. %macro car_fix(rd, list) -%car(rd, list) -%sari(rd, rd, 3) + %car(rd, list) + %sari(rd, rd, 3) %endm # car-then-load-bytevector-data-pointer: rd = (car(list)).data_ptr. %macro car_bvdata(rd, list) -%car(rd, list) -%ld(rd, rd, 5) + %car(rd, list) + %ld(rd, rd, 5) %endm # Positional list-arg extraction. r_n receives the nth element of `list`; # the last destination register doubles as the in-flight rest cursor # during extraction (its final value is the last argument). %macro args2(r0, r1, list) -%car(r0, list) -%cdr(r1, list) -%car(r1, r1) + %car(r0, list) + %cdr(r1, list) + %car(r1, r1) %endm %macro args3(r0, r1, r2, list) -%car(r0, list) -%cdr(r2, list) -%car(r1, r2) -%cdr(r2, r2) -%car(r2, r2) + %car(r0, list) + %cdr(r2, list) + %car(r1, r2) + %cdr(r2, r2) + %car(r2, r2) %endm %macro args4(r0, r1, r2, r3, list) -%car(r0, list) -%cdr(r3, list) -%car(r1, r3) -%cdr(r3, r3) -%car(r2, r3) -%cdr(r3, r3) -%car(r3, r3) + %car(r0, list) + %cdr(r3, list) + %car(r1, r3) + %cdr(r3, r3) + %car(r2, r3) + %cdr(r3, r3) + %car(r3, r3) %endm # ========================================================================= # p1_main -- runtime spine # ========================================================================= -# -# Frame layout (16 bytes): -# +0 saved argv - -%fn(p1_main, 16, { - %st(a1, sp, 0) - - # Stash argc/argv globally so the sys-argv primitive can rebuild a - # list of bytevectors on demand without re-plumbing them through - # every call frame. - %la(t0, &saved_argc) - %st(a0, t0, 0) - %la(t0, &saved_argv) - %st(a1, t0, 0) +%fn(p1_main, 0, { + # Stash argc/argv + %st_global(a0, &saved_argc, t0) + %st_global(a1, &saved_argv, t0) + + # if argc < 2 goto usage %li(t0, 2) %bltu(a0, t0, &::usage) - # Initialize BSS pointer slots from ELF_end + OFF_*. Same idiom as - # M1pp.P1: a tiny init table walked once. - %la(t0, &ELF_end) - %la(t1, &bss_init_tbl) - %la(t2, &bss_init_tbl_end) - ::bss_loop - %beq(t1, t2, &::bss_done) - %ld(a0, t1, 0) - %ld(a2, t1, 8) - %add(a2, a2, t0) - %st(a2, a0, 0) - %addi(t1, t1, 16) - %b(&::bss_loop) - ::bss_done - - # heap_next = &heap_buf, rounded up to 8-byte alignment. The BSS arena - # starts at &ELF_end + OFF_heap, but &ELF_end's alignment depends on - # the data section above it; cons assumes 8-byte-aligned heap_next so - # every pair pointer's low 3 bits are exactly the PAIR tag. - %la(t0, &heap_buf_ptr) - %ld(t0, t0, 0) - %alignup(t0, t0, 8, t1) - %la(t1, &heap_next) - %st(t0, t1, 0) - - # heap_end = heap_buf_ptr + HEAP_CAP_BYTES. cons / alloc_hdr / - # alloc_bytes test (heap_next + bytes <= heap_end) on every - # allocation and abort via runtime_error on overflow. - %la(t0, &heap_buf_ptr) - %ld(t0, t0, 0) - %li(t1, %HEAP_CAP_BYTES) - %add(t0, t0, t1) - %la(t1, &heap_end) - %st(t0, t1, 0) - - # Reserve special-form symbol indices, then bind built-in primitives. - # The Scheme prelude is catm'd into argv[1] by scripts/run-scheme1.sh - # before scheme1 starts, so there's no separate prelude eval pass. + # Initialize + %call(&bss_init) + %call(&heap_init) %call(&intern_special_forms) %call(&register_primitives) - # argv[1] is the source path (NUL-terminated cstr from the kernel). - %ld(a1, sp, 0) - %ld(a0, a1, 8) + # load_source(argv[1]) + %ld_global(a0, &saved_argv) + %ld(a0, a0, 8) %call(&load_source) - # Top-level read-eval loop. Each iteration: skip ws+comments, stop - # at EOF, otherwise parse one form and eval it under NIL. - ::repl - %call(&skip_ws) - %la(t0, &readbuf_pos) - %ld(t0, t0, 0) - %la(t1, &readbuf_len) - %ld(t1, t1, 0) - %beq(t0, t1, &::done) - %call(&parse_one) - %li(a1, %imm_val(%IMM.NIL)) - %call(&eval) - %b(&::repl) - - ::done + # read-eval loop + %loop_tag(eval, { + # eof = skip_ws() + %call(&skip_ws) + # if eof break + %bnez(a0, &eval_end) + # expr = parse_one() + %call(&parse_one) + # eval(expr, env=nil) + %li(a1, %imm_val(%IMM.NIL)) + %call(&eval) + }) + + # return 0 %li(a0, 0) %eret @@ -336,218 +243,6 @@ }) # ========================================================================= -# Runtime error -- single abort entry point -# ========================================================================= -# -# runtime_error(msg_cstr=a0) -> never returns. Every overflow / bounds / -# unbound / type-failure path lands here so error reporting (and the -# eventual user-facing `error` primitive) only have to be implemented -# once. Today we tail into libp1pp's `panic`, which writes msg + LF to -# stderr and sys_exits 1. -:runtime_error - %tail(&panic) - -# ========================================================================= -# Source loading -- argv[1] -> readbuf, length stored in readbuf_len -# ========================================================================= - -%fn(load_source, 0, { - %la(t0, &readbuf_buf_ptr) - %ld(a1, t0, 0) - %li(a2, %READBUF_CAP_BYTES) - %call(&read_file) - %bltz(a0, &::fail) - - # If the read filled (or would have filled) the buffer, the source - # is at least cap bytes; refuse rather than silently truncate. - # read_file does a single sys_read so n == cap is the only saturation - # signal we have. We treat n >= cap as overflow defensively. - %li(t0, %READBUF_CAP_BYTES) - %bltu(a0, t0, &::ok) - %die(msg_readbuf_full) - - ::ok - %la(t0, &readbuf_len) - %st(a0, t0, 0) - %li(a0, 0) - %la(t0, &readbuf_pos) - %st(a0, t0, 0) - %eret - - ::fail - %die(msg_load_fail) -}) - -# ========================================================================= -# Heap: cons (leaf) and alloc_hdr (leaf) -# ========================================================================= -# -# Both are call-free leaves: bump heap_next, write fields, return tagged -# pointer. Each allocation tests (new_next <= heap_end) and aborts via -# runtime_error if the bump would overflow the heap arena. heap_next is -# kept 8-byte aligned so every PAIR/HEAP tag bit is exact: cons always -# bumps by a multiple of 8 (16); alloc_hdr / alloc_bytes round their -# argument up via %alignup(_,_,8,_). - -# cons(car=a0, cdr=a1) -> tagged pair (a0). Allocates 16 bytes. -:cons -%scope cons - %la(t2, &heap_next) - %ld(t0, t2, 0) - %addi(t1, t0, 16) - %la(a3, &heap_end) - %ld(a3, a3, 0) - %bltu(a3, t1, &::oom) - %st(a0, t0, 0) - %st(a1, t0, 8) - %st(t1, t2, 0) - %addi(a0, t0, 1) - %ret - ::oom - %die(msg_heap_full) -%endscope - -# alloc_hdr(bytes=a0, hdr_word=a1) -> tagged heap obj (a0) -# Rounds bytes up to a multiple of 8 and writes hdr_word at offset 0. -:alloc_hdr -%scope alloc_hdr - %alignup(a0, a0, 8, t0) - %la(t2, &heap_next) - %ld(t0, t2, 0) - %add(t1, t0, a0) - %la(a3, &heap_end) - %ld(a3, a3, 0) - %bltu(a3, t1, &::oom) - %st(t1, t2, 0) - %st(a1, t0, 0) - %addi(a0, t0, 3) - %ret - ::oom - %die(msg_heap_full) -%endscope - -# list_length(list=a0) -> count (a0). Linear walk; clobbers a0 (used as -# the cursor). Callers that need the list afterward must save it first. -:list_length -%scope list_length - %li(t0, 0) - ::loop - %if_nil(t1, a0, &::done) - %addi(t0, t0, 1) - %cdr(a0, a0) - %b(&::loop) - ::done - %mov(a0, t0) - %ret -%endscope - -# ========================================================================= -# 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) - -%fn(intern, 32, { - %st(a0, sp, 0) - %st(a1, sp, 8) - - %li(t0, 0) - %st(t0, sp, 16) - - ::scan - # idx >= count? -> append - %ld(t0, sp, 16) - %la(t1, &symtab_count) - %ld(t1, t1, 0) - %bltu(t0, t1, &::probe) - %b(&::append) - - ::probe - %symtab_entry(t1, t0, t2) - %st(t1, sp, 24) - - # entry.name_len == name_len ? - %ld(t2, t1, 8) - %ld(a2, sp, 8) - %bne(t2, a2, &::next) - - # memcmp(entry.name_ptr, name_ptr, len) - %ld(a0, t1, 0) - %ld(a1, sp, 0) - %ld(a2, sp, 8) - %call(&memcmp) - %beqz(a0, &::found) - - ::next - %ld(t0, sp, 16) - %addi(t0, t0, 1) - %st(t0, sp, 16) - %b(&::scan) - - ::append - # Bounds check; on overflow exit 5 with a message. - %ld(t0, sp, 16) - %li(t1, %SYMTAB_CAP_SLOTS) - %bltu(t0, t1, &::append_ok) - %die(msg_symtab_full) - - ::append_ok - # 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, 8) - %call(&alloc_bytes) - %ld(a1, sp, 0) - %ld(a2, sp, 8) - %call(&memcpy) ; returns dst in a0 = stable copy - - %ld(t0, sp, 16) - %symtab_entry(t1, t0, t2) - %st(a0, t1, 0) ; entry.name_ptr = stable copy - %ld(a0, sp, 8) - %st(a0, t1, 8) - %li(a0, %imm_val(%IMM.UNBOUND)) - %st(a0, t1, 16) - %li(a0, 0) - %st(a0, t1, 24) - - # symtab_count = idx + 1 - %addi(a0, t0, 1) - %la(t2, &symtab_count) - %st(a0, t2, 0) - - # fall through with idx in t0 = sp[16] - - ::found - %ld(t0, sp, 16) - %shli(a0, t0, 3) - %ori(a0, a0, %TAG.SYM) -}) - -# Lookup by sym_idx (untagged, in a0). Returns symtab[idx].global_val in a0. -# Leaf. -:sym_global - %la(t0, &symtab_buf_ptr) - %ld(t0, t0, 0) - %shli(t1, a0, 5) - %add(t0, t0, t1) - %ld(a0, t0, 16) - %ret - -# sym_set_global(idx=a0, val=a1). Leaf. -:sym_set_global - %la(t0, &symtab_buf_ptr) - %ld(t0, t0, 0) - %shli(t1, a0, 5) - %add(t0, t0, t1) - %st(a1, t0, 16) - %ret - -# ========================================================================= # Reader -- parse_one over readbuf with a single byte cursor # ========================================================================= # @@ -555,47 +250,47 @@ # The reader is called recursively from parse_list, so every state goes # through frame slots, not s-registers. -# Skip whitespace (ASCII 32, 9, 10, 13) and `;`-to-LF comments. Leaf. +# Skip whitespace (ASCII 32, 9, 10, 13) and `;`-to-LF comments. Returns +# a0 = 1 if readbuf_pos >= readbuf_len after skipping (caller hit EOF), +# else 0. Leaf. :skip_ws %scope skip_ws - %la(t2, &readbuf_pos) - %ld(t0, t2, 0) - %la(t1, &readbuf_len) - %ld(t1, t1, 0) + %lda_global(t0, t2, &readbuf_pos) + %ld_global(t1, &readbuf_len) ::loop - %beq(t0, t1, &::done) - %readbuf_byte(a0, t0) - %is_ws_branch(a1, a0, &::step) - %addi(a1, a0, -59) ; ';' - %beqz(a1, &::comment) - %b(&::done) - ::comment - # Consume up to and including the next LF, or to EOF. - %addi(t0, t0, 1) - %beq(t0, t1, &::done) - %readbuf_byte(a0, t0) - %addi(a1, a0, -10) - %bnez(a1, &::comment) - %addi(t0, t0, 1) - %b(&::loop) - ::step - %addi(t0, t0, 1) - %b(&::loop) + %beq(t0, t1, &::done) + %readbuf_byte(a0, t0) + %is_ws_branch(a1, a0, &::step) + %addi(a1, a0, -59) ; ';' + %beqz(a1, &::comment) + %b(&::done) + ::comment + # Consume up to and including the next LF, or to EOF. + %addi(t0, t0, 1) + %beq(t0, t1, &::done) + %readbuf_byte(a0, t0) + %addi(a1, a0, -10) + %bnez(a1, &::comment) + %addi(t0, t0, 1) + %b(&::loop) + ::step + %addi(t0, t0, 1) + %b(&::loop) ::done %st(t0, t2, 0) + %li(a0, 1) + %beq(t0, t1, &::ret) + %li(a0, 0) + ::ret %ret %endscope # parse_one() -> tagged value in a0 %fn(parse_one, 0, { %call(&skip_ws) + %bnez(a0, &::eof) - %la(t0, &readbuf_pos) - %ld(t0, t0, 0) - %la(t1, &readbuf_len) - %ld(t1, t1, 0) - %beq(t0, t1, &::eof) - + %ld_global(t0, &readbuf_pos) %readbuf_byte(a0, t0) %addi(a1, a0, -40) @@ -615,8 +310,7 @@ ::lparen # Consume '(' and read items until ')'. - %la(t0, &readbuf_pos) - %ld(t1, t0, 0) + %lda_global(t1, t0, &readbuf_pos) %addi(t1, t1, 1) %st(t1, t0, 0) %tail(&parse_list) @@ -627,19 +321,16 @@ ::string # Consume opening '"' and tail to parse_string. parse_string scans # through the matching '"' (consuming it) and returns a tagged bv. - %la(t0, &readbuf_pos) - %ld(t1, t0, 0) + %lda_global(t1, t0, &readbuf_pos) %addi(t1, t1, 1) %st(t1, t0, 0) %tail(&parse_string) ::hash # Consume '#' plus its type byte; dispatch on the type byte. - %la(t2, &readbuf_pos) - %ld(t0, t2, 0) + %lda_global(t0, t2, &readbuf_pos) %addi(t0, t0, 1) - %la(t1, &readbuf_len) - %ld(t1, t1, 0) + %ld_global(t1, &readbuf_len) %beq(t0, t1, &::eof) %readbuf_byte(a0, t0) %addi(t0, t0, 1) @@ -671,20 +362,19 @@ # then parse_hex over the slice (with optional leading '-'). %mov(a3, t0) ::hex_scan - %beq(t0, t1, &::hex_end) - %readbuf_byte(a0, t0) - %is_ws_branch(a1, a0, &::hex_end) - %addi(a1, a0, -40) - %beqz(a1, &::hex_end) - %addi(a1, a0, -41) - %beqz(a1, &::hex_end) - %addi(t0, t0, 1) - %b(&::hex_scan) + %beq(t0, t1, &::hex_end) + %readbuf_byte(a0, t0) + %is_ws_branch(a1, a0, &::hex_end) + %addi(a1, a0, -40) + %beqz(a1, &::hex_end) + %addi(a1, a0, -41) + %beqz(a1, &::hex_end) + %addi(t0, t0, 1) + %b(&::hex_scan) ::hex_end - %la(t2, &readbuf_pos) - %st(t0, t2, 0) - %la(a0, &readbuf_buf_ptr) - %ld(a0, a0, 0) + + %st_global(t0, &readbuf_pos, t2) + %ld_global(a0, &readbuf_buf_ptr) %add(a0, a0, a3) %sub(a1, t0, a3) %lb(t2, a0, 0) @@ -705,15 +395,13 @@ ::quote # Consume the leading '\''; recurse into parse_one for the datum; # then build (quote <datum>). - %la(t2, &readbuf_pos) - %ld(t0, t2, 0) + %lda_global(t0, t2, &readbuf_pos) %addi(t0, t0, 1) %st(t0, t2, 0) %call(&parse_one) %li(a1, %imm_val(%IMM.NIL)) %call(&cons) - %la(t0, &sym_quote) - %ld(t0, t0, 0) + %ld_global(t0, &sym_quote) %mov(a1, a0) %mov(a0, t0) %tail(&cons) @@ -725,15 +413,13 @@ # quasiquote evaluator. Outside a pmatch pattern this list reaches # eval as an application of the (unbound) `unquote` and dies through # the standard unbound-variable path. - %la(t2, &readbuf_pos) - %ld(t0, t2, 0) + %lda_global(t0, t2, &readbuf_pos) %addi(t0, t0, 1) %st(t0, t2, 0) %call(&parse_one) %li(a1, %imm_val(%IMM.NIL)) %call(&cons) - %la(t0, &sym_unquote) - %ld(t0, t0, 0) + %ld_global(t0, &sym_unquote) %mov(a1, a0) %mov(a0, t0) %tail(&cons) @@ -746,10 +432,8 @@ ::u8_lit # Cursor is past '#u'. Demand '8' then '('; consume both and tail to # parse_u8_body, which reads the element list and packs it into a bv. - %la(t2, &readbuf_pos) - %ld(t0, t2, 0) - %la(t1, &readbuf_len) - %ld(t1, t1, 0) + %lda_global(t0, t2, &readbuf_pos) + %ld_global(t1, &readbuf_len) %beq(t0, t1, &::u8_bad) %readbuf_byte(a0, t0) %addi(a1, a0, -56) ; '8' @@ -776,18 +460,17 @@ # Frame: 16 bytes # +0 head (NIL until first item) # +8 tail (most recent cons; set-cdr! target) -%fn(parse_list, 16, { +%struct PARSE_LIST_LOCALS { head tail } # .SIZE = 16 +%fn(parse_list, %PARSE_LIST_LOCALS.SIZE, { %li(t0, %imm_val(%IMM.NIL)) - %st(t0, sp, 0) - %st(t0, sp, 8) + %st(t0, sp, %PARSE_LIST_LOCALS.head) + %st(t0, sp, %PARSE_LIST_LOCALS.tail) ::loop %call(&skip_ws) - %la(t0, &readbuf_pos) - %ld(t0, t0, 0) - %la(t1, &readbuf_len) - %ld(t1, t1, 0) - %beq(t0, t1, &::eof) + %bnez(a0, &::eof) + %ld_global(t0, &readbuf_pos) + %ld_global(t1, &readbuf_len) %readbuf_byte(a0, t0) %addi(a1, a0, -41) @@ -813,52 +496,46 @@ %call(&cons) # If head is NIL, both head and tail = new cons; else set-cdr! tail = new. - %ld(t0, sp, 0) + %ld(t0, sp, %PARSE_LIST_LOCALS.head) %li(t1, %imm_val(%IMM.NIL)) %bne(t0, t1, &::link) - %st(a0, sp, 0) - %st(a0, sp, 8) + %st(a0, sp, %PARSE_LIST_LOCALS.head) + %st(a0, sp, %PARSE_LIST_LOCALS.tail) %b(&::loop) ::link - %ld(t0, sp, 8) + %ld(t0, sp, %PARSE_LIST_LOCALS.tail) # set-cdr! tail = a0 -> store a0 at [tail + 7] (raw + 8) - %st(a0, t0, 7) - %st(a0, sp, 8) + %set_cdr(a0, t0) + %st(a0, sp, %PARSE_LIST_LOCALS.tail) %b(&::loop) ::do_dot # Consume the '.', read one datum, splice it in as the cdr of the # tail cons. Then expect a closing ')' (with optional ws). - %la(t1, &readbuf_pos) - %ld(t0, t1, 0) + %lda_global(t0, t1, &readbuf_pos) %addi(t0, t0, 1) %st(t0, t1, 0) %call(&parse_one) - %ld(t0, sp, 8) - %st(a0, t0, 7) + %ld(t0, sp, %PARSE_LIST_LOCALS.tail) + %set_cdr(a0, t0) %call(&skip_ws) - %la(t0, &readbuf_pos) - %ld(t0, t0, 0) - %la(t1, &readbuf_len) - %ld(t1, t1, 0) - %beq(t0, t1, &::eof) + %bnez(a0, &::eof) + %lda_global(t0, t1, &readbuf_pos) %readbuf_byte(a0, t0) %addi(a1, a0, -41) %bnez(a1, &::eof) %addi(t0, t0, 1) - %la(t1, &readbuf_pos) %st(t0, t1, 0) - %ld(a0, sp, 0) + %ld(a0, sp, %PARSE_LIST_LOCALS.head) %eret ::close # Consume ')' and return head. - %la(t0, &readbuf_pos) - %ld(t1, t0, 0) + %lda_global(t1, t0, &readbuf_pos) %addi(t1, t1, 1) %st(t1, t0, 0) - %ld(a0, sp, 0) + %ld(a0, sp, %PARSE_LIST_LOCALS.head) %eret ::eof @@ -873,27 +550,28 @@ # Frame: 16 bytes # +0 list head (cursor during fill pass) # +8 result bv -%fn(parse_u8_body, 16, { +%struct PARSE_U8_BODY_LOCALS { list result } # .SIZE = 16 +%fn(parse_u8_body, %PARSE_U8_BODY_LOCALS.SIZE, { %call(&parse_list) - %st(a0, sp, 0) + %st(a0, sp, %PARSE_U8_BODY_LOCALS.list) %call(&list_length) ; clobbers a0 -> count %call(&bv_alloc) ; a0 = bv - %st(a0, sp, 8) + %st(a0, sp, %PARSE_U8_BODY_LOCALS.result) - %ld(t0, a0, 5) ; data ptr - %ld(a0, sp, 0) ; list cursor + %heap_ld(t0, a0, %BV.data) + %ld(a0, sp, %PARSE_U8_BODY_LOCALS.list) ; list cursor ::loop - %if_nil(t1, a0, &::done) - %car(t1, a0) - %sari(t1, t1, 3) ; untag fixnum -> raw byte - %sb(t1, t0, 0) - %addi(t0, t0, 1) - %cdr(a0, a0) - %b(&::loop) + %if_nil(t1, a0, &::done) + %car(t1, a0) + %sari(t1, t1, 3) ; untag fixnum -> raw byte + %sb(t1, t0, 0) + %addi(t0, t0, 1) + %cdr(a0, a0) + %b(&::loop) ::done - %ld(a0, sp, 8) + %ld(a0, sp, %PARSE_U8_BODY_LOCALS.result) }) # is_ident_byte(c=a0) -> a1 (1 if c is a valid identifier byte, else 0). @@ -963,13 +641,12 @@ # +0 start cursor (byte offset) # +8 end cursor (byte offset) # +16 cursor (scratch slot for the symbol-validation loop) -%fn(parse_atom, 24, { - %la(t0, &readbuf_pos) - %ld(t1, t0, 0) - %st(t1, sp, 0) +%struct PARSE_ATOM_LOCALS { start end cursor } # .SIZE = 24 +%fn(parse_atom, %PARSE_ATOM_LOCALS.SIZE, { + %lda_global(t1, t0, &readbuf_pos) + %st(t1, sp, %PARSE_ATOM_LOCALS.start) - %la(t2, &readbuf_len) - %ld(t2, t2, 0) + %ld_global(t2, &readbuf_len) ::scan %beq(t1, t2, &::end) @@ -986,14 +663,12 @@ %b(&::scan) ::end - %st(t1, sp, 8) - %la(t0, &readbuf_pos) + %st(t1, sp, %PARSE_ATOM_LOCALS.end) %st(t1, t0, 0) # Dispatch on the first byte. - %ld(t0, sp, 0) - %la(a0, &readbuf_buf_ptr) - %ld(a0, a0, 0) + %ld(t0, sp, %PARSE_ATOM_LOCALS.start) + %ld_global(a0, &readbuf_buf_ptr) %add(a0, a0, t0) %lb(t1, a0, 0) @@ -1009,7 +684,7 @@ %beqz(a1, &::sign) %b(&::is_sym) ::sign - %ld(t2, sp, 8) + %ld(t2, sp, %PARSE_ATOM_LOCALS.end) %addi(t0, t0, 1) %beq(t0, t2, &::is_sym) %readbuf_byte(a0, t0) @@ -1019,38 +694,36 @@ ::is_sym # Validate every byte; abort on the first non-ident byte. - %ld(t0, sp, 0) - %st(t0, sp, 16) + %ld(t0, sp, %PARSE_ATOM_LOCALS.start) + %st(t0, sp, %PARSE_ATOM_LOCALS.cursor) ::sym_loop - %ld(t0, sp, 16) - %ld(t1, sp, 8) + %ld(t0, sp, %PARSE_ATOM_LOCALS.cursor) + %ld(t1, sp, %PARSE_ATOM_LOCALS.end) %beq(t0, t1, &::sym_intern) %readbuf_byte(a0, t0) %call(&is_ident_byte) %beqz(a1, &::sym_bad) - %ld(t0, sp, 16) + %ld(t0, sp, %PARSE_ATOM_LOCALS.cursor) %addi(t0, t0, 1) - %st(t0, sp, 16) + %st(t0, sp, %PARSE_ATOM_LOCALS.cursor) %b(&::sym_loop) ::sym_bad %die(msg_bad_ident) ::sym_intern - %ld(a0, sp, 0) - %la(t0, &readbuf_buf_ptr) - %ld(t0, t0, 0) + %ld(a0, sp, %PARSE_ATOM_LOCALS.start) + %ld_global(t0, &readbuf_buf_ptr) %add(a0, t0, a0) - %ld(t1, sp, 8) - %ld(t2, sp, 0) + %ld(t1, sp, %PARSE_ATOM_LOCALS.end) + %ld(t2, sp, %PARSE_ATOM_LOCALS.start) %sub(a1, t1, t2) %tail(&intern) ::is_int - %ld(t0, sp, 0) ; start_off - %ld(t1, sp, 8) ; end_off - %la(a0, &readbuf_buf_ptr) - %ld(a0, a0, 0) + %ld(t0, sp, %PARSE_ATOM_LOCALS.start) ; start_off + %ld(t1, sp, %PARSE_ATOM_LOCALS.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 %call(&parse_dec) ; -> (a0=value, a1=ok) @@ -1072,65 +745,64 @@ # +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) -%fn(parse_string, 32, { - %la(t0, &readbuf_pos) - %ld(t1, t0, 0) - %st(t1, sp, 0) +%struct PARSE_STRING_LOCALS { start end bv spill } # .SIZE = 32 +%fn(parse_string, %PARSE_STRING_LOCALS.SIZE, { + %ld_global(t1, &readbuf_pos) + %st(t1, sp, %PARSE_STRING_LOCALS.start) - %la(t2, &readbuf_len) - %ld(t2, t2, 0) + %ld_global(t2, &readbuf_len) %li(a0, 0) ::scan - %beq(t1, t2, &::eof) - %readbuf_byte(a3, t1) - %addi(a1, a3, -34) ; '"' - %beqz(a1, &::scan_done) - %addi(a1, a3, -92) ; '\\' - %beqz(a1, &::scan_esc) - %addi(t1, t1, 1) - %addi(a0, a0, 1) - %b(&::scan) - - ::scan_esc - # Backslash plus the next byte yield one decoded byte. \xHEX; runs - # until the terminating ';' (validated in pass 2); every other escape - # is exactly two source bytes. - %addi(t1, t1, 1) - %beq(t1, t2, &::eof) - %readbuf_byte(a3, t1) - %addi(a1, a3, -120) ; 'x' - %beqz(a1, &::scan_hex) - %addi(t1, t1, 1) - %addi(a0, a0, 1) - %b(&::scan) - - ::scan_hex - # Skip past 'x' and scan to the terminating ';'. EOF before ';' - # falls into the unterminated-string path below, matching how an - # unterminated body is reported. - %addi(t1, t1, 1) - ::scan_hex_loop - %beq(t1, t2, &::eof) - %readbuf_byte(a3, t1) - %addi(a1, a3, -59) ; ';' - %beqz(a1, &::scan_hex_done) - %addi(t1, t1, 1) - %b(&::scan_hex_loop) - ::scan_hex_done - %addi(t1, t1, 1) ; consume ';' - %addi(a0, a0, 1) ; +1 output byte - %b(&::scan) - + %beq(t1, t2, &::eof) + %readbuf_byte(a3, t1) + %addi(a1, a3, -34) ; '"' + %beqz(a1, &::scan_done) + %addi(a1, a3, -92) ; '\\' + %beqz(a1, &::scan_esc) + %addi(t1, t1, 1) + %addi(a0, a0, 1) + %b(&::scan) + + ::scan_esc + # Backslash plus the next byte yield one decoded byte. \xHEX; runs + # until the terminating ';' (validated in pass 2); every other escape + # is exactly two source bytes. + %addi(t1, t1, 1) + %beq(t1, t2, &::eof) + %readbuf_byte(a3, t1) + %addi(a1, a3, -120) ; 'x' + %beqz(a1, &::scan_hex) + %addi(t1, t1, 1) + %addi(a0, a0, 1) + %b(&::scan) + + ::scan_hex + # Skip past 'x' and scan to the terminating ';'. EOF before ';' + # falls into the unterminated-string path below, matching how an + # unterminated body is reported. + %addi(t1, t1, 1) + ::scan_hex_loop + %beq(t1, t2, &::eof) + %readbuf_byte(a3, t1) + %addi(a1, a3, -59) ; ';' + %beqz(a1, &::scan_hex_done) + %addi(t1, t1, 1) + %b(&::scan_hex_loop) + ::scan_hex_done + %addi(t1, t1, 1) ; consume ';' + %addi(a0, a0, 1) ; +1 output byte + %b(&::scan) ::scan_done - %st(t1, sp, 8) + + %st(t1, sp, %PARSE_STRING_LOCALS.end) %call(&bv_alloc) - %st(a0, sp, 16) + %st(a0, sp, %PARSE_STRING_LOCALS.bv) # Pass 2: decode into the freshly allocated data buffer. - %ld(t1, sp, 0) ; start - %ld(t2, sp, 8) ; end - %ld(a3, a0, 5) ; data_ptr + %ld(t1, sp, %PARSE_STRING_LOCALS.start) ; start + %ld(t2, sp, %PARSE_STRING_LOCALS.end) ; end + %heap_ld(a3, a0, %BV.data) ::fill %beq(t1, t2, &::fill_done) @@ -1179,25 +851,24 @@ # 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, 0) - %st(a3, sp, 24) - %la(t0, &readbuf_buf_ptr) - %ld(t0, t0, 0) + %st(t1, sp, %PARSE_STRING_LOCALS.start) + %st(a3, sp, %PARSE_STRING_LOCALS.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) %call(&parse_hex) ; -> (a0=value, a1=consumed) %beqz(a1, &::hex_bad) %li(t0, 255) %bltu(t0, a0, &::hex_bad) - %ld(t1, sp, 0) + %ld(t1, sp, %PARSE_STRING_LOCALS.start) %add(t1, t1, a1) ; t1 = position of expected ';' - %ld(t2, sp, 8) + %ld(t2, sp, %PARSE_STRING_LOCALS.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, 24) + %ld(a3, sp, %PARSE_STRING_LOCALS.spill) %sb(a0, a3, 0) %addi(a3, a3, 1) %b(&::fill) @@ -1207,9 +878,8 @@ ::fill_done %addi(t1, t1, 1) ; consume closing '"' - %la(t0, &readbuf_pos) - %st(t1, t0, 0) - %ld(a0, sp, 16) + %st_global(t1, &readbuf_pos, t0) + %ld(a0, sp, %PARSE_STRING_LOCALS.bv) %eret ::eof @@ -1225,13 +895,12 @@ # Frame: 16 bytes # +0 start cursor # +8 end cursor -%fn(parse_char, 16, { - %la(t0, &readbuf_pos) - %ld(t1, t0, 0) - %st(t1, sp, 0) +%struct PARSE_CHAR_LOCALS { start end } # .SIZE = 16 +%fn(parse_char, %PARSE_CHAR_LOCALS.SIZE, { + %lda_global(t1, t0, &readbuf_pos) + %st(t1, sp, %PARSE_CHAR_LOCALS.start) - %la(t2, &readbuf_len) - %ld(t2, t2, 0) + %ld_global(t2, &readbuf_len) %beq(t1, t2, &::short) @@ -1251,19 +920,17 @@ %b(&::scan) ::scan_done - %st(t1, sp, 8) - %la(t0, &readbuf_pos) + %st(t1, sp, %PARSE_CHAR_LOCALS.end) %st(t1, t0, 0) - %ld(t0, sp, 0) - %ld(t1, sp, 8) + %ld(t0, sp, %PARSE_CHAR_LOCALS.start) + %ld(t1, sp, %PARSE_CHAR_LOCALS.end) %sub(a2, t1, t0) ; length %li(a3, 1) %beq(a2, a3, &::single) - %la(t2, &readbuf_buf_ptr) - %ld(t2, t2, 0) + %ld_global(t2, &readbuf_buf_ptr) %add(t2, t2, t0) ; t2 = slice ptr # Hex form: first byte is 'x'. @@ -1285,8 +952,7 @@ %b(&::bad) ::single - %la(t2, &readbuf_buf_ptr) - %ld(t2, t2, 0) + %ld_global(t2, &readbuf_buf_ptr) %add(t2, t2, t0) %lb(a0, t2, 0) %mkfix(a0, a0) @@ -1441,9 +1107,10 @@ # +0 expr # +8 env # +16 fn (head value, while args are being evaluated) -%fn(eval, 32, { - %st(a0, sp, 0) - %st(a1, sp, 8) +%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) %tagof(t0, a0) %li(t1, %TAG.PAIR) @@ -1483,7 +1150,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, 0) + %ld(t0, sp, %EVAL_LOCALS.expr) %car(t0, t0) ; t0 = head %dispatch_form(&sym_quote, &::do_quote) %dispatch_form(&sym_if, &::do_if) @@ -1501,26 +1168,26 @@ %dispatch_form(&sym_pmatch, &::do_pmatch) # head = eval(car(expr), env) - %ld(a0, sp, 0) + %ld(a0, sp, %EVAL_LOCALS.expr) %car(a0, a0) - %ld(a1, sp, 8) + %ld(a1, sp, %EVAL_LOCALS.env) %call(&eval) - %st(a0, sp, 16) + %st(a0, sp, %EVAL_LOCALS.fn) # args = eval_args(cdr(expr), env) - %ld(a0, sp, 0) + %ld(a0, sp, %EVAL_LOCALS.expr) %cdr(a0, a0) - %ld(a1, sp, 8) + %ld(a1, sp, %EVAL_LOCALS.env) %call(&eval_args) # apply(fn, args) -- tail call %mov(a1, a0) - %ld(a0, sp, 16) + %ld(a0, sp, %EVAL_LOCALS.fn) %tail(&apply) ::do_quote # (quote datum) -> car(cdr(expr)) - %ld(a0, sp, 0) + %ld(a0, sp, %EVAL_LOCALS.expr) %cdr(a0, a0) %car(a0, a0) %eret @@ -1564,43 +1231,44 @@ # +8 env # +16 head (NIL until first val is appended) # +24 tail (most recent cell; set-cdr! target) -%fn(eval_args, 32, { - %st(a0, sp, 0) - %st(a1, sp, 8) +%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) %li(t0, %imm_val(%IMM.NIL)) - %st(t0, sp, 16) - %st(t0, sp, 24) + %st(t0, sp, %EVAL_ARGS_LOCALS.head) + %st(t0, sp, %EVAL_ARGS_LOCALS.tail) ::loop - %ld(t0, sp, 0) + %ld(t0, sp, %EVAL_ARGS_LOCALS.args) %if_nil(t1, t0, &::done) # val = eval(car(args), env) %car(a0, t0) - %ld(a1, sp, 8) + %ld(a1, sp, %EVAL_ARGS_LOCALS.env) %call(&eval) # cell = cons(val, NIL); append to head/tail. %li(a1, %imm_val(%IMM.NIL)) %call(&cons) - %ld(t0, sp, 16) + %ld(t0, sp, %EVAL_ARGS_LOCALS.head) %if_nil(t1, t0, &::first) - %ld(t0, sp, 24) - %st(a0, t0, 7) - %st(a0, sp, 24) + %ld(t0, sp, %EVAL_ARGS_LOCALS.tail) + %set_cdr(a0, t0) + %st(a0, sp, %EVAL_ARGS_LOCALS.tail) %b(&::advance) ::first - %st(a0, sp, 16) - %st(a0, sp, 24) + %st(a0, sp, %EVAL_ARGS_LOCALS.head) + %st(a0, sp, %EVAL_ARGS_LOCALS.tail) ::advance %advance_walk(0) %b(&::loop) ::done - %ld(a0, sp, 16) + %ld(a0, sp, %EVAL_ARGS_LOCALS.head) }) # apply(fn=a0, args=a1) -> result (a0) @@ -1608,8 +1276,9 @@ # Frame: 16 bytes # +0 args # +8 body (saved across bind_params for the closure path) -%fn(apply, 16, { - %st(a1, sp, 0) +%struct APPLY_LOCALS { args body } # .SIZE = 16 +%fn(apply, %APPLY_LOCALS.SIZE, { + %st(a1, sp, %APPLY_LOCALS.args) # Only HEAP-tagged values can be applicable. %tagof(t0, a0) @@ -1637,26 +1306,26 @@ # primitive. prim_apply_entry maintains the same contract when it # tail-calls back into apply. %mov(a1, a0) - %ld(t0, a0, 5) - %ld(a0, sp, 0) + %heap_ld(t0, a0, %PRIM.entry_w) + %ld(a0, sp, %APPLY_LOCALS.args) %tailr(t0) ::closure # Closure layout (HEAP-tagged): [hdr][params][body][env] # field offsets from tagged ptr: params=5, body=13, env=21. - %ld(t1, a0, 13) ; body (must survive bind_params) - %st(t1, sp, 8) - %ld(t2, a0, 21) ; captured env - %ld(t0, a0, 5) ; params + %heap_ld(t1, a0, %CLOSURE.body) ; must survive bind_params + %st(t1, sp, %APPLY_LOCALS.body) + %heap_ld(t2, a0, %CLOSURE.env) + %heap_ld(t0, a0, %CLOSURE.params) %mov(a0, t0) ; bind_params(params, args, env) - %ld(a1, sp, 0) + %ld(a1, sp, %APPLY_LOCALS.args) %mov(a2, t2) %call(&bind_params) # eval_body(body, new_env) -- tail call %mov(a1, a0) - %ld(a0, sp, 8) + %ld(a0, sp, %APPLY_LOCALS.body) %tail(&eval_body) }) @@ -1698,9 +1367,10 @@ # Frame: 16 bytes # +0 rest # +8 env -%fn(eval_if, 16, { - %st(a0, sp, 0) - %st(a1, sp, 8) +%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) # val = eval(car(rest), env) %car(a0, a0) @@ -1710,19 +1380,19 @@ %beq(a0, t0, &::else_branch) # then-branch: tail-eval(cadr(rest), env) - %ld(a0, sp, 0) + %ld(a0, sp, %EVAL_IF_LOCALS.rest) %cdr(a0, a0) %car(a0, a0) - %ld(a1, sp, 8) + %ld(a1, sp, %EVAL_IF_LOCALS.env) %tail(&eval) ::else_branch # else-branch: tail-eval(caddr(rest), env) - %ld(a0, sp, 0) + %ld(a0, sp, %EVAL_IF_LOCALS.rest) %cdr(a0, a0) %cdr(a0, a0) %car(a0, a0) - %ld(a1, sp, 8) + %ld(a1, sp, %EVAL_IF_LOCALS.env) %tail(&eval) }) @@ -1734,31 +1404,32 @@ # +0 rest # +8 env # +16 closure ptr (HEAP-tagged) -%fn(eval_lambda, 24, { - %st(a0, sp, 0) - %st(a1, sp, 8) +%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) %li(a0, 32) %li(a1, %HDR.CLOSURE) %call(&alloc_hdr) - %st(a0, sp, 16) + %st(a0, sp, %EVAL_LAMBDA_LOCALS.closure) # closure[params] = car(rest) - %ld(t0, sp, 0) + %ld(t0, sp, %EVAL_LAMBDA_LOCALS.rest) %car(t1, t0) - %ld(t0, sp, 16) - %st(t1, t0, 5) + %ld(t0, sp, %EVAL_LAMBDA_LOCALS.closure) + %heap_st(t1, t0, %CLOSURE.params) # closure[body] = cdr(rest) - %ld(t1, sp, 0) + %ld(t1, sp, %EVAL_LAMBDA_LOCALS.rest) %cdr(t1, t1) - %st(t1, t0, 13) + %heap_st(t1, t0, %CLOSURE.body) # closure[env] = captured env - %ld(t1, sp, 8) - %st(t1, t0, 21) + %ld(t1, sp, %EVAL_LAMBDA_LOCALS.env) + %heap_st(t1, t0, %CLOSURE.env) - %ld(a0, sp, 16) + %ld(a0, sp, %EVAL_LAMBDA_LOCALS.closure) }) # eval_define(rest=a0, env=a1) -> UNSPEC (a0). @@ -1773,9 +1444,10 @@ # Frame: 16 bytes # +0 rest # +8 env -%fn(eval_define, 16, { - %st(a0, sp, 0) - %st(a1, sp, 8) +%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) # If car(rest) is a pair, this is the lambda-sugar form. %car(t0, a0) @@ -1784,13 +1456,13 @@ %beq(t1, t2, &::sugar) # Plain define: value = eval(car(cdr(rest)), env) - %ld(t0, sp, 0) + %ld(t0, sp, %EVAL_DEFINE_LOCALS.rest) %cdr(a0, t0) %car(a0, a0) - %ld(a1, sp, 8) + %ld(a1, sp, %EVAL_DEFINE_LOCALS.env) %call(&eval) - %ld(t0, sp, 0) + %ld(t0, sp, %EVAL_DEFINE_LOCALS.rest) %car(t0, t0) %bind_global_from_t0() %li(a0, %imm_val(%IMM.UNSPEC)) @@ -1798,16 +1470,16 @@ ::sugar # rest = ((name . params) . body); build (params . body) for eval_lambda. - %ld(t0, sp, 0) + %ld(t0, sp, %EVAL_DEFINE_LOCALS.rest) %car(t0, t0) %cdr(a0, t0) ; params - %ld(t0, sp, 0) + %ld(t0, sp, %EVAL_DEFINE_LOCALS.rest) %cdr(a1, t0) ; body %call(&cons) - %ld(a1, sp, 8) + %ld(a1, sp, %EVAL_DEFINE_LOCALS.env) %call(&eval_lambda) - %ld(t0, sp, 0) + %ld(t0, sp, %EVAL_DEFINE_LOCALS.rest) %car(t0, t0) %car(t0, t0) ; name %bind_global_from_t0() @@ -1826,43 +1498,44 @@ # +0 rest (sym . (value-expr . ())) # +8 env # +16 saved value (eval'd value-expr) -%fn(eval_setbang, 24, { - %st(a0, sp, 0) - %st(a1, sp, 8) +%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) # value = eval(cadr(rest), env) %cdr(a0, a0) %car(a0, a0) - %ld(a1, sp, 8) + %ld(a1, sp, %EVAL_SETBANG_LOCALS.env) %call(&eval) - %st(a0, sp, 16) + %st(a0, sp, %EVAL_SETBANG_LOCALS.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, 0) + %ld(t1, sp, %EVAL_SETBANG_LOCALS.rest) %car(t1, t1) ; target sym ::lp - %ld(t2, sp, 8) + %ld(t2, sp, %EVAL_SETBANG_LOCALS.env) %if_nil(t0, t2, &::ms) %car(t0, t2) %car(t0, t0) ; cell sym %beq(t0, t1, &::ht) %cdr(t2, t2) - %st(t2, sp, 8) + %st(t2, sp, %EVAL_SETBANG_LOCALS.env) %b(&::lp) ::ht %car(t0, t2) ; re-fetch binding cell - %ld(a0, sp, 16) - %st(a0, t0, 7) ; mutate cell's cdr + %ld(a0, sp, %EVAL_SETBANG_LOCALS.saved) + %set_cdr(a0, t0) ; mutate cell's cdr %li(a0, %imm_val(%IMM.UNSPEC)) %eret ::ms # Miss: rebind global. - %ld(a0, sp, 16) - %ld(t0, sp, 0) + %ld(a0, sp, %EVAL_SETBANG_LOCALS.saved) + %ld(t0, sp, %EVAL_SETBANG_LOCALS.rest) %car(t0, t0) %bind_global_from_t0() %li(a0, %imm_val(%IMM.UNSPEC)) @@ -1879,61 +1552,60 @@ # +8 env # +16 test value (live across the => eval/cons calls) # +24 proc (live across the => cons call) -%fn(eval_cond, 32, { - %st(a0, sp, 0) - %st(a1, sp, 8) +%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) ::loop - %ld(t0, sp, 0) + %ld(t0, sp, %EVAL_COND_LOCALS.clauses) %if_nil(t1, t0, &::nm) %car(t1, t0) ; clause %car(t2, t1) ; test_expr - %la(a0, &sym_else) - %ld(a0, a0, 0) + %ld_global(a0, &sym_else) %beq(t2, a0, &::dm) %mov(a0, t2) - %ld(a1, sp, 8) + %ld(a1, sp, %EVAL_COND_LOCALS.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, 16) - %ld(t0, sp, 0) + %st(a0, sp, %EVAL_COND_LOCALS.test) + %ld(t0, sp, %EVAL_COND_LOCALS.clauses) %car(t0, t0) %cdr(t0, t0) %if_nil(t1, t0, &::nm) %car(t1, t0) - %la(t2, &sym_arrow) - %ld(t2, t2, 0) + %ld_global(t2, &sym_arrow) %beq(t1, t2, &::ar) %mov(a0, t0) ; regular body - %ld(a1, sp, 8) + %ld(a1, sp, %EVAL_COND_LOCALS.env) %tail(&eval_body) ::ar %cdr(t0, t0) %car(a0, t0) ; proc-expr - %ld(a1, sp, 8) + %ld(a1, sp, %EVAL_COND_LOCALS.env) %call(&eval) - %st(a0, sp, 24) - %ld(a0, sp, 16) + %st(a0, sp, %EVAL_COND_LOCALS.proc) + %ld(a0, sp, %EVAL_COND_LOCALS.test) %li(a1, %imm_val(%IMM.NIL)) %call(&cons) %mov(a1, a0) - %ld(a0, sp, 24) + %ld(a0, sp, %EVAL_COND_LOCALS.proc) %tail(&apply) ::dm - %ld(t0, sp, 0) + %ld(t0, sp, %EVAL_COND_LOCALS.clauses) %car(t0, t0) %cdr(a0, t0) - %ld(a1, sp, 8) + %ld(a1, sp, %EVAL_COND_LOCALS.env) %tail(&eval_body) ::nx @@ -1956,9 +1628,10 @@ # +8 env (original) # +16 walk (bindings, advances) # +24 new_env (built up) -%fn(eval_let, 32, { - %st(a0, sp, 0) - %st(a1, sp, 8) +%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) # Named let? %car(t0, a0) @@ -1966,14 +1639,14 @@ %li(t2, %TAG.SYM) %beq(t1, t2, &::named) - %ld(t0, sp, 0) + %ld(t0, sp, %EVAL_LET_LOCALS.rest) %car(t0, t0) ; bindings - %st(t0, sp, 16) - %ld(t0, sp, 8) - %st(t0, sp, 24) ; new_env = env + %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 ::loop - %ld(t0, sp, 16) + %ld(t0, sp, %EVAL_LET_LOCALS.walk) %if_nil(t1, t0, &::done) %car(t1, t0) ; pair = (name init) @@ -1982,11 +1655,11 @@ # val = eval(init, env_orig) %mov(a0, t2) - %ld(a1, sp, 8) + %ld(a1, sp, %EVAL_LET_LOCALS.env) %call(&eval) # binding = cons(name, val) - %ld(t0, sp, 16) + %ld(t0, sp, %EVAL_LET_LOCALS.walk) %car(t1, t0) %car(t2, t1) %mov(a1, a0) @@ -1994,22 +1667,22 @@ %call(&cons) # new_env = cons(binding, new_env) - %ld(a1, sp, 24) + %ld(a1, sp, %EVAL_LET_LOCALS.new_env) %call(&cons) - %st(a0, sp, 24) + %st(a0, sp, %EVAL_LET_LOCALS.new_env) %advance_walk(16) %b(&::loop) ::done - %ld(a0, sp, 0) + %ld(a0, sp, %EVAL_LET_LOCALS.rest) %cdr(a0, a0) ; body - %ld(a1, sp, 24) + %ld(a1, sp, %EVAL_LET_LOCALS.new_env) %tail(&eval_body) ::named - %ld(a0, sp, 0) - %ld(a1, sp, 8) + %ld(a0, sp, %EVAL_LET_LOCALS.rest) + %ld(a1, sp, %EVAL_LET_LOCALS.env) %tail(&eval_let_named) }) @@ -2018,18 +1691,19 @@ # bindings of the same let* form (left-to-right shadowing). # # Frame: 32 bytes (same layout as eval_let) -%fn(eval_letstar, 32, { - %st(a0, sp, 0) - %st(a1, sp, 8) +%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, 0) + %ld(t0, sp, %EVAL_LETSTAR_LOCALS.rest) %car(t0, t0) - %st(t0, sp, 16) - %ld(t0, sp, 8) - %st(t0, sp, 24) + %st(t0, sp, %EVAL_LETSTAR_LOCALS.walk) + %ld(t0, sp, %EVAL_LETSTAR_LOCALS.env) + %st(t0, sp, %EVAL_LETSTAR_LOCALS.new_env) ::loop - %ld(t0, sp, 16) + %ld(t0, sp, %EVAL_LETSTAR_LOCALS.walk) %if_nil(t1, t0, &::done) %car(t1, t0) @@ -2038,27 +1712,27 @@ # val = eval(init, new_env) %mov(a0, t2) - %ld(a1, sp, 24) + %ld(a1, sp, %EVAL_LETSTAR_LOCALS.new_env) %call(&eval) - %ld(t0, sp, 16) + %ld(t0, sp, %EVAL_LETSTAR_LOCALS.walk) %car(t1, t0) %car(t2, t1) %mov(a1, a0) %mov(a0, t2) %call(&cons) - %ld(a1, sp, 24) + %ld(a1, sp, %EVAL_LETSTAR_LOCALS.new_env) %call(&cons) - %st(a0, sp, 24) + %st(a0, sp, %EVAL_LETSTAR_LOCALS.new_env) %advance_walk(16) %b(&::loop) ::done - %ld(a0, sp, 0) + %ld(a0, sp, %EVAL_LETSTAR_LOCALS.rest) %cdr(a0, a0) - %ld(a1, sp, 24) + %ld(a1, sp, %EVAL_LETSTAR_LOCALS.new_env) %tail(&eval_body) }) @@ -2073,18 +1747,19 @@ # +8 env_orig # +16 walk # +24 new_env -%fn(eval_letrec, 32, { - %st(a0, sp, 0) - %st(a1, sp, 8) +%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, 0) + %ld(t0, sp, %EVAL_LETREC_LOCALS.rest) %car(t0, t0) - %st(t0, sp, 16) - %ld(t0, sp, 8) - %st(t0, sp, 24) + %st(t0, sp, %EVAL_LETREC_LOCALS.walk) + %ld(t0, sp, %EVAL_LETREC_LOCALS.env_orig) + %st(t0, sp, %EVAL_LETREC_LOCALS.new_env) ::phase1 - %ld(t0, sp, 16) + %ld(t0, sp, %EVAL_LETREC_LOCALS.walk) %if_nil(t1, t0, &::p1_done) %car(t1, t0) @@ -2092,34 +1767,34 @@ %mov(a0, t2) %li(a1, %imm_val(%IMM.UNSPEC)) %call(&cons) - %ld(a1, sp, 24) + %ld(a1, sp, %EVAL_LETREC_LOCALS.new_env) %call(&cons) - %st(a0, sp, 24) + %st(a0, sp, %EVAL_LETREC_LOCALS.new_env) %advance_walk(16) %b(&::phase1) ::p1_done - %ld(t0, sp, 0) + %ld(t0, sp, %EVAL_LETREC_LOCALS.rest) %car(t0, t0) - %st(t0, sp, 16) ; reset walk + %st(t0, sp, %EVAL_LETREC_LOCALS.walk) ; reset walk ::phase2 - %ld(t0, sp, 16) + %ld(t0, sp, %EVAL_LETREC_LOCALS.walk) %if_nil(t1, t0, &::p2_done) %car(t1, t0) %cdr(t2, t1) %car(t2, t2) ; init %mov(a0, t2) - %ld(a1, sp, 24) + %ld(a1, sp, %EVAL_LETREC_LOCALS.new_env) %call(&eval) ; val in a0 - %ld(t0, sp, 16) + %ld(t0, sp, %EVAL_LETREC_LOCALS.walk) %car(t1, t0) %car(t2, t1) ; name (in t2) - %ld(t1, sp, 24) + %ld(t1, sp, %EVAL_LETREC_LOCALS.new_env) ::scan %car(a1, t1) %car(a2, a1) @@ -2129,15 +1804,15 @@ ::found %car(a1, t1) - %st(a0, a1, 7) ; set-cdr! binding val + %set_cdr(a0, a1) ; set-cdr! binding val %advance_walk(16) %b(&::phase2) ::p2_done - %ld(a0, sp, 0) + %ld(a0, sp, %EVAL_LETREC_LOCALS.rest) %cdr(a0, a0) - %ld(a1, sp, 24) + %ld(a1, sp, %EVAL_LETREC_LOCALS.new_env) %tail(&eval_body) }) @@ -2149,13 +1824,14 @@ # Frame: 16 bytes # +0 rest # +8 env -%fn(eval_and, 16, { +%struct EVAL_AND_LOCALS { rest env } # .SIZE = 16 +%fn(eval_and, %EVAL_AND_LOCALS.SIZE, { %li(t0, %imm_val(%IMM.TRUE)) %if_nil(t1, a0, &::done_imm) ::loop - %st(a0, sp, 0) - %st(a1, sp, 8) + %st(a0, sp, %EVAL_AND_LOCALS.rest) + %st(a1, sp, %EVAL_AND_LOCALS.env) # If cdr(rest) is NIL, the head is the last form -> tail-eval. %cdr(t0, a0) @@ -2166,15 +1842,15 @@ %call(&eval) %li(t0, %imm_val(%IMM.FALSE)) %beq(a0, t0, &::done) - %ld(a0, sp, 0) + %ld(a0, sp, %EVAL_AND_LOCALS.rest) %cdr(a0, a0) - %ld(a1, sp, 8) + %ld(a1, sp, %EVAL_AND_LOCALS.env) %b(&::loop) ::last - %ld(a0, sp, 0) + %ld(a0, sp, %EVAL_AND_LOCALS.rest) %car(a0, a0) - %ld(a1, sp, 8) + %ld(a1, sp, %EVAL_AND_LOCALS.env) %tail(&eval) ::done @@ -2192,13 +1868,14 @@ # Frame: 16 bytes # +0 rest # +8 env -%fn(eval_or, 16, { +%struct EVAL_OR_LOCALS { rest env } # .SIZE = 16 +%fn(eval_or, %EVAL_OR_LOCALS.SIZE, { %li(t0, %imm_val(%IMM.FALSE)) %if_nil(t1, a0, &::done_imm) ::loop - %st(a0, sp, 0) - %st(a1, sp, 8) + %st(a0, sp, %EVAL_OR_LOCALS.rest) + %st(a1, sp, %EVAL_OR_LOCALS.env) %cdr(t0, a0) %if_nil(t1, t0, &::last) @@ -2207,15 +1884,15 @@ %call(&eval) %li(t0, %imm_val(%IMM.FALSE)) %bne(a0, t0, &::done) - %ld(a0, sp, 0) + %ld(a0, sp, %EVAL_OR_LOCALS.rest) %cdr(a0, a0) - %ld(a1, sp, 8) + %ld(a1, sp, %EVAL_OR_LOCALS.env) %b(&::loop) ::last - %ld(a0, sp, 0) + %ld(a0, sp, %EVAL_OR_LOCALS.rest) %car(a0, a0) - %ld(a1, sp, 8) + %ld(a1, sp, %EVAL_OR_LOCALS.env) %tail(&eval) ::done @@ -2244,40 +1921,40 @@ # +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) -%fn(eval_pmatch, 48, { - %st(a1, sp, 8) +%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) # subject = eval(car(rest), env_outer); clauses = cdr(rest). %mov(t0, a0) %cdr(t1, t0) - %st(t1, sp, 16) + %st(t1, sp, %EVAL_PMATCH_LOCALS.clauses) %car(a0, t0) - %ld(a1, sp, 8) + %ld(a1, sp, %EVAL_PMATCH_LOCALS.env_outer) %call(&eval) - %st(a0, sp, 0) + %st(a0, sp, %EVAL_PMATCH_LOCALS.subject) ::loop - %ld(t0, sp, 16) + %ld(t0, sp, %EVAL_PMATCH_LOCALS.clauses) %if_nil(t1, t0, &::no_match) %car(t1, t0) ; clause %car(t2, t1) ; pat - %la(a3, &sym_else) - %ld(a3, a3, 0) + %ld_global(a3, &sym_else) %beq(t2, a3, &::do_else) # pmatch_match(pat, subject, env_outer) -> (a0=env_ext, a1=ok) %mov(a0, t2) - %ld(a1, sp, 0) - %ld(a2, sp, 8) + %ld(a1, sp, %EVAL_PMATCH_LOCALS.subject) + %ld(a2, sp, %EVAL_PMATCH_LOCALS.env_outer) %call(&pmatch_match) %beqz(a1, &::next) - %st(a0, sp, 24) ; env_ext + %st(a0, sp, %EVAL_PMATCH_LOCALS.env_ext) ; env_ext # tail = cdr(clause) - %ld(t0, sp, 16) + %ld(t0, sp, %EVAL_PMATCH_LOCALS.clauses) %car(t0, t0) %cdr(t0, t0) ; tail = (body...) or ((guard ...) body...) @@ -2291,34 +1968,33 @@ %li(a0, %TAG.PAIR) %bne(t2, a0, &::body_simple) %car(a0, t1) ; head of first form - %la(a1, &sym_guard) - %ld(a1, a1, 0) + %ld_global(a1, &sym_guard) %bne(a0, a1, &::body_simple) # Guard clause. guards = cdr(car(tail)); body = cdr(tail). %cdr(a0, t1) - %st(a0, sp, 32) + %st(a0, sp, %EVAL_PMATCH_LOCALS.guard) %cdr(t0, t0) - %st(t0, sp, 40) + %st(t0, sp, %EVAL_PMATCH_LOCALS.body) ::g_loop - %ld(t0, sp, 32) + %ld(t0, sp, %EVAL_PMATCH_LOCALS.guard) %if_nil(t1, t0, &::body_run) %car(a0, t0) ; guard expr - %ld(a1, sp, 24) ; env_ext + %ld(a1, sp, %EVAL_PMATCH_LOCALS.env_ext) ; env_ext %call(&eval) %li(t0, %imm_val(%IMM.FALSE)) %beq(a0, t0, &::next) - %ld(t0, sp, 32) + %ld(t0, sp, %EVAL_PMATCH_LOCALS.guard) %cdr(t0, t0) - %st(t0, sp, 32) + %st(t0, sp, %EVAL_PMATCH_LOCALS.guard) %b(&::g_loop) ::body_run - %ld(a0, sp, 40) - %ld(a1, sp, 24) + %ld(a0, sp, %EVAL_PMATCH_LOCALS.body) + %ld(a1, sp, %EVAL_PMATCH_LOCALS.env_ext) %tail(&eval_body) ::body_simple @@ -2326,20 +2002,20 @@ # with the extended env; tail position of the matched clause's body # is preserved. %mov(a0, t0) - %ld(a1, sp, 24) + %ld(a1, sp, %EVAL_PMATCH_LOCALS.env_ext) %tail(&eval_body) ::do_else - %ld(t0, sp, 16) + %ld(t0, sp, %EVAL_PMATCH_LOCALS.clauses) %car(t0, t0) %cdr(a0, t0) ; body - %ld(a1, sp, 8) ; env_outer (no bindings introduced) + %ld(a1, sp, %EVAL_PMATCH_LOCALS.env_outer) ; env_outer (no bindings introduced) %tail(&eval_body) ::next - %ld(t0, sp, 16) + %ld(t0, sp, %EVAL_PMATCH_LOCALS.clauses) %cdr(t0, t0) - %st(t0, sp, 16) + %st(t0, sp, %EVAL_PMATCH_LOCALS.clauses) %b(&::loop) ::no_match @@ -2367,10 +2043,11 @@ # +0 pat # +8 subj # +16 env -%fn(pmatch_match, 24, { - %st(a0, sp, 0) - %st(a1, sp, 8) - %st(a2, sp, 16) +%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) %tagof(t0, a0) %li(t1, %TAG.PAIR) @@ -2399,8 +2076,7 @@ ::pair_pat %car(t0, a0) ; phead - %la(t1, &sym_unquote) - %ld(t1, t1, 0) + %ld_global(t1, &sym_unquote) %beq(t0, t1, &::binder) # Structural pair. subj must be a pair too. @@ -2409,25 +2085,25 @@ %bne(t0, t1, &::no) # Recurse on the cars; on success, recurse on the cdrs as a tail call. - %ld(t0, sp, 0) + %ld(t0, sp, %PMATCH_MATCH_LOCALS.pat) %car(a0, t0) - %ld(t0, sp, 8) + %ld(t0, sp, %PMATCH_MATCH_LOCALS.subj) %car(a1, t0) - %ld(a2, sp, 16) + %ld(a2, sp, %PMATCH_MATCH_LOCALS.env) %call(&pmatch_match) %beqz(a1, &::no) %mov(a2, a0) ; env_after_car - %ld(t0, sp, 0) + %ld(t0, sp, %PMATCH_MATCH_LOCALS.pat) %cdr(a0, t0) - %ld(t0, sp, 8) + %ld(t0, sp, %PMATCH_MATCH_LOCALS.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, 0) + %ld(t0, sp, %PMATCH_MATCH_LOCALS.pat) %cdr(t1, t0) ; cdr(pat) %tagof(t0, t1) %li(t2, %TAG.PAIR) @@ -2441,22 +2117,21 @@ %bne(t2, a3, &::bad) # Wildcard? Compare against sym_underscore; if so, no binding. - %la(t1, &sym_underscore) - %ld(t1, t1, 0) + %ld_global(t1, &sym_underscore) %beq(t0, t1, &::ok) # 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, 8) + %ld(a1, sp, %PMATCH_MATCH_LOCALS.subj) %call(&cons) - %ld(a1, sp, 16) + %ld(a1, sp, %PMATCH_MATCH_LOCALS.env) %call(&cons) %li(a1, 1) %eret ::ok - %ld(a0, sp, 16) + %ld(a0, sp, %PMATCH_MATCH_LOCALS.env) %li(a1, 1) %eret @@ -2484,31 +2159,32 @@ # +40 head (current pass's list head — params, then args) # +48 tail (current pass's list tail) # +56 params (saved between passes) -%fn(eval_let_named, 64, { - %st(a0, sp, 0) - %st(a1, sp, 8) +%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) # 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, 16) - %ld(a1, sp, 8) + %st(a0, sp, %EVAL_LET_NAMED_LOCALS.self_binding) + %ld(a1, sp, %EVAL_LET_NAMED_LOCALS.env_orig) %call(&cons) - %st(a0, sp, 24) + %st(a0, sp, %EVAL_LET_NAMED_LOCALS.self_env) # 2. Pass 1: build params list (cdr-tail trick) by walking bindings. %li(t0, %imm_val(%IMM.NIL)) - %st(t0, sp, 40) - %st(t0, sp, 48) - %ld(t0, sp, 0) + %st(t0, sp, %EVAL_LET_NAMED_LOCALS.head) + %st(t0, sp, %EVAL_LET_NAMED_LOCALS.tail) + %ld(t0, sp, %EVAL_LET_NAMED_LOCALS.rest) %cdr(t0, t0) %car(t0, t0) ; bindings - %st(t0, sp, 32) + %st(t0, sp, %EVAL_LET_NAMED_LOCALS.walk) ::p1_loop - %ld(t0, sp, 32) + %ld(t0, sp, %EVAL_LET_NAMED_LOCALS.walk) %if_nil(t1, t0, &::p1_done) %car(t1, t0) @@ -2517,58 +2193,58 @@ %li(a1, %imm_val(%IMM.NIL)) %call(&cons) ; cell = (name . NIL) - %ld(t0, sp, 40) + %ld(t0, sp, %EVAL_LET_NAMED_LOCALS.head) %if_nil(t1, t0, &::p1_first) - %ld(t0, sp, 48) - %st(a0, t0, 7) - %st(a0, sp, 48) + %ld(t0, sp, %EVAL_LET_NAMED_LOCALS.tail) + %set_cdr(a0, t0) + %st(a0, sp, %EVAL_LET_NAMED_LOCALS.tail) %b(&::p1_advance) ::p1_first - %st(a0, sp, 40) - %st(a0, sp, 48) + %st(a0, sp, %EVAL_LET_NAMED_LOCALS.head) + %st(a0, sp, %EVAL_LET_NAMED_LOCALS.tail) ::p1_advance %advance_walk(32) %b(&::p1_loop) ::p1_done - %ld(t0, sp, 40) - %st(t0, sp, 56) ; save params + %ld(t0, sp, %EVAL_LET_NAMED_LOCALS.head) + %st(t0, sp, %EVAL_LET_NAMED_LOCALS.params) ; save params # 3. Pass 2: build args list (eval inits in env_orig). %li(t0, %imm_val(%IMM.NIL)) - %st(t0, sp, 40) - %st(t0, sp, 48) - %ld(t0, sp, 0) + %st(t0, sp, %EVAL_LET_NAMED_LOCALS.head) + %st(t0, sp, %EVAL_LET_NAMED_LOCALS.tail) + %ld(t0, sp, %EVAL_LET_NAMED_LOCALS.rest) %cdr(t0, t0) %car(t0, t0) - %st(t0, sp, 32) + %st(t0, sp, %EVAL_LET_NAMED_LOCALS.walk) ::p2_loop - %ld(t0, sp, 32) + %ld(t0, sp, %EVAL_LET_NAMED_LOCALS.walk) %if_nil(t1, t0, &::p2_done) %car(t1, t0) %cdr(t2, t1) %car(t2, t2) ; init %mov(a0, t2) - %ld(a1, sp, 8) + %ld(a1, sp, %EVAL_LET_NAMED_LOCALS.env_orig) %call(&eval) ; val %li(a1, %imm_val(%IMM.NIL)) %call(&cons) ; cell = (val . NIL) - %ld(t0, sp, 40) + %ld(t0, sp, %EVAL_LET_NAMED_LOCALS.head) %if_nil(t1, t0, &::p2_first) - %ld(t0, sp, 48) - %st(a0, t0, 7) - %st(a0, sp, 48) + %ld(t0, sp, %EVAL_LET_NAMED_LOCALS.tail) + %set_cdr(a0, t0) + %st(a0, sp, %EVAL_LET_NAMED_LOCALS.tail) %b(&::p2_advance) ::p2_first - %st(a0, sp, 40) - %st(a0, sp, 48) + %st(a0, sp, %EVAL_LET_NAMED_LOCALS.head) + %st(a0, sp, %EVAL_LET_NAMED_LOCALS.tail) ::p2_advance %advance_walk(32) @@ -2576,20 +2252,20 @@ ::p2_done # 4. Closure: eval_lambda((params . body), self_env). - %ld(a0, sp, 56) ; params - %ld(t0, sp, 0) + %ld(a0, sp, %EVAL_LET_NAMED_LOCALS.params) ; params + %ld(t0, sp, %EVAL_LET_NAMED_LOCALS.rest) %cdr(t0, t0) %cdr(a1, t0) ; body %call(&cons) - %ld(a1, sp, 24) + %ld(a1, sp, %EVAL_LET_NAMED_LOCALS.self_env) %call(&eval_lambda) # 5. Patch self_binding cdr to closure. - %ld(t0, sp, 16) - %st(a0, t0, 7) + %ld(t0, sp, %EVAL_LET_NAMED_LOCALS.self_binding) + %set_cdr(a0, t0) # 6. apply(closure, args). - %ld(a1, sp, 40) + %ld(a1, sp, %EVAL_LET_NAMED_LOCALS.head) %tail(&apply) }) @@ -2602,13 +2278,14 @@ # +0 params (advanced each iteration) # +8 args (advanced each iteration) # +16 env (extended each iteration) -%fn(bind_params, 24, { - %st(a0, sp, 0) - %st(a1, sp, 8) - %st(a2, sp, 16) +%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) ::loop - %ld(t0, sp, 0) + %ld(t0, sp, %BIND_PARAMS_LOCALS.params) %tagof(t1, t0) %li(t2, %TAG.PAIR) %beq(t1, t2, &::pair) @@ -2618,16 +2295,16 @@ ::pair # binding = cons(car(params), car(args)) - %ld(t0, sp, 0) + %ld(t0, sp, %BIND_PARAMS_LOCALS.params) %car(a0, t0) - %ld(t0, sp, 8) + %ld(t0, sp, %BIND_PARAMS_LOCALS.args) %car(a1, t0) %call(&cons) # env = cons(binding, env) - %ld(a1, sp, 16) + %ld(a1, sp, %BIND_PARAMS_LOCALS.env) %call(&cons) - %st(a0, sp, 16) + %st(a0, sp, %BIND_PARAMS_LOCALS.env) # advance params and args %advance_walk(0) @@ -2636,15 +2313,15 @@ ::rest_bind # binding = cons(params_sym, args_list); env = cons(binding, env) - %ld(a0, sp, 0) - %ld(a1, sp, 8) + %ld(a0, sp, %BIND_PARAMS_LOCALS.params) + %ld(a1, sp, %BIND_PARAMS_LOCALS.args) %call(&cons) - %ld(a1, sp, 16) + %ld(a1, sp, %BIND_PARAMS_LOCALS.env) %call(&cons) - %st(a0, sp, 16) + %st(a0, sp, %BIND_PARAMS_LOCALS.env) ::done - %ld(a0, sp, 16) + %ld(a0, sp, %BIND_PARAMS_LOCALS.env) }) # eval_body(body=a0, env=a1) -> value of last form (a0). @@ -2659,10 +2336,11 @@ # Frame: 16 bytes # +0 body # +8 env -%fn(eval_body, 16, { +%struct EVAL_BODY_LOCALS { body env } # .SIZE = 16 +%fn(eval_body, %EVAL_BODY_LOCALS.SIZE, { ::loop - %st(a0, sp, 0) - %st(a1, sp, 8) + %st(a0, sp, %EVAL_BODY_LOCALS.body) + %st(a1, sp, %EVAL_BODY_LOCALS.env) # Reject internal `define`. Detect (define ...) at the head of any # form before dispatching it to eval. @@ -2671,29 +2349,28 @@ %li(t2, %TAG.PAIR) %bne(t1, t2, &::not_define) %car(t1, t0) ; head sym - %la(t2, &sym_define) - %ld(t2, t2, 0) + %ld_global(t2, &sym_define) %beq(t1, t2, &::internal_define) ::not_define # If cdr(body) is NIL, body's car is the last form. - %ld(a0, sp, 0) + %ld(a0, sp, %EVAL_BODY_LOCALS.body) %cdr(t0, a0) %if_nil(t1, t0, &::last) # Non-last form: eval and discard, advance. %car(a0, a0) - %ld(a1, sp, 8) + %ld(a1, sp, %EVAL_BODY_LOCALS.env) %call(&eval) - %ld(a0, sp, 0) + %ld(a0, sp, %EVAL_BODY_LOCALS.body) %cdr(a0, a0) - %ld(a1, sp, 8) + %ld(a1, sp, %EVAL_BODY_LOCALS.env) %b(&::loop) ::last - %ld(a0, sp, 0) + %ld(a0, sp, %EVAL_BODY_LOCALS.body) %car(a0, a0) - %ld(a1, sp, 8) + %ld(a1, sp, %EVAL_BODY_LOCALS.env) %tail(&eval) ::internal_define @@ -2701,6 +2378,203 @@ }) # ========================================================================= +# Runtime error -- single abort entry point +# ========================================================================= +# +# runtime_error(msg_cstr=a0) -> never returns. Every overflow / bounds / +# unbound / type-failure path lands here so error reporting (and the +# eventual user-facing `error` primitive) only have to be implemented +# once. Today we tail into libp1pp's `panic`, which writes msg + LF to +# stderr and sys_exits 1. +:runtime_error + %tail(&panic) + +# ========================================================================= +# Source loading -- argv[1] -> readbuf, length stored in readbuf_len +# ========================================================================= + +%fn(load_source, 0, { + %ld_global(a1, &readbuf_buf_ptr) + %li(a2, %READBUF_CAP_BYTES) + %call(&read_file) + %bltz(a0, &::fail) + + # If the read filled (or would have filled) the buffer, the source + # is at least cap bytes; refuse rather than silently truncate. + # read_file does a single sys_read so n == cap is the only saturation + # signal we have. We treat n >= cap as overflow defensively. + %li(t0, %READBUF_CAP_BYTES) + %bltu(a0, t0, &::ok) + %die(msg_readbuf_full) + + ::ok + %st_global(a0, &readbuf_len, t0) + %li(a0, 0) + %st_global(a0, &readbuf_pos, t0) + %eret + + ::fail + %die(msg_load_fail) +}) + +# ========================================================================= +# Heap: cons (leaf) and alloc_hdr (leaf) +# ========================================================================= +# +# Both are call-free leaves: bump heap_next, write fields, return tagged +# pointer. Each allocation tests (new_next <= heap_end) and aborts via +# runtime_error if the bump would overflow the heap arena. heap_next is +# kept 8-byte aligned so every PAIR/HEAP tag bit is exact: cons always +# bumps by a multiple of 8 (16); alloc_hdr / alloc_bytes round their +# argument up via %alignup(_,_,8,_). + +# cons(car=a0, cdr=a1) -> tagged pair (a0). Allocates 16 bytes. +:cons +%scope cons + %lda_global(t0, t2, &heap_next) + %addi(t1, t0, %PAIR.SIZE) + %ld_global(a3, &heap_end) + %bltu(a3, t1, &::oom) + %st(a0, t0, %PAIR.car) + %st(a1, t0, %PAIR.cdr) + %st(t1, t2, 0) + %addi(a0, t0, %TAG.PAIR) + %ret + ::oom + %die(msg_heap_full) +%endscope + +# alloc_hdr(bytes=a0, hdr_word=a1) -> tagged heap obj (a0) +# Rounds bytes up to a multiple of 8 and writes hdr_word at offset 0. +:alloc_hdr +%scope alloc_hdr + %alignup(a0, a0, 8, t0) + %lda_global(t0, t2, &heap_next) + %add(t1, t0, a0) + %ld_global(a3, &heap_end) + %bltu(a3, t1, &::oom) + %st(t1, t2, 0) + %st(a1, t0, 0) + %addi(a0, t0, 3) + %ret + ::oom + %die(msg_heap_full) +%endscope + +# list_length(list=a0) -> count (a0). Linear walk; clobbers a0 (used as +# the cursor). Callers that need the list afterward must save it first. +:list_length +%scope list_length + %li(t0, 0) + ::loop + %if_nil(t1, a0, &::done) + %addi(t0, t0, 1) + %cdr(a0, a0) + %b(&::loop) + ::done + %mov(a0, t0) + %ret +%endscope + +# ========================================================================= +# 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) + + %li(t0, 0) + %st(t0, sp, %INTERN_LOCALS.idx) + + ::scan + # idx >= count? -> append + %ld(t0, sp, %INTERN_LOCALS.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) + + # entry.name_len == name_len ? + %ld(t2, t1, %SYMENT.name_len) + %ld(a2, sp, %INTERN_LOCALS.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) + %call(&memcmp) + %beqz(a0, &::found) + + ::next + %ld(t0, sp, %INTERN_LOCALS.idx) + %addi(t0, t0, 1) + %st(t0, sp, %INTERN_LOCALS.idx) + %b(&::scan) + + ::append + # Bounds check; on overflow exit 5 with a message. + %ld(t0, sp, %INTERN_LOCALS.idx) + %li(t1, %SYMTAB_CAP_SLOTS) + %bltu(t0, t1, &::append_ok) + %die(msg_symtab_full) + + ::append_ok + # 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) + %call(&alloc_bytes) + %ld(a1, sp, %INTERN_LOCALS.name_ptr) + %ld(a2, sp, %INTERN_LOCALS.name_len) + %call(&memcpy) ; returns dst in a0 = stable copy + + %ld(t0, sp, %INTERN_LOCALS.idx) + %symtab_entry(t1, t0, t2) + %st(a0, t1, %SYMENT.name_ptr) ; stable copy + %ld(a0, sp, %INTERN_LOCALS.name_len) + %st(a0, t1, %SYMENT.name_len) + %li(a0, %imm_val(%IMM.UNBOUND)) + %st(a0, t1, %SYMENT.global_val) + %li(a0, 0) + %st(a0, t1, %SYMENT.pad) + + # symtab_count = idx + 1 + %addi(a0, t0, 1) + %st_global(a0, &symtab_count, t2) + + # fall through with idx in t0 = sp[16] + + ::found + %ld(t0, sp, %INTERN_LOCALS.idx) + %shli(a0, t0, 3) + %ori(a0, a0, %TAG.SYM) +}) + +# Lookup by sym_idx (untagged, in a0). Returns symtab[idx].global_val in a0. +# Leaf. +:sym_global + %ld_global(t0, &symtab_buf_ptr) + %ld_array(a0, t0, %SYMENT.SIZE, a0, %SYMENT.global_val, t1) + %ret + +# sym_set_global(idx=a0, val=a1). Leaf. +:sym_set_global + %ld_global(t0, &symtab_buf_ptr) + %st_array(a1, t0, %SYMENT.SIZE, a0, %SYMENT.global_val, t1) + %ret + +# ========================================================================= # Primitives # ========================================================================= # @@ -2722,15 +2596,16 @@ # +8 walk (current table cursor) # +16 end (table_end) -%fn(register_primitives, 24, { +%struct REGISTER_PRIMITIVES_LOCALS { prim walk end } # .SIZE = 24 +%fn(register_primitives, %REGISTER_PRIMITIVES_LOCALS.SIZE, { %la(t0, &prim_table) - %st(t0, sp, 8) + %st(t0, sp, %REGISTER_PRIMITIVES_LOCALS.walk) %la(t0, &prim_table_end) - %st(t0, sp, 16) + %st(t0, sp, %REGISTER_PRIMITIVES_LOCALS.end) ::loop - %ld(t0, sp, 8) - %ld(t1, sp, 16) + %ld(t0, sp, %REGISTER_PRIMITIVES_LOCALS.walk) + %ld(t1, sp, %REGISTER_PRIMITIVES_LOCALS.end) %beq(t0, t1, &::done) # alloc_hdr(24, HDR.PRIM) -> HEAP-tagged a0. The third slot (offset 13 @@ -2740,26 +2615,26 @@ %li(a0, 24) %li(a1, %HDR.PRIM) %call(&alloc_hdr) - %st(a0, sp, 0) + %st(a0, sp, %REGISTER_PRIMITIVES_LOCALS.prim) - # Write entry-label into prim's entry slot (raw+8 == tagged+5). - %ld(t0, sp, 8) + # Write entry-label into prim's entry slot. + %ld(t0, sp, %REGISTER_PRIMITIVES_LOCALS.walk) %ld(t1, t0, 16) - %ld(t2, sp, 0) - %st(t1, t2, 5) + %ld(t2, sp, %REGISTER_PRIMITIVES_LOCALS.prim) + %heap_st(t1, t2, %PRIM.entry_w) # Intern surface name; bind global to prim ptr. - %ld(t0, sp, 8) + %ld(t0, sp, %REGISTER_PRIMITIVES_LOCALS.walk) %ld(a0, t0, 0) %ld(a1, t0, 8) %call(&intern) %untag_sym(a0, a0) - %ld(a1, sp, 0) + %ld(a1, sp, %REGISTER_PRIMITIVES_LOCALS.prim) %call(&sym_set_global) - %ld(t0, sp, 8) + %ld(t0, sp, %REGISTER_PRIMITIVES_LOCALS.walk) %addi(t0, t0, 24) - %st(t0, sp, 8) + %st(t0, sp, %REGISTER_PRIMITIVES_LOCALS.walk) %b(&::loop) ::done @@ -2847,13 +2722,13 @@ # No type check (matches car/cdr's lax stance); both return UNSPEC. :prim_set_car_entry %args2(t0, t1, a0) - %st(t1, t0, -1) + %set_car(t1, t0) %li(a0, %imm_val(%IMM.UNSPEC)) %ret :prim_set_cdr_entry %args2(t0, t1, a0) - %st(t1, t0, 7) + %set_cdr(t1, t0) %li(a0, %imm_val(%IMM.UNSPEC)) %ret @@ -2898,53 +2773,54 @@ # +8 total length (raw) # +16 result bv # +24 write offset (raw, into result.data) -%fn(prim_bv_append_entry, 32, { - %st(a0, sp, 0) +%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) %li(t0, 0) %mov(t1, a0) ::sum_loop %if_nil(t2, t1, &::sum_done) %car(t2, t1) - %ld(a0, t2, -3) + %heap_ld(a0, t2, %BV.hdr) %shri(a0, a0, 8) %add(t0, t0, a0) %cdr(t1, t1) %b(&::sum_loop) ::sum_done - %st(t0, sp, 8) + %st(t0, sp, %PRIM_BV_APPEND_ENTRY_LOCALS.total) %mov(a0, t0) %call(&bv_alloc) - %st(a0, sp, 16) + %st(a0, sp, %PRIM_BV_APPEND_ENTRY_LOCALS.result) %li(t0, 0) - %st(t0, sp, 24) + %st(t0, sp, %PRIM_BV_APPEND_ENTRY_LOCALS.write) ::copy_loop - %ld(t0, sp, 0) + %ld(t0, sp, %PRIM_BV_APPEND_ENTRY_LOCALS.args) %if_nil(t1, t0, &::copy_done) %car(t1, t0) ; src bv - %ld(t2, t1, -3) + %heap_ld(t2, t1, %BV.hdr) %shri(t2, t2, 8) ; src length - %ld(a0, sp, 16) - %ld(a0, a0, 5) ; result.data - %ld(a3, sp, 24) + %ld(a0, sp, %PRIM_BV_APPEND_ENTRY_LOCALS.result) + %heap_ld(a0, a0, %BV.data) ; result.data + %ld(a3, sp, %PRIM_BV_APPEND_ENTRY_LOCALS.write) %add(a0, a0, a3) ; dst = result.data + offset - %ld(a1, t1, 5) ; src.data + %heap_ld(a1, t1, %BV.data) ; src.data %mov(a2, t2) ; count %add(a3, a3, t2) - %st(a3, sp, 24) + %st(a3, sp, %PRIM_BV_APPEND_ENTRY_LOCALS.write) %cdr(t0, t0) - %st(t0, sp, 0) + %st(t0, sp, %PRIM_BV_APPEND_ENTRY_LOCALS.args) %call(&memcpy) %b(&::copy_loop) ::copy_done - %ld(a0, sp, 16) + %ld(a0, sp, %PRIM_BV_APPEND_ENTRY_LOCALS.result) }) # (string->symbol bv) -- intern the bytes and return the SYM-tagged @@ -2952,8 +2828,8 @@ # append, so the bv's data buffer is safe to relocate afterwards. :prim_string_to_symbol_entry %car(t0, a0) - %ld(a0, t0, 5) ; data ptr - %ld(a1, t0, -3) + %heap_ld(a0, t0, %BV.data) + %heap_ld(a1, t0, %BV.hdr) %shri(a1, a1, 8) ; length %b(&intern) @@ -2961,21 +2837,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. -%fn(prim_symbol_to_string_entry, 24, { +%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, { %car(a0, a0) %sari(a0, a0, 3) ; raw sym idx %call(&sym_name) ; -> ptr (a0), len (a1) - %st(a0, sp, 0) - %st(a1, sp, 8) + %st(a0, sp, %PRIM_SYMBOL_TO_STRING_ENTRY_LOCALS.ptr) + %st(a1, sp, %PRIM_SYMBOL_TO_STRING_ENTRY_LOCALS.len) %mov(a0, a1) %call(&bv_alloc) ; tagged bv in a0 - %st(a0, sp, 16) - %ld(a1, sp, 0) ; src ptr - %ld(a2, sp, 8) ; len - %ld(t0, a0, 5) ; dst = bv.data + %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 + %heap_ld(t0, a0, %BV.data) ; dst = bv.data %mov(a0, t0) %call(&memcpy) - %ld(a0, sp, 16) + %ld(a0, sp, %PRIM_SYMBOL_TO_STRING_ENTRY_LOCALS.bv) }) # (number->string n [radix]) -- decimal repr in a fresh bv. The radix @@ -2984,13 +2861,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. -%fn(prim_number_to_string_entry, 16, { +%struct PRIM_NUMBER_TO_STRING_ENTRY_LOCALS { value pad } # .SIZE = 16 +%fn(prim_number_to_string_entry, %PRIM_NUMBER_TO_STRING_ENTRY_LOCALS.SIZE, { %car(t0, a0) %sari(t0, t0, 3) ; raw value - %st(t0, sp, 0) + %st(t0, sp, %PRIM_NUMBER_TO_STRING_ENTRY_LOCALS.value) %li(a0, 0) %call(&bv_alloc) - %ld(a1, sp, 0) + %ld(a1, sp, %PRIM_NUMBER_TO_STRING_ENTRY_LOCALS.value) %tail(&bv_putint) }) @@ -3007,8 +2885,8 @@ %li(t1, %HDR.BV) %bne(t0, t1, &::fail) - %ld(t0, a0, 5) ; data ptr - %ld(t1, a0, -3) + %heap_ld(t0, a0, %BV.data) + %heap_ld(t1, a0, %BV.hdr) %shri(t1, t1, 8) ; length %mov(a0, t0) %mov(a1, t1) @@ -3262,11 +3140,9 @@ :alloc_bytes %scope alloc_bytes %alignup(a0, a0, 8, t0) - %la(t2, &heap_next) - %ld(t1, t2, 0) + %lda_global(t1, t2, &heap_next) %add(t0, t1, a0) - %la(a3, &heap_end) - %ld(a3, a3, 0) + %ld_global(a3, &heap_end) %bltu(a3, t0, &::oom) %st(t0, t2, 0) %mov(a0, t1) @@ -3299,23 +3175,24 @@ # +0 raw_len # +8 capacity # +16 data_ptr (raw) -%fn(bv_alloc, 24, { - %st(a0, sp, 0) +%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) %call(&bv_capacity_for) - %st(a0, sp, 8) + %st(a0, sp, %BV_ALLOC_LOCALS.capacity) %call(&alloc_bytes) - %st(a0, sp, 16) + %st(a0, sp, %BV_ALLOC_LOCALS.data_ptr) - %ld(a1, sp, 0) + %ld(a1, sp, %BV_ALLOC_LOCALS.raw_len) %shli(a1, a1, 8) ; hdr = (raw_len << 8) | HDR.BV (BV == 0) %li(a0, 24) %call(&alloc_hdr) - %ld(t0, sp, 16) - %st(t0, a0, 5) - %ld(t1, sp, 8) - %st(t1, a0, 13) + %ld(t0, sp, %BV_ALLOC_LOCALS.data_ptr) + %heap_st(t0, a0, %BV.data) + %ld(t1, sp, %BV_ALLOC_LOCALS.capacity) + %st(t1, a0, 13) ; bv.cap (raw offset 16; not in BV struct) }) # bv_grow(bv=a0, min_cap=a1) -> bv (a0). Doubles capacity until ≥ min_cap; @@ -3328,49 +3205,51 @@ # +8 min_cap (input) / new_cap (during loop) # +16 new_data_ptr # +24 raw length -%fn(bv_grow, 32, { - %st(a0, sp, 0) - %st(a1, sp, 8) +%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) - %ld(t0, a0, 13) + %ld(t0, a0, 13) ; bv.cap (raw offset 16; not in BV struct) %bltu(t0, a1, &::need) - %ld(a0, sp, 0) + %ld(a0, sp, %BV_GROW_LOCALS.bv) %eret ::need ::loop %shli(t0, t0, 1) - %ld(t1, sp, 8) + %ld(t1, sp, %BV_GROW_LOCALS.min_cap) %bltu(t0, t1, &::loop) - %st(t0, sp, 8) + %st(t0, sp, %BV_GROW_LOCALS.min_cap) %mov(a0, t0) %call(&alloc_bytes) - %st(a0, sp, 16) + %st(a0, sp, %BV_GROW_LOCALS.new_data_ptr) - %ld(t0, sp, 0) - %ld(t1, t0, -3) + %ld(t0, sp, %BV_GROW_LOCALS.bv) + %heap_ld(t1, t0, %BV.hdr) %shri(t1, t1, 8) ; raw length - %st(t1, sp, 24) - %ld(a0, sp, 16) - %ld(a1, t0, 5) ; old data ptr - %ld(a2, sp, 24) + %st(t1, sp, %BV_GROW_LOCALS.raw) + %ld(a0, sp, %BV_GROW_LOCALS.new_data_ptr) + %heap_ld(a1, t0, %BV.data) ; old data ptr + %ld(a2, sp, %BV_GROW_LOCALS.raw) %call(&memcpy) - %ld(t0, sp, 0) - %ld(t1, sp, 16) - %st(t1, t0, 5) - %ld(t1, sp, 8) - %st(t1, t0, 13) - %ld(a0, sp, 0) + %ld(t0, sp, %BV_GROW_LOCALS.bv) + %ld(t1, sp, %BV_GROW_LOCALS.new_data_ptr) + %heap_st(t1, t0, %BV.data) + %ld(t1, sp, %BV_GROW_LOCALS.min_cap) + %st(t1, t0, 13) ; bv.cap (raw offset 16; not in BV struct) + %ld(a0, sp, %BV_GROW_LOCALS.bv) }) # (make-bytevector len) or (make-bytevector len fill) -%fn(prim_make_bytevector_entry, 24, { +%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, 0) + %st(a0, sp, %PRIM_MAKE_BYTEVECTOR_ENTRY_LOCALS.args) %li(t2, 0) %cdr(t0, a0) @@ -3378,19 +3257,19 @@ %car(t0, t0) %sari(t2, t0, 3) ::no_fill - %st(t2, sp, 8) + %st(t2, sp, %PRIM_MAKE_BYTEVECTOR_ENTRY_LOCALS.fill) - %ld(a0, sp, 0) + %ld(a0, sp, %PRIM_MAKE_BYTEVECTOR_ENTRY_LOCALS.args) %car_fix(a0, a0) %bltz(a0, &::bad_len) %call(&bv_alloc) - %st(a0, sp, 16) + %st(a0, sp, %PRIM_MAKE_BYTEVECTOR_ENTRY_LOCALS.wrapper) - %ld(t0, sp, 0) + %ld(t0, sp, %PRIM_MAKE_BYTEVECTOR_ENTRY_LOCALS.args) %car_fix(t0, t0) ; raw_len - %ld(t1, sp, 8) ; fill - %ld(a1, sp, 16) - %ld(t2, a1, 5) ; data_ptr + %ld(t1, sp, %PRIM_MAKE_BYTEVECTOR_ENTRY_LOCALS.fill) ; fill + %ld(a1, sp, %PRIM_MAKE_BYTEVECTOR_ENTRY_LOCALS.wrapper) + %heap_ld(t2, a1, %BV.data) %li(a1, 0) ::fill_loop @@ -3401,7 +3280,7 @@ %b(&::fill_loop) ::fill_done - %ld(a0, sp, 16) + %ld(a0, sp, %PRIM_MAKE_BYTEVECTOR_ENTRY_LOCALS.wrapper) %eret ::bad_len @@ -3410,7 +3289,7 @@ :prim_bv_length_entry %car(t0, a0) - %ld(t1, t0, -3) + %heap_ld(t1, t0, %BV.hdr) %shri(a0, t1, 5) %ret @@ -3419,13 +3298,13 @@ %args2(t0, t1, a0) ; bv, tagged idx %sari(t1, t1, 3) ; raw idx %bltz(t1, &::oob) - %ld(a0, t0, -3) + %heap_ld(a0, t0, %BV.hdr) %shri(a0, a0, 8) ; length %bltu(t1, a0, &::ok) ::oob %die(msg_bv_oob) ::ok - %ld(t2, t0, 5) + %heap_ld(t2, t0, %BV.data) %add(t2, t2, t1) %lb(a0, t2, 0) %mkfix(a0, a0) @@ -3438,13 +3317,13 @@ %sari(t2, t2, 3) ; raw idx %sari(t1, t1, 3) ; raw val %bltz(t2, &::oob) - %ld(a0, t0, -3) + %heap_ld(a0, t0, %BV.hdr) %shri(a0, a0, 8) ; length %bltu(t2, a0, &::ok) ::oob %die(msg_bv_oob) ::ok - %ld(a0, t0, 5) + %heap_ld(a0, t0, %BV.data) %add(a0, a0, t2) %sb(t1, a0, 0) %li(a0, %imm_val(%IMM.UNSPEC)) @@ -3458,11 +3337,12 @@ # +0 args # +8 src tagged # +16 wrapper (saved after bv_alloc) -%fn(prim_bv_copy_entry, 24, { - %st(a0, sp, 0) +%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) %args3(t0, t2, t1, a0) ; src, start, end - %st(t0, sp, 8) + %st(t0, sp, %PRIM_BV_COPY_ENTRY_LOCALS.src) %sari(t2, t2, 3) ; raw start %sari(t1, t1, 3) ; raw end @@ -3470,24 +3350,24 @@ # start is now non-negative); src.length >= end. %bltz(t2, &::oob) %blt(t1, t2, &::oob) - %ld(a0, t0, -3) + %heap_ld(a0, t0, %BV.hdr) %shri(a0, a0, 8) ; src.length %blt(a0, t1, &::oob) %sub(a0, t1, t2) ; count %call(&bv_alloc) - %st(a0, sp, 16) + %st(a0, sp, %PRIM_BV_COPY_ENTRY_LOCALS.wrapper) # Recompute src ptr at start; dst ptr at 0; count from new bv's hdr. - %ld(t0, sp, 0) + %ld(t0, sp, %PRIM_BV_COPY_ENTRY_LOCALS.args) %cdr(t0, t0) %car_fix(t0, t0) ; raw start - %ld(t1, sp, 8) - %ld(t2, t1, 5) + %ld(t1, sp, %PRIM_BV_COPY_ENTRY_LOCALS.src) + %heap_ld(t2, t1, %BV.data) %add(t2, t2, t0) ; src ptr - %ld(a3, sp, 16) - %ld(a2, a3, 5) ; dst ptr - %ld(a1, a3, -3) + %ld(a3, sp, %PRIM_BV_COPY_ENTRY_LOCALS.wrapper) + %heap_ld(a2, a3, %BV.data) ; dst ptr + %heap_ld(a1, a3, %BV.hdr) %shri(a1, a1, 8) ; count ::copy_loop @@ -3500,7 +3380,7 @@ %b(&::copy_loop) ::copy_done - %ld(a0, sp, 16) + %ld(a0, sp, %PRIM_BV_COPY_ENTRY_LOCALS.wrapper) %eret ::oob @@ -3517,58 +3397,59 @@ # +16 src (tagged) # +24 src-start (raw) # +32 src-end (raw) -%fn(prim_bv_copy_bang_entry, 40, { +%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, { %car(t0, a0) - %st(t0, sp, 0) ; dst + %st(t0, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.dst) ; dst %cdr(a0, a0) %car(t0, a0) %sari(t0, t0, 3) - %st(t0, sp, 8) ; dst-start + %st(t0, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.dst_start) ; dst-start %cdr(a0, a0) %car(t0, a0) - %st(t0, sp, 16) ; src + %st(t0, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.src) ; src %cdr(a0, a0) %car(t0, a0) %sari(t0, t0, 3) - %st(t0, sp, 24) ; src-start + %st(t0, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.src_start) ; src-start %cdr(a0, a0) %car(t0, a0) %sari(t0, t0, 3) - %st(t0, sp, 32) ; src-end + %st(t0, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.src_end) ; src-end # src-start >= 0 - %ld(t0, sp, 24) + %ld(t0, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.src_start) %bltz(t0, &::oob) # src-end >= src-start (signed catches negative src-end) - %ld(t1, sp, 32) + %ld(t1, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.src_end) %blt(t1, t0, &::oob) # src-end <= src.length - %ld(t2, sp, 16) - %ld(a0, t2, -3) + %ld(t2, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.src) + %heap_ld(a0, t2, %BV.hdr) %shri(a0, a0, 8) %blt(a0, t1, &::oob) # dst-start >= 0 - %ld(t2, sp, 8) + %ld(t2, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.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, 0) - %ld(t2, t1, -3) + %ld(t1, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.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, 0) - %ld(t0, t0, 5) - %ld(a1, sp, 8) + %ld(t0, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.dst) + %heap_ld(t0, t0, %BV.data) + %ld(a1, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.dst_start) %add(t0, t0, a1) ; dst ptr - %ld(a1, sp, 16) - %ld(a1, a1, 5) - %ld(a2, sp, 24) + %ld(a1, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.src) + %heap_ld(a1, a1, %BV.data) + %ld(a2, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.src_start) %add(a1, a1, a2) ; src ptr - %ld(a3, sp, 32) + %ld(a3, sp, %PRIM_BV_COPY_BANG_ENTRY_LOCALS.src_end) %sub(a3, a3, a2) ; count ::loop @@ -3595,14 +3476,14 @@ # on every backend, so a single %bne is enough for the byte test. :bv_equal_check %scope bv_equal_check - %ld(t0, a0, -3) + %heap_ld(t0, a0, %BV.hdr) %shri(t0, t0, 8) ; len_a - %ld(t1, a1, -3) + %heap_ld(t1, a1, %BV.hdr) %shri(t1, t1, 8) ; len_b %bne(t0, t1, &::false) - %ld(a2, a0, 5) ; data ptr a - %ld(a3, a1, 5) ; data ptr b + %heap_ld(a2, a0, %BV.data) + %heap_ld(a3, a1, %BV.data) ::loop %beqz(t0, &::true) @@ -3655,9 +3536,10 @@ # identity-only). Tail-calls the cdr-side recursion and the BV check. # # Frame: 16 bytes (a, b spilled across each %call). -%fn(equal_recurse, 16, { - %st(a0, sp, 0) - %st(a1, sp, 8) +%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) %beq(a0, a1, &::true) @@ -3672,22 +3554,22 @@ %b(&::false) ::pair - %ld(t0, sp, 0) - %ld(t1, sp, 8) + %ld(t0, sp, %EQUAL_RECURSE_LOCALS.a) + %ld(t1, sp, %EQUAL_RECURSE_LOCALS.b) %car(a0, t0) %car(a1, t1) %call(&equal_recurse) %li(t0, %imm_val(%IMM.FALSE)) %beq(a0, t0, &::done) - %ld(t0, sp, 0) - %ld(t1, sp, 8) + %ld(t0, sp, %EQUAL_RECURSE_LOCALS.a) + %ld(t1, sp, %EQUAL_RECURSE_LOCALS.b) %cdr(a0, t0) %cdr(a1, t1) %tail(&equal_recurse) ::heap - %ld(t0, sp, 0) - %ld(t1, sp, 8) + %ld(t0, sp, %EQUAL_RECURSE_LOCALS.a) + %ld(t1, sp, %EQUAL_RECURSE_LOCALS.b) %hdr_type(t2, t0) %hdr_type(a0, t1) %bne(t2, a0, &::false) ; differing heap classes -> #f @@ -3727,39 +3609,40 @@ # +8 b (rec, tagged) # +16 i (raw counter) # +24 nfields (raw) -%fn(rec_equal_check, 32, { - %st(a0, sp, 0) - %st(a1, sp, 8) +%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) - %ld(t0, a0, 5) ; td_a - %ld(t1, a1, 5) ; td_b + %heap_ld(t0, a0, %REC.td) ; td_a + %heap_ld(t1, a1, %REC.td) ; td_b %bne(t0, t1, &::false) - %ld(t1, t0, 13) ; nfields (raw) - %st(t1, sp, 24) + %heap_ld(t1, t0, %TD.nfields) + %st(t1, sp, %REC_EQUAL_CHECK_LOCALS.nfields) %li(t0, 0) - %st(t0, sp, 16) ; i = 0 + %st(t0, sp, %REC_EQUAL_CHECK_LOCALS.i) ; i = 0 ::loop - %ld(t0, sp, 16) - %ld(t1, sp, 24) + %ld(t0, sp, %REC_EQUAL_CHECK_LOCALS.i) + %ld(t1, sp, %REC_EQUAL_CHECK_LOCALS.nfields) %beq(t0, t1, &::true) %shli(t2, t0, 3) %addi(t2, t2, 13) ; field offset = 13 + 8*i - %ld(t1, sp, 0) + %ld(t1, sp, %REC_EQUAL_CHECK_LOCALS.a) %add(t1, t1, t2) %ld(a0, t1, 0) ; a's field i - %ld(t1, sp, 8) + %ld(t1, sp, %REC_EQUAL_CHECK_LOCALS.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, 16) + %ld(t0, sp, %REC_EQUAL_CHECK_LOCALS.i) %addi(t0, t0, 1) - %st(t0, sp, 16) + %st(t0, sp, %REC_EQUAL_CHECK_LOCALS.i) %b(&::loop) ::true @@ -3791,12 +3674,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. -%fn(prim_apply_entry, 16, { - %st(a0, sp, 0) +%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) %cdr(a0, a0) %call(&apply_build_args) %mov(t0, a0) - %ld(a0, sp, 0) + %ld(a0, sp, %PRIM_APPLY_ENTRY_LOCALS.args) %car(a0, a0) %mov(a1, t0) %tail(&apply) @@ -3813,14 +3697,15 @@ # +0 walk (advances; current cell of rest) # +8 head (NIL until first leading arg appended) # +16 tail (most recent cell; set-cdr! target) -%fn(apply_build_args, 24, { - %st(a0, sp, 0) +%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) %li(t0, %imm_val(%IMM.NIL)) - %st(t0, sp, 8) - %st(t0, sp, 16) + %st(t0, sp, %APPLY_BUILD_ARGS_LOCALS.head) + %st(t0, sp, %APPLY_BUILD_ARGS_LOCALS.tail) ::loop - %ld(t0, sp, 0) + %ld(t0, sp, %APPLY_BUILD_ARGS_LOCALS.walk) %cdr(t1, t0) %if_nil(t2, t1, &::last) @@ -3829,16 +3714,16 @@ %li(a1, %imm_val(%IMM.NIL)) %call(&cons) - %ld(t0, sp, 8) + %ld(t0, sp, %APPLY_BUILD_ARGS_LOCALS.head) %if_nil(t1, t0, &::first) - %ld(t0, sp, 16) - %st(a0, t0, 7) - %st(a0, sp, 16) + %ld(t0, sp, %APPLY_BUILD_ARGS_LOCALS.tail) + %set_cdr(a0, t0) + %st(a0, sp, %APPLY_BUILD_ARGS_LOCALS.tail) %b(&::advance) ::first - %st(a0, sp, 8) - %st(a0, sp, 16) + %st(a0, sp, %APPLY_BUILD_ARGS_LOCALS.head) + %st(a0, sp, %APPLY_BUILD_ARGS_LOCALS.tail) ::advance %advance_walk(0) @@ -3849,11 +3734,11 @@ # args -- return the trailing list directly. Otherwise splice it onto # the tail and return head. %car(a0, t0) - %ld(t1, sp, 8) + %ld(t1, sp, %APPLY_BUILD_ARGS_LOCALS.head) %if_nil(t2, t1, &::done) - %ld(t1, sp, 16) - %st(a0, t1, 7) - %ld(a0, sp, 8) + %ld(t1, sp, %APPLY_BUILD_ARGS_LOCALS.tail) + %set_cdr(a0, t1) + %ld(a0, sp, %APPLY_BUILD_ARGS_LOCALS.head) ::done }) @@ -3867,18 +3752,19 @@ # make_param_prim(entry=a0, data=a1) -> prim (a0). Allocates a 24-byte # PRIM, sets the entry label and data word. -%fn(make_param_prim, 16, { - %st(a0, sp, 0) - %st(a1, sp, 8) +%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) %li(a0, 24) %li(a1, %HDR.PRIM) %call(&alloc_hdr) - %ld(t0, sp, 0) - %st(t0, a0, 5) - %ld(t1, sp, 8) - %st(t1, a0, 13) + %ld(t0, sp, %MAKE_PARAM_PRIM_LOCALS.entry) + %heap_st(t0, a0, %PRIM.entry_w) + %ld(t1, sp, %MAKE_PARAM_PRIM_LOCALS.data) + %heap_st(t1, a0, %PRIM.data) }) # Parameterized PRIM entries used by define-record-type. Each receives @@ -3892,13 +3778,14 @@ # ctor: prim.data = TD (HEAP); args = (f0 f1 ...). Inlines the # %make-record body so we don't have to cons (TD . args) first. -%fn(prim_ctor_entry, 24, { +%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, 0) - %ld(t0, a1, 13) - %st(t0, sp, 8) + %st(a0, sp, %PRIM_CTOR_ENTRY_LOCALS.args) + %heap_ld(t0, a1, %PRIM.data) + %st(t0, sp, %PRIM_CTOR_ENTRY_LOCALS.td) # Count = length(args). %call(&list_length) @@ -3906,12 +3793,12 @@ %addi(a0, a0, 16) %li(a1, %HDR.REC) %call(&alloc_hdr) - %st(a0, sp, 16) + %st(a0, sp, %PRIM_CTOR_ENTRY_LOCALS.record) - %ld(t0, sp, 8) - %st(t0, a0, 5) + %ld(t0, sp, %PRIM_CTOR_ENTRY_LOCALS.td) + %heap_st(t0, a0, %REC.td) - %ld(t0, sp, 0) + %ld(t0, sp, %PRIM_CTOR_ENTRY_LOCALS.args) %addi(t1, a0, 13) ::fill_loop @@ -3923,14 +3810,14 @@ %b(&::fill_loop) ::fill_done - %ld(a0, sp, 16) + %ld(a0, sp, %PRIM_CTOR_ENTRY_LOCALS.record) }) # predicate: prim.data = TD; args = (rec). :prim_predicate_entry %scope prim_predicate %car(t0, a0) - %ld(t1, a1, 13) + %heap_ld(t1, a1, %PRIM.data) %tagof(t2, t0) %li(a0, %imm_val(%IMM.FALSE)) %li(a2, %TAG.HEAP) @@ -3938,7 +3825,7 @@ %hdr_type(t2, t0) %li(a2, %HDR.REC) %bne(t2, a2, &::end) - %ld(t2, t0, 5) + %heap_ld(t2, t0, %REC.td) %bne(t2, t1, &::end) %li(a0, %imm_val(%IMM.TRUE)) ::end @@ -3948,7 +3835,7 @@ # accessor: prim.data = tagged field index; args = (rec). :prim_accessor_entry %car(t0, a0) - %ld(t1, a1, 13) + %heap_ld(t1, a1, %PRIM.data) %addi(t1, t1, 13) %add(t1, t1, t0) %ld(a0, t1, 0) @@ -3960,7 +3847,7 @@ %car(t0, a0) %cdr(t1, a0) %car(t1, t1) - %ld(t2, a1, 13) + %heap_ld(t2, a1, %PRIM.data) %addi(t2, t2, 13) %add(t2, t2, t0) %st(t1, t2, 0) @@ -3981,35 +3868,36 @@ # +24 walk (clauses, advancing) # +32 idx (raw counter) # +40 nfields -%fn(eval_define_record_type, 56, { - %st(a0, sp, 0) - %st(a1, sp, 8) +%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) # clauses = cdddr(rest); count them via list_length. - %ld(a0, sp, 0) + %ld(a0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.rest) %cdr(a0, a0) %cdr(a0, a0) %cdr(a0, a0) - %st(a0, sp, 24) + %st(a0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.walk) %call(&list_length) - %st(a0, sp, 40) + %st(a0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.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, 16) - %ld(t0, sp, 0) + %st(a0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.td) + %ld(t0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.rest) %car(t0, t0) - %st(t0, a0, 5) - %ld(t1, sp, 40) - %st(t1, a0, 13) + %heap_st(t0, a0, %TD.name) + %ld(t1, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.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, 16) + %ld(a1, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.td) %call(&make_param_prim) - %ld(t0, sp, 0) + %ld(t0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.rest) %cdr(t0, t0) %car(t0, t0) %car(t0, t0) @@ -4017,9 +3905,9 @@ # pred-prim = make_param_prim(prim_predicate_entry, td); bind pred. %la(a0, &prim_predicate_entry) - %ld(a1, sp, 16) + %ld(a1, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.td) %call(&make_param_prim) - %ld(t0, sp, 0) + %ld(t0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.rest) %cdr(t0, t0) %cdr(t0, t0) %car(t0, t0) @@ -4027,37 +3915,37 @@ # Iterate clauses: bind accessor + optional mutator per clause. %li(t0, 0) - %st(t0, sp, 32) + %st(t0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.idx) ::clause_loop - %ld(t0, sp, 24) + %ld(t0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.walk) %if_nil(t1, t0, &::done) # accessor-prim with data = tagged idx; bind cadr(clause). - %ld(a1, sp, 32) + %ld(a1, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.idx) %shli(a1, a1, 3) %la(a0, &prim_accessor_entry) %call(&make_param_prim) - %ld(t0, sp, 24) + %ld(t0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.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, 24) + %ld(t0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.walk) %car(t0, t0) %cdr(t0, t0) %cdr(t0, t0) %if_nil(t1, t0, &::no_mutator) - %ld(a1, sp, 32) + %ld(a1, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.idx) %shli(a1, a1, 3) %la(a0, &prim_mutator_entry) %call(&make_param_prim) - %ld(t0, sp, 24) + %ld(t0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.walk) %car(t0, t0) %cdr(t0, t0) %cdr(t0, t0) @@ -4066,9 +3954,9 @@ ::no_mutator %advance_walk(24) - %ld(t0, sp, 32) + %ld(t0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.idx) %addi(t0, t0, 1) - %st(t0, sp, 32) + %st(t0, sp, %EVAL_DEFINE_RECORD_TYPE_LOCALS.idx) %b(&::clause_loop) ::done @@ -4102,89 +3990,89 @@ # `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. -%fn(bv_putn, 32, { - %st(a0, sp, 0) - %st(a1, sp, 8) - %st(a2, sp, 16) +%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) - %ld(t0, a0, -3) + %heap_ld(t0, a0, %BV.hdr) %shri(t0, t0, 8) ; old_len - %st(t0, sp, 24) + %st(t0, sp, %BV_PUTN_LOCALS.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, 0) - %ld(a0, t0, 5) - %ld(t1, sp, 24) + %ld(t0, sp, %BV_PUTN_LOCALS.bv) + %heap_ld(a0, t0, %BV.data) + %ld(t1, sp, %BV_PUTN_LOCALS.old_len) %add(a0, a0, t1) ; dst = data + old_len - %ld(a1, sp, 8) - %ld(a2, sp, 16) + %ld(a1, sp, %BV_PUTN_LOCALS.src) + %ld(a2, sp, %BV_PUTN_LOCALS.n) %call(&memcpy) # hdr = (old_len + n) << 8 | HDR.BV. HDR.BV is 0. - %ld(t0, sp, 24) - %ld(t1, sp, 16) + %ld(t0, sp, %BV_PUTN_LOCALS.old_len) + %ld(t1, sp, %BV_PUTN_LOCALS.n) %add(t0, t0, t1) %shli(t0, t0, 8) - %ld(t1, sp, 0) - %st(t0, t1, -3) + %ld(t1, sp, %BV_PUTN_LOCALS.bv) + %heap_st(t0, t1, %BV.hdr) - %ld(a0, sp, 0) + %ld(a0, sp, %BV_PUTN_LOCALS.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. -%fn(bv_putc, 16, { - %st(a0, sp, 0) - %st(a1, sp, 8) +%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) - %ld(t0, a0, -3) + %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, 0) - %ld(t1, t0, -3) + %ld(t0, sp, %BV_PUTC_LOCALS.bv) + %heap_ld(t1, t0, %BV.hdr) %shri(t1, t1, 8) ; old_len (re-read after grow) - %ld(t2, t0, 5) + %heap_ld(t2, t0, %BV.data) %add(t2, t2, t1) - %ld(a0, sp, 8) + %ld(a0, sp, %BV_PUTC_LOCALS.byte) %sb(a0, t2, 0) %addi(t1, t1, 1) %shli(t1, t1, 8) - %st(t1, t0, -3) + %heap_st(t1, t0, %BV.hdr) - %ld(a0, sp, 0) + %ld(a0, sp, %BV_PUTC_LOCALS.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). -%fn(bv_putint, 16, { - %st(a0, sp, 0) +%struct BV_PUTINT_LOCALS { bv pad } # .SIZE = 16 +%fn(bv_putint, %BV_PUTINT_LOCALS.SIZE, { + %st(a0, sp, %BV_PUTINT_LOCALS.bv) %la(a0, &writer_num_buf) %call(&fmt_dec) ; n_bytes (a0) %mov(a2, a0) %la(a1, &writer_num_buf) - %ld(a0, sp, 0) + %ld(a0, sp, %BV_PUTINT_LOCALS.bv) %tail(&bv_putn) }) # sym_name(idx=a0) -> (ptr=a0, len=a1). Leaf. idx is the untagged sym # slot index; both fields come straight out of the symtab entry. :sym_name - %la(t0, &symtab_buf_ptr) - %ld(t0, t0, 0) - %shli(t1, a0, 5) - %add(t0, t0, t1) - %ld(a1, t0, 8) - %ld(a0, t0, 0) + %ld_global(t0, &symtab_buf_ptr) + %lda_array(a1, t1, t0, %SYMENT.SIZE, a0, %SYMENT.name_len) + %ld(a0, t1, %SYMENT.name_ptr) %ret # write_to_bv(val=a0, bv=a1, mode=a2) -> bv (a0). Recursively appends @@ -4192,10 +4080,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. -%fn(write_to_bv, 32, { - %st(a0, sp, 0) - %st(a1, sp, 8) - %st(a2, sp, 16) +%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) %tagof(t0, a0) %li(t1, %TAG.PAIR) @@ -4208,24 +4097,24 @@ %beq(t0, t1, &::imm) # Fall-through: FIXNUM (the only remaining tag). - %ld(a0, sp, 8) - %ld(a1, sp, 0) + %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) + %ld(a1, sp, %WRITE_TO_BV_LOCALS.val) %sari(a1, a1, 3) %tail(&bv_putint) ::sym - %ld(a0, sp, 0) + %ld(a0, sp, %WRITE_TO_BV_LOCALS.val) %sari(a0, a0, 3) %call(&sym_name) %mov(a2, a1) %mov(a1, a0) - %ld(a0, sp, 8) + %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) %tail(&bv_putn) ::pair - %ld(a0, sp, 0) - %ld(a1, sp, 8) - %ld(a2, sp, 16) + %ld(a0, sp, %WRITE_TO_BV_LOCALS.val) + %ld(a1, sp, %WRITE_TO_BV_LOCALS.bv) + %ld(a2, sp, %WRITE_TO_BV_LOCALS.mode) %tail(&write_pair_to_bv) ::heap @@ -4243,60 +4132,60 @@ %b(&::heap_unknown) ::heap_bv - %ld(t0, sp, 16) + %ld(t0, sp, %WRITE_TO_BV_LOCALS.mode) %beqz(t0, &::heap_bv_raw) # write mode: emit `"`, then the raw bytes, then `"`. - %ld(a0, sp, 8) + %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) %li(a1, 34) %call(&bv_putc) - %ld(t0, sp, 0) - %ld(a1, t0, 5) - %ld(a2, t0, -3) + %ld(t0, sp, %WRITE_TO_BV_LOCALS.val) + %heap_ld(a1, t0, %BV.data) + %heap_ld(a2, t0, %BV.hdr) %shri(a2, a2, 8) %call(&bv_putn) %li(a1, 34) %tail(&bv_putc) ::heap_bv_raw - %ld(t0, sp, 0) - %ld(a1, t0, 5) - %ld(a2, t0, -3) + %ld(t0, sp, %WRITE_TO_BV_LOCALS.val) + %heap_ld(a1, t0, %BV.data) + %heap_ld(a2, t0, %BV.hdr) %shri(a2, a2, 8) - %ld(a0, sp, 8) + %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) %tail(&bv_putn) ::heap_closure %la(a1, &str_closure) %li(a2, 10) - %ld(a0, sp, 8) + %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) %tail(&bv_putn) ::heap_prim %la(a1, &str_prim) %li(a2, 7) - %ld(a0, sp, 8) + %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) %tail(&bv_putn) ::heap_td %la(a1, &str_td) %li(a2, 11) - %ld(a0, sp, 8) + %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) %tail(&bv_putn) ::heap_rec %la(a1, &str_rec) %li(a2, 9) - %ld(a0, sp, 8) + %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) %tail(&bv_putn) ::heap_unknown %la(a1, &str_unknown) %li(a2, 10) - %ld(a0, sp, 8) + %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) %tail(&bv_putn) ::imm - %ld(a0, sp, 0) + %ld(a0, sp, %WRITE_TO_BV_LOCALS.val) %sari(a0, a0, 3) %beqz(a0, &::imm_false) %addi(t0, a0, -1) @@ -4310,37 +4199,37 @@ # EOF (idx == 5) is the only remaining IMM. %la(a1, &str_eof) %li(a2, 5) - %ld(a0, sp, 8) + %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) %tail(&bv_putn) ::imm_false %la(a1, &str_false) %li(a2, 2) - %ld(a0, sp, 8) + %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) %tail(&bv_putn) ::imm_true %la(a1, &str_true) %li(a2, 2) - %ld(a0, sp, 8) + %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) %tail(&bv_putn) ::imm_nil %la(a1, &str_nil) %li(a2, 2) - %ld(a0, sp, 8) + %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) %tail(&bv_putn) ::imm_unspec %la(a1, &str_unspec) %li(a2, 8) - %ld(a0, sp, 8) + %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) %tail(&bv_putn) ::imm_unbound %la(a1, &str_unbound) %li(a2, 9) - %ld(a0, sp, 8) + %ld(a0, sp, %WRITE_TO_BV_LOCALS.bv) %tail(&bv_putn) }) @@ -4354,25 +4243,26 @@ # +0 pair walk # +8 bv (stable wrapper; reused across recursive calls) # +16 mode -%fn(write_pair_to_bv, 32, { - %st(a0, sp, 0) - %st(a1, sp, 8) - %st(a2, sp, 16) +%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, 8) + %ld(a0, sp, %WRITE_PAIR_TO_BV_LOCALS.bv) %li(a1, 40) %call(&bv_putc) ::loop - %ld(t0, sp, 0) + %ld(t0, sp, %WRITE_PAIR_TO_BV_LOCALS.pair) %car(a0, t0) - %ld(a1, sp, 8) - %ld(a2, sp, 16) + %ld(a1, sp, %WRITE_PAIR_TO_BV_LOCALS.bv) + %ld(a2, sp, %WRITE_PAIR_TO_BV_LOCALS.mode) %call(&write_to_bv) - %ld(t0, sp, 0) + %ld(t0, sp, %WRITE_PAIR_TO_BV_LOCALS.pair) %cdr(t0, t0) - %st(t0, sp, 0) + %st(t0, sp, %WRITE_PAIR_TO_BV_LOCALS.pair) %if_nil(t1, t0, &::done) %tagof(t1, t0) @@ -4380,29 +4270,29 @@ %beq(t1, t2, &::cont) # Dotted tail: emit ` . ` then write_to_bv(cdr). - %ld(a0, sp, 8) + %ld(a0, sp, %WRITE_PAIR_TO_BV_LOCALS.bv) %li(a1, 32) %call(&bv_putc) - %ld(a0, sp, 8) + %ld(a0, sp, %WRITE_PAIR_TO_BV_LOCALS.bv) %li(a1, 46) %call(&bv_putc) - %ld(a0, sp, 8) + %ld(a0, sp, %WRITE_PAIR_TO_BV_LOCALS.bv) %li(a1, 32) %call(&bv_putc) - %ld(a0, sp, 0) - %ld(a1, sp, 8) - %ld(a2, sp, 16) + %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) %call(&write_to_bv) %b(&::done) ::cont - %ld(a0, sp, 8) + %ld(a0, sp, %WRITE_PAIR_TO_BV_LOCALS.bv) %li(a1, 32) %call(&bv_putc) %b(&::loop) ::done - %ld(a0, sp, 8) + %ld(a0, sp, %WRITE_PAIR_TO_BV_LOCALS.bv) %li(a1, 41) %tail(&bv_putc) }) @@ -4411,14 +4301,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. -%fn(value_to_bv, 16, { - %st(a0, sp, 0) - %st(a1, sp, 8) +%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) %li(a0, 0) %call(&bv_alloc) %mov(a1, a0) - %ld(a0, sp, 0) - %ld(a2, sp, 8) + %ld(a0, sp, %VALUE_TO_BV_LOCALS.val) + %ld(a2, sp, %VALUE_TO_BV_LOCALS.mode) %tail(&write_to_bv) }) @@ -4431,8 +4322,8 @@ %car(a0, a0) %li(a1, 0) %call(&value_to_bv) - %ld(a1, a0, 5) - %ld(a2, a0, -3) + %heap_ld(a1, a0, %BV.data) + %heap_ld(a2, a0, %BV.hdr) %shri(a2, a2, 8) %li(a0, 1) %call(&sys_write) @@ -4443,8 +4334,8 @@ %car(a0, a0) %li(a1, 1) %call(&value_to_bv) - %ld(a1, a0, 5) - %ld(a2, a0, -3) + %heap_ld(a1, a0, %BV.data) + %heap_ld(a2, a0, %BV.hdr) %shri(a2, a2, 8) %li(a0, 1) %call(&sys_write) @@ -4460,51 +4351,52 @@ # Frame: 16 bytes # +0 walk (initially args; advances over irritants) # +8 bv -%fn(prim_error_entry, 16, { - %st(a0, sp, 0) +%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) %li(a0, 0) %call(&bv_alloc) - %st(a0, sp, 8) + %st(a0, sp, %PRIM_ERROR_ENTRY_LOCALS.bv) %la(a1, &str_error_prefix) %li(a2, 16) - %ld(a0, sp, 8) + %ld(a0, sp, %PRIM_ERROR_ENTRY_LOCALS.bv) %call(&bv_putn) # First arg (the message) goes through write_to_bv with display mode. - %ld(t0, sp, 0) + %ld(t0, sp, %PRIM_ERROR_ENTRY_LOCALS.walk) %car(a0, t0) - %ld(a1, sp, 8) + %ld(a1, sp, %PRIM_ERROR_ENTRY_LOCALS.bv) %li(a2, 0) %call(&write_to_bv) - %ld(t0, sp, 0) + %ld(t0, sp, %PRIM_ERROR_ENTRY_LOCALS.walk) %cdr(t0, t0) - %st(t0, sp, 0) + %st(t0, sp, %PRIM_ERROR_ENTRY_LOCALS.walk) ::loop - %ld(t0, sp, 0) + %ld(t0, sp, %PRIM_ERROR_ENTRY_LOCALS.walk) %if_nil(t1, t0, &::done) - %ld(a0, sp, 8) + %ld(a0, sp, %PRIM_ERROR_ENTRY_LOCALS.bv) %li(a1, 32) %call(&bv_putc) - %ld(t0, sp, 0) + %ld(t0, sp, %PRIM_ERROR_ENTRY_LOCALS.walk) %car(a0, t0) - %ld(a1, sp, 8) + %ld(a1, sp, %PRIM_ERROR_ENTRY_LOCALS.bv) %li(a2, 0) %call(&write_to_bv) - %ld(t0, sp, 0) + %ld(t0, sp, %PRIM_ERROR_ENTRY_LOCALS.walk) %cdr(t0, t0) - %st(t0, sp, 0) + %st(t0, sp, %PRIM_ERROR_ENTRY_LOCALS.walk) %b(&::loop) ::done - %ld(t0, sp, 8) - %ld(a0, t0, 5) + %ld(t0, sp, %PRIM_ERROR_ENTRY_LOCALS.bv) + %heap_ld(a0, t0, %BV.data) %tail(&runtime_error) }) @@ -4519,30 +4411,31 @@ # +8 template bv # +16 args walk # +24 idx (current byte offset into template) -%fn(prim_format_entry, 32, { - %st(a0, sp, 16) ; spill incoming args while we set up +%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 %li(a0, 0) %call(&bv_alloc) - %st(a0, sp, 0) + %st(a0, sp, %PRIM_FORMAT_ENTRY_LOCALS.out) - %ld(t0, sp, 16) + %ld(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.args) %car(t1, t0) - %st(t1, sp, 8) + %st(t1, sp, %PRIM_FORMAT_ENTRY_LOCALS.template) %cdr(t0, t0) - %st(t0, sp, 16) + %st(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.args) %li(t0, 0) - %st(t0, sp, 24) + %st(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.idx) ::loop - %ld(t1, sp, 8) - %ld(t2, t1, -3) + %ld(t1, sp, %PRIM_FORMAT_ENTRY_LOCALS.template) + %heap_ld(t2, t1, %BV.hdr) %shri(t2, t2, 8) ; template length - %ld(t0, sp, 24) + %ld(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.idx) %beq(t0, t2, &::done) - %ld(a3, t1, 5) + %heap_ld(a3, t1, %BV.data) %add(a3, a3, t0) %lb(a3, a3, 0) ; byte = template.data[idx] @@ -4550,28 +4443,28 @@ %beqz(t1, &::tilde) # Plain byte: emit and advance. - %ld(a0, sp, 0) + %ld(a0, sp, %PRIM_FORMAT_ENTRY_LOCALS.out) %mov(a1, a3) %call(&bv_putc) - %ld(t0, sp, 24) + %ld(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.idx) %addi(t0, t0, 1) - %st(t0, sp, 24) + %st(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.idx) %b(&::loop) ::tilde - %ld(t0, sp, 24) + %ld(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.idx) %addi(t0, t0, 1) - %ld(t1, sp, 8) - %ld(t2, t1, -3) + %ld(t1, sp, %PRIM_FORMAT_ENTRY_LOCALS.template) + %heap_ld(t2, t1, %BV.hdr) %shri(t2, t2, 8) %beq(t0, t2, &::tilde_lit) - %ld(t1, t1, 5) + %heap_ld(t1, t1, %BV.data) %add(t1, t1, t0) %lb(a3, t1, 0) ; spec %addi(t0, t0, 1) ; advance past spec - %st(t0, sp, 24) + %st(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.idx) %addi(t1, a3, -97) ; 'a' %beqz(t1, &::spec_a) @@ -4586,73 +4479,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, 0) + %ld(a0, sp, %PRIM_FORMAT_ENTRY_LOCALS.out) %li(a1, 126) %call(&bv_putc) - %ld(t0, sp, 8) - %ld(t1, t0, 5) - %ld(t0, sp, 24) + %ld(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.template) + %heap_ld(t1, t0, %BV.data) + %ld(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.idx) %addi(t0, t0, -1) %add(t1, t1, t0) %lb(a1, t1, 0) - %ld(a0, sp, 0) + %ld(a0, sp, %PRIM_FORMAT_ENTRY_LOCALS.out) %call(&bv_putc) %b(&::loop) ::tilde_lit # `~` at end of template: emit literal `~` and finish next iter. - %ld(a0, sp, 0) + %ld(a0, sp, %PRIM_FORMAT_ENTRY_LOCALS.out) %li(a1, 126) %call(&bv_putc) - %ld(t0, sp, 24) + %ld(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.idx) %addi(t0, t0, 1) - %st(t0, sp, 24) + %st(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.idx) %b(&::loop) ::spec_a - %ld(t0, sp, 16) + %ld(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.args) %car(a0, t0) %cdr(t0, t0) - %st(t0, sp, 16) - %ld(a1, sp, 0) + %st(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.args) + %ld(a1, sp, %PRIM_FORMAT_ENTRY_LOCALS.out) %li(a2, 0) %call(&write_to_bv) %b(&::loop) ::spec_s - %ld(t0, sp, 16) + %ld(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.args) %car(a0, t0) %cdr(t0, t0) - %st(t0, sp, 16) - %ld(a1, sp, 0) + %st(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.args) + %ld(a1, sp, %PRIM_FORMAT_ENTRY_LOCALS.out) %li(a2, 1) %call(&write_to_bv) %b(&::loop) ::spec_d - %ld(t0, sp, 16) + %ld(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.args) %car(t1, t0) %cdr(t0, t0) - %st(t0, sp, 16) + %st(t0, sp, %PRIM_FORMAT_ENTRY_LOCALS.args) %sari(a1, t1, 3) - %ld(a0, sp, 0) + %ld(a0, sp, %PRIM_FORMAT_ENTRY_LOCALS.out) %call(&bv_putint) %b(&::loop) ::spec_pct - %ld(a0, sp, 0) + %ld(a0, sp, %PRIM_FORMAT_ENTRY_LOCALS.out) %li(a1, 10) %call(&bv_putc) %b(&::loop) ::spec_tilde - %ld(a0, sp, 0) + %ld(a0, sp, %PRIM_FORMAT_ENTRY_LOCALS.out) %li(a1, 126) %call(&bv_putc) %b(&::loop) ::done - %ld(a0, sp, 0) + %ld(a0, sp, %PRIM_FORMAT_ENTRY_LOCALS.out) }) # ========================================================================= @@ -4669,15 +4562,16 @@ # to syscalls expecting a C string. # wrap_syscall_result(raw=a0) -> (#t . r) or (#f . errno). -%fn(wrap_syscall_result, 16, { - %st(a0, sp, 0) +%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) %bltz(a0, &::err) %shli(a1, a0, 3) %li(a0, %imm_val(%IMM.TRUE)) %tail(&cons) ::err - %ld(t0, sp, 0) + %ld(t0, sp, %WRAP_SYSCALL_RESULT_LOCALS.raw) %li(t1, 0) %sub(t0, t1, t0) %shli(a1, t0, 3) @@ -4698,8 +4592,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. -%fn(sys_clone, 16, { - %st(s0, sp, 0) +%struct SYS_CLONE_LOCALS { saved_s0 pad } # .SIZE = 16 +%fn(sys_clone, %SYS_CLONE_LOCALS.SIZE, { + %st(s0, sp, %SYS_CLONE_LOCALS.saved_s0) %li(s0, 0) %li(a1, 17) @@ -4709,7 +4604,7 @@ %li(a0, %p1_sys_clone) %syscall - %ld(s0, sp, 0) + %ld(s0, sp, %SYS_CLONE_LOCALS.saved_s0) }) # sys_execve(path=a0, argv=a1, envp=a2) -> -errno (a0). Only returns on @@ -4740,23 +4635,24 @@ # +0 list # +8 count # +16 array ptr (raw) -%fn(build_execve_argv, 24, { - %st(a0, sp, 0) +%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) %call(&list_length) ; clobbers a0 -> count - %st(a0, sp, 8) + %st(a0, sp, %BUILD_EXECVE_ARGV_LOCALS.count) %addi(a0, a0, 1) %shli(a0, a0, 3) %call(&alloc_bytes) - %st(a0, sp, 16) + %st(a0, sp, %BUILD_EXECVE_ARGV_LOCALS.array) - %ld(t0, sp, 0) - %ld(t1, sp, 16) + %ld(t0, sp, %BUILD_EXECVE_ARGV_LOCALS.list) + %ld(t1, sp, %BUILD_EXECVE_ARGV_LOCALS.array) ::fill_loop %if_nil(t2, t0, &::fill_done) %car(a3, t0) - %ld(a2, a3, 5) + %heap_ld(a2, a3, %BV.data) %st(a2, t1, 0) %addi(t1, t1, 8) %cdr(t0, t0) @@ -4766,14 +4662,14 @@ %li(t2, 0) %st(t2, t1, 0) - %ld(a0, sp, 16) + %ld(a0, sp, %BUILD_EXECVE_ARGV_LOCALS.array) }) # (sys-read fd buf count) %fn(prim_sys_read_entry, 0, { %args3(t0, t1, t2, a0) %sari(t0, t0, 3) ; fd - %ld(t1, t1, 5) ; buf data ptr + %heap_ld(t1, t1, %BV.data) ; buf data ptr %sari(t2, t2, 3) ; count %mov(a0, t0) %mov(a1, t1) @@ -4786,7 +4682,7 @@ %fn(prim_sys_write_entry, 0, { %args3(t0, t1, t2, a0) %sari(t0, t0, 3) ; fd - %ld(t1, t1, 5) ; buf data ptr + %heap_ld(t1, t1, %BV.data) ; buf data ptr %sari(t2, t2, 3) ; count %mov(a0, t0) %mov(a1, t1) @@ -4806,7 +4702,7 @@ %fn(prim_sys_openat_entry, 0, { %args4(t0, t1, t2, a3, a0) %sari(t0, t0, 3) ; dirfd - %ld(t1, t1, 5) ; path data_ptr + %heap_ld(t1, t1, %BV.data) ; path data_ptr %sari(t2, t2, 3) ; flags %sari(a3, a3, 3) ; mode %mov(a0, t0) @@ -4823,13 +4719,14 @@ }) # (sys-execve path argv-list) -%fn(prim_sys_execve_entry, 16, { +%struct PRIM_SYS_EXECVE_ENTRY_LOCALS { path pad } # .SIZE = 16 +%fn(prim_sys_execve_entry, %PRIM_SYS_EXECVE_ENTRY_LOCALS.SIZE, { %args2(t0, a0, a0) ; t0 = path bv, a0 = argv-list - %st(t0, sp, 0) + %st(t0, sp, %PRIM_SYS_EXECVE_ENTRY_LOCALS.path) %call(&build_execve_argv) %mov(a1, a0) - %ld(a0, sp, 0) - %ld(a0, a0, 5) ; path data ptr + %ld(a0, sp, %PRIM_SYS_EXECVE_ENTRY_LOCALS.path) + %heap_ld(a0, a0, %BV.data) ; path data ptr %li(a2, 0) %call(&sys_execve) %tail(&wrap_syscall_result) @@ -4840,7 +4737,7 @@ %args4(t0, t1, t2, a3, a0) %sari(t0, t0, 3) ; idtype %sari(t1, t1, 3) ; id - %ld(t2, t2, 5) ; infop bv data ptr + %heap_ld(t2, t2, %BV.data) ; infop bv data ptr %sari(a3, a3, 3) ; options %mov(a0, t0) %mov(a1, t1) @@ -4859,66 +4756,65 @@ # +16 list head # +24 list tail # +32 current bv (across memcpy) -%fn(prim_sys_argv_entry, 40, { - %la(t0, &saved_argv) - %ld(t0, t0, 0) - %st(t0, sp, 0) - %la(t0, &saved_argc) - %ld(t0, t0, 0) - %st(t0, sp, 8) +%struct PRIM_SYS_ARGV_ENTRY_LOCALS { argv count head tail bv } # .SIZE = 40 +%fn(prim_sys_argv_entry, %PRIM_SYS_ARGV_ENTRY_LOCALS.SIZE, { + %ld_global(t0, &saved_argv) + %st(t0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.argv) + %ld_global(t0, &saved_argc) + %st(t0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.count) %li(t0, %imm_val(%IMM.NIL)) - %st(t0, sp, 16) - %st(t0, sp, 24) + %st(t0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.head) + %st(t0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.tail) ::loop - %ld(t0, sp, 8) + %ld(t0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.count) %beqz(t0, &::done) # len = strlen(*argv) - %ld(t0, sp, 0) + %ld(t0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.argv) %ld(a0, t0, 0) %call(&strlen) # bv = bv_alloc(len) %call(&bv_alloc) - %st(a0, sp, 32) + %st(a0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.bv) # memcpy(bv.data_ptr, *argv, len-from-bv-hdr). - %ld(t0, sp, 32) - %ld(a0, t0, 5) - %ld(t1, sp, 0) + %ld(t0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.bv) + %heap_ld(a0, t0, %BV.data) + %ld(t1, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.argv) %ld(a1, t1, 0) - %ld(t1, t0, -3) + %heap_ld(t1, t0, %BV.hdr) %shri(a2, t1, 8) %call(&memcpy) # cell = cons(bv, NIL); append to list head/tail. - %ld(a0, sp, 32) + %ld(a0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.bv) %li(a1, %imm_val(%IMM.NIL)) %call(&cons) - %ld(t0, sp, 16) + %ld(t0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.head) %if_nil(t1, t0, &::first) - %ld(t0, sp, 24) - %st(a0, t0, 7) - %st(a0, sp, 24) + %ld(t0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.tail) + %set_cdr(a0, t0) + %st(a0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.tail) %b(&::advance) ::first - %st(a0, sp, 16) - %st(a0, sp, 24) + %st(a0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.head) + %st(a0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.tail) ::advance - %ld(t0, sp, 0) + %ld(t0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.argv) %addi(t0, t0, 8) - %st(t0, sp, 0) - %ld(t0, sp, 8) + %st(t0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.argv) + %ld(t0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.count) %addi(t0, t0, -1) - %st(t0, sp, 8) + %st(t0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.count) %b(&::loop) ::done - %ld(a0, sp, 16) + %ld(a0, sp, %PRIM_SYS_ARGV_ENTRY_LOCALS.head) }) # (eof-object) and (eof-object? x). @@ -5130,6 +5026,50 @@ :name_ch_newline "newline" # ========================================================================= +# Startup -- bss_init / 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 +# the PAIR tag; &ELF_end's alignment depends on the data section above +# it. cons / alloc_hdr / alloc_bytes test (heap_next + bytes <= heap_end) +# on every allocation and abort via runtime_error on overflow. Leaf. +:heap_init + %ld_global(t0, &heap_buf_ptr) + %alignup(t0, t0, 8, t1) + %st_global(t0, &heap_next, t1) + + %ld_global(t0, &heap_buf_ptr) + %li(t1, %HEAP_CAP_BYTES) + %add(t0, t0, t1) + %st_global(t0, &heap_end, t1) + + %ret + +# ========================================================================= # BSS pointer-init table # ========================================================================= #