commit 837a27f3af091754af36f471a1da603449e3e06a
parent 4b41d120da4d603fd18c7a860f4acdf5af3acfe2
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Tue, 21 Apr 2026 20:14:40 -0700
lisp gc spike; P1 frame pointer
Diffstat:
| A | docs/LISP-GC.md | | | 367 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | src/lisp.M1 | | | 2092 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------- |
| M | src/p1_gen.py | | | 86 | +++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------- |
3 files changed, 1982 insertions(+), 563 deletions(-)
diff --git a/docs/LISP-GC.md b/docs/LISP-GC.md
@@ -0,0 +1,367 @@
+# LISP-GC: Mark-sweep GC for `lisp.M1`
+
+Implementation plan for [LISP.md](LISP.md) step 13. Scope is the GC itself
+plus the prerequisites it imposes on P1 frame layout and the heap shape.
+
+## Position in the staged plan
+
+Step 13 in LISP.md. Prerequisite: steps 1–12 complete (runtime, values,
+reader core, printer, eval, TAIL, argv, test harness, all ~40 primitives,
+prelude). Follow-on: steps 14 (`pmatch`), 15 (REPL), 16 (source
+locations), 17 (end-to-end).
+
+## Locked decisions
+
+Load-bearing; the rest of the document assumes them.
+
+1. **Segregated arenas, headerless pairs.** Two bump pointers inside
+ a single 1 MB heap. Pair arena: 16-byte objects, no header, tag
+ `010` points directly to the pair's first word. Object arena:
+ headered objects only (string / symbol / vector / closure /
+ primitive); marks live in the `gc-flags` byte. One mark bitmap
+ over the pair arena only; the object arena needs none. Sweep
+ walks each arena with its own stride rule.
+2. **P1 frame-pointer chain, metadata before slots.** Every
+ `PROLOGUE_Nk` frame stores the caller's `sp` as `saved_fp`, plus
+ the slot count `k`, ahead of its slots. GC walks the chain
+ precisely; no conservative scan.
+3. **Explicit mark stack in BSS.** 32 KB (~4k entries). Overflow
+ falls back to recursive `CALL` into the marker so no structure
+ is too deep to mark.
+4. **Heap size 1 MB for this step.** Triggers GC easily under stress
+ tests. Grow to 20 MB in a follow-up once C-compiler workloads
+ demand it.
+
+These supersede the following LISP.md text, which must be updated when
+this work lands: decision 11 (single bitmap over whole heap), §Heap
+layout (single arena), §GC §Roots (conservative stack filter), and
+P1.md §Semantics prologue/epilogue/TAIL.
+
+## Heap layout
+
+Single BSS region, split in two by fixed labels:
+
+```
+:pair_heap_start
+ <600 KB>
+:pair_heap_end / :obj_heap_start
+ <400 KB>
+:obj_heap_end
+```
+
+Bump pointers:
+
+- `pair_heap_next` — starts at `pair_heap_start`, bumps by 16.
+- `obj_heap_next` — starts at `obj_heap_start`, bumps by 8-rounded
+ size.
+
+Free lists:
+
+- `free_list_pair` — head of the 16-byte pair free list.
+- `free_lists[10]` — heads for 16, 24, 32, 40, 48, 56, 64, 80, 96, 128
+ byte object-arena size classes.
+
+Split is tunable: the only code that cares is the two label
+definitions. Pairs dominate compiler workloads so the pair arena gets
+the larger share.
+
+### Allocators
+
+- `pair_alloc` → 16-byte raw pointer. Tries `free_list_pair`; else
+ bumps `pair_heap_next`. If the bump would cross `pair_heap_end`,
+ invokes `gc`, retries, else `error "heap exhausted"`.
+- `obj_alloc(size)` → raw pointer to 8-byte-aligned region. Size is
+ rounded up to 8. If size ≤ 128, tries the matching free list
+ before bumping `obj_heap_next`. Same GC-retry-else-error overflow
+ path.
+
+`cons` routes through `pair_alloc`. Every other allocator (strings,
+symbols, vectors, closures, primitives) routes through `obj_alloc`.
+
+### Pair mark bitmap
+
+One bit per 16-byte slot in the pair arena, indexed by
+`(pair_ptr - pair_heap_start) / 16`. At 600 KB pair arena:
+600 KB / 16 = 37 500 bits ≈ 4.7 KB BSS. Declared as
+`pair_mark_bitmap` with fixed size.
+
+Live pairs also set the bit; clearing happens during sweep.
+
+### Object gc-flags
+
+Already present in every headered object: `header byte 6` (second
+byte of the header word) is the `gc-flags` byte. Bit 0 is the mark
+bit. All other bits reserved.
+
+## Frame layout
+
+New `PROLOGUE_Nk` shape (supersedes P1.md §Semantics lines 230–247):
+
+```
+[sp + 0] = caller's retaddr
+[sp + 8] = saved_fp (caller's sp value at the moment of CALL)
+[sp + 16] = k (slot count, stored as integer; low 3 bits zero)
+[sp + 24] = slot 1
+[sp + 32] = slot 2 (k >= 2)
+[sp + 40] = slot 3 (k >= 3)
+[sp + 48] = slot 4 (k >= 4)
+```
+
+Frame size: `round_up_16(24 + 8*k)`. Concrete sizes:
+
+| k | Old size | New size |
+|---|----------|----------|
+| 1 | 16 | 32 |
+| 2 | 32 | 48 |
+| 3 | 32 | 48 |
+| 4 | 48 | 64 |
+
+Cost: +16 bytes per call frame. For a 10⁶-deep tail loop the stack
+cost is unchanged (TAIL reuses the frame); for a 10⁴-deep recursive
+call it adds ~160 KB, well under any reasonable stack limit.
+
+`EPILOGUE_Nk` / `TAIL_Nk` restore `saved_fp` from `[sp+8]` before
+returning or branching.
+
+### Chain termination
+
+`_start` writes `0` to BSS `stack_bottom_fp` before its first `CALL`.
+Any frame whose `saved_fp` is `0` is the bottom frame; the walker
+stops there.
+
+### Per-arch implementation in `p1_gen.py`
+
+Three arch-specific emitters change:
+
+- **amd64**: `call` already pushes retaddr natively. Prologue does
+ `pop rcx; mov rax, rsp; sub rsp, frame_size; mov [rsp], rcx;
+ mov [rsp+8], rax; mov qword [rsp+16], k`. Epilogue reverses.
+- **aarch64**: `sub sp, sp, frame_size; str lr, [sp, 0]; mov x9, sp
+ (pre-sub sp + frame_size, computed); str x9, [sp, 8];
+ mov x9, #k; str x9, [sp, 16]`. Epilogue reverses.
+- **riscv64**: analogous to aarch64, using `ra` and a scratch `t0`.
+
+The "caller's sp" is `sp + frame_size` on all three arches after the
+sub has run, since prologue ran immediately after call.
+
+### Slot-offset shift in `lisp.M1`
+
+Every `sp`-relative access throughout `lisp.M1` shifts:
+
+| Old offset | New offset |
+|------------|------------|
+| 8 | 24 |
+| 16 | 32 |
+| 24 | 40 |
+| 32 | 48 |
+
+The change is mechanical but wide. Step 1 of the implementation
+sequence is entirely about landing this shift cleanly on all three
+arches before any GC work begins.
+
+## Roots
+
+Five sources, walked in this order on each GC invocation:
+
+1. **`global_env_cell`.** Alist head in BSS. Traced transitively via
+ `mark_object`.
+2. **`symbol_table[0..4095]`.** Intern table's backing array. Walk
+ each slot; mark occupied entries as tagged heap pointers.
+3. **`prim_argv[0..prim_argc)`.** Scratch region live during a
+ primitive call. The current `prim_argc` BSS slot bounds the walk.
+4. **Reader scratch** (`saved_src_*`, ~5 slots). Live during a
+ `read` invocation.
+5. **Stack (fp chain).** Start at the caller of `gc()`, walk
+ `saved_fp` until `stack_bottom_fp` (value 0). At each frame,
+ read `k` from `[fp+16]` and pass every word in `[fp+24..+24+8*k)`
+ through `mark_object`.
+
+The `sym_*` special-form symbols are reachable via `symbol_table`
+so they need no separate root entry.
+
+## Mark phase
+
+```
+mark_sp ← mark_stack_base
+for each root r: mark_object(r)
+while mark_sp > mark_stack_base:
+ v ← pop(mark_stack)
+ mark_object(v)
+```
+
+`mark_object(v)` dispatches on the low-3-bit tag:
+
+- **Fixnum, singleton**: no-op.
+- **Pair (`010`)**: compute bit index from pointer; if bit set,
+ return (already marked). Else set bit, push `car` and `cdr` onto
+ `mark_stack`.
+- **Vector (`011`)**: read header; if gc-flags bit 0 set, return.
+ Else set it, read length, push every element.
+- **String (`100`)**: set mark bit; no children.
+- **Symbol (`101`)**: set mark bit; push name string and intern-chain
+ link.
+- **Closure / primitive (`110`)**: set mark bit; closures push
+ params list, body list, env pointer; primitives have no children.
+
+### Mark-stack overflow
+
+If `push(mark_stack)` would overflow, recurse by `CALL mark_object`
+on the overflowing value instead. P1 stack depth grows temporarily
+but every reachable object still gets marked. Compiler workloads
+should never hit this path; `06-gc-stress.scm` and
+`18-gc-deep-list.scm` will exercise it intentionally.
+
+## Sweep phase
+
+Two per-arena loops.
+
+### Pair arena sweep
+
+```
+p ← pair_heap_start
+while p < pair_heap_next:
+ i ← (p - pair_heap_start) / 16
+ if bit i of pair_mark_bitmap is set:
+ clear bit i
+ else:
+ push p onto free_list_pair
+ p ← p + 16
+```
+
+Tail rewind: track the last live pair address; if the tail of the
+arena is all-dead, rewind `pair_heap_next` to just past the last
+live. Optional for correctness; small win for fragmentation. Land
+it in step 4 only if the pair-arena free list grows pathologically
+in the stress tests.
+
+### Object arena sweep
+
+Every live headered object has a valid header. Free chunks carry a
+pseudo-header `type=0, gc-flags=0, length=chunk_size` so the walker
+strides uniformly.
+
+```
+p ← obj_heap_start
+while p < obj_heap_next:
+ h ← LD [p]
+ sz ← header_length(h)
+ rounded ← round_up_8(sz)
+ if header_type(h) != 0 and header_mark(h):
+ clear mark bit
+ else:
+ if rounded ≤ 128:
+ push p onto free_lists[class_of(rounded)]
+ overwrite header as free pseudo-header
+ else:
+ coalesce with following chunk if also dead; write
+ combined pseudo-header
+ p ← p + rounded
+```
+
+Tail rewind: same optional logic; rewind `obj_heap_next` past the
+trailing all-dead region.
+
+Coalescing pass runs before the free-list push so adjacent dead
+>128-byte runs merge into one. Free-list push happens after, for
+the sub-128 chunks.
+
+## Allocator integration
+
+`pair_alloc` and `obj_alloc` both follow the same template:
+
+```
+head ← free_list_head (for size class)
+if head != 0:
+ free_list_head ← [head + 0]
+ return head
+new ← bump_pointer
+if new + size > arena_end:
+ call gc
+ retry from top once
+ if still no room: error "heap exhausted"
+bump_pointer ← new + size
+return new
+```
+
+`gc` is a normal P1 function that does mark+sweep and returns. It
+assumes GC-safety invariants hold at its entry (every live tagged
+value is either in a named BSS root or in a `PROLOGUE_Nk` slot
+somewhere on the fp chain). These invariants already hold in the
+existing codebase; the step-13 work adds none.
+
+## Implementation sequence
+
+Each step assembles through P1 → M1 → hex2 and passes
+`make test-lisp-all` before the next begins.
+
+1. **`p1_gen.py` frame change + `lisp.M1` offset shift.**
+ ~150 Py LOC + mechanical `lisp.M1` edit. Rewrite
+ prologue/epilogue/TAIL for amd64, aarch64, riscv64; shift every
+ `sp`-relative offset in `lisp.M1`. Write `stack_bottom_fp = 0`
+ in `_start`. **Gate**: every existing test passes on all three
+ arches. No GC code yet.
+2. **Segregated heap + free-list scaffolding.** ~100 P1 LOC.
+ Split heap labels; route `cons` through `pair_alloc`, others
+ through `obj_alloc`; declare `free_list_pair` and `free_lists[10]`
+ (heads only, no populating logic yet); overflow still routes to
+ `alloc_oom`. **Gate**: existing tests pass.
+3. **Mark phase.** ~200 P1 LOC. `mark_stack[4096]`,
+ `pair_mark_bitmap`, `mark_object`, root walk including fp-chain
+ traversal. Add hidden `(gc-mark-only)` primitive that runs mark
+ and returns live-object count. **Gate**: a test validates the
+ count matches a known reachable-set size; hitting the mark-stack
+ overflow path is exercised by `18-gc-deep-list.scm`.
+4. **Sweep phase + wire into allocator.** ~200 P1 LOC. Both
+ per-arena sweeps, free-list population, free-chunk pseudo-headers,
+ coalescing for >128 chunks. Replace the `alloc_oom` first-check
+ branch with `gc → retry → error`. **Gate**: existing tests pass;
+ free lists populated after first GC.
+5. **Stress tests.** Five new `.scm` files in `tests/lisp/`:
+ - `17-gc-cons-churn.scm` — 100k pair allocations in a loop, verify
+ deterministic checksum.
+ - `18-gc-deep-list.scm` — 10k-element list, post-GC sum.
+ - `19-gc-vector-churn.scm` — vector alloc/drop, exercises >128-byte
+ coalesce.
+ - `20-gc-closure-churn.scm` — nested lambdas creating closures
+ faster than arena; validates env-chain rooting.
+ - `21-gc-mixed.scm` — interleaved pair/vector/string/closure.
+ **Gate**: all pass on `make test-lisp-all`.
+
+Rough total: ~700 P1 LOC + ~150 Py LOC + 5 test files.
+
+## Doc updates bundled with the code
+
+- **LISP.md decision 11**: rewrite to "Pair mark bits live in a
+ per-pair-arena bitmap; object arena marks live in each object's
+ `gc-flags` byte. Pairs stay 16 bytes."
+- **LISP.md §Heap layout**: describe the two-arena split.
+- **LISP.md §GC §Algorithm**: replace the single-walk description
+ with per-arena sweep.
+- **LISP.md §GC §Roots**: replace conservative-scan language with
+ the fp-chain walker description.
+- **P1.md §Semantics** (prologue / epilogue / TAIL sections, lines
+ 230–269): new frame shape with `saved_fp` at `[sp+8]` and `k` at
+ `[sp+16]`; updated frame size formula; per-arch mechanics updated.
+
+## Risks and mitigations
+
+- **Missed slot-offset shift in step 1.** Silent: a function with
+ a stale `sp,8` access reads `saved_fp` as if it were a slot, and
+ corrupts it on store. Mitigation: grep every `,sp,` pattern in
+ `lisp.M1` before declaring step 1 done; run every test on every
+ arch.
+- **GC fires during primitive with live values only in registers.**
+ Primitives must honor the per-frame-slot root discipline. The
+ existing code already does; step-13 work must not introduce new
+ violations. Audit every new GC code path for this at review time.
+- **fp-chain consistency during mark-stack overflow recursion.**
+ The recursive `CALL mark_object` pushes a new frame with its own
+ `saved_fp`. The root walker must not re-walk frames it's already
+ processed. Simple fix: the walker captures the chain length at
+ entry and only walks down to that depth; frames above are the
+ walker's own and are skipped.
+- **Free-list corruption from a bug in sweep.** Free chunks double
+ as linked-list nodes via their first word. An off-by-one in sweep
+ can overwrite a live object with a free-list link. Mitigation:
+ stress tests `17` and `21` allocate + read back; any corruption
+ surfaces as a crash or wrong checksum.
diff --git a/src/lisp.M1 b/src/lisp.M1
@@ -50,11 +50,10 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## ---- heap-state --------------------------------------------------------
-## Step-2 layout preserved: two 8-byte cells whose upper 4 bytes are
-## zero (ELF_base < 4 GiB, so `&label` fits in 32 bits). Keep the high
-## word as `%0`; bare `00 00 00 00` byte tokens still upset riscv64 M0.
-:heap_next &heap_start %0
-:heap_end &heap_tail %0
+## The two arenas are fixed in BSS; only the bump heads are mutable.
+## Upper 4 bytes stay zero because all labels fit below 4 GiB.
+:pair_heap_next &pair_heap_start %0
+:obj_heap_next &obj_heap_start %0
## ---- _start ----------------------------------------------------------
@@ -81,6 +80,11 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
blt_r0,r1 ## argc < 2 → err_usage
ld_r2,sp,16 ## r2 = argv[1] (survives SYSCALL)
+ ## Seed the frame-chain terminator before the first CALL.
+ li_r1 &stack_bottom_fp
+ mov_r0,sp
+ st_r0,r1,0
+
## openat(AT_FDCWD=-100, argv[1], O_RDONLY=0, mode=0) → r0 = fd.
## Use openat rather than open because open is amd64-only
## (removed from the asm-generic table used by aarch64/riscv64).
@@ -126,14 +130,6 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
mov_r1,r4
syscall
- ## Align heap_next up to 8 (pair/closure tags need 8-aligned raw
- ## pointers — see step 2).
- li_r4 &heap_next
- ld_r0,r4,0
- ori_r0,r0,7
- addi_r0,r0,1
- st_r0,r4,0
-
## Intern the special-form symbols up front. eval_pair compares the
## car of every compound expression against these pointers, so they
## must exist before the first eval call. The intern result (r0 =
@@ -377,7 +373,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## every top-level form, then restore the outer reader state.
:eval_source
prologue
- st_r4,sp,8 ## save caller's r4
+ st_r4,sp,24 ## save caller's r4
li_r3 &src_base
ld_r0,r3,0
@@ -462,7 +458,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
st_r0,r1,0
mov_r0,r4
- ld_r4,sp,8
+ ld_r4,sp,24
epilogue
ret
@@ -473,7 +469,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## saves/restores it via a PROLOGUE_N1 slot.
:hash
prologue
- st_r6,sp,8
+ st_r6,sp,24
li_r6 %0 ## zero const
li_r0 %0 ## h = 0
@@ -492,7 +488,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
b
:hash_done
- ld_r6,sp,8
+ ld_r6,sp,24
epilogue
ret
@@ -503,8 +499,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## scratch; both are callee-saved, so PROLOGUE_N2 stashes them.
:sym_name_equal
prologue_n2
- st_r6,sp,8
- st_r7,sp,16
+ st_r6,sp,24
+ st_r7,sp,32
addi_r1,r1,neg5 ## r1 = raw sym ptr
ld_r6,r1,0 ## r6 = header word
@@ -542,8 +538,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_r0 %0
:sym_name_equal_done
- ld_r6,sp,8
- ld_r7,sp,16
+ ld_r6,sp,24
+ ld_r7,sp,32
epilogue_n2
ret
@@ -558,8 +554,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## [sp + 24] raw_ptr across the copy loop
:make_symbol
prologue_n3
- st_r6,sp,8
- st_r7,sp,16
+ st_r6,sp,24
+ st_r7,sp,32
mov_r6,r1 ## r6 = src (mutated during byte copy)
mov_r7,r2 ## r7 = len (mutated during byte copy)
@@ -572,14 +568,16 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &alloc
call ## r0 = raw ptr
- st_r0,sp,24 ## stash raw ptr
+ st_r0,sp,40 ## stash raw ptr
## Header: store len in low 48 bits, then stamp type=2 in byte 7.
+ ld_r0,sp,40
st_r7,r0,0
li_r1 %2
sb_r1,r0,7
## Byte copy: dst = r3 (= raw+8), src = r6, len = r7.
+ ld_r0,sp,40
addi_r3,r0,8
li_r1 %0 ## zero const for loop test
@@ -596,11 +594,11 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
b
:make_symbol_done
- ld_r0,sp,24
+ ld_r0,sp,40
ori_r0,r0,4 ## set bit 2 (aarch64 bitmask-imm can't do 0b101 directly)
ori_r0,r0,1 ## set bit 0 → tag = 101 (SYMBOL)
- ld_r6,sp,8
- ld_r7,sp,16
+ ld_r6,sp,24
+ ld_r7,sp,32
epilogue_n3
ret
@@ -631,7 +629,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## leaves the payload for the caller to fill.
:alloc_string
prologue
- st_r1,sp,8
+ st_r1,sp,24
addi_r1,r1,7
shri_r1,r1,3
@@ -640,7 +638,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &alloc
call
- ld_r1,sp,8
+ ld_r1,sp,24
st_r1,r0,0
li_r1 %1
sb_r1,r0,7
@@ -652,22 +650,22 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## ---- make_string(r1=src, r2=len) -> r0 = tagged string -------------
:make_string
prologue_n3
- st_r1,sp,8
- st_r2,sp,16
+ st_r1,sp,24
+ st_r2,sp,32
mov_r1,r2
li_br &alloc_string
call
- st_r0,sp,24
+ st_r0,sp,40
addi_r1,r0,neg4 ## raw string ptr
addi_r1,r1,8 ## dst payload
- ld_r2,sp,8 ## src payload
- ld_r3,sp,16 ## len
+ ld_r2,sp,24 ## src payload
+ ld_r3,sp,32 ## len
li_br &byte_copy
call
- ld_r0,sp,24
+ ld_r0,sp,40
epilogue_n3
ret
@@ -677,22 +675,22 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## element with `init`.
:make_vector
prologue_n2
- st_r1,sp,8
- st_r2,sp,16
+ st_r1,sp,24
+ st_r2,sp,32
shli_r1,r1,3
addi_r1,r1,8
li_br &alloc
call
- ld_r1,sp,8 ## len_raw
- st_r0,sp,8 ## overwrite slot1 = raw ptr
+ ld_r1,sp,24 ## len_raw
+ st_r0,sp,24 ## overwrite slot1 = raw ptr
st_r1,r0,0
li_r1 %3
sb_r1,r0,7
- ld_r1,sp,8 ## raw ptr
- ld_r2,sp,16 ## init
+ ld_r1,sp,24 ## raw ptr
+ ld_r2,sp,32 ## init
ld_r0,r1,0 ## header word (type|len)
shli_r0,r0,16 ## mask off type byte
shri_r0,r0,16 ## r0 = len_raw
@@ -707,7 +705,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &make_vector_loop
b
:make_vector_done
- ld_r0,sp,8
+ ld_r0,sp,24
ori_r0,r0,2
ori_r0,r0,1 ## tag = 011 (vector)
epilogue_n2
@@ -731,12 +729,12 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
b
:string_to_c_path_ok
mov_r0,sp
- st_r2,r0,8
+ st_r2,r0,24
addi_r1,r1,8 ## payload ptr
- st_r1,r0,16
+ st_r1,r0,32
li_r1 &path_buf
- ld_r2,r0,16
- ld_r3,r0,8
+ ld_r2,r0,32
+ ld_r3,r0,24
li_br &byte_copy
call
li_r1 %0
@@ -746,125 +744,917 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
ret
-## ---- intern(r1=name_ptr, r2=name_len) -> r0 = tagged sym -----------
-## Open-addressing linear probe over a 4096-slot table. Empty slot =
-## 0 word (nil is 0x07, never 0, so this sentinel is unambiguous).
-## Frame layout: slot 1 = saved r6, slot 2 = saved r7, slot 3 = current
-## probe index h.
-:intern
- prologue_n3
- st_r6,sp,8
- st_r7,sp,16
-
- mov_r6,r1 ## r6 = name_ptr (callee-saved copy)
- mov_r7,r2 ## r7 = name_len
+## ---- intern(r1=name_ptr, r2=name_len) -> r0 = tagged sym -----------
+## Open-addressing linear probe over a 4096-slot table. Empty slot =
+## 0 word (nil is 0x07, never 0, so this sentinel is unambiguous).
+## Frame layout: slot 1 = saved r6, slot 2 = saved r7, slot 3 = current
+## probe index h.
+:intern
+ prologue_n3
+ st_r6,sp,24
+ st_r7,sp,32
+
+ mov_r6,r1 ## r6 = name_ptr (callee-saved copy)
+ mov_r7,r2 ## r7 = name_len
+
+ li_br &hash
+ call ## r0 = 64-bit hash
+
+ shli_r0,r0,52
+ shri_r0,r0,52 ## r0 = h & 4095
+
+ mov_r3,sp
+ st_r0,sp,40 ## slot 3 = h
+
+:intern_probe
+ ld_r0,sp,40
+ shli_r0,r0,3 ## r0 = h * 8
+ li_r2 &symbol_table
+ add_r2,r2,r0 ## r2 = &symbol_table[h]
+ ld_r3,r2,0 ## r3 = slot value
+
+ li_br &intern_empty
+ beqz_r3 ## slot == 0 → allocate
+
+ ## Compare existing symbol to (r6, r7).
+ mov_r1,r3 ## r1 = tagged sym
+ mov_r2,r6 ## r2 = cmp_ptr
+ mov_r3,r7 ## r3 = cmp_len
+ li_br &sym_name_equal
+ call ## r0 = 0 or 1
+
+ li_br &intern_hit
+ bnez_r0
+
+ ## Advance h = (h+1) & 4095.
+ ld_r0,sp,40
+ addi_r0,r0,1
+ shli_r0,r0,52
+ shri_r0,r0,52
+ st_r0,sp,40
+ li_br &intern_probe
+ b
+
+:intern_empty
+ ## Allocate a fresh symbol and plant it in the slot.
+ mov_r1,r6
+ mov_r2,r7
+ li_br &make_symbol
+ call ## r0 = tagged sym
+
+ ld_r1,sp,40
+ shli_r1,r1,3
+ li_r2 &symbol_table
+ add_r2,r2,r1
+ st_r0,r2,0 ## write into slot
+
+ li_br &intern_done
+ b
+
+:intern_hit
+ mov_r3,sp
+ ld_r0,sp,40
+ shli_r0,r0,3
+ li_r2 &symbol_table
+ add_r2,r2,r0
+ ld_r0,r2,0 ## r0 = existing tagged sym
+
+:intern_done
+ ld_r6,sp,24
+ ld_r7,sp,32
+ epilogue_n3
+ ret
+
+
+## ---- cons / car / cdr (preserved from step 2) -----------------------
+:cons
+ prologue_n2
+ st_r1,sp,24
+ st_r2,sp,32
+ li_br &pair_alloc
+ call
+ ld_r1,sp,24
+ st_r1,r0,0
+ ld_r2,sp,32
+ st_r2,r0,8
+ ori_r0,r0,2
+ epilogue_n2
+ ret
+
+:car
+ addi_r1,r1,neg2
+ ld_r0,r1,0
+ ret
+
+:cdr
+ addi_r1,r1,neg2
+ ld_r0,r1,8
+ ret
+
+
+## ---- obj_freelist_for_size(size) -> &head cell or 0 -----------------
+:obj_freelist_for_size
+ li_r0 %16
+ li_br &obj_freelist_16
+ beq_r1,r0
+ li_r0 %24
+ li_br &obj_freelist_24
+ beq_r1,r0
+ li_r0 %32
+ li_br &obj_freelist_32
+ beq_r1,r0
+ li_r0 %40
+ li_br &obj_freelist_40
+ beq_r1,r0
+ li_r0 %48
+ li_br &obj_freelist_48
+ beq_r1,r0
+ li_r0 %56
+ li_br &obj_freelist_56
+ beq_r1,r0
+ li_r0 %64
+ li_br &obj_freelist_64
+ beq_r1,r0
+ li_r0 %80
+ li_br &obj_freelist_80
+ beq_r1,r0
+ li_r0 %96
+ li_br &obj_freelist_96
+ beq_r1,r0
+ li_r0 %128
+ li_br &obj_freelist_128
+ beq_r1,r0
+ li_r0 %0
+ ret
+:obj_freelist_16
+ li_r0 &free_list_obj16
+ ret
+:obj_freelist_24
+ li_r0 &free_list_obj24
+ ret
+:obj_freelist_32
+ li_r0 &free_list_obj32
+ ret
+:obj_freelist_40
+ li_r0 &free_list_obj40
+ ret
+:obj_freelist_48
+ li_r0 &free_list_obj48
+ ret
+:obj_freelist_56
+ li_r0 &free_list_obj56
+ ret
+:obj_freelist_64
+ li_r0 &free_list_obj64
+ ret
+:obj_freelist_80
+ li_r0 &free_list_obj80
+ ret
+:obj_freelist_96
+ li_r0 &free_list_obj96
+ ret
+:obj_freelist_128
+ li_r0 &free_list_obj128
+ ret
+
+
+## ---- pair_alloc() -> raw pair ptr -----------------------------------
+:pair_alloc
+ prologue
+
+ li_r3 &free_list_pair
+ ld_r0,r3,0
+ li_br &pair_alloc_bump
+ beqz_r0
+ ld_r1,r0,0
+ st_r1,r3,0
+ epilogue
+ ret
+
+:pair_alloc_bump
+ li_r3 &pair_heap_next
+ ld_r0,r3,0
+ addi_r0,r0,7
+ shri_r0,r0,3
+ shli_r0,r0,3
+ addi_r2,r0,16
+ li_r1 &pair_heap_end
+ li_br &pair_alloc_need_gc
+ blt_r1,r2
+ st_r2,r3,0
+ epilogue
+ ret
+
+:pair_alloc_need_gc
+ li_br &alloc_oom
+ b
+
+:pair_alloc_retry_bump
+ li_r3 &pair_heap_next
+ ld_r0,r3,0
+ addi_r0,r0,7
+ shri_r0,r0,3
+ shli_r0,r0,3
+ addi_r2,r0,16
+ li_r1 &pair_heap_end
+ li_br &alloc_oom
+ blt_r1,r2
+ st_r2,r3,0
+ epilogue
+ ret
+
+
+## ---- alloc / obj_alloc(size) -> raw object ptr ----------------------
+:alloc
+:obj_alloc
+ prologue_n2
+
+ addi_r1,r1,7
+ shri_r1,r1,3
+ shli_r1,r1,3
+ st_r1,sp,24 ## slot 1 = rounded size
+
+ li_br &obj_freelist_for_size
+ call
+ st_r0,sp,32 ## slot 2 = &free_list head or 0
+
+ li_br &obj_alloc_bump
+ beqz_r0
+ ld_r1,r0,0
+ li_br &obj_alloc_bump
+ beqz_r1
+ ld_r2,r1,8
+ ld_r3,sp,32
+ st_r2,r3,0
+ mov_r0,r1
+ epilogue_n2
+ ret
+
+:obj_alloc_bump
+ ld_r1,sp,24
+ li_r3 &obj_heap_next
+ ld_r0,r3,0
+ addi_r0,r0,7
+ shri_r0,r0,3
+ shli_r0,r0,3
+ add_r2,r0,r1
+ li_r1 &obj_heap_end
+ li_br &obj_alloc_need_gc
+ blt_r1,r2
+ st_r2,r3,0
+ epilogue_n2
+ ret
+
+:obj_alloc_need_gc
+ li_br &alloc_oom
+ b
+
+:obj_alloc_retry_bump
+ ld_r1,sp,24
+ li_r3 &obj_heap_next
+ ld_r0,r3,0
+ addi_r0,r0,7
+ shri_r0,r0,3
+ shli_r0,r0,3
+ add_r2,r0,r1
+ li_r1 &obj_heap_end
+ li_br &alloc_oom
+ blt_r1,r2
+ st_r2,r3,0
+ epilogue_n2
+ ret
+
+
+## ---- obj_size_bytes(raw obj ptr) -> r0 = rounded chunk size ---------
+:obj_size_bytes
+ lb_r2,r1,7
+ li_r0 %0
+ li_br &obj_size_free
+ beq_r2,r0
+ li_r0 %1
+ li_br &obj_size_string
+ beq_r2,r0
+ li_r0 %2
+ li_br &obj_size_symbol
+ beq_r2,r0
+ li_r0 %3
+ li_br &obj_size_vector
+ beq_r2,r0
+ li_r0 %4
+ li_br &obj_size_closure
+ beq_r2,r0
+ li_r0 %5
+ li_br &obj_size_primitive
+ beq_r2,r0
+ li_r0 %6
+ li_br &obj_size_primitive
+ beq_r2,r0
+ li_r0 %16
+ ret
+
+:obj_size_free
+ ld_r0,r1,0
+ shli_r0,r0,16
+ shri_r0,r0,16
+ ret
+
+:obj_size_string
+:obj_size_symbol
+ ld_r0,r1,0
+ shli_r0,r0,16
+ shri_r0,r0,16
+ addi_r0,r0,15
+ shri_r0,r0,3
+ shli_r0,r0,3
+ ret
+
+:obj_size_vector
+ ld_r0,r1,0
+ shli_r0,r0,16
+ shri_r0,r0,16
+ shli_r0,r0,3
+ addi_r0,r0,8
+ ret
+
+:obj_size_closure
+ li_r0 %32
+ ret
+
+:obj_size_primitive
+ li_r0 %16
+ ret
+
+
+## ---- obj_is_live_marked(raw obj ptr) -> r0 = 1 iff marked live -----
+:obj_is_live_marked
+ lb_r2,r1,7
+ li_br &obj_is_dead
+ beqz_r2
+ lb_r2,r1,6
+ andi_r2,r2,1
+ li_br &obj_is_dead
+ beqz_r2
+ li_r0 %1
+ ret
+:obj_is_dead
+ li_r0 %0
+ ret
+
+
+## ---- obj_mark_seen_or_set(raw obj ptr) -> r0 = 1 iff already marked -
+:obj_mark_seen_or_set
+ lb_r2,r1,6
+ andi_r3,r2,1
+ li_br &obj_mark_seen
+ bnez_r3
+ ori_r2,r2,1
+ sb_r2,r1,6
+ li_r0 %0
+ ret
+:obj_mark_seen
+ li_r0 %1
+ ret
+
+
+## ---- pair mark-bitmap helpers ---------------------------------------
+:pair_mark_seen_or_set
+ li_r2 &pair_heap_start
+ sub_r2,r1,r2
+ shri_r2,r2,4 ## slot index
+ mov_r3,r2
+ shri_r3,r3,3 ## byte index
+ li_r0 &pair_mark_bitmap
+ add_r0,r0,r3 ## r0 = byte ptr
+ lb_r3,r0,0 ## r3 = byte
+ andi_r1,r2,7 ## r1 = bit index
+ li_r2 %1
+ shl_r2,r2,r1 ## r2 = mask
+ mov_r1,r3
+ and_r1,r1,r2
+ li_br &pair_mark_seen
+ bnez_r1
+ or_r3,r3,r2
+ sb_r3,r0,0
+ li_r0 %0
+ ret
+:pair_mark_seen
+ li_r0 %1
+ ret
+
+:pair_mark_test
+ li_r2 &pair_heap_start
+ sub_r2,r1,r2
+ shri_r2,r2,4
+ mov_r3,r2
+ shri_r3,r3,3
+ li_r0 &pair_mark_bitmap
+ add_r0,r0,r3
+ lb_r3,r0,0
+ andi_r1,r2,7
+ li_r2 %1
+ shl_r2,r2,r1
+ and_r3,r3,r2
+ li_r0 %0
+ li_br &pair_mark_test_done
+ beqz_r3
+ li_r0 %1
+:pair_mark_test_done
+ ret
+
+:pair_mark_clear
+ li_r2 &pair_heap_start
+ sub_r2,r1,r2
+ shri_r2,r2,4
+ mov_r3,r2
+ shri_r3,r3,3
+ li_r0 &pair_mark_bitmap
+ add_r0,r0,r3
+ lb_r3,r0,0
+ andi_r1,r2,7
+ li_r2 %1
+ shl_r2,r2,r1
+ sub_r3,r3,r2
+ sb_r3,r0,0
+ ret
+
+
+## ---- mark_push(tagged value) ----------------------------------------
+:mark_push
+ li_r2 &mark_stack_next
+ ld_r3,r2,0
+ li_r0 &mark_stack_end
+ li_br &mark_push_recurse
+ beq_r3,r0
+ st_r1,r3,0
+ addi_r3,r3,8
+ st_r3,r2,0
+ ret
+:mark_push_recurse
+ li_br &mark_value
+ call
+ ret
+
+
+## ---- mark_value(tagged value) ---------------------------------------
+:mark_value
+ prologue_n4
+
+ andi_r0,r1,7
+ li_r2 %2
+ li_br &mark_value_pair
+ beq_r0,r2
+ li_r2 %3
+ li_br &mark_value_object
+ beq_r0,r2
+ li_r2 %4
+ li_br &mark_value_object
+ beq_r0,r2
+ li_r2 %5
+ li_br &mark_value_object
+ beq_r0,r2
+ li_r2 %6
+ li_br &mark_value_object
+ beq_r0,r2
+ epilogue_n4
+ ret
+
+:mark_value_pair
+ addi_r1,r1,neg2
+ st_r1,sp,24 ## slot 1 = raw pair ptr
+ li_br &pair_mark_seen_or_set
+ call
+ li_br &mark_value_done
+ bnez_r0
+
+ ld_r1,sp,24
+ ld_r0,r1,0
+ st_r0,sp,32 ## slot 2 = car
+ ld_r1,r1,8
+ li_br &mark_push
+ call
+ ld_r1,sp,32
+ li_br &mark_push
+ call
+ li_br &mark_value_done
+ b
+
+:mark_value_object
+ andi_r0,r1,7
+ sub_r1,r1,r0 ## raw object ptr
+ st_r1,sp,24 ## slot 1 = raw object ptr
+ li_br &obj_mark_seen_or_set
+ call
+ li_br &mark_value_done
+ bnez_r0
+
+ ld_r1,sp,24
+ lb_r0,r1,7
+ li_r2 %3
+ li_br &mark_value_vector
+ beq_r0,r2
+ li_r2 %4
+ li_br &mark_value_closure
+ beq_r0,r2
+ li_br &mark_value_done
+ b
+
+:mark_value_vector
+ ld_r0,r1,0
+ shli_r0,r0,16
+ shri_r0,r0,16
+ addi_r2,r1,8
+ st_r2,sp,24 ## slot 1 = cursor
+ st_r0,sp,32 ## slot 2 = remaining
+
+:mark_value_vector_loop
+ ld_r0,sp,32
+ li_br &mark_value_done
+ beqz_r0
+ ld_r2,sp,24
+ ld_r1,r2,0
+ li_br &mark_push
+ call
+ ld_r2,sp,24
+ addi_r2,r2,8
+ st_r2,sp,24
+ ld_r0,sp,32
+ addi_r0,r0,neg1
+ st_r0,sp,32
+ li_br &mark_value_vector_loop
+ b
+
+:mark_value_closure
+ ld_r0,r1,16
+ st_r0,sp,32 ## slot 2 = body
+ ld_r0,r1,24
+ st_r0,sp,40 ## slot 3 = env
+ ld_r1,r1,8
+ li_br &mark_push
+ call
+ ld_r1,sp,32
+ li_br &mark_push
+ call
+ ld_r1,sp,40
+ li_br &mark_push
+ call
+
+:mark_value_done
+ epilogue_n4
+ ret
+
+
+## ---- mark_frame_slots(raw frame ptr) --------------------------------
+:mark_frame_slots
+ prologue_n2
+ ld_r0,r1,16
+ addi_r2,r1,24
+ st_r2,sp,24 ## slot 1 = cursor
+ st_r0,sp,32 ## slot 2 = remaining
+
+:mark_frame_slots_loop
+ ld_r0,sp,32
+ li_br &mark_frame_slots_done
+ beqz_r0
+ ld_r2,sp,24
+ ld_r1,r2,0
+ li_br &mark_value
+ call
+ ld_r2,sp,24
+ addi_r2,r2,8
+ st_r2,sp,24
+ ld_r0,sp,32
+ addi_r0,r0,neg1
+ st_r0,sp,32
+ li_br &mark_frame_slots_loop
+ b
+
+:mark_frame_slots_done
+ epilogue_n2
+ ret
+
+
+## ---- gc_mark_symbol_table() -----------------------------------------
+:gc_mark_symbol_table
+ prologue
+ li_r1 &symbol_table
+ st_r1,sp,24
+
+:gc_mark_symbol_table_loop
+ ld_r2,sp,24
+ li_r0 &symbol_table_end
+ li_br &gc_mark_symbol_table_done
+ beq_r2,r0
+ ld_r1,r2,0
+ li_br &mark_value
+ call
+ ld_r2,sp,24
+ addi_r2,r2,8
+ st_r2,sp,24
+ li_br &gc_mark_symbol_table_loop
+ b
+
+:gc_mark_symbol_table_done
+ epilogue
+ ret
+
+
+## ---- gc_mark_prim_argv() --------------------------------------------
+:gc_mark_prim_argv
+ prologue_n2
+ li_r1 &prim_argv
+ st_r1,sp,24 ## slot 1 = cursor
+ li_r1 %32
+ st_r1,sp,32 ## slot 2 = remaining slots
+
+:gc_mark_prim_argv_loop
+ ld_r0,sp,32
+ li_br &gc_mark_prim_argv_done
+ beqz_r0
+ ld_r2,sp,24
+ ld_r1,r2,0
+ li_br &mark_value
+ call
+ ld_r2,sp,24
+ addi_r2,r2,8
+ st_r2,sp,24
+ ld_r0,sp,32
+ addi_r0,r0,neg1
+ st_r0,sp,32
+ li_br &gc_mark_prim_argv_loop
+ b
+
+:gc_mark_prim_argv_done
+ epilogue_n2
+ ret
+
+
+## ---- gc_mark_stack_roots() ------------------------------------------
+:gc_mark_stack_roots
+ prologue_n2
+ li_r1 &gc_root_fp
+ ld_r1,r1,0
+ st_r1,sp,24 ## slot 1 = current frame ptr
+ li_r1 &stack_bottom_fp
+ ld_r1,r1,0
+ st_r1,sp,32 ## slot 2 = bottom caller sp
+
+:gc_mark_stack_roots_loop
+ ld_r1,sp,24
+ li_br &gc_mark_stack_roots_done
+ beqz_r1
+ li_br &mark_frame_slots
+ call
+ ld_r1,sp,24
+ ld_r1,r1,8
+ st_r1,sp,24
+ ld_r0,sp,32
+ li_br &gc_mark_stack_roots_done
+ beq_r1,r0
+ li_br &gc_mark_stack_roots_loop
+ b
+
+:gc_mark_stack_roots_done
+ epilogue_n2
+ ret
+
+
+## ---- gc_mark_all() ---------------------------------------------------
+:gc_mark_all
+ prologue
+ li_r1 &mark_stack
+ li_r2 &mark_stack_next
+ st_r1,r2,0
+
+ li_r1 &global_env_cell
+ ld_r1,r1,0
+ li_br &mark_value
+ call
+
+ li_br &gc_mark_symbol_table
+ call
+ li_br &gc_mark_prim_argv
+ call
+ li_br &gc_mark_stack_roots
+ call
+
+:gc_mark_drain
+ li_r2 &mark_stack_next
+ ld_r0,r2,0
+ li_r1 &mark_stack
+ li_br &gc_mark_done
+ beq_r0,r1
+ addi_r0,r0,neg8
+ st_r0,r2,0
+ ld_r1,r0,0
+ li_br &mark_value
+ call
+ li_br &gc_mark_drain
+ b
+
+:gc_mark_done
+ epilogue
+ ret
+
+
+## ---- gc_clear_freelists() -------------------------------------------
+:gc_clear_freelists
+ li_r0 %0
+ li_r1 &free_list_pair
+ st_r0,r1,0
+ li_r1 &free_list_obj16
+ st_r0,r1,0
+ li_r1 &free_list_obj24
+ st_r0,r1,0
+ li_r1 &free_list_obj32
+ st_r0,r1,0
+ li_r1 &free_list_obj40
+ st_r0,r1,0
+ li_r1 &free_list_obj48
+ st_r0,r1,0
+ li_r1 &free_list_obj56
+ st_r0,r1,0
+ li_r1 &free_list_obj64
+ st_r0,r1,0
+ li_r1 &free_list_obj80
+ st_r0,r1,0
+ li_r1 &free_list_obj96
+ st_r0,r1,0
+ li_r1 &free_list_obj128
+ st_r0,r1,0
+ ret
+
+
+## ---- gc_sweep_pair() -------------------------------------------------
+:gc_sweep_pair
+ prologue
+ li_r1 &pair_heap_start
+ st_r1,sp,24
- li_br &hash
- call ## r0 = 64-bit hash
- shli_r0,r0,52
- shri_r0,r0,52 ## r0 = h & 4095
+:gc_sweep_pair_loop
+ ld_r1,sp,24
+ li_r0 &pair_heap_next
+ ld_r0,r0,0
+ li_br &gc_sweep_pair_done
+ beq_r1,r0
- mov_r3,sp
- st_r0,sp,24 ## slot 3 = h
+ li_br &pair_mark_test
+ call
+ li_br &gc_sweep_pair_dead
+ beqz_r0
-:intern_probe
- ld_r0,sp,24
- shli_r0,r0,3 ## r0 = h * 8
- li_r2 &symbol_table
- add_r2,r2,r0 ## r2 = &symbol_table[h]
- ld_r3,r2,0 ## r3 = slot value
+ ld_r1,sp,24
+ li_br &pair_mark_clear
+ call
+ li_br &gc_sweep_pair_advance
+ b
- li_br &intern_empty
- beqz_r3 ## slot == 0 → allocate
+:gc_sweep_pair_dead
+ ld_r1,sp,24
+ li_r2 &free_list_pair
+ ld_r0,r2,0
+ st_r0,r1,0
+ st_r1,r2,0
- ## Compare existing symbol to (r6, r7).
- mov_r1,r3 ## r1 = tagged sym
- mov_r2,r6 ## r2 = cmp_ptr
- mov_r3,r7 ## r3 = cmp_len
- li_br &sym_name_equal
- call ## r0 = 0 or 1
+:gc_sweep_pair_advance
+ ld_r1,sp,24
+ addi_r1,r1,16
+ st_r1,sp,24
+ li_br &gc_sweep_pair_loop
+ b
- li_br &intern_hit
- bnez_r0
+:gc_sweep_pair_done
+ epilogue
+ ret
- ## Advance h = (h+1) & 4095.
- ld_r0,sp,24
- addi_r0,r0,1
- shli_r0,r0,52
- shri_r0,r0,52
- st_r0,sp,24
- li_br &intern_probe
- b
-:intern_empty
- ## Allocate a fresh symbol and plant it in the slot.
- mov_r1,r6
- mov_r2,r7
- li_br &make_symbol
- call ## r0 = tagged sym
+## ---- gc_sweep_obj() --------------------------------------------------
+:gc_sweep_obj
+ prologue_n4
+ li_r1 &obj_heap_start
+ st_r1,sp,24 ## slot 1 = current ptr
+:gc_sweep_obj_loop
ld_r1,sp,24
- shli_r1,r1,3
- li_r2 &symbol_table
- add_r2,r2,r1
- st_r0,r2,0 ## write into slot
+ li_r0 &obj_heap_next
+ ld_r0,r0,0
+ li_br &gc_sweep_obj_done
+ beq_r1,r0
- li_br &intern_done
+ li_br &obj_is_live_marked
+ call
+ li_br &gc_sweep_obj_dead
+ beqz_r0
+
+ ld_r1,sp,24
+ lb_r0,r1,6
+ addi_r0,r0,neg1
+ sb_r0,r1,6
+ li_br &obj_size_bytes
+ call
+ ld_r1,sp,24
+ add_r1,r1,r0
+ st_r1,sp,24
+ li_br &gc_sweep_obj_loop
b
-:intern_hit
- mov_r3,sp
- ld_r0,sp,24
- shli_r0,r0,3
- li_r2 &symbol_table
- add_r2,r2,r0
- ld_r0,r2,0 ## r0 = existing tagged sym
+:gc_sweep_obj_dead
+ ld_r1,sp,24
+ li_br &obj_size_bytes
+ call
+ ld_r1,sp,24
+ add_r2,r1,r0
+ st_r2,sp,32 ## slot 2 = run end
-:intern_done
- ld_r6,sp,8
- ld_r7,sp,16
- epilogue_n3
- ret
+:gc_sweep_obj_dead_scan
+ ld_r2,sp,32
+ li_r0 &obj_heap_next
+ ld_r0,r0,0
+ li_br &gc_sweep_obj_tail_dead
+ beq_r2,r0
+ mov_r1,r2
+ li_br &obj_is_live_marked
+ call
+ li_br &gc_sweep_obj_dead_run_ready
+ bnez_r0
-## ---- cons / car / cdr (preserved from step 2) -----------------------
-:cons
- prologue_n2
- st_r1,sp,8
- st_r2,sp,16
- li_r1 %16
- li_br &alloc
+ ld_r1,sp,32
+ li_br &obj_size_bytes
call
- ld_r1,sp,8
+ ld_r2,sp,32
+ add_r2,r2,r0
+ st_r2,sp,32
+ li_br &gc_sweep_obj_dead_scan
+ b
+
+:gc_sweep_obj_tail_dead
+ ld_r1,sp,24
+ li_r0 &obj_heap_next
st_r1,r0,0
- ld_r2,sp,16
- st_r2,r0,8
- ori_r0,r0,2
- epilogue_n2
- ret
+ li_br &gc_sweep_obj_done
+ b
-:car
- addi_r1,r1,neg2
- ld_r0,r1,0
+:gc_sweep_obj_dead_run_ready
+ ld_r1,sp,32
+ ld_r2,sp,24
+ sub_r1,r1,r2 ## r1 = run size
+ st_r1,sp,40 ## slot 3 = run size
+ li_br &obj_freelist_for_size
+ call
+ st_r0,sp,48 ## slot 4 = &free_list head or 0
+
+ li_br &gc_sweep_obj_write_pseudo
+ beqz_r0
+
+ ld_r2,sp,24 ## run start
+ ld_r1,sp,40 ## run size
+ st_r1,r2,0
+ ld_r3,sp,48
+ ld_r0,r3,0
+ st_r0,r2,8
+ st_r2,r3,0
+ li_br &gc_sweep_obj_advance
+ b
+
+:gc_sweep_obj_write_pseudo
+ ld_r2,sp,24
+ ld_r1,sp,40
+ st_r1,r2,0
+
+:gc_sweep_obj_advance
+ ld_r1,sp,32
+ st_r1,sp,24
+ li_br &gc_sweep_obj_loop
+ b
+
+:gc_sweep_obj_done
+ epilogue_n4
ret
-:cdr
- addi_r1,r1,neg2
- ld_r0,r1,8
+
+## ---- gc_sweep_all() --------------------------------------------------
+:gc_sweep_all
+ prologue
+ li_br &gc_clear_freelists
+ call
+ li_br &gc_sweep_pair
+ call
+ li_br &gc_sweep_obj
+ call
+ epilogue
ret
-## ---- alloc(size) -> raw_ptr (preserved from step 2) -----------------
-:alloc
+## ---- gc() ------------------------------------------------------------
+:gc
prologue
- li_r3 &heap_next
- ld_r0,r3,0
- add_r2,r0,r1
- li_r1 &heap_end
- ld_r1,r1,0
- li_br &alloc_oom
- blt_r1,r2
- st_r2,r3,0
+ ld_r1,sp,8 ## metadata: caller frame ptr
+ li_r2 &gc_root_fp
+ st_r1,r2,0
+ li_br &gc_mark_all
+ call
+ li_br &gc_sweep_all
+ call
epilogue
ret
@@ -1182,7 +1972,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
:read_number
prologue
mov_r3,sp
- st_r6,sp,8
+ st_r6,sp,24
li_r6 %0 ## r6 = 0 (accumulator)
@@ -1265,7 +2055,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## Tag as fixnum (low 3 bits = 001 = shift-left-3 + OR 1).
shli_r0,r6,3
ori_r0,r0,1
- ld_r6,sp,8
+ ld_r6,sp,24
epilogue
ret
@@ -1351,8 +2141,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
:read_symbol
prologue_n2
mov_r3,sp
- st_r6,sp,8
- st_r7,sp,16
+ st_r6,sp,24
+ st_r7,sp,32
## r7 = &src[cursor] (stored start)
li_r1 &src_cursor
@@ -1392,8 +2182,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &intern
call
- ld_r6,sp,8
- ld_r7,sp,16
+ ld_r6,sp,24
+ ld_r7,sp,32
epilogue_n2
ret
@@ -1403,8 +2193,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## four escape forms the seed needs: \n, \t, \\, and \".
:read_string
prologue_n2
- st_r6,sp,8
- st_r7,sp,16
+ st_r6,sp,24
+ st_r7,sp,32
li_br &advance_char
call ## eat opening '"'
@@ -1500,8 +2290,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &make_string
call
- ld_r6,sp,8
- ld_r7,sp,16
+ ld_r6,sp,24
+ ld_r7,sp,32
epilogue_n2
ret
@@ -1542,13 +2332,13 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &read_expr
call ## r0 = head
- st_r0,sp,8 ## slot 1 = head (spill across next call)
+ st_r0,sp,24 ## slot 1 = head (spill across next call)
li_br &read_list
call ## r0 = tail (recursive)
## cons(head, tail)
- ld_r1,sp,8
+ ld_r1,sp,24
mov_r2,r0
li_br &cons
call
@@ -1569,10 +2359,10 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## Bare `.` followed by non-delim → fall back to regular item path.
li_br &read_expr
call
- st_r0,sp,8
+ st_r0,sp,24
li_br &read_list
call
- ld_r1,sp,8
+ ld_r1,sp,24
mov_r2,r0
li_br &cons
call
@@ -1586,7 +2376,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
call
li_br &read_expr
call ## r0 = dotted tail expr
- st_r0,sp,8
+ st_r0,sp,24
li_br &skip_ws
call
li_br &peek_char
@@ -1596,7 +2386,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
bne_r0,r1
li_br &advance_char
call ## eat ')'
- ld_r0,sp,8
+ ld_r0,sp,24
epilogue
ret
@@ -1829,7 +2619,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## four reader shorthand forms (quote/quasi/unquote/unquote-splicing).
:read_quoted_wrap
prologue
- st_r1,sp,8 ## save tagged sym
+ st_r1,sp,24 ## save tagged sym
li_br &read_expr
call ## r0 = expr
mov_r1,r0
@@ -1837,7 +2627,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &cons
call ## r0 = (expr . nil)
mov_r2,r0
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cons ## r0 = (sym . (expr . nil))
tail
@@ -1854,7 +2644,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_r1 &src_cursor
ld_r0,r1,0
- st_r0,sp,8 ## slot 1 = start cursor
+ st_r0,sp,24 ## slot 1 = start cursor
li_br &peek_char
call ## r0 = first char (must not be EOF)
@@ -1863,7 +2653,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &err_reader_bad
beq_r0,r1
- st_r0,sp,16 ## slot 2 = first char
+ st_r0,sp,32 ## slot 2 = first char
li_br &advance_char
call ## eat first char
@@ -1894,7 +2684,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## r2 = len (cursor - start), r0 = ptr (base + start).
li_r1 &src_cursor
ld_r2,r1,0
- ld_r1,sp,8
+ ld_r1,sp,24
sub_r2,r2,r1
li_r0 &src_base
ld_r0,r0,0
@@ -2008,7 +2798,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
ret
:rcl_single
- ld_r0,sp,16
+ ld_r0,sp,32
shli_r0,r0,3
ori_r0,r0,1
epilogue_n4
@@ -2024,7 +2814,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
call ## eat '('
li_br &read_list
call ## r0 = tagged list
- st_r0,sp,8 ## slot 1 = list head
+ st_r0,sp,24 ## slot 1 = list head
mov_r1,r0
li_r2 %0 ## r2 = count
@@ -2043,11 +2833,11 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_r2 NIL
li_br &make_vector
call ## r0 = tagged vector
- st_r0,sp,16 ## slot 2 = vec
+ st_r0,sp,32 ## slot 2 = vec
addi_r3,r0,neg3
addi_r3,r3,8 ## r3 = payload cursor
- ld_r1,sp,8
+ ld_r1,sp,24
:rvl_fill
li_r0 NIL
@@ -2062,7 +2852,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
b
:rvl_done
- ld_r0,sp,16
+ ld_r0,sp,32
epilogue_n3
ret
@@ -2096,9 +2886,9 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
:display_uint
prologue_n3
- st_r6,sp,8
- st_r7,sp,16
- st_r2,sp,24 ## save fd
+ st_r6,sp,24
+ st_r7,sp,32
+ st_r2,sp,40 ## save fd
li_r6 &digit_buf_end ## r6 = end-of-buffer cursor (moves left)
mov_r7,r1 ## r7 = value (mutated)
@@ -2136,7 +2926,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
:du_write
## Stash fd into r0 first — computing len clobbers r3 (we need it
## as SP for the slot-3 load), so grab fd before that step.
- ld_r0,sp,24 ## r0 = fd (temp; sys_write will overwrite r0)
+ ld_r0,sp,40 ## r0 = fd (temp; sys_write will overwrite r0)
li_r1 &digit_buf_end
sub_r3,r1,r6 ## r3 = len = digit_buf_end - r6
mov_r1,r0 ## r1 = fd
@@ -2144,8 +2934,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_r0 sys_write
syscall
- ld_r6,sp,8
- ld_r7,sp,16
+ ld_r6,sp,24
+ ld_r7,sp,32
epilogue_n3
ret
@@ -2244,8 +3034,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
addi_r2,r2,neg5 ## r2 = raw header ptr (tag=0b101 → -5)
ld_r3,r2,0 ## r3 = header
## Low 48 bits = length. Mask by SHL/SHR 16.
- shli_r3,r3,16 ## clear top 16
- shri_r3,r3,16 ## r3 = length (0..2^48)
+ shli_r3,r3,32 ## clear top 16
+ shri_r3,r3,32 ## r3 = length (0..2^48)
addi_r2,r2,8 ## r2 = name ptr
li_r0 sys_write
li_r1 %1
@@ -2255,8 +3045,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
:display_pair
## r2 = tagged pair. Shares display's PROLOGUE_N2 frame.
- st_r6,sp,8 ## save r6 into display's slot 1
- st_r7,sp,16 ## save r7 into display's slot 2
+ st_r6,sp,24 ## save r6 into display's slot 1
+ st_r7,sp,32 ## save r7 into display's slot 2
mov_r6,r2 ## r6 = tagged pair (we'll walk cdr chain)
@@ -2319,8 +3109,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &putc
call
- ld_r6,sp,8
- ld_r7,sp,16
+ ld_r6,sp,24
+ ld_r7,sp,32
epilogue_n2
ret
@@ -2334,8 +3124,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
:display_string
addi_r2,r2,neg4 ## r2 = raw header ptr
ld_r3,r2,0 ## r3 = header word
- shli_r3,r3,16
- shri_r3,r3,16 ## r3 = length (low 48 bits)
+ shli_r3,r3,32
+ shri_r3,r3,32 ## r3 = length (low 48 bits)
addi_r2,r2,8 ## r2 = payload ptr
li_r0 sys_write
li_r1 %1
@@ -2392,13 +3182,13 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## write's PROLOGUE_N2 frame; r6=cursor, r7=remaining-bytes saved in
## the two slots across putc calls.
:write_string
- st_r6,sp,8
- st_r7,sp,16
+ st_r6,sp,24
+ st_r7,sp,32
addi_r2,r2,neg4
ld_r3,r2,0
- shli_r3,r3,16
- shri_r3,r3,16 ## r3 = length
+ shli_r3,r3,32
+ shri_r3,r3,32 ## r3 = length
addi_r2,r2,8 ## r2 = payload ptr
mov_r6,r2 ## r6 = cursor
mov_r7,r3 ## r7 = length
@@ -2445,8 +3235,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_r1 %34 ## '"'
li_br &putc
call
- ld_r6,sp,8
- ld_r7,sp,16
+ ld_r6,sp,24
+ ld_r7,sp,32
epilogue_n2
ret
@@ -2455,8 +3245,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## Mirror of display_pair but recurses through write (strings quoted).
## Shares write's PROLOGUE_N2 frame.
:write_pair
- st_r6,sp,8
- st_r7,sp,16
+ st_r6,sp,24
+ st_r7,sp,32
mov_r6,r2 ## r6 = tagged pair cursor
@@ -2515,8 +3305,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &putc
call
- ld_r6,sp,8
- ld_r7,sp,16
+ ld_r6,sp,24
+ ld_r7,sp,32
epilogue_n2
ret
@@ -2534,8 +3324,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## fall out naturally because lookup_alist returns the leftmost match.
:gset
prologue_n2
- st_r1,sp,8 ## save sym
- st_r2,sp,16 ## save val
+ st_r1,sp,24 ## save sym
+ st_r2,sp,32 ## save val
li_br &cons
call ## r0 = (sym . val)
@@ -2558,12 +3348,12 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
:lookup_alist
prologue_n3
mov_r3,sp
- st_r1,sp,8 ## slot 1 = sym
- st_r2,sp,16 ## slot 2 = cursor
+ st_r1,sp,24 ## slot 1 = sym
+ st_r2,sp,32 ## slot 2 = cursor
:lookup_alist_loop
mov_r3,sp
- ld_r2,sp,16
+ ld_r2,sp,32
li_r1 %7
li_br &lookup_alist_miss
beq_r2,r1 ## cursor == nil → miss
@@ -2573,27 +3363,27 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &car
call ## r0 = (k . v)
- st_r0,sp,24 ## slot 3 = pair
+ st_r0,sp,40 ## slot 3 = pair
mov_r1,r0
li_br &car
call ## r0 = key
mov_r3,sp
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &lookup_alist_hit
beq_r0,r1
## advance cursor := cdr(cursor)
- ld_r1,sp,16
+ ld_r1,sp,32
li_br &cdr
call
- st_r0,sp,16
+ st_r0,sp,32
li_br &lookup_alist_loop
b
:lookup_alist_hit
- ld_r0,sp,24
+ ld_r0,sp,40
epilogue_n3
ret
@@ -2607,7 +3397,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## Local first, global second. Errors out via &err_unbound on miss.
:lookup
prologue_n2
- st_r1,sp,8 ## save sym for possible global retry
+ st_r1,sp,24 ## save sym for possible global retry
li_br &lookup_alist
call ## r0 = local pair or nil
@@ -2623,7 +3413,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
ret
:lookup_global
- ld_r1,sp,8
+ ld_r1,sp,24
li_r2 &global_env_cell
ld_r2,r2,0
li_br &lookup_alist
@@ -2649,64 +3439,64 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
prologue_n4
mov_r0,r3 ## save env before clobbering r3
mov_r3,sp
- st_r1,sp,8 ## slot 1 = names
- st_r2,sp,16 ## slot 2 = vals
- st_r0,sp,24 ## slot 3 = env accumulator
+ st_r1,sp,24 ## slot 1 = names
+ st_r2,sp,32 ## slot 2 = vals
+ st_r0,sp,40 ## slot 3 = env accumulator
:env_extend_loop
mov_r3,sp
- ld_r1,sp,8
+ ld_r1,sp,24
li_r2 %7
li_br &env_extend_done
beq_r1,r2 ## names == nil → done
- ld_r1,sp,16
+ ld_r1,sp,32
li_r2 %7
li_br &err_arity
beq_r1,r2 ## vals == nil → arity error
## name = car(names)
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &car
call ## r0 = name
- st_r0,sp,32 ## slot 4 = name
+ st_r0,sp,48 ## slot 4 = name
## val = car(vals)
- ld_r1,sp,16
+ ld_r1,sp,32
li_br &car
call ## r0 = val
## pair = cons(name, val)
mov_r2,r0
- ld_r1,sp,32
+ ld_r1,sp,48
li_br &cons
call ## r0 = (name . val)
## env := cons(pair, env)
mov_r1,r0
- ld_r2,sp,24
+ ld_r2,sp,40
li_br &cons
call ## r0 = new env
- st_r0,sp,24
+ st_r0,sp,40
## names := cdr(names)
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cdr
call
- st_r0,sp,8
+ st_r0,sp,24
## vals := cdr(vals)
- ld_r1,sp,16
+ ld_r1,sp,32
li_br &cdr
call
- st_r0,sp,16
+ st_r0,sp,32
li_br &env_extend_loop
b
:env_extend_done
- ld_r0,sp,24
+ ld_r0,sp,40
epilogue_n4
ret
@@ -2719,9 +3509,9 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
:make_closure
prologue_n3
mov_r0,r3 ## save env
- st_r1,sp,8 ## slot 1 = params
- st_r2,sp,16 ## slot 2 = body
- st_r0,sp,24 ## slot 3 = env
+ st_r1,sp,24 ## slot 1 = params
+ st_r2,sp,32 ## slot 2 = body
+ st_r0,sp,40 ## slot 3 = env
li_r1 %32 ## 32 bytes
li_br &alloc
@@ -2730,11 +3520,11 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_r1 %4
sb_r1,r0,7 ## type = 4 (closure)
- ld_r1,sp,8
+ ld_r1,sp,24
st_r1,r0,8
- ld_r1,sp,16
+ ld_r1,sp,32
st_r1,r0,16
- ld_r1,sp,24
+ ld_r1,sp,40
st_r1,r0,24
ori_r0,r0,4
@@ -2752,19 +3542,19 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
:make_primitive
prologue_n3
mov_r0,r3 ## save type → r0 (frees r3)
- st_r1,sp,8 ## slot 1 = code_id
- st_r2,sp,16 ## slot 2 = arity
- st_r0,sp,24 ## slot 3 = type
+ st_r1,sp,24 ## slot 1 = code_id
+ st_r2,sp,32 ## slot 2 = arity
+ st_r0,sp,40 ## slot 3 = type
li_r1 %16 ## 16 bytes
li_br &alloc
call ## r0 = raw ptr
- ld_r1,sp,24
+ ld_r1,sp,40
sb_r1,r0,7 ## byte 7 = type
- ld_r1,sp,16
+ ld_r1,sp,32
sb_r1,r0,0 ## byte 0 = arity
- ld_r1,sp,8
+ ld_r1,sp,24
st_r1,r0,8 ## +8 = code id
ori_r0,r0,4
@@ -2779,8 +3569,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
:eval_args
prologue_n3
mov_r3,sp
- st_r1,sp,8 ## slot 1 = args cursor
- st_r2,sp,16 ## slot 2 = env
+ st_r1,sp,24 ## slot 1 = args cursor
+ st_r2,sp,32 ## slot 2 = env
li_r2 %7
li_br &eval_args_done
@@ -2790,23 +3580,23 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &car
call
mov_r1,r0
- ld_r2,sp,16
+ ld_r2,sp,32
li_br &eval
call ## r0 = head value
- st_r0,sp,24
+ st_r0,sp,40
## tail = eval_args(cdr(args), env)
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cdr
call
mov_r1,r0
- ld_r2,sp,16
+ ld_r2,sp,32
li_br &eval_args
call ## r0 = tail
mov_r2,r0
- ld_r1,sp,24
+ ld_r1,sp,40
li_br &cons ## tail: cons is last work in this frame
tail_n3
@@ -2825,8 +3615,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
:apply
prologue_n4
mov_r3,sp
- st_r1,sp,8 ## slot 1 = callee (tagged)
- st_r2,sp,16 ## slot 2 = args
+ st_r1,sp,24 ## slot 1 = callee (tagged)
+ st_r2,sp,32 ## slot 2 = args
andi_r1,r1,7
li_r0 TAG_PROC
@@ -2834,7 +3624,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
bne_r1,r0
mov_r3,sp
- ld_r1,sp,8
+ ld_r1,sp,24
addi_r1,r1,neg6 ## r1 = raw ptr
## Fork on header type byte.
@@ -2848,21 +3638,21 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## Fall through: type == 4 (closure).
ld_r0,r1,8 ## params
- st_r0,sp,8 ## slot 1 = params (overwrite)
+ st_r0,sp,24 ## slot 1 = params (overwrite)
ld_r0,r1,16 ## body
- st_r0,sp,24 ## slot 3 = body
+ st_r0,sp,40 ## slot 3 = body
ld_r0,r1,24 ## closure env
- st_r0,sp,32 ## slot 4 = closure env
+ st_r0,sp,48 ## slot 4 = closure env
## env_extend(params, args, closure_env)
- ld_r1,sp,8
- ld_r2,sp,16
- ld_r3,sp,32
+ ld_r1,sp,24
+ ld_r2,sp,32
+ ld_r3,sp,48
li_br &env_extend
call ## r0 = new env
mov_r2,r0
- ld_r1,sp,24 ## r1 = body
+ ld_r1,sp,40 ## r1 = body
li_br &eval
tail_n4 ## Scheme tail call: closure body
@@ -2875,21 +3665,21 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
:apply_prim_fixed
## r3 is still sp from the MOV above; slot 3/4 are free.
lb_r0,r1,0 ## r0 = expected arity
- st_r0,r3,32 ## slot 4 = expected arity
+ st_r0,r3,48 ## slot 4 = expected arity
ld_r0,r1,8 ## r0 = code id
- st_r0,r3,24 ## slot 3 = code id
+ st_r0,r3,40 ## slot 3 = code id
- ld_r1,r3,16 ## r1 = args
+ ld_r1,r3,32 ## r1 = args
li_br &marshal_argv
call ## r0 = argc, r2 = &prim_argv
mov_r3,sp
- ld_r1,sp,32
+ ld_r1,sp,48
li_br &err_arity
bne_r0,r1
mov_r1,r0
- ld_r3,sp,24 ## r3 = code id
+ ld_r3,sp,40 ## r3 = code id
epilogue_n4
li_br &prim_dispatch
b
@@ -2898,14 +3688,14 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## ---- apply_prim_variadic (r1 = raw prim ptr) ----------------------
:apply_prim_variadic
ld_r0,r1,8 ## r0 = code id
- st_r0,r3,24 ## slot 3 = code id
+ st_r0,r3,40 ## slot 3 = code id
- ld_r1,r3,16 ## r1 = args
+ ld_r1,r3,32 ## r1 = args
li_br &marshal_argv
call ## r0 = argc, r2 = &prim_argv
mov_r1,r0
- ld_r3,sp,24 ## r3 = code id
+ ld_r3,sp,40 ## r3 = code id
epilogue_n4
li_br &prim_dispatch
b
@@ -3637,28 +4427,28 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
prologue_n3
mov_r3,sp
li_r0 NIL ## nil
- st_r0,sp,8 ## slot1 = accumulator
- st_r2,sp,24 ## slot3 = base
+ st_r0,sp,24 ## slot1 = accumulator
+ st_r2,sp,40 ## slot3 = base
shli_r1,r1,3 ## r1 = argc * 8
add_r1,r1,r2 ## r1 = end ptr (one past last)
- st_r1,sp,16 ## slot2 = cursor
+ st_r1,sp,32 ## slot2 = cursor
:prim_list_loop
mov_r3,sp
- ld_r0,sp,16 ## cursor
- ld_r1,sp,24 ## base
+ ld_r0,sp,32 ## cursor
+ ld_r1,sp,40 ## base
li_br &prim_list_done
beq_r0,r1
addi_r0,r0,neg8 ## cursor -= 8
- st_r0,sp,16
+ st_r0,sp,32
ld_r1,r0,0 ## r1 = arg
- ld_r2,sp,8 ## r2 = accumulator
+ ld_r2,sp,24 ## r2 = accumulator
li_br &cons
call
- st_r0,sp,8 ## accumulator = new pair
+ st_r0,sp,24 ## accumulator = new pair
li_br &prim_list_loop
b
:prim_list_done
- ld_r0,sp,8
+ ld_r0,sp,24
epilogue_n3
ret
@@ -3713,14 +4503,14 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_r0 %7
li_br &append_one_base
beq_r1,r0
- st_r1,sp,8 ## save xs
- st_r2,sp,16 ## save ys
+ st_r1,sp,24 ## save xs
+ st_r2,sp,32 ## save ys
addi_r0,r1,neg2
ld_r1,r0,8 ## r1 = cdr(xs)
li_br &append_one
call ## r0 = appended tail
mov_r2,r0
- ld_r0,sp,8
+ ld_r0,sp,24
addi_r0,r0,neg2
ld_r1,r0,0 ## r1 = car(xs)
li_br &cons
@@ -3740,30 +4530,30 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &prim_append_zero
beqz_r1
mov_r3,sp
- st_r2,sp,8 ## slot1 = argv base
+ st_r2,sp,24 ## slot1 = argv base
addi_r1,r1,neg1 ## r1 = argc-1
shli_r1,r1,3 ## r1 = (argc-1)*8
add_r1,r1,r2 ## r1 = &argv[argc-1]
- st_r1,sp,24 ## slot3 = cursor
+ st_r1,sp,40 ## slot3 = cursor
ld_r0,r1,0 ## r0 = last arg
- st_r0,sp,16 ## slot2 = result
+ st_r0,sp,32 ## slot2 = result
:prim_append_loop
mov_r3,sp
- ld_r0,sp,24 ## cursor
- ld_r1,sp,8 ## base
+ ld_r0,sp,40 ## cursor
+ ld_r1,sp,24 ## base
li_br &prim_append_done
beq_r0,r1
addi_r0,r0,neg8
- st_r0,sp,24
+ st_r0,sp,40
ld_r1,r0,0 ## r1 = arg at cursor
- ld_r2,sp,16 ## r2 = result
+ ld_r2,sp,32 ## r2 = result
li_br &append_one
call
- st_r0,sp,16
+ st_r0,sp,32
li_br &prim_append_loop
b
:prim_append_done
- ld_r0,sp,16
+ ld_r0,sp,32
epilogue_n3
ret
:prim_append_zero
@@ -3777,27 +4567,27 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
prologue_n2
ld_r0,r2,0
mov_r3,sp
- st_r0,sp,8 ## slot1 = current
+ st_r0,sp,24 ## slot1 = current
li_r0 %7
- st_r0,sp,16 ## slot2 = accumulator (nil)
+ st_r0,sp,32 ## slot2 = accumulator (nil)
:prim_reverse_loop
mov_r3,sp
- ld_r0,sp,8
+ ld_r0,sp,24
li_r1 %7
li_br &prim_reverse_done
beq_r0,r1
addi_r0,r0,neg2
ld_r1,r0,0 ## r1 = car
ld_r2,r0,8 ## r2 = cdr (next current)
- st_r2,sp,8 ## slot1 = next
- ld_r2,sp,16 ## r2 = accumulator
+ st_r2,sp,24 ## slot1 = next
+ ld_r2,sp,32 ## r2 = accumulator
li_br &cons
call
- st_r0,sp,16
+ st_r0,sp,32
li_br &prim_reverse_loop
b
:prim_reverse_done
- ld_r0,sp,16
+ ld_r0,sp,32
epilogue_n2
ret
@@ -3880,8 +4670,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## ---- (substring s start end) — copies s[start:end] -----------------
:prim_substring
prologue_n3
- st_r6,sp,8
- st_r7,sp,16
+ st_r6,sp,24
+ st_r7,sp,32
ld_r1,r2,0 ## tagged string
ld_r0,r2,8 ## tagged start
@@ -3901,8 +4691,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &make_string
call
- ld_r6,sp,8
- ld_r7,sp,16
+ ld_r6,sp,24
+ ld_r7,sp,32
epilogue_n3
ret
@@ -3913,15 +4703,15 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## need to spill the r2 it came in on.
:prim_string_append
prologue_n4
- st_r6,sp,8 ## save r6 (cursor/total reuse)
- st_r7,sp,16 ## save r7 (i)
- st_r1,sp,24 ## slot3 = argc
+ st_r6,sp,24 ## save r6 (cursor/total reuse)
+ st_r7,sp,32 ## save r7 (i)
+ st_r1,sp,40 ## slot3 = argc
li_r6 %0 ## r6 = total length
li_r7 %0 ## r7 = i
:psa_lp1
mov_r3,sp
- ld_r0,r3,24 ## argc
+ ld_r0,r3,40 ## argc
li_br &psa_done1
beq_r7,r0
@@ -3943,7 +4733,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &alloc_string
call ## r0 = tagged result
mov_r3,sp
- st_r0,r3,32 ## slot4 = tagged result
+ st_r0,r3,48 ## slot4 = tagged result
## r6 = payload cursor
addi_r6,r0,neg4
@@ -3952,7 +4742,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
:psa_lp2
mov_r3,sp
- ld_r0,r3,24
+ ld_r0,r3,40
li_br &psa_done2
beq_r7,r0
@@ -3962,8 +4752,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
ld_r2,r0,0 ## tagged str
addi_r2,r2,neg4 ## raw header
ld_r3,r2,0
- shli_r3,r3,16
- shri_r3,r3,16 ## len
+ shli_r3,r3,32
+ shri_r3,r3,32 ## len
addi_r2,r2,8 ## src payload
mov_r1,r6 ## dst
li_br &byte_copy
@@ -3975,9 +4765,9 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
:psa_done2
mov_r3,sp
- ld_r0,r3,32
- ld_r6,sp,8
- ld_r7,sp,16
+ ld_r0,r3,48
+ ld_r6,sp,24
+ ld_r7,sp,32
epilogue_n4
ret
@@ -4071,26 +4861,26 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## order matches slot order.
:prim_vector_to_list
prologue_n4
- st_r6,sp,8
- st_r7,sp,16
+ st_r6,sp,24
+ st_r7,sp,32
ld_r0,r2,0 ## tagged vec
addi_r0,r0,neg3 ## raw
ld_r3,r0,0
- shli_r3,r3,16
- shri_r3,r3,16 ## len
+ shli_r3,r3,32
+ shri_r3,r3,32 ## len
addi_r0,r0,8 ## payload base
shli_r1,r3,3
mov_r2,r0 ## r2 = payload base
add_r6,r1,r2 ## r6 = end cursor = base + len*8
mov_r3,sp
- st_r0,r3,24 ## slot3 = base ptr
+ st_r0,r3,40 ## slot3 = base ptr
li_r7 NIL ## acc
:pvtl_loop
mov_r3,sp
- ld_r0,r3,24
+ ld_r0,r3,40
li_br &pvtl_done
beq_r6,r0
@@ -4104,8 +4894,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
b
:pvtl_done
mov_r0,r7
- ld_r6,sp,8
- ld_r7,sp,16
+ ld_r6,sp,24
+ ld_r7,sp,32
epilogue_n4
ret
@@ -4113,12 +4903,12 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## ---- (list->vector lst) — count then fill ---------------------------
:prim_list_to_vector
prologue_n4
- st_r6,sp,8
- st_r7,sp,16
+ st_r6,sp,24
+ st_r7,sp,32
ld_r0,r2,0 ## tagged list
mov_r3,sp
- st_r0,r3,24 ## slot3 = list head
+ st_r0,r3,40 ## slot3 = list head
li_r6 %0 ## r6 = count
mov_r7,r0 ## r7 = cursor
@@ -4137,11 +4927,11 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &make_vector
call ## r0 = tagged vector
mov_r3,sp
- st_r0,r3,32 ## slot4 = result
+ st_r0,r3,48 ## slot4 = result
addi_r6,r0,neg3
addi_r6,r6,8 ## r6 = payload cursor
- ld_r7,r3,24 ## r7 = list cursor
+ ld_r7,r3,40 ## r7 = list cursor
:pltv_lp2
li_r0 NIL
li_br &pltv_done2
@@ -4155,9 +4945,9 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
b
:pltv_done2
mov_r3,sp
- ld_r0,r3,32
- ld_r6,sp,8
- ld_r7,sp,16
+ ld_r0,r3,48
+ ld_r6,sp,24
+ ld_r7,sp,32
epilogue_n4
ret
@@ -4201,13 +4991,13 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## silently. argv is &prim_argv; arg_index counts past the fmt string.
:prim_format
prologue_n4
- st_r6,sp,8
- st_r7,sp,16
+ st_r6,sp,24
+ st_r7,sp,32
mov_r3,sp
- st_r1,r3,24 ## slot3 = argc
+ st_r1,r3,40 ## slot3 = argc
li_r0 %1
- st_r0,r3,32 ## slot4 = arg_index (skip fmt)
+ st_r0,r3,48 ## slot4 = arg_index (skip fmt)
ld_r0,r2,0 ## tagged fmt string
addi_r0,r0,neg4
@@ -4254,13 +5044,13 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## a/s/d — fetch arg
li_r1 &prim_argv
mov_r3,sp
- ld_r2,r3,32
+ ld_r2,r3,48
shli_r2,r2,3
add_r1,r1,r2
ld_r1,r1,0 ## r1 = tagged arg
- ld_r2,r3,32
+ ld_r2,r3,48
addi_r2,r2,1
- st_r2,r3,32
+ st_r2,r3,48
li_r2 %97 ## 'a'
li_br &pf_dir_a
@@ -4311,8 +5101,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
:pf_done
li_r0 UNSPEC
- ld_r6,sp,8
- ld_r7,sp,16
+ ld_r6,sp,24
+ ld_r7,sp,32
epilogue_n4
ret
@@ -4333,8 +5123,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## Uses io_buf as scratch (512B); make_string copies into the heap.
:prim_read_file
prologue_n3
- st_r6,sp,8
- st_r7,sp,16
+ st_r6,sp,24
+ st_r7,sp,32
ld_r1,r2,0
li_br &string_to_c_path
@@ -4368,8 +5158,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &make_string
call
- ld_r6,sp,8
- ld_r7,sp,16
+ ld_r6,sp,24
+ ld_r7,sp,32
epilogue_n3
ret
@@ -4377,8 +5167,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## ---- (write-file path data) — overwrite/truncate ------------------
:prim_write_file
prologue_n3
- st_r6,sp,8
- st_r7,sp,16
+ st_r6,sp,24
+ st_r7,sp,32
ld_r7,r2,8 ## r7 = tagged data string
@@ -4401,8 +5191,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
mov_r1,r7
addi_r1,r1,neg4 ## raw header
ld_r3,r1,0
- shli_r3,r3,16
- shri_r3,r3,16 ## len
+ shli_r3,r3,32
+ shri_r3,r3,32 ## len
addi_r2,r1,8 ## payload
mov_r1,r6 ## fd
li_br &write_file_all
@@ -4413,8 +5203,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
syscall
li_r0 UNSPEC
- ld_r6,sp,8
- ld_r7,sp,16
+ ld_r6,sp,24
+ ld_r7,sp,32
epilogue_n3
ret
@@ -4426,8 +5216,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## paths that need them across recursive calls.
:equal_helper
prologue_n4
- st_r6,sp,8
- st_r7,sp,16
+ st_r6,sp,24
+ st_r7,sp,32
li_br &eq_true
beq_r1,r2
@@ -4451,21 +5241,21 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
:eq_false
li_r0 %0
- ld_r6,sp,8
- ld_r7,sp,16
+ ld_r6,sp,24
+ ld_r7,sp,32
epilogue_n4
ret
:eq_true
li_r0 %1
- ld_r6,sp,8
- ld_r7,sp,16
+ ld_r6,sp,24
+ ld_r7,sp,32
epilogue_n4
ret
:eq_pair
- st_r1,sp,24 ## slot3 = tagged a
- st_r2,sp,32 ## slot4 = tagged b
+ st_r1,sp,40 ## slot3 = tagged a
+ st_r2,sp,48 ## slot4 = tagged b
addi_r0,r1,neg2
ld_r1,r0,0 ## a.car
@@ -4478,17 +5268,17 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
beqz_r0
mov_r3,sp
- ld_r0,r3,24
+ ld_r0,r3,40
addi_r0,r0,neg2
ld_r1,r0,8 ## a.cdr
- ld_r0,r3,32
+ ld_r0,r3,48
addi_r0,r0,neg2
ld_r2,r0,8 ## b.cdr
li_br &equal_helper
call
- ld_r6,sp,8
- ld_r7,sp,16
+ ld_r6,sp,24
+ ld_r7,sp,32
epilogue_n4
ret
@@ -4496,8 +5286,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
addi_r1,r1,neg4
addi_r2,r2,neg4
ld_r3,r1,0
- shli_r3,r3,16
- shri_r3,r3,16 ## len_a
+ shli_r3,r3,32
+ shri_r3,r3,32 ## len_a
ld_r0,r2,0
shli_r0,r0,16
shri_r0,r0,16 ## len_b
@@ -4523,8 +5313,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
addi_r1,r1,neg3
addi_r2,r2,neg3
ld_r3,r1,0
- shli_r3,r3,16
- shri_r3,r3,16 ## len_a
+ shli_r3,r3,32
+ shri_r3,r3,32 ## len_a
ld_r0,r2,0
shli_r0,r0,16
shri_r0,r0,16 ## len_b
@@ -4539,7 +5329,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
ld_r1,r6,0
ld_r2,r7,0
- st_r3,sp,24 ## save remaining count
+ st_r3,sp,40 ## save remaining count
li_br &equal_helper
call
@@ -4547,7 +5337,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
beqz_r0
mov_r3,sp
- ld_r3,r3,24
+ ld_r3,r3,40
addi_r6,r6,8
addi_r7,r7,8
addi_r3,r3,neg1
@@ -4579,8 +5369,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## the C-level apply with (callee, args).
:prim_apply
prologue_n4
- st_r6,sp,8
- st_r7,sp,16
+ st_r6,sp,24
+ st_r7,sp,32
ld_r6,r2,0 ## r6 = callee
@@ -4602,7 +5392,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
add_r1,r1,r0
ld_r1,r1,0 ## r1 = arg
- st_r3,sp,24 ## save i
+ st_r3,sp,40 ## save i
mov_r2,r7
li_br &cons
@@ -4610,7 +5400,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
mov_r7,r0
mov_r3,sp
- ld_r3,r3,24
+ ld_r3,r3,40
addi_r3,r3,neg1
li_br &papply_lp
b
@@ -4621,8 +5411,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &apply
call
- ld_r6,sp,8
- ld_r7,sp,16
+ ld_r6,sp,24
+ ld_r7,sp,32
epilogue_n4
ret
@@ -4634,8 +5424,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
:eval
prologue_n3
mov_r3,sp
- st_r1,sp,8 ## slot 1 = expr
- st_r2,sp,16 ## slot 2 = env
+ st_r1,sp,24 ## slot 1 = expr
+ st_r2,sp,32 ## slot 2 = env
li_r2 %7
li_br &eval_self_slot1
@@ -4660,20 +5450,20 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
b
:eval_self_slot1
- ld_r0,sp,8
+ ld_r0,sp,24
epilogue_n3
ret
:eval_sym
- ld_r1,sp,8
- ld_r2,sp,16
+ ld_r1,sp,24
+ ld_r2,sp,32
li_br &lookup
tail_n3
:eval_pair
## Compound expression. Dispatch on car against cached sym_*
## pointers; otherwise treat as function application.
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &car
call ## r0 = callee-expr
@@ -4734,23 +5524,23 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## Application: callee = eval(callee-expr, env)
mov_r1,r0
- ld_r2,sp,16
+ ld_r2,sp,32
li_br &eval
call ## r0 = callee value
- st_r0,sp,24 ## slot 3 = callee
+ st_r0,sp,40 ## slot 3 = callee
## args = eval_args(cdr(expr), env)
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cdr
call
mov_r1,r0
- ld_r2,sp,16
+ ld_r2,sp,32
li_br &eval_args
call ## r0 = args list
mov_r2,r0
- ld_r1,sp,24
+ ld_r1,sp,40
li_br &apply
tail_n3 ## Scheme tail call: application
@@ -4759,32 +5549,32 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## All run inside eval's PROLOGUE_N3 frame: slot 1 = expr, slot 2 =
## env, slot 3 = per-form scratch.
:eval_quote
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cdr
call ## r0 = (x)
mov_r1,r0
- li_br &car ## tail: r0 = x
- tail_n3
+ li_br &car
+ tail_n3 ## Scheme tail: quote result = datum
:eval_if
## (if cond then else). Save (then else) tail into slot 3, eval
## cond, branch to the correct arm.
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cdr
call ## r0 = (cond then else)
mov_r1,r0
li_br &cdr
call ## r0 = (then else)
- st_r0,sp,24
+ st_r0,sp,40
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cdr
call ## r0 = (cond then else)
mov_r1,r0
li_br &car
call ## r0 = cond expr
mov_r1,r0
- ld_r2,sp,16
+ ld_r2,sp,32
li_br &eval
call ## r0 = cond value
@@ -4793,24 +5583,24 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
beq_r0,r1
## Then branch: eval car(slot3)
- ld_r1,sp,24
+ ld_r1,sp,40
li_br &car
call
mov_r1,r0
- ld_r2,sp,16
+ ld_r2,sp,32
li_br &eval ## Scheme tail: then-branch
tail_n3
:eval_if_else
## Else branch: eval car(cdr(slot3))
- ld_r1,sp,24
+ ld_r1,sp,40
li_br &cdr
call
mov_r1,r0
li_br &car
call
mov_r1,r0
- ld_r2,sp,16
+ ld_r2,sp,32
li_br &eval ## Scheme tail: else-branch
tail_n3
@@ -4820,15 +5610,15 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## cdr(cursor): nil next → TAIL eval the current head; otherwise
## CALL eval and advance. Slot 3 bridges the saved next-cursor
## across the CALL eval in the non-tail branch.
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cdr
call ## r0 = body list
mov_r3,sp
- st_r0,sp,8 ## slot 1 := cursor
+ st_r0,sp,24 ## slot 1 := cursor
:eval_begin_loop
mov_r3,sp
- ld_r1,sp,8
+ ld_r1,sp,24
li_r2 %7
li_br &eval_begin_empty
beq_r1,r2 ## cursor == nil → empty begin → nil
@@ -4843,27 +5633,27 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## Non-tail: stash next in slot 3, CALL eval(car(cursor), env),
## discard result, advance cursor := next.
- st_r0,sp,24 ## slot 3 := next
- ld_r1,sp,8
+ st_r0,sp,40 ## slot 3 := next
+ ld_r1,sp,24
li_br &car
call
mov_r1,r0
- ld_r2,sp,16
+ ld_r2,sp,32
li_br &eval
call
- ld_r0,sp,24
- st_r0,sp,8 ## cursor := next
+ ld_r0,sp,40
+ st_r0,sp,24 ## cursor := next
li_br &eval_begin_loop
b
:eval_begin_tail
## Scheme tail: TAIL eval(car(cursor), env). Frame torn down.
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &car
call ## r0 = head expr
mov_r1,r0
- ld_r2,sp,16
+ ld_r2,sp,32
li_br &eval
tail_n3
@@ -4876,17 +5666,17 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## (lambda params body1 body2 ...). Collect body-list, rewrite any
## leading (define …) forms into a letrec (step 12e), then stash
## the resulting single expression as the closure's body.
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cdr
call ## r0 = (params body1 body2 ...)
- st_r0,sp,24 ## slot 3 = after-head
+ st_r0,sp,40 ## slot 3 = after-head
mov_r1,r0
li_br &car
call ## r0 = params
- st_r0,sp,8 ## slot 1 := params
+ st_r0,sp,24 ## slot 1 := params
- ld_r1,sp,24
+ ld_r1,sp,40
li_br &cdr
call ## r0 = body list
mov_r1,r0
@@ -4894,37 +5684,37 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
call ## r0 = rewritten single expr
mov_r2,r0
- ld_r1,sp,8 ## r1 = params
- ld_r3,sp,16 ## r3 = env
+ ld_r1,sp,24 ## r1 = params
+ ld_r3,sp,32 ## r3 = env
li_br &make_closure
tail_n3
:eval_define
## (define sym val-expr). gset-binds the evaluated val into the
## global alist, returns nil.
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cdr
call ## r0 = (sym val-expr)
- st_r0,sp,24 ## slot 3 := (sym val-expr)
+ st_r0,sp,40 ## slot 3 := (sym val-expr)
mov_r1,r0
li_br &car
call ## r0 = sym
- st_r0,sp,8 ## slot 1 := sym
+ st_r0,sp,24 ## slot 1 := sym
- ld_r1,sp,24
+ ld_r1,sp,40
li_br &cdr
call ## r0 = (val-expr)
mov_r1,r0
li_br &car
call ## r0 = val-expr
mov_r1,r0
- ld_r2,sp,16
+ ld_r2,sp,32
li_br &eval
call ## r0 = val
mov_r2,r0
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &gset
call
@@ -4937,15 +5727,15 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## (set! sym val-expr). Evaluates val-expr, then mutates the first
## binding of sym found in local→global search order.
:eval_set
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cdr
call ## r0 = (sym val-expr)
mov_r1,r0
li_br &car
call ## r0 = sym
- st_r0,sp,24 ## slot 3 = sym
+ st_r0,sp,40 ## slot 3 = sym
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cdr
call
mov_r1,r0
@@ -4955,13 +5745,13 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &car
call ## r0 = val-expr
mov_r1,r0
- ld_r2,sp,16
+ ld_r2,sp,32
li_br &eval
call ## r0 = val
mov_r3,r0
- ld_r1,sp,24
- ld_r2,sp,16
+ ld_r1,sp,40
+ ld_r2,sp,32
li_br &set_binding
tail_n3
@@ -4971,8 +5761,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## global) and mutates the cdr to val. Errors out on unbound sym.
:set_binding
prologue_n3
- st_r3,sp,24 ## slot 3 = val
- st_r1,sp,8 ## slot 1 = sym (for global fallback)
+ st_r3,sp,40 ## slot 3 = val
+ st_r1,sp,24 ## slot 1 = sym (for global fallback)
li_br &lookup_alist
call ## r0 = (sym . v) pair or nil
@@ -4980,7 +5770,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &set_binding_global
beq_r0,r1
- ld_r1,sp,24
+ ld_r1,sp,40
addi_r0,r0,neg2 ## raw pair ptr
st_r1,r0,8 ## mutate cdr
li_r0 UNSPEC
@@ -4988,7 +5778,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
ret
:set_binding_global
- ld_r1,sp,8
+ ld_r1,sp,24
li_r2 &global_env_cell
ld_r2,r2,0
li_br &lookup_alist
@@ -4997,7 +5787,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &err_unbound
beq_r0,r1
- ld_r1,sp,24
+ ld_r1,sp,40
addi_r0,r0,neg2
st_r1,r0,8
li_r0 UNSPEC
@@ -5009,21 +5799,21 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## (let ((n1 e1) …) body…). Each RHS is evaluated in the *outer* env;
## then body evaluates in env extended with all new bindings.
:eval_let
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cdr
call ## r0 = (bindings body…)
- st_r0,sp,24 ## slot 3 = after-head
+ st_r0,sp,40 ## slot 3 = after-head
mov_r1,r0
li_br &car
call ## r0 = bindings
mov_r1,r0
- ld_r2,sp,16
+ ld_r2,sp,32
li_br &build_let_env
call ## r0 = new env
- st_r0,sp,16 ## slot 2 = new env
+ st_r0,sp,32 ## slot 2 = new env
- ld_r1,sp,24
+ ld_r1,sp,40
li_br &cdr
call ## r0 = body list
mov_r2,r0
@@ -5032,7 +5822,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &cons
call ## r0 = (begin . body)
mov_r1,r0
- ld_r2,sp,16
+ ld_r2,sp,32
li_br &eval
tail_n3
@@ -5041,21 +5831,21 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## (let* ((n1 e1) …) body…). Each RHS is evaluated in the env built
## from previous bindings, then body evaluates in the final env.
:eval_letstar
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cdr
call
- st_r0,sp,24
+ st_r0,sp,40
mov_r1,r0
li_br &car
call
mov_r1,r0
- ld_r2,sp,16
+ ld_r2,sp,32
li_br &build_letstar_env
call
- st_r0,sp,16
+ st_r0,sp,32
- ld_r1,sp,24
+ ld_r1,sp,40
li_br &cdr
call
mov_r2,r0
@@ -5064,7 +5854,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &cons
call
mov_r1,r0
- ld_r2,sp,16
+ ld_r2,sp,32
li_br &eval
tail_n3
@@ -5074,21 +5864,21 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## walks the binding list a second time evaluating each RHS in the
## fully pre-bound env and mutating the binding cell.
:eval_letrec
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cdr
call
- st_r0,sp,24
+ st_r0,sp,40
mov_r1,r0
li_br &car
call
mov_r1,r0
- ld_r2,sp,16
+ ld_r2,sp,32
li_br &build_letrec_env
call
- st_r0,sp,16
+ st_r0,sp,32
- ld_r1,sp,24
+ ld_r1,sp,40
li_br &cdr
call
mov_r2,r0
@@ -5097,7 +5887,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &cons
call
mov_r1,r0
- ld_r2,sp,16
+ ld_r2,sp,32
li_br &eval
tail_n3
@@ -5107,20 +5897,20 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## each test; on truthy test or literal `else`, evaluates body as a begin.
## Returns unspec if no clause fires.
:eval_cond
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cdr
call ## r0 = clauses
- st_r0,sp,8 ## slot 1 = cursor
+ st_r0,sp,24 ## slot 1 = cursor
:eval_cond_loop
- ld_r1,sp,8
+ ld_r1,sp,24
li_r0 NIL
li_br &eval_cond_done
beq_r1,r0
li_br &car
call ## r0 = clause
- st_r0,sp,24 ## slot 3 = clause
+ st_r0,sp,40 ## slot 3 = clause
mov_r1,r0
li_br &car
@@ -5132,7 +5922,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
beq_r0,r1
mov_r1,r0
- ld_r2,sp,16
+ ld_r2,sp,32
li_br &eval
call ## r0 = test value
@@ -5141,7 +5931,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
beq_r0,r1
:eval_cond_body
- ld_r1,sp,24
+ ld_r1,sp,40
li_br &cdr
call ## r0 = body list
mov_r2,r0
@@ -5150,15 +5940,15 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &cons
call
mov_r1,r0
- ld_r2,sp,16
+ ld_r2,sp,32
li_br &eval
tail_n3
:eval_cond_next
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cdr
call
- st_r0,sp,8
+ st_r0,sp,24
li_br &eval_cond_loop
b
@@ -5173,14 +5963,14 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## template recursively, handling `,x` (unquote) and `,@x`
## (unquote-splicing) forms.
:eval_quasiquote
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cdr
call
mov_r1,r0
li_br &car
call ## r0 = template
mov_r1,r0
- ld_r2,sp,16
+ ld_r2,sp,32
li_br &quasi_expand
tail_n3
@@ -5190,19 +5980,19 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## evaluated in the *outer* env — the let semantics.
:build_let_env
prologue_n4
- st_r1,sp,8 ## slot 1 = cursor
- st_r2,sp,16 ## slot 2 = outer env
- st_r2,sp,24 ## slot 3 = new env acc
+ st_r1,sp,24 ## slot 1 = cursor
+ st_r2,sp,32 ## slot 2 = outer env
+ st_r2,sp,40 ## slot 3 = new env acc
:ble_loop
- ld_r1,sp,8
+ ld_r1,sp,24
li_r0 NIL
li_br &ble_done
beq_r1,r0
li_br &car
call ## r0 = (name expr)
- st_r0,sp,32 ## slot 4 = (name expr)
+ st_r0,sp,48 ## slot 4 = (name expr)
mov_r1,r0
li_br &cdr
@@ -5212,32 +6002,32 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
call ## r0 = expr
mov_r1,r0
- ld_r2,sp,16 ## outer env
+ ld_r2,sp,32 ## outer env
li_br &eval
call ## r0 = val
mov_r2,r0
- ld_r1,sp,32
+ ld_r1,sp,48
addi_r1,r1,neg2
ld_r1,r1,0 ## r1 = name
li_br &cons
call ## r0 = (name . val)
mov_r1,r0
- ld_r2,sp,24
+ ld_r2,sp,40
li_br &cons
call ## r0 = extended env
- st_r0,sp,24
+ st_r0,sp,40
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cdr
call
- st_r0,sp,8
+ st_r0,sp,24
li_br &ble_loop
b
:ble_done
- ld_r0,sp,24
+ ld_r0,sp,40
epilogue_n4
ret
@@ -5247,18 +6037,18 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## so far (not the fixed outer env).
:build_letstar_env
prologue_n4
- st_r1,sp,8
- st_r2,sp,24 ## slot 3 = current env
+ st_r1,sp,24
+ st_r2,sp,40 ## slot 3 = current env
:bls_loop
- ld_r1,sp,8
+ ld_r1,sp,24
li_r0 NIL
li_br &bls_done
beq_r1,r0
li_br &car
call
- st_r0,sp,32
+ st_r0,sp,48
mov_r1,r0
li_br &cdr
@@ -5268,32 +6058,32 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
call ## r0 = expr
mov_r1,r0
- ld_r2,sp,24 ## current env
+ ld_r2,sp,40 ## current env
li_br &eval
call ## r0 = val
mov_r2,r0
- ld_r1,sp,32
+ ld_r1,sp,48
addi_r1,r1,neg2
ld_r1,r1,0
li_br &cons
call
mov_r1,r0
- ld_r2,sp,24
+ ld_r2,sp,40
li_br &cons
call
- st_r0,sp,24
+ st_r0,sp,40
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cdr
call
- st_r0,sp,8
+ st_r0,sp,24
li_br &bls_loop
b
:bls_done
- ld_r0,sp,24
+ ld_r0,sp,40
epilogue_n4
ret
@@ -5304,12 +6094,12 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## set_binding. Supports mutual recursion.
:build_letrec_env
prologue_n4
- st_r1,sp,8 ## slot 1 = cursor
- st_r1,sp,16 ## slot 2 = original bindings (for pass 2)
- st_r2,sp,24 ## slot 3 = new env
+ st_r1,sp,24 ## slot 1 = cursor
+ st_r1,sp,32 ## slot 2 = original bindings (for pass 2)
+ st_r2,sp,40 ## slot 3 = new env
:blr_pre_loop
- ld_r1,sp,8
+ ld_r1,sp,24
li_r0 NIL
li_br &blr_pre_done
beq_r1,r0
@@ -5326,31 +6116,31 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
call ## r0 = (name . UNSPEC)
mov_r1,r0
- ld_r2,sp,24
+ ld_r2,sp,40
li_br &cons
call
- st_r0,sp,24
+ st_r0,sp,40
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cdr
call
- st_r0,sp,8
+ st_r0,sp,24
li_br &blr_pre_loop
b
:blr_pre_done
- ld_r1,sp,16
- st_r1,sp,8 ## reset cursor
+ ld_r1,sp,32
+ st_r1,sp,24 ## reset cursor
:blr_eval_loop
- ld_r1,sp,8
+ ld_r1,sp,24
li_r0 NIL
li_br &blr_eval_done
beq_r1,r0
li_br &car
call
- st_r0,sp,32 ## slot 4 = (name expr)
+ st_r0,sp,48 ## slot 4 = (name expr)
mov_r1,r0
li_br &cdr
@@ -5360,27 +6150,27 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
call ## r0 = expr
mov_r1,r0
- ld_r2,sp,24
+ ld_r2,sp,40
li_br &eval
call ## r0 = val
mov_r3,r0
- ld_r1,sp,32
+ ld_r1,sp,48
addi_r1,r1,neg2
ld_r1,r1,0 ## r1 = name
- ld_r2,sp,24
+ ld_r2,sp,40
li_br &set_binding
call
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cdr
call
- st_r0,sp,8
+ st_r0,sp,24
li_br &blr_eval_loop
b
:blr_eval_done
- ld_r0,sp,24
+ ld_r0,sp,40
epilogue_n4
ret
@@ -5392,12 +6182,12 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## (begin . body) wrapper.
:rewrite_lambda_body
prologue_n4
- st_r1,sp,8 ## slot 1 = cursor
+ st_r1,sp,24 ## slot 1 = cursor
li_r0 NIL
- st_r0,sp,16 ## slot 2 = reversed bindings acc
+ st_r0,sp,32 ## slot 2 = reversed bindings acc
:rlb_loop
- ld_r1,sp,8
+ ld_r1,sp,24
li_r0 NIL
li_br &rlb_done
beq_r1,r0
@@ -5410,7 +6200,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &rlb_done
bne_r0,r2
- st_r1,sp,24 ## slot 3 = form
+ st_r1,sp,40 ## slot 3 = form
li_br &car
call ## r0 = head
li_r1 &sym_define
@@ -5418,36 +6208,36 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &rlb_done
bne_r0,r1
- ld_r1,sp,24
+ ld_r1,sp,40
li_br &cdr
call ## r0 = (name val)
mov_r1,r0
- ld_r2,sp,16
+ ld_r2,sp,32
li_br &cons
call
- st_r0,sp,16
+ st_r0,sp,32
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cdr
call
- st_r0,sp,8
+ st_r0,sp,24
li_br &rlb_loop
b
:rlb_done
- ld_r1,sp,16
+ ld_r1,sp,32
li_r0 NIL
li_br &rlb_no_defs
beq_r1,r0
- ld_r1,sp,16
+ ld_r1,sp,32
li_br &list_reverse
call
- st_r0,sp,16
+ st_r0,sp,32
- ld_r1,sp,16
- ld_r2,sp,8
+ ld_r1,sp,32
+ ld_r2,sp,24
li_br &cons
call ## r0 = (bindings . body-tail)
mov_r2,r0
@@ -5457,21 +6247,21 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
tail_n4
:rlb_no_defs
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cdr
call
li_r1 NIL
li_br &rlb_single
beq_r0,r1
- ld_r2,sp,8
+ ld_r2,sp,24
li_r1 &sym_begin
ld_r1,r1,0
li_br &cons
tail_n4
:rlb_single
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &car
tail_n4
@@ -5481,12 +6271,12 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## source-order bindings.
:list_reverse
prologue_n2
- st_r1,sp,8
+ st_r1,sp,24
li_r0 NIL
- st_r0,sp,16
+ st_r0,sp,32
:lrv_loop
- ld_r1,sp,8
+ ld_r1,sp,24
li_r0 NIL
li_br &lrv_done
beq_r1,r0
@@ -5494,20 +6284,20 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &car
call
mov_r1,r0
- ld_r2,sp,16
+ ld_r2,sp,32
li_br &cons
call
- st_r0,sp,16
+ st_r0,sp,32
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cdr
call
- st_r0,sp,8
+ st_r0,sp,24
li_br &lrv_loop
b
:lrv_done
- ld_r0,sp,16
+ ld_r0,sp,32
epilogue_n2
ret
@@ -5518,20 +6308,20 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## Otherwise the pair is walked as a list via quasi_list.
:quasi_expand
prologue_n3
- st_r1,sp,8
- st_r2,sp,16
+ st_r1,sp,24
+ st_r2,sp,32
mov_r0,r1
andi_r0,r0,7
li_r2 TAG_PAIR
li_br &qe_pair
beq_r0,r2
- ld_r0,sp,8
+ ld_r0,sp,24
epilogue_n3
ret
:qe_pair
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &car
call
li_r1 &sym_unquote
@@ -5539,20 +6329,20 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &qe_unquote
beq_r0,r1
- ld_r1,sp,8
- ld_r2,sp,16
+ ld_r1,sp,24
+ ld_r2,sp,32
li_br &quasi_list
tail_n3
:qe_unquote
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cdr
call
mov_r1,r0
li_br &car
call
mov_r1,r0
- ld_r2,sp,16
+ ld_r2,sp,32
li_br &eval
tail_n3
@@ -5563,8 +6353,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## recursively calls quasi_expand on the element.
:quasi_list
prologue_n4
- st_r1,sp,8 ## slot 1 = cursor
- st_r2,sp,16 ## slot 2 = env
+ st_r1,sp,24 ## slot 1 = cursor
+ st_r2,sp,32 ## slot 2 = env
li_r0 NIL
li_br &ql_nil
@@ -5578,7 +6368,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &car
call ## r0 = head
- st_r0,sp,24 ## slot 3 = head
+ st_r0,sp,40 ## slot 3 = head
mov_r1,r0
andi_r0,r0,7
@@ -5586,7 +6376,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
li_br &ql_head_reg
bne_r0,r2
- ld_r1,sp,24
+ ld_r1,sp,40
li_br &car
call ## r0 = car(head)
li_r1 &sym_unquote_splicing
@@ -5595,48 +6385,48 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
beq_r0,r1
:ql_head_reg
- ld_r1,sp,24
- ld_r2,sp,16
+ ld_r1,sp,40
+ ld_r2,sp,32
li_br &quasi_expand
call
- st_r0,sp,24 ## slot 3 = expanded head
+ st_r0,sp,40 ## slot 3 = expanded head
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cdr
call
mov_r1,r0
- ld_r2,sp,16
+ ld_r2,sp,32
li_br &quasi_list
call
mov_r2,r0
- ld_r1,sp,24
+ ld_r1,sp,40
li_br &cons
tail_n4
:ql_splice
- ld_r1,sp,24
+ ld_r1,sp,40
li_br &cdr
call
mov_r1,r0
li_br &car
call ## r0 = X (the spliced expression)
mov_r1,r0
- ld_r2,sp,16
+ ld_r2,sp,32
li_br &eval
call ## r0 = evaluated list
- st_r0,sp,24
+ st_r0,sp,40
- ld_r1,sp,8
+ ld_r1,sp,24
li_br &cdr
call
mov_r1,r0
- ld_r2,sp,16
+ ld_r2,sp,32
li_br &quasi_list
call
mov_r2,r0
- ld_r1,sp,24
+ ld_r1,sp,40
li_br &append_one
tail_n4
@@ -5646,7 +6436,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
ret
:ql_nonpair
- ld_r0,sp,8
+ ld_r0,sp,24
epilogue_n4
ret
@@ -6197,6 +6987,44 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000'
## otherwise).
:global_env_cell NIL %0
+:stack_bottom_fp %0 %0
+:gc_root_fp %0 %0
+
+:free_list_pair %0 %0
+:free_list_obj16 %0 %0
+:free_list_obj24 %0 %0
+:free_list_obj32 %0 %0
+:free_list_obj40 %0 %0
+:free_list_obj48 %0 %0
+:free_list_obj56 %0 %0
+:free_list_obj64 %0 %0
+:free_list_obj80 %0 %0
+:free_list_obj96 %0 %0
+:free_list_obj128 %0 %0
+
+:mark_stack_next &mark_stack %0
+:pair_mark_bitmap
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+
+:mark_stack
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+:mark_stack_end
+
## ---- Primitive argv scratch buffer (32 slots × 8B = 256B) -----------
## apply fills this with tagged values before cascading to a primitive
@@ -6380,10 +7208,203 @@ ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
:symbol_table_end
-## ---- Heap arena (16 KiB — bump region for step-3 allocations) -------
-## Step 9 will grow this to 20 MiB via ELF memsz > filesz; for now a
-## file-inlined block is fine (makes the binary ~50 KiB).
+## ---- Heap arena (64 KiB split 32 KiB / 32 KiB for GC bring-up) -------
:heap_start
+:pair_heap_start
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+:pair_heap_end
+:obj_heap_start
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
@@ -6448,6 +7469,7 @@ ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
+:obj_heap_end
:heap_tail
diff --git a/src/p1_gen.py b/src/p1_gen.py
@@ -77,6 +77,7 @@ NAT_AMD64 = {'r0': 0, # rax
NAT_RV64 = {'r0': 10, 'r1': 11, 'r2': 12, 'r3': 13,
'r4': 20, 'r5': 21, 'r6': 9, 'r7': 18,
'br': 30, # t5 (caller-saved temp)
+ 't0': 5,
'sp': 2, 'ra': 1, 'zero': 0, 'a7': 17,
's3': 19, 's6': 22, 's7': 23}
@@ -314,18 +315,19 @@ AA64_LOGI_ENC = {
## Frame layout after PROLOGUE_Nk (k >= 1, rounded up so total frame
-## bytes stay 16-byte aligned on aarch64):
+## bytes stay 16-byte aligned):
## [sp + 0] = retaddr (aarch64 lr / riscv64 ra / amd64 retaddr)
-## [sp + 8] = slot 1 (callee-private scratch)
-## [sp + 16] = slot 2
+## [sp + 8] = saved_fp (caller's sp at call/prologue entry)
+## [sp + 16] = k (slot count)
+## [sp + 24] = slot 1 (callee-private scratch)
+## [sp + 32] = slot 2
## ...
-## [sp + 8*k] = slot k
##
-## Frame size = round_up_to_16(8 + 8*k). So k=1 → 16, k=2 → 24 → 32,
-## k=3 → 32, k=4 → 40 → 48.
+## Frame size = round_up_to_16(24 + 8*k). So k=1 → 32, k=2 → 40 → 48,
+## k=3 → 48, k=4 → 56 → 64.
def prologue_frame_bytes(k: int) -> int:
- raw = 8 + 8 * k
+ raw = 24 + 8 * k
return (raw + 15) & ~15
@@ -384,6 +386,8 @@ class AA64(Encoder):
return aa_sbfm(rD, rA, imm, 63)
def mov(self, rD, rA):
+ if rD == 'sp':
+ return aa_add_imm('sp', rA, 0, sub=False)
if rA == 'sp':
return aa_add_imm(rD, 'sp', 0, sub=False)
# MOV xD, xA = ORR xD, xzr, xA
@@ -455,13 +459,17 @@ class AA64(Encoder):
fb = prologue_frame_bytes(k)
sub = aa_add_imm('sp', 'sp', fb, sub=True)
str_lr = aa_ldst_uimm12(0xF9000000, 'lr', 'sp', 0, 3)
- return sub + str_lr
+ save_fp = aa_add_imm('x8', 'sp', fb, sub=False)
+ str_fp = aa_ldst_uimm12(0xF9000000, 'x8', 'sp', 8, 3)
+ mov_k = aa_add_imm('x8', 'xzr', k, sub=False)
+ str_k = aa_ldst_uimm12(0xF9000000, 'x8', 'sp', 16, 3)
+ return sub + str_lr + save_fp + str_fp + mov_k + str_k
def epilogue(self, k):
- fb = prologue_frame_bytes(k)
ldr_lr = aa_ldst_uimm12(0xF9400000, 'lr', 'sp', 0, 3)
- add = aa_add_imm('sp', 'sp', fb, sub=False)
- return ldr_lr + add
+ ldr_fp = aa_ldst_uimm12(0xF9400000, 'x8', 'sp', 8, 3)
+ mov_sp = aa_add_imm('sp', 'x8', 0, sub=False)
+ return ldr_lr + ldr_fp + mov_sp
def tail(self, k):
return self.epilogue(k) + self.b()
@@ -588,19 +596,30 @@ class AMD64(Encoder):
return 'C3'
def prologue(self, k):
- # pop rcx ; sub rsp,fb ; push rcx. rcx is the retaddr-carry
- # scratch — caller-save, never a P1 reg. r11 (= 'br') is
- # off-limits because TAIL = EPILOGUE + `jmp r11`, and using
- # r11 here would clobber the LI_BR-loaded tail target.
+ # pop rcx ; mov rax,rsp ; sub rsp,fb ; [rsp]=rcx ; [rsp+8]=rax ;
+ # xor rax,rax ; add rax,k ; [rsp+16]=rax.
+ # rcx carries the retaddr, rax carries saved_fp then k. Neither
+ # is a live incoming P1 argument register at function entry.
fb = prologue_frame_bytes(k)
assert fb <= 127
- return '59' + '4883EC' + byte(fb) + '51'
+ seq = '59' # pop rcx
+ seq += amd_mov_rr('r0', 'sp') # mov rax, rsp
+ seq += '4883EC' + byte(fb) # sub rsp, fb
+ seq += amd_mem_rm('89', 'rcx', 'sp', 0) # [rsp] = rcx
+ seq += amd_mem_rm('89', 'r0', 'sp', 8) # [rsp+8] = rax
+ seq += amd_alu_rr('31', 'r0', 'r0') # xor rax, rax
+ seq += amd_alu_ri8(0, 'r0', k) # add rax, k
+ seq += amd_mem_rm('89', 'r0', 'sp', 16) # [rsp+16] = rax
+ return seq
def epilogue(self, k):
- # Mirror of prologue: pop rcx ; add rsp,fb ; push rcx.
- fb = prologue_frame_bytes(k)
- assert fb <= 127
- return '59' + '4883C4' + byte(fb) + '51'
+ # r10 = retaddr ; rcx = saved_fp ; rsp = saved_fp ; push r10.
+ # Keep r0/rax intact so function return values survive EPILOGUE.
+ seq = amd_mem_rm('8B', 'r10', 'sp', 0) # r10 = [rsp]
+ seq += amd_mem_rm('8B', 'rcx', 'sp', 8) # rcx = [rsp+8]
+ seq += amd_mov_rr('sp', 'rcx') # rsp = rcx
+ seq += '4152' # push r10
+ return seq
def tail(self, k):
return self.epilogue(k) + self.b()
@@ -675,13 +694,17 @@ class RV64(Encoder):
fb = prologue_frame_bytes(k)
sub = rv_i(0x00000013, 'sp', 'sp', -fb)
sd = rv_s(0x00003023, 'ra', 'sp', 0)
- return sub + sd
+ fp = rv_i(0x00000013, 't0', 'sp', fb)
+ sd_fp = rv_s(0x00003023, 't0', 'sp', 8)
+ k_imm = rv_i(0x00000013, 't0', 'zero', k)
+ sd_k = rv_s(0x00003023, 't0', 'sp', 16)
+ return sub + sd + fp + sd_fp + k_imm + sd_k
def epilogue(self, k):
- fb = prologue_frame_bytes(k)
ld = rv_i(0x00003003, 'ra', 'sp', 0)
- add = rv_i(0x00000013, 'sp', 'sp', fb)
- return ld + add
+ ld_fp = rv_i(0x00003003, 't0', 'sp', 8)
+ mov_sp = rv_i(0x00000013, 'sp', 't0', 0)
+ return ld + ld_fp + mov_sp
def tail(self, k):
return self.epilogue(k) + self.b()
@@ -911,21 +934,21 @@ SYS_NUM = {
## stripping, loop counters, and — at 40 — the primitive-registration
## table's per-record stride. Full reg product × this set = 8²×N.
ADDI_IMMS = (-48, -8, -7, -6, -5, -4, -3, -2, -1,
- 1, 2, 3, 4, 5, 6, 7, 8, 40, 48)
+ 1, 2, 3, 4, 5, 6, 7, 8, 15, 16, 24, 40, 48)
## Shift amounts (for SHLI/SHRI/SARI). 32/52 implement low-N-bit masks
## (length field extraction; 4096-slot symbol-table index); the small
## values scale-by-N for byte offsets and fixnum encode/decode.
-SHIFT_IMMS = (1, 2, 3, 5, 13, 16, 32, 52)
+SHIFT_IMMS = (1, 2, 3, 4, 5, 13, 16, 32, 52)
## ANDI/ORI imms. Every entry must appear in AA64_LOGI_ENC.
LOGI_IMMS = (1, 2, 3, 4, 6, 7, 8)
-## Memory offsets for LD/ST/LB/SB. 0/8/16/24/32 cover slot offsets in
+## Memory offsets for LD/ST/LB/SB. 8*(0-6) cover slot offsets in
## N-slot frames and common struct fields; 7 is the NUL terminator
## position inside an 8-byte zero-padded slot; -8 reaches one slot
## below the current base.
-MEM_OFFS = (-8, 0, 7, 8, 16, 24, 32)
+MEM_OFFS = (-8, 0, 6, 7, 8, 16, 24, 32, 40, 48)
CONDB_OPS = ('BEQ', 'BNE', 'BLT')
CONDBZ_OPS = ('BEQZ', 'BNEZ', 'BLTZ')
@@ -950,16 +973,23 @@ RRR_TABLE = (
('DIV','r1','r1','r2'),
('REM','r1','r1','r5'),
('SHL','r1','r1','r2'),
+ ('SHL','r2','r2','r1'),
('SHR','r1','r1','r2'),
('SAR','r4','r4','r2'),
# alloc / pointer arithmetic
('ADD','r2','r0','r1'),
+ ('ADD','r2','r1','r0'),
('ADD','r0','r0','r1'),
('ADD','r0','r0','r2'),
('ADD','r0','r0','r3'),
('ADD','r2','r2','r0'),
('ADD','r2','r2','r1'),
+ ('SUB','r1','r1','r0'),
+ ('SUB','r2','r1','r2'),
('SUB','r3','r3','r0'),
+ ('AND','r1','r1','r2'),
+ ('AND','r3','r3','r2'),
+ ('OR', 'r3','r3','r2'),
# reader / display index+offset fold
('ADD','r6','r1','r2'),
('ADD','r6','r6','r0'),