commit efa02b4ed5866ee6796438feed0f562d465281de
parent 65d201320ea94388e45a60bb03831a769cba3382
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Sat, 25 Apr 2026 13:08:13 -0700
scheme1: runtime errors, bytevector bounds, helper macros
scheme1.P1pp gains a runtime_error helper plus a heap_end /
HEAP_CAP_BYTES bound; cons, alloc_hdr, alloc_bytes, load_source, and
eval_prelude all check their bumps and abort cleanly on overflow.
make-bytevector, bytevector-grow!, bytevector-u8-ref/set!,
bytevector-copy, and bytevector-copy! check index / range / negative
arguments through the same path.
On top of that: introduce if_nil, advance_walk, bind_global_from_t0,
car_fix, car_bvdata, args2/3/4 macros plus a list_length leaf helper,
and apply them across eval/apply, special-form handlers, and
primitives. Convert eval_args and apply_build_args to iterative
head/tail-cdr builds so the host stack stays O(1) on long arg lists.
M1pp expander (M1pp.P1, P1 ISA files): widen source tokens to 32
bytes with a tight field tracking whether whitespace preceded the
token. Paren-call recognizers (%foo(...), !(...), @(...), %(...),
\$(...), %select(...), %str(...)) consult the bit so a parenthesized
argument list is only matched when the open paren is tight against
the directive. Bump source / body / expansion-pool to ~2 MB each so
the larger scheme1.P1pp fits.
docs/scheme-shell-todo.md: tick the heap-exhaustion, symtab-name, and
bytevector-bounds items.
Diffstat:
| M | M1pp/M1pp.P1 | | | 747 | ++++++++++++++++++++++++++++++++++++++++++++++++------------------------------- |
| M | P1/P1-aarch64.M1 | | | 54 | ++++++++++++++++++++++++++++++++++++------------------ |
| M | P1/P1-amd64.M1 | | | 54 | ++++++++++++++++++++++++++++++++++++------------------ |
| M | P1/P1-riscv64.M1 | | | 54 | ++++++++++++++++++++++++++++++++++++------------------ |
| M | docs/scheme-shell-todo.md | | | 134 | ++++++++++++++++--------------------------------------------------------------- |
| M | scheme1/scheme1.P1pp | | | 745 | ++++++++++++++++++++++++++++++++++++++++++++++++------------------------------- |
6 files changed, 1039 insertions(+), 749 deletions(-)
diff --git a/M1pp/M1pp.P1 b/M1pp/M1pp.P1
@@ -30,15 +30,16 @@
DEFINE M1PP_INPUT_CAP 0000040000000000
DEFINE M1PP_OUTPUT_CAP 0000040000000000
DEFINE M1PP_TEXT_CAP 0000080000000000
-DEFINE M1PP_TOKENS_END 0000c00000000000
+## source_tokens cap: 2 MB / 32-byte tokens = 65536 tokens.
+DEFINE M1PP_TOKENS_END 0000200000000000
## Macro record is 296 bytes: name (16) + param_count (8) + params[16]*16 (256)
## + body_start (8) + body_end (8). MACROS_CAP fits 512 records (151552 B).
-## Body-token arena fits 4096 tokens (98304 B = 0x18000).
+## Body-token arena fits 65536 tokens (2 MB = 0x200000).
DEFINE M1PP_MACRO_RECORD_SIZE 2801000000000000
DEFINE M1PP_MACRO_BODY_START_OFF 1801000000000000
DEFINE M1PP_MACRO_BODY_END_OFF 2001000000000000
DEFINE M1PP_MACROS_CAP 0050020000000000
-DEFINE M1PP_MACRO_BODY_CAP 0000030000000000
+DEFINE M1PP_MACRO_BODY_CAP 0000200000000000
DEFINE O_WRONLY_CREAT_TRUNC 4102000000000000
DEFINE MODE_0644 A401000000000000
DEFINE AT_FDCWD 9CFFFFFFFFFFFFFF
@@ -56,8 +57,11 @@ DEFINE TOK_PASTE 0600000000000000
DEFINE TOK_LBRACE 0700000000000000
DEFINE TOK_RBRACE 0800000000000000
-## Token record stride (kind + text_ptr + text_len). Advance a Token* by this.
-DEFINE M1PP_TOK_SIZE 1800000000000000
+## Token record stride (kind + text_ptr + text_len + tight). Advance by this.
+## Layout: +0 kind (8), +8 text_ptr (8), +16 text_len (8), +24 tight (8) = 32.
+## Only byte 0 of the tight word is meaningful (0/1); upper bytes are zero.
+DEFINE M1PP_TOK_SIZE 2000000000000000
+DEFINE M1PP_TOK_TIGHT_OFF 1800000000000000
## --- Stream / expansion-pool / expression-frame sizes ------------------------
## Stream record: 40 bytes. Fields (each 8 bytes):
@@ -75,8 +79,8 @@ DEFINE M1PP_STREAM_MARK_OFF 2000000000000000
## Stream stack cap: 16 streams × 40 = 640 bytes.
DEFINE M1PP_STREAM_STACK_CAP 8002000000000000
-## Expansion pool fits 4096 Token slots × 24 bytes = 98304 bytes (0x18000).
-DEFINE M1PP_EXPAND_CAP 0080010000000000
+## Expansion pool fits 65536 Token slots × 32 bytes = 2 MB (0x200000).
+DEFINE M1PP_EXPAND_CAP 0000200000000000
## ExprFrame record: 144 bytes. Fields:
## +0 op_code u64
@@ -118,6 +122,9 @@ DEFINE EXPR_GE 1000000000000000
DEFINE EXPR_STRLEN 1100000000000000
DEFINE EXPR_INVALID 1200000000000000
## --- BSS layout (offsets from ELF_end) -------------------------------------
+## With 32-byte tokens we need ~2 MB per token region (source/body/pool) to
+## fit large expansions like the scheme1 build. Total BSS ~7.8 MB, under
+## the 8 MB segment memsz set by vendor/seed/<arch>/ELF.hex2.
DEFINE OFF_paste_scratch 0000000000000000
DEFINE OFF_local_label_scratch 0001000000000000
DEFINE OFF_scope_stack 8001000000000000
@@ -129,11 +136,11 @@ DEFINE OFF_input_buf 8006000000000000
DEFINE OFF_output_buf 8006080000000000
DEFINE OFF_text_buf 80060c0000000000
DEFINE OFF_source_tokens 8006140000000000
-DEFINE OFF_macros 8006200000000000
-DEFINE OFF_macro_body_tokens 8046290000000000
-DEFINE OFF_streams 8046350000000000
-DEFINE OFF_expand_pool 0049350000000000
-DEFINE OFF_expr_frames 0049410000000000
+DEFINE OFF_macros 8006340000000000
+DEFINE OFF_macro_body_tokens 80463d0000000000
+DEFINE OFF_streams 80465d0000000000
+DEFINE OFF_expand_pool 00495d0000000000
+DEFINE OFF_expr_frames 00497d0000000000
## --- Runtime shell: argv, read input, call pipeline, write output, exit ------
@@ -364,12 +371,15 @@ DEFINE OFF_expr_frames 0049410000000000
mov_a0,t0
ret
-## push_source_token(a0=kind, a1=text_ptr, a2=text_len). Leaf.
-## Token layout: +0 kind, +8 text_ptr, +16 text_len (24 bytes total).
+## push_source_token(a0=kind, a1=text_ptr, a2=text_len, a3=tight). Leaf.
+## Token layout: +0 kind, +8 text_ptr, +16 text_len, +24 tight (32B total).
+## tight=1 means "no whitespace before this token"; consulted only on LPAREN
+## by the paren-call recognizers (%FOO(...), !(...), @(...), %(...), $(...),
+## %select(...), %str(...)). All other kinds carry the bit but ignore it.
:push_source_token
# tok = source_end
- la_a3 &source_end
- ld_t0,a3,0
+ la_t2 &source_end
+ ld_t0,t2,0
# if (tok == &source_tokens[0] + TOKENS_END) fatal
la_t1 &source_tokens_ptr
@@ -379,14 +389,17 @@ DEFINE OFF_expr_frames 0049410000000000
la_br &err_token_overflow
beq_t0,t1
- # tok->kind = kind; tok->text_ptr = text_ptr; tok->text_len = text_len
+ # tok->kind = kind; tok->text_ptr = text_ptr; tok->text_len = text_len;
+ # tok->tight = tight (8-byte stores zero the rest of each slot).
st_a0,t0,0
st_a1,t0,8
st_a2,t0,16
+ st_a3,t0,24
- # source_end = tok + 1 (advance 24 bytes)
- addi_t0,t0,24
- st_t0,a3,0
+ # source_end = tok + 1 (advance 32 bytes)
+ la_t2 &source_end
+ addi_t0,t0,32
+ st_t0,t2,0
ret
## tok_eq_const(a0=token_ptr, a1=const_ptr, a2=const_len) -> a0=0/1. Leaf.
@@ -438,12 +451,19 @@ DEFINE OFF_expr_frames 0049410000000000
## the terminating NUL that _start writes past the end of input_buf.
## lex_source(): fills source_tokens[] from input_buf.
+## lex_saw_separator tracks whether whitespace (sp/tab/CR/etc., newline,
+## or `;`/`#` line comment) precedes the next token. Each non-newline push
+## passes tight = !lex_saw_separator and then sets lex_saw_separator = 0.
+## Initialized to 1 at start of file so the first token is never tight.
:lex_source
enter_0
la_a0 &input_buf_ptr
ld_a0,a0,0
la_a1 &lex_ptr
st_a0,a1,0
+ la_a0 &lex_saw_separator
+ li_t0 %1 %0
+ st_t0,a0,0
:lex_loop
# c = *lex_ptr; dispatch on lex_char_class[c].
# 0 word, 1 skip ws, 2 newline, 3 string, 4 hash, 5 comment,
@@ -492,7 +512,10 @@ DEFINE OFF_expr_frames 0049410000000000
b
:lex_skip_one
- # lex_ptr++
+ # whitespace separator: lex_saw_separator = 1; lex_ptr++
+ la_a1 &lex_saw_separator
+ li_a2 %1 %0
+ st_a2,a1,0
addi_t0,t0,1
la_a0 &lex_ptr
st_t0,a0,0
@@ -500,13 +523,19 @@ DEFINE OFF_expr_frames 0049410000000000
b
:lex_newline
- # push_source_token(TOK_NEWLINE, lex_ptr, 1)
+ # push_source_token(TOK_NEWLINE, lex_ptr, 1, tight=0); newline acts as a
+ # separator for the NEXT token, so lex_saw_separator = 1 afterwards.
mov_a1,t0
li_a0 TOK_NEWLINE
li_a2 %1 %0
+ li_a3 %0 %0
la_br &push_source_token
call
+ la_a0 &lex_saw_separator
+ li_t0 %1 %0
+ st_t0,a0,0
+
# lex_ptr++
la_a0 &lex_ptr
ld_t0,a0,0
@@ -551,7 +580,8 @@ DEFINE OFF_expr_frames 0049410000000000
la_br &append_text
call
- # push_source_token(TOK_STRING, text_ptr, lex_ptr - lex_start)
+ # push_source_token(TOK_STRING, text_ptr, lex_ptr - lex_start,
+ # tight = !lex_saw_separator); then lex_saw_separator = 0.
la_a1 &lex_ptr
ld_t0,a1,0
la_a1 &lex_start
@@ -559,8 +589,15 @@ DEFINE OFF_expr_frames 0049410000000000
sub_a2,t0,t1
mov_a1,a0
li_a0 TOK_STRING
+ la_a3 &lex_saw_separator
+ ld_a3,a3,0
+ li_t1 %1 %0
+ sub_a3,t1,a3
la_br &push_source_token
call
+ la_a0 &lex_saw_separator
+ li_t0 %0 %0
+ st_t0,a0,0
la_br &lex_loop
b
@@ -581,13 +618,21 @@ DEFINE OFF_expr_frames 0049410000000000
la_br &append_text
call
- # push_source_token(TOK_PASTE, text_ptr, 2)
+ # push_source_token(TOK_PASTE, text_ptr, 2, tight = !lex_saw_separator).
mov_a1,a0
li_a0 TOK_PASTE
li_a2 %2 %0
+ la_a3 &lex_saw_separator
+ ld_a3,a3,0
+ li_t1 %1 %0
+ sub_a3,t1,a3
la_br &push_source_token
call
+ la_a0 &lex_saw_separator
+ li_t0 %0 %0
+ st_t0,a0,0
+
# lex_ptr += 2
la_a0 &lex_ptr
ld_t0,a0,0
@@ -611,8 +656,12 @@ DEFINE OFF_expr_frames 0049410000000000
la_br &lex_comment_loop
b
:lex_comment_done
+ # `;` / `#` line comment counts as a separator for the next token.
la_a0 &lex_ptr
st_t0,a0,0
+ la_a0 &lex_saw_separator
+ li_t0 %1 %0
+ st_t0,a0,0
la_br &lex_loop
b
@@ -661,8 +710,16 @@ DEFINE OFF_expr_frames 0049410000000000
la_t0 &lex_punct_kind
ld_a0,t0,0
li_a2 %1 %0
+ # tight = !lex_saw_separator (the load is consumed before push_source_token).
+ la_a3 &lex_saw_separator
+ ld_a3,a3,0
+ li_t1 %1 %0
+ sub_a3,t1,a3
la_br &push_source_token
call
+ la_a0 &lex_saw_separator
+ li_t0 %0 %0
+ st_t0,a0,0
## fall through to lex_advance_one_then_loop
:lex_advance_one_then_loop
@@ -701,7 +758,8 @@ DEFINE OFF_expr_frames 0049410000000000
la_br &append_text
call
- # push_source_token(TOK_WORD, text_ptr, lex_ptr - lex_start)
+ # push_source_token(TOK_WORD, text_ptr, lex_ptr - lex_start,
+ # tight = !lex_saw_separator); lex_saw_separator = 0.
la_a1 &lex_ptr
ld_t0,a1,0
la_a1 &lex_start
@@ -709,8 +767,15 @@ DEFINE OFF_expr_frames 0049410000000000
sub_a2,t0,t1
mov_a1,a0
li_a0 TOK_WORD
+ la_a3 &lex_saw_separator
+ ld_a3,a3,0
+ li_t1 %1 %0
+ sub_a3,t1,a3
la_br &push_source_token
call
+ la_a0 &lex_saw_separator
+ li_t0 %0 %0
+ st_t0,a0,0
la_br &lex_loop
b
@@ -1075,10 +1140,8 @@ DEFINE OFF_expr_frames 0049410000000000
# tok = s->pos
st_t0,sp,8
- # ---- line_start && tok->kind == TOK_WORD && tok eq "%macro" ----
- ld_a1,a0,24
- la_br &proc_check_newline
- beqz_a1
+ # ---- tok->kind == TOK_WORD && tok eq "%macro" ----
+ # Directives are recognized anywhere in the input, not only at line-start.
ld_a1,t0,0
li_a2 TOK_WORD
la_br &proc_check_newline
@@ -1095,14 +1158,17 @@ DEFINE OFF_expr_frames 0049410000000000
# define_macro reads/writes proc_pos and walks against source_end,
# so it only behaves correctly when s is the source stream — which
# holds in practice (line_start in expansion streams is cleared
- # before any %macro could matter). After it returns we copy
- # proc_pos back into s->pos and set s->line_start = 1.
+ # before any %macro could matter). proc_line_start receives the stream's
+ # line_start at directive entry — define_macro consults it to decide
+ # whether to consume the trailing newline after %endm. After the call
+ # we copy proc_pos back into s->pos and set s->line_start = 1.
+ ld_a0,sp,0
ld_t0,sp,8
- la_a0 &proc_pos
- st_t0,a0,0
- la_a0 &proc_line_start
- li_a1 %1 %0
- st_a1,a0,0
+ la_a1 &proc_pos
+ st_t0,a1,0
+ ld_a2,a0,24
+ la_a1 &proc_line_start
+ st_a2,a1,0
la_br &define_macro
call
ld_a0,sp,0
@@ -1114,9 +1180,9 @@ DEFINE OFF_expr_frames 0049410000000000
la_br &proc_loop
b
-## ---- line_start && tok eq "%struct" ----
-## The %macro guard above already proved line_start && kind == TOK_WORD; if
-## we reach here via a %macro non-match, those gates still hold.
+## ---- tok eq "%struct" ----
+## The %macro guard above already proved kind == TOK_WORD; if we reach here
+## via a %macro non-match, that gate still holds.
:proc_check_struct
ld_t0,sp,8
mov_a0,t0
@@ -1128,12 +1194,13 @@ DEFINE OFF_expr_frames 0049410000000000
beqz_a0
# %struct matched: shim into define_fielded(stride=8, total="SIZE", len=4)
+ ld_a0,sp,0
ld_t0,sp,8
- la_a0 &proc_pos
- st_t0,a0,0
- la_a0 &proc_line_start
- li_a1 %1 %0
- st_a1,a0,0
+ la_a1 &proc_pos
+ st_t0,a1,0
+ ld_a2,a0,24
+ la_a1 &proc_line_start
+ st_a2,a1,0
li_a0 %8 %0
la_a1 &const_size
li_a2 %4 %0
@@ -1148,7 +1215,7 @@ DEFINE OFF_expr_frames 0049410000000000
la_br &proc_loop
b
-## ---- line_start && tok eq "%enum" ----
+## ---- tok eq "%enum" ----
:proc_check_enum
ld_t0,sp,8
mov_a0,t0
@@ -1160,12 +1227,13 @@ DEFINE OFF_expr_frames 0049410000000000
beqz_a0
# %enum matched: shim into define_fielded(stride=1, total="COUNT", len=5)
+ ld_a0,sp,0
ld_t0,sp,8
- la_a0 &proc_pos
- st_t0,a0,0
- la_a0 &proc_line_start
- li_a1 %1 %0
- st_a1,a0,0
+ la_a1 &proc_pos
+ st_t0,a1,0
+ ld_a2,a0,24
+ la_a1 &proc_line_start
+ st_a2,a1,0
li_a0 %1 %0
la_a1 &const_count
li_a2 %5 %0
@@ -1180,7 +1248,7 @@ DEFINE OFF_expr_frames 0049410000000000
la_br &proc_loop
b
-## ---- line_start && tok eq "%scope" ----
+## ---- tok eq "%scope" ----
:proc_check_scope
ld_t0,sp,8
mov_a0,t0
@@ -1192,12 +1260,13 @@ DEFINE OFF_expr_frames 0049410000000000
beqz_a0
# %scope matched: shim into push_scope(stream_end).
+ ld_a0,sp,0
ld_t0,sp,8
- la_a0 &proc_pos
- st_t0,a0,0
- la_a0 &proc_line_start
- li_a1 %1 %0
- st_a1,a0,0
+ la_a1 &proc_pos
+ st_t0,a1,0
+ ld_a2,a0,24
+ la_a1 &proc_line_start
+ st_a2,a1,0
ld_a0,sp,0
ld_a0,a0,8
la_br &push_scope
@@ -1211,7 +1280,7 @@ DEFINE OFF_expr_frames 0049410000000000
la_br &proc_loop
b
-## ---- line_start && tok eq "%endscope" ----
+## ---- tok eq "%endscope" ----
:proc_check_endscope
ld_t0,sp,8
mov_a0,t0
@@ -1223,12 +1292,13 @@ DEFINE OFF_expr_frames 0049410000000000
beqz_a0
# %endscope matched: shim into pop_scope(stream_end).
+ ld_a0,sp,0
ld_t0,sp,8
- la_a0 &proc_pos
- st_t0,a0,0
- la_a0 &proc_line_start
- li_a1 %1 %0
- st_a1,a0,0
+ la_a1 &proc_pos
+ st_t0,a1,0
+ ld_a2,a0,24
+ la_a1 &proc_line_start
+ st_a2,a1,0
ld_a0,sp,0
ld_a0,a0,8
la_br &pop_scope
@@ -1252,7 +1322,7 @@ DEFINE OFF_expr_frames 0049410000000000
bne_a1,a2
# newline: s->pos += 24; s->line_start = 1; emit_newline()
- addi_t0,t0,24
+ addi_t0,t0,32
st_t0,a0,16
li_t1 %1 %0
st_t1,a0,24
@@ -1269,7 +1339,7 @@ DEFINE OFF_expr_frames 0049410000000000
li_a2 TOK_WORD
la_br &proc_check_macro
bne_a1,a2
- addi_t1,t0,24
+ addi_t1,t0,32
ld_a1,a0,8
la_br &proc_check_builtin_has_next
blt_t1,a1
@@ -1281,6 +1351,12 @@ DEFINE OFF_expr_frames 0049410000000000
la_br &proc_check_macro
bne_a1,a2
+ # require (tok+1)->tight — `! ( ... )` with whitespace before `(` is
+ # NOT a builtin paren-call; emit `!` literally then handle `(` later.
+ ld_a1,t1,24
+ la_br &proc_check_macro
+ beqz_a1
+
# try the six builtin names: ! @ % $ %select %str
mov_a0,t0
la_a1 &const_bang
@@ -1348,7 +1424,7 @@ DEFINE OFF_expr_frames 0049410000000000
mov_t2,a0
ld_a0,sp,0
ld_t0,sp,8
- addi_t1,t0,24
+ addi_t1,t0,32
ld_a1,a0,8
la_br &proc_macro_has_next
blt_t1,a1
@@ -1359,6 +1435,11 @@ DEFINE OFF_expr_frames 0049410000000000
li_a2 TOK_LPAREN
la_br &proc_macro_zero_arg
bne_a1,a2
+ # require (tok+1)->tight — `%FOO ( ... )` with whitespace is the
+ # paren-less form (zero-arg only) followed by a literal `(`.
+ ld_a1,t1,24
+ la_br &proc_macro_zero_arg
+ beqz_a1
ld_a0,sp,0
mov_a1,t2
la_br &expand_call
@@ -1366,7 +1447,7 @@ DEFINE OFF_expr_frames 0049410000000000
la_br &proc_loop
b
:proc_macro_zero_arg
- # No trailing LPAREN. Expand only if macro->param_count == 0.
+ # No trailing LPAREN (or LPAREN not tight). Expand only if param_count == 0.
ld_t0,t2,16
la_br &proc_emit
bnez_t0
@@ -1384,7 +1465,7 @@ DEFINE OFF_expr_frames 0049410000000000
call
ld_a0,sp,0
ld_t0,a0,16
- addi_t0,t0,24
+ addi_t0,t0,32
st_t0,a0,16
li_t1 %0 %0
st_t1,a0,24
@@ -1410,17 +1491,28 @@ DEFINE OFF_expr_frames 0049410000000000
## Input: a0 = stream end (pointer one past last token in the current stream).
## Output: proc_pos advanced past the trailing newline (or stream end).
-## push_scope(a0 = stream_end): consume `%scope NAME\n`.
-## Name must be a single WORD token; anything else on the line is an error.
+## push_scope(a0 = stream_end): consume `%scope NAME` (header self-terminates
+## at NAME). Name must be a single WORD token. Newlines between %scope and
+## NAME, and between NAME and the body, are insignificant.
:push_scope
enter_0
# proc_pos += 24 (skip past the `%scope` token).
la_t0 &proc_pos
ld_t1,t0,0
- addi_t1,t1,24
+ addi_t1,t1,32
st_t1,t0,0
+ # Skip newlines between `%scope` and NAME.
+ la_a1 &psc_stream_end
+ st_a0,a1,0 # save stream_end across the call
+ la_br &proc_skip_newlines
+ call
+ la_a1 &psc_stream_end
+ ld_a0,a1,0
+ la_t0 &proc_pos
+ ld_t1,t0,0
+
# Require a WORD name token within the stream.
la_br &err_bad_scope_header
beq_t1,a0
@@ -1453,25 +1545,16 @@ DEFINE OFF_expr_frames 0049410000000000
# proc_pos += 24 (past the name).
la_t0 &proc_pos
ld_t1,t0,0
- addi_t1,t1,24
+ addi_t1,t1,32
st_t1,t0,0
- # EOF here is tolerated (caller handles stream end). Otherwise the next
- # token must be TOK_NEWLINE — anything else is a header error.
- la_br &psc_done
- beq_t1,a0
- ld_t2,t1,0
- li_t0 TOK_NEWLINE
- la_br &err_bad_scope_header
- bne_t2,t0
- addi_t1,t1,24
- la_t0 &proc_pos
- st_t1,t0,0
-:psc_done
+ # Newlines between `%scope NAME` and the body content are insignificant.
+ la_br &proc_skip_newlines
+ call
eret
-## pop_scope(a0 = stream_end): consume `%endscope\n`. Extra tokens on the line
-## are tolerated (matches %endm's behavior) — skip to the next newline.
+## pop_scope(a0 = stream_end): consume `%endscope` followed by a strict
+## TOK_NEWLINE — extra tokens on the line are now an error.
:pop_scope
enter_0
@@ -1486,23 +1569,23 @@ DEFINE OFF_expr_frames 0049410000000000
# proc_pos += 24 (past the `%endscope` token).
la_t0 &proc_pos
ld_t1,t0,0
- addi_t1,t1,24
+ addi_t1,t1,32
st_t1,t0,0
-:pop_skip_loop
- la_br &pop_done
+ # Strict: the token immediately after `%endscope` must be TOK_NEWLINE.
+ la_br &err_bad_scope_header
beq_t1,a0
ld_t2,t1,0
li_t0 TOK_NEWLINE
- la_br &pop_consume_newline
- beq_t2,t0
- addi_t1,t1,24
- la_t0 &proc_pos
- st_t1,t0,0
- la_br &pop_skip_loop
- b
-:pop_consume_newline
- addi_t1,t1,24
+ la_br &err_bad_scope_header
+ bne_t2,t0
+ # Consume the trailing newline only when %endscope sat at line-start;
+ # mid-line %endscope leaves the newline so it can be emitted.
+ la_t0 &proc_line_start
+ ld_a1,t0,0
+ la_br &pop_done
+ beqz_a1
+ addi_t1,t1,32
la_t0 &proc_pos
st_t1,t0,0
:pop_done
@@ -1542,10 +1625,17 @@ DEFINE OFF_expr_frames 0049410000000000
# advance past the %macro token itself
la_a0 &proc_pos
ld_t0,a0,0
- addi_t0,t0,24
+ addi_t0,t0,32
st_t0,a0,0
+ # Header is whitespace-insensitive: newlines between the keyword and
+ # any header element (NAME, '(', params, ',', ')') are skipped.
+ la_br &proc_skip_newlines
+ call
+
# ---- header: name (WORD) ----
+ la_a0 &proc_pos
+ ld_t0,a0,0
la_a1 &source_end
ld_t1,a1,0
la_br &err_bad_macro_header
@@ -1563,7 +1653,7 @@ DEFINE OFF_expr_frames 0049410000000000
st_a2,t2,0
st_a3,t2,8
- # m->param_count = 0; def_param_ptr = m + 24 (first TextSpan slot)
+ # m->param_count = 0; def_param_ptr = m + 24 (first TextSpan slot in macro)
li_a0 %0 %0
st_a0,t2,16
addi_t2,t2,24
@@ -1571,11 +1661,16 @@ DEFINE OFF_expr_frames 0049410000000000
st_t2,a0,0
# advance past name
- addi_t0,t0,24
+ addi_t0,t0,32
la_a0 &proc_pos
st_t0,a0,0
+ la_br &proc_skip_newlines
+ call
+
# ---- header: LPAREN ----
+ la_a0 &proc_pos
+ ld_t0,a0,0
la_a1 &source_end
ld_t1,a1,0
la_br &err_bad_macro_header
@@ -1586,14 +1681,19 @@ DEFINE OFF_expr_frames 0049410000000000
bne_a1,a2
# advance past '('
- addi_t0,t0,24
+ addi_t0,t0,32
la_a0 &proc_pos
st_t0,a0,0
+ la_br &proc_skip_newlines
+ call
+
# ---- header: optional param list ----
# if at end -> fall through to RPAREN check (which will fail)
# if next is RPAREN -> skip the param loop
# else enter param loop
+ la_a0 &proc_pos
+ ld_t0,a0,0
la_a1 &source_end
ld_t1,a1,0
la_br &def_header_close
@@ -1642,11 +1742,17 @@ DEFINE OFF_expr_frames 0049410000000000
st_a1,t2,16
# advance past the param word
- addi_t0,t0,24
+ addi_t0,t0,32
la_a0 &proc_pos
st_t0,a0,0
+ # Skip newlines between a param and the following ',' or ')'.
+ la_br &proc_skip_newlines
+ call
+
# if next is COMMA, consume and loop; else break
+ la_a0 &proc_pos
+ ld_t0,a0,0
la_a1 &source_end
ld_t1,a1,0
la_br &def_header_close
@@ -1655,9 +1761,12 @@ DEFINE OFF_expr_frames 0049410000000000
li_a2 TOK_COMMA
la_br &def_header_close
bne_a1,a2
- addi_t0,t0,24
+ addi_t0,t0,32
la_a0 &proc_pos
st_t0,a0,0
+ # Skip newlines after ',' so the next param can be on a new line.
+ la_br &proc_skip_newlines
+ call
la_br &def_param_loop
b
@@ -1674,25 +1783,16 @@ DEFINE OFF_expr_frames 0049410000000000
la_br &err_bad_macro_header
bne_a1,a2
- addi_t0,t0,24
+ addi_t0,t0,32
la_a0 &proc_pos
st_t0,a0,0
- # ---- header: terminating NEWLINE ----
- la_a1 &source_end
- ld_t1,a1,0
- la_br &err_bad_macro_header
- beq_t0,t1
- ld_a1,t0,0
- li_a2 TOK_NEWLINE
- la_br &err_bad_macro_header
- bne_a1,a2
-
- addi_t0,t0,24
- la_a0 &proc_pos
- st_t0,a0,0
+ # ---- header self-terminates at ')'. Newlines between the header line
+ # ---- and the body content are insignificant — skip them.
+ la_br &proc_skip_newlines
+ call
- # ---- body: m->body_start = macro_body_end; body_line_start = 1 ----
+ # ---- body: m->body_start = macro_body_end ----
la_a1 ¯o_body_end
ld_t2,a1,0
la_a0 &def_m_ptr
@@ -1700,9 +1800,6 @@ DEFINE OFF_expr_frames 0049410000000000
li_a0 M1PP_MACRO_BODY_START_OFF
add_a0,t1,a0
st_t2,a0,0
- la_a0 &def_body_line_start
- li_a1 %1 %0
- st_a1,a0,0
:def_body_loop
# if proc_pos == source_end: unterminated %macro
@@ -1713,19 +1810,13 @@ DEFINE OFF_expr_frames 0049410000000000
la_br &err_unterminated_macro
beq_t0,t1
- # if (!body_line_start) copy token
- la_a0 &def_body_line_start
- ld_t2,a0,0
- la_br &def_body_copy
- beqz_t2
-
- # if (tok.kind != TOK_WORD) copy token
+ # %endm is recognized anywhere in the body (no line-start gating). If
+ # (tok.kind == TOK_WORD) and (tok eq "%endm"), break; else copy token.
ld_a1,t0,0
li_a2 TOK_WORD
la_br &def_body_copy
bne_a1,a2
- # if (!tok_eq_const(tok, "%endm", 5)) copy token
mov_a0,t0
la_a1 &const_endm
li_a2 %5 %0
@@ -1734,23 +1825,23 @@ DEFINE OFF_expr_frames 0049410000000000
la_br &def_body_copy
beqz_a0
- # matched %endm at line start -> skip to end of the line, then finish
- la_br &def_endm_skip_to_newline
+ # matched %endm: advance past it and require a TOK_NEWLINE next.
+ la_br &def_endm_after
b
:def_body_copy
- # bounds: if (macro_body_end - macro_body_tokens + 24 > MACRO_BODY_CAP) fail
+ # bounds: if (macro_body_end - macro_body_tokens + 32 > MACRO_BODY_CAP) fail
la_a0 ¯o_body_end
ld_t1,a0,0
la_a2 ¯o_body_tokens_ptr
ld_a2,a2,0
sub_a3,t1,a2
- addi_a3,a3,24
+ addi_a3,a3,32
li_t2 M1PP_MACRO_BODY_CAP
la_br &err_macro_body_overflow
blt_t2,a3
- # copy 24 bytes from *proc_pos to *macro_body_end
+ # copy 32 bytes from *proc_pos to *macro_body_end (preserves tight at +24)
la_a0 &proc_pos
ld_t0,a0,0
ld_a1,t0,0
@@ -1759,52 +1850,45 @@ DEFINE OFF_expr_frames 0049410000000000
st_a1,t1,8
ld_a1,t0,16
st_a1,t1,16
+ ld_a1,t0,24
+ st_a1,t1,24
- # macro_body_end += 24
- addi_t1,t1,24
+ # macro_body_end += 32
+ addi_t1,t1,32
la_a0 ¯o_body_end
st_t1,a0,0
- # body_line_start = (tok.kind == TOK_NEWLINE)
- ld_a1,t0,0
- li_a2 TOK_NEWLINE
- la_br &def_body_clear_ls
- bne_a1,a2
- la_a0 &def_body_line_start
- li_a1 %1 %0
- st_a1,a0,0
- la_br &def_body_advance
- b
-:def_body_clear_ls
- la_a0 &def_body_line_start
- li_a1 %0 %0
- st_a1,a0,0
-:def_body_advance
- # proc_pos += 24
+ # proc_pos += 32
+ addi_t0,t0,32
la_a0 &proc_pos
- ld_t0,a0,0
- addi_t0,t0,24
st_t0,a0,0
la_br &def_body_loop
b
-:def_endm_skip_to_newline
- # consume tokens through the first NEWLINE (inclusive); tolerate EOF
+:def_endm_after
+ # advance past the %endm token; the next token MUST be TOK_NEWLINE.
la_a0 &proc_pos
ld_t0,a0,0
+ addi_t0,t0,32
+ st_t0,a0,0
la_a1 &source_end
ld_t1,a1,0
- la_br &def_finish
+ la_br &err_bad_macro_header
beq_t0,t1
ld_a1,t0,0
li_a2 TOK_NEWLINE
- addi_t0,t0,24
+ la_br &err_bad_macro_header
+ bne_a1,a2
+ # Consume the trailing NEWLINE only when the directive started at
+ # line-start; mid-line directives leave the newline in the stream so
+ # the outer loop emits it (preserving line layout).
+ la_a0 &proc_line_start
+ ld_a1,a0,0
+ la_br &def_finish
+ beqz_a1
+ addi_t0,t0,32
la_a0 &proc_pos
st_t0,a0,0
- la_br &def_finish
- beq_a1,a2
- la_br &def_endm_skip_to_newline
- b
:def_finish
# m->body_end = macro_body_end
@@ -1853,10 +1937,17 @@ DEFINE OFF_expr_frames 0049410000000000
# advance past the %struct / %enum directive token
la_a0 &proc_pos
ld_t0,a0,0
- addi_t0,t0,24
+ addi_t0,t0,32
st_t0,a0,0
+ # Header is whitespace-insensitive: newlines between %struct/%enum and
+ # the NAME (and between NAME and '{') are skipped.
+ la_br &proc_skip_newlines
+ call
+
# ---- header: name (WORD) ----
+ la_a0 &proc_pos
+ ld_t0,a0,0
la_a1 &source_end
ld_t1,a1,0
la_br &err_bad_directive
@@ -1875,7 +1966,7 @@ DEFINE OFF_expr_frames 0049410000000000
st_a2,a3,0
# advance past the base name
- addi_t0,t0,24
+ addi_t0,t0,32
la_a0 &proc_pos
st_t0,a0,0
@@ -1891,7 +1982,7 @@ DEFINE OFF_expr_frames 0049410000000000
li_a2 TOK_NEWLINE
la_br &df_require_lbrace
bne_a1,a2
- addi_t0,t0,24
+ addi_t0,t0,32
la_a0 &proc_pos
st_t0,a0,0
la_br &df_skip_nl_before_lbrace
@@ -1904,7 +1995,7 @@ DEFINE OFF_expr_frames 0049410000000000
bne_a1,a2
# advance past '{'
- addi_t0,t0,24
+ addi_t0,t0,32
la_a0 &proc_pos
st_t0,a0,0
@@ -1971,13 +2062,13 @@ DEFINE OFF_expr_frames 0049410000000000
# advance past the field word
la_a0 &proc_pos
ld_t0,a0,0
- addi_t0,t0,24
+ addi_t0,t0,32
st_t0,a0,0
la_br &df_field_loop
b
:df_field_skip_sep
- addi_t0,t0,24
+ addi_t0,t0,32
la_a0 &proc_pos
st_t0,a0,0
la_br &df_field_loop
@@ -1985,7 +2076,7 @@ DEFINE OFF_expr_frames 0049410000000000
:df_fields_done
# advance past '}'
- addi_t0,t0,24
+ addi_t0,t0,32
la_a0 &proc_pos
st_t0,a0,0
@@ -2010,23 +2101,26 @@ DEFINE OFF_expr_frames 0049410000000000
la_br &df_emit_field
call
- # consume tokens through the first trailing NEWLINE (tolerate EOF)
-:df_skip_trailing_loop
+ # Strict: the closing '}' must be immediately followed by TOK_NEWLINE.
+ # Consume that newline only when the directive started at line-start,
+ # mirroring %endm / %endscope.
la_a0 &proc_pos
ld_t0,a0,0
la_a1 &source_end
ld_t1,a1,0
- la_br &df_finish
+ la_br &err_bad_directive
beq_t0,t1
ld_a1,t0,0
li_a2 TOK_NEWLINE
- addi_t0,t0,24
+ la_br &err_bad_directive
+ bne_a1,a2
+ la_a0 &proc_line_start
+ ld_a1,a0,0
+ la_br &df_finish
+ beqz_a1
+ addi_t0,t0,32
la_a0 &proc_pos
st_t0,a0,0
- la_br &df_finish
- beq_a1,a2
- la_br &df_skip_trailing_loop
- b
:df_finish
la_a0 &proc_line_start
@@ -2144,18 +2238,20 @@ DEFINE OFF_expr_frames 0049410000000000
la_a2 ¯o_body_tokens_ptr
ld_a2,a2,0
sub_a3,t0,a2
- addi_a3,a3,24
+ addi_a3,a3,32
li_t2 M1PP_MACRO_BODY_CAP
la_br &err_macro_body_overflow
blt_t2,a3
- # body_tok = TOK_WORD { durable_digits, df_digit_count }
+ # body_tok = TOK_WORD { durable_digits, df_digit_count, tight=0 }
li_a1 TOK_WORD
st_a1,t0,0
st_a0,t0,8
la_a2 &df_digit_count
ld_a2,a2,0
st_a2,t0,16
+ li_a1 %0 %0
+ st_a1,t0,24
# m->body_start = macro_body_end (the slot we just wrote)
la_a0 ¯os_end
@@ -2165,7 +2261,7 @@ DEFINE OFF_expr_frames 0049410000000000
st_t0,a1,0
# macro_body_end += 24
- addi_t0,t0,24
+ addi_t0,t0,32
la_a1 ¯o_body_end
st_t0,a1,0
@@ -2316,8 +2412,8 @@ DEFINE OFF_expr_frames 0049410000000000
ret
## copy_span_to_pool(a0=start_tok, a1=end_tok) -> void (fatal on pool overflow)
-## Append each 24-byte Token in [start, end) to expand_pool at pool_used,
-## advancing pool_used accordingly.
+## Append each 32-byte Token in [start, end) to expand_pool at pool_used,
+## advancing pool_used accordingly. Preserves tight bit at +24.
## Reads/writes: expand_pool, pool_used. Leaf.
:copy_span_to_pool
:cstp_loop
@@ -2325,10 +2421,10 @@ DEFINE OFF_expr_frames 0049410000000000
la_br &cstp_done
beq_a0,a1
- # bounds: pool_used + 24 must fit in EXPAND_CAP
+ # bounds: pool_used + 32 must fit in EXPAND_CAP
la_a2 &pool_used
ld_t0,a2,0
- addi_t1,t0,24
+ addi_t1,t0,32
li_t2 M1PP_EXPAND_CAP
la_br &err_token_overflow
blt_t2,t1
@@ -2338,18 +2434,20 @@ DEFINE OFF_expr_frames 0049410000000000
ld_a3,a3,0
add_a3,a3,t0
- # copy 24 bytes (3 × u64)
+ # copy 32 bytes (4 × u64)
ld_t1,a0,0
st_t1,a3,0
ld_t1,a0,8
st_t1,a3,8
ld_t1,a0,16
st_t1,a3,16
+ ld_t1,a0,24
+ st_t1,a3,24
- # pool_used += 24; start += 24
- addi_t0,t0,24
+ # pool_used += 32; start += 32
+ addi_t0,t0,32
st_t0,a2,0
- addi_a0,a0,24
+ addi_a0,a0,32
la_br &cstp_loop
b
:cstp_done
@@ -2398,7 +2496,7 @@ DEFINE OFF_expr_frames 0049410000000000
## Fatal on: > 16 args, reaching limit without matching RPAREN.
:parse_args
# tok = lparen + 1; arg_start = tok; depth = 1; arg_index = 0; brace_depth = 0
- addi_a0,a0,24
+ addi_a0,a0,32
la_a2 &pa_pos
st_a0,a2,0
la_a2 &pa_arg_start
@@ -2445,7 +2543,7 @@ DEFINE OFF_expr_frames 0049410000000000
beq_a2,a3
# default: tok++
- addi_t0,t0,24
+ addi_t0,t0,32
la_a0 &pa_pos
st_t0,a0,0
la_br &pa_loop
@@ -2456,7 +2554,7 @@ DEFINE OFF_expr_frames 0049410000000000
ld_t1,a0,0
addi_t1,t1,1
st_t1,a0,0
- addi_t0,t0,24
+ addi_t0,t0,32
la_a0 &pa_pos
st_t0,a0,0
la_br &pa_loop
@@ -2471,7 +2569,7 @@ DEFINE OFF_expr_frames 0049410000000000
# if (depth != 0) tok++; loop
la_br &pa_rparen_close
beqz_t1
- addi_t0,t0,24
+ addi_t0,t0,32
la_a0 &pa_pos
st_t0,a0,0
la_br &pa_loop
@@ -2528,7 +2626,7 @@ DEFINE OFF_expr_frames 0049410000000000
:pa_finish
# call_end_pos = tok + 24
- addi_t0,t0,24
+ addi_t0,t0,32
la_a0 &call_end_pos
st_t0,a0,0
ret
@@ -2571,7 +2669,7 @@ DEFINE OFF_expr_frames 0049410000000000
la_a0 &pa_arg_index
st_a2,a0,0
# arg_start = tok + 24
- addi_t0,t0,24
+ addi_t0,t0,32
la_a0 &pa_arg_start
st_t0,a0,0
la_a0 &pa_pos
@@ -2581,7 +2679,7 @@ DEFINE OFF_expr_frames 0049410000000000
:pa_default_advance
# comma at depth != 1: just advance
- addi_t0,t0,24
+ addi_t0,t0,32
la_a0 &pa_pos
st_t0,a0,0
la_br &pa_loop
@@ -2593,7 +2691,7 @@ DEFINE OFF_expr_frames 0049410000000000
ld_t1,a0,0
addi_t1,t1,1
st_t1,a0,0
- addi_t0,t0,24
+ addi_t0,t0,32
la_a0 &pa_pos
st_t0,a0,0
la_br &pa_loop
@@ -2608,7 +2706,7 @@ DEFINE OFF_expr_frames 0049410000000000
# brace_depth--; tok++
addi_t1,t1,neg1
st_t1,a0,0
- addi_t0,t0,24
+ addi_t0,t0,32
la_a0 &pa_pos
st_t0,a0,0
la_br &pa_loop
@@ -2735,7 +2833,7 @@ DEFINE OFF_expr_frames 0049410000000000
la_br &find_param_zero
beq_t0,a1
- # param_ptr = fp_macro + 24 + idx * 16
+ # param_ptr = fp_macro + 24 + idx * 16 (macro record params start at +24)
la_a0 &fp_macro
ld_a2,a0,0
addi_a2,a2,24
@@ -2795,9 +2893,9 @@ DEFINE OFF_expr_frames 0049410000000000
## outer { ... } pair (outer RBRACE is the same-level mate of the leading
## LBRACE), else 0. Leaf.
:arg_is_braced
- # if (end - start < 2 tokens = 48 bytes) return 0
+ # if (end - start < 2 tokens = 64 bytes) return 0
sub_a2,a1,a0
- li_a3 %48 %0
+ li_a3 %64 %0
la_br &aib_zero
blt_a2,a3
@@ -2808,7 +2906,7 @@ DEFINE OFF_expr_frames 0049410000000000
bne_a2,a3
# if ((end - 24)->kind != TOK_RBRACE) return 0
- addi_t0,a1,neg24
+ addi_t0,a1,neg32
ld_a2,t0,0
li_a3 TOK_RBRACE
la_br &aib_zero
@@ -2819,7 +2917,7 @@ DEFINE OFF_expr_frames 0049410000000000
# t0 = tok, t1 = depth, t2 = last_tok = end - 24
mov_t0,a0
li_t1 %0 %0
- addi_t2,a1,neg24
+ addi_t2,a1,neg32
:aib_loop
la_br &aib_done
beq_t0,a1
@@ -2831,12 +2929,12 @@ DEFINE OFF_expr_frames 0049410000000000
la_br &aib_decr
beq_a2,a3
# non-brace: advance
- addi_t0,t0,24
+ addi_t0,t0,32
la_br &aib_loop
b
:aib_incr
addi_t1,t1,1
- addi_t0,t0,24
+ addi_t0,t0,32
la_br &aib_loop
b
:aib_decr
@@ -2847,7 +2945,7 @@ DEFINE OFF_expr_frames 0049410000000000
la_br &aib_zero
bne_t0,t2
:aib_decr_skip
- addi_t0,t0,24
+ addi_t0,t0,32
la_br &aib_loop
b
:aib_done
@@ -2879,8 +2977,8 @@ DEFINE OFF_expr_frames 0049410000000000
# braced: strip outer braces (start+24, end-24)
ld_a0,sp,0
ld_a1,sp,8
- addi_a0,a0,24
- addi_a1,a1,neg24
+ addi_a0,a0,32
+ addi_a1,a1,neg32
la_br &catp_done
beq_a0,a1
la_br ©_span_to_pool
@@ -2942,7 +3040,7 @@ DEFINE OFF_expr_frames 0049410000000000
st_a2,a3,0
# lparen = call_tok + 24
- addi_a0,a0,24
+ addi_a0,a0,32
# Branch split for paren-less 0-arg calls:
# if lparen < limit AND lparen->kind == TOK_LPAREN: parse_args as usual.
@@ -2961,6 +3059,12 @@ DEFINE OFF_expr_frames 0049410000000000
la_br &emt_try_zero_arg
bne_a2,a3
+ # if (!lparen->tight) goto emt_try_zero_arg — `%FOO ( ... )` with a space
+ # is a paren-less zero-arg call followed by a literal `(`.
+ ld_a2,a0,24
+ la_br &emt_try_zero_arg
+ beqz_a2
+
# parse_args(lparen, limit)
# a0 already lparen; a1 already limit
la_br &parse_args
@@ -3009,7 +3113,7 @@ DEFINE OFF_expr_frames 0049410000000000
# emt_after_pos = call_tok + 24
la_a0 &emt_call_tok
ld_t0,a0,0
- addi_t0,t0,24
+ addi_t0,t0,32
la_a1 &emt_after_pos
st_t0,a1,0
@@ -3077,7 +3181,7 @@ DEFINE OFF_expr_frames 0049410000000000
beq_t0,t1
# prev_kind = (body_pos - 24)->kind
- addi_t2,t0,neg24
+ addi_t2,t0,neg32
ld_a2,t2,0
li_a3 TOK_PASTE
la_br &emt_pasted
@@ -3085,7 +3189,7 @@ DEFINE OFF_expr_frames 0049410000000000
:emt_check_after
# next_pos = body_pos + 24; if (next_pos >= body_end) skip
- addi_t2,t0,24
+ addi_t2,t0,32
la_a1 &emt_body_end
ld_a3,a1,0
# if (next_pos == body_end) -> not pasted (need next_pos < body_end)
@@ -3314,15 +3418,18 @@ DEFINE OFF_expr_frames 0049410000000000
la_a0 &ll_total_len
ld_a2,a0,0
st_a2,a3,16
- # pool_used += 24
- addi_t0,t0,24
+ # tight = 0 (synthetic local-label token)
+ li_a2 %0 %0
+ st_a2,a3,24
+ # pool_used += 32
+ addi_t0,t0,32
la_a1 &pool_used
st_t0,a1,0
- # body_pos += 24
+ # body_pos += 32
la_a0 &emt_body_pos
ld_t0,a0,0
- addi_t0,t0,24
+ addi_t0,t0,32
st_t0,a0,0
la_br &emt_loop
b
@@ -3341,19 +3448,21 @@ DEFINE OFF_expr_frames 0049410000000000
# src = body_pos
la_a0 &emt_body_pos
ld_a3,a0,0
- # copy 24 bytes (3 x 8)
+ # copy 32 bytes (4 x 8) — preserves tight at +24
ld_a0,a3,0
st_a0,a2,0
ld_a0,a3,8
st_a0,a2,8
ld_a0,a3,16
st_a0,a2,16
- # pool_used += 24
- addi_t0,t0,24
+ ld_a0,a3,24
+ st_a0,a2,24
+ # pool_used += 32
+ addi_t0,t0,32
la_a0 &pool_used
st_t0,a0,0
- # body_pos += 24
- addi_a3,a3,24
+ # body_pos += 32
+ addi_a3,a3,32
la_a0 &emt_body_pos
st_a3,a0,0
la_br &emt_loop
@@ -3384,7 +3493,7 @@ DEFINE OFF_expr_frames 0049410000000000
# body_pos += 24
la_a0 &emt_body_pos
ld_t0,a0,0
- addi_t0,t0,24
+ addi_t0,t0,32
st_t0,a0,0
la_br &emt_loop
b
@@ -3414,7 +3523,7 @@ DEFINE OFF_expr_frames 0049410000000000
# body_pos += 24
la_a0 &emt_body_pos
ld_t0,a0,0
- addi_t0,t0,24
+ addi_t0,t0,32
st_t0,a0,0
la_br &emt_loop
b
@@ -3561,7 +3670,7 @@ DEFINE OFF_expr_frames 0049410000000000
call
# a0 = text_ptr (returned)
- # ---- *dst = { TOK_WORD, text_ptr, total_len } ----
+ # ---- *dst = { TOK_WORD, text_ptr, total_len, tight=0 } ----
la_t0 &paste_dst_save
ld_t0,t0,0
li_a2 TOK_WORD
@@ -3570,15 +3679,19 @@ DEFINE OFF_expr_frames 0049410000000000
la_a1 &paste_total_len
ld_a1,a1,0
st_a1,t0,16
+ li_a1 %0 %0
+ st_a1,t0,24
eret
## paste_pool_range(a0=mark) -> void (fatal on bad paste)
## In-place compactor over expand_pool[mark..pool_used). For each TOK_PASTE,
-## paste (prev, next) into prev via append_pasted_token and skip both the
-## PASTE and the next token. Copy other tokens forward. Update pool_used to
-## the new end. Fatal (err_bad_macro_header — closest "bad input" label) if
-## ## is first, last, or adjacent to NEWLINE/PASTE.
+## walk back from `out` over already-copied NEWLINE tokens to find the left
+## operand, walk forward from `in+1` over NEWLINE tokens to find the right
+## operand, then paste left+right into the left slot. The discarded newlines
+## on either side are dropped. Copy other tokens forward. Update pool_used
+## to the new end. Fatal if ## has no operand on a side, or its operand is
+## itself PASTE.
:paste_pool_range
enter_0
@@ -3619,7 +3732,7 @@ DEFINE OFF_expr_frames 0049410000000000
la_br &paste_pool_handle_paste
beq_a2,a3
- # ---- non-PASTE: copy *in to *out, advance both by 24 ----
+ # ---- non-PASTE: copy *in to *out, advance both by 32 ----
la_a0 &paste_out
ld_t2,a0,0
# if (in == out) skip the copy
@@ -3631,9 +3744,11 @@ DEFINE OFF_expr_frames 0049410000000000
st_a3,t2,8
ld_a3,t0,16
st_a3,t2,16
+ ld_a3,t0,24
+ st_a3,t2,24
:paste_pool_skip_copy
- addi_t0,t0,24
- addi_t2,t2,24
+ addi_t0,t0,32
+ addi_t2,t2,32
la_a0 &paste_in
st_t0,a0,0
la_a0 &paste_out
@@ -3643,64 +3758,71 @@ DEFINE OFF_expr_frames 0049410000000000
:paste_pool_handle_paste
# ---- TOK_PASTE handling ----
- # Validate:
- # out == start -> ## is first (fatal)
+ # Find left operand: start at out, walk back over NEWLINE tokens, then
+ # step one more to land on the actual left operand. If we cannot step
+ # back past start, the ## has no left operand -> fatal.
la_a0 &paste_out
- ld_t1,a0,0
+ ld_t1,a0,0 # t1 = left (initially = out)
la_a1 &paste_start
- ld_t2,a1,0
- la_br &err_bad_macro_header
+ ld_t2,a1,0 # t2 = start
+:paste_pool_left_skip_nl
+ la_br &paste_pool_left_step
beq_t1,t2
-
- # in+1 >= end -> ## is last (fatal)
- # Equivalent: in+24 >= end, i.e. !(in+24 < end).
- addi_t0,t0,24 # t0 = in + 24 (right operand ptr)
- la_a1 &paste_end
- ld_t2,a1,0
- # fatal if (in+1) >= end, i.e. if (in+24) >= end. blt branches when
- # left < right, so branch over fatal when (in+24) < end.
- la_br &paste_pool_paste_right_in_range
- blt_t0,t2
- la_br &err_bad_macro_header
- b
-:paste_pool_paste_right_in_range
- # t0 currently = in+24 (right operand)
- # Validate (out-1)->kind not in {NEWLINE, PASTE}.
- # out is in t1; out-1 = t1 - 24. (out-1)->kind = *(t1-24+0).
- # Use mem offset: ld with offset NEG24.
- ld_a2,t1,neg24
+ ld_a2,t1,neg32
li_a3 TOK_NEWLINE
+ la_br &paste_pool_left_step
+ bne_a2,a3
+ addi_t1,t1,neg32
+ la_br &paste_pool_left_skip_nl
+ b
+:paste_pool_left_step
+ # left == start? then ## is first (fatal).
la_br &err_bad_macro_header
- beq_a2,a3
+ beq_t1,t2
+ addi_t1,t1,neg32 # left now points at the operand
+ # Validate left->kind != TOK_PASTE.
+ ld_a2,t1,0
li_a3 TOK_PASTE
la_br &err_bad_macro_header
beq_a2,a3
- # Validate (in+1)->kind not in {NEWLINE, PASTE}.
- # t0 = in+24 (right operand), so kind = *(t0+0).
- ld_a2,t0,0
- li_a3 TOK_NEWLINE
+ # Find right operand: start at in+1, walk forward over NEWLINE tokens.
+ # If we run out of tokens, ## is last (fatal). If right is PASTE, fatal.
+ addi_t0,t0,32 # t0 = right (initially = in + 1)
+ la_a1 &paste_end
+ ld_a2,a1,0 # a2 = end
+:paste_pool_right_skip_nl
la_br &err_bad_macro_header
- beq_a2,a3
- li_a3 TOK_PASTE
+ beq_t0,a2
+ ld_a3,t0,0
+ li_a1 TOK_NEWLINE
+ la_br &paste_pool_right_step
+ bne_a3,a1
+ addi_t0,t0,32
+ la_br &paste_pool_right_skip_nl
+ b
+:paste_pool_right_step
+ # Validate right->kind != TOK_PASTE.
+ li_a1 TOK_PASTE
la_br &err_bad_macro_header
- beq_a2,a3
-
- # ---- append_pasted_token(out-1, out-1, in+1) ----
- # t1 = out, t0 = in+1 (right operand).
- addi_t1,t1,neg24 # t1 = out - 1 (left = dst)
+ beq_a3,a1
+
+ # Pre-publish out = left + 1 and in = right + 1, since the call below
+ # clobbers the t* / a* registers we used to track them.
+ addi_a3,t1,32
+ la_a1 &paste_out
+ st_a3,a1,0
+ addi_a3,t0,32
+ la_a1 &paste_in
+ st_a3,a1,0
+
+ # append_pasted_token(left, left, right)
mov_a0,t1
mov_a1,t1
mov_a2,t0
la_br &append_pasted_token
call
- # in += 48 (skip ## and the right operand). Out is unchanged.
- la_a0 &paste_in
- ld_t0,a0,0
- addi_t0,t0,48
- st_t0,a0,0
-
la_br &paste_pool_loop
b
@@ -4625,7 +4747,9 @@ DEFINE OFF_expr_frames 0049410000000000
## skip_expr_newlines(a0=pos, a1=end) -> a0 = new pos. Leaf.
## Advance pos past consecutive TOK_NEWLINE tokens so expressions may span
-## lines.
+## lines. Also used by directive header parsers to make whitespace
+## (newlines specifically) insignificant inside %macro/%struct/%scope
+## headers and around `##` paste operands.
:skip_expr_newlines
:sen_loop
# if (pos == end) done
@@ -4637,12 +4761,33 @@ DEFINE OFF_expr_frames 0049410000000000
la_br &sen_done
bne_t0,t1
# pos += 24
- addi_a0,a0,24
+ addi_a0,a0,32
la_br &sen_loop
b
:sen_done
ret
+## proc_skip_newlines(): advance proc_pos past TOK_NEWLINE tokens, bounded by
+## source_end. Convenience wrapper used by directive header parsers.
+:proc_skip_newlines
+:psn_loop
+ la_a0 &proc_pos
+ ld_t0,a0,0
+ la_a1 &source_end
+ ld_t1,a1,0
+ la_br &psn_done
+ beq_t0,t1
+ ld_a2,t0,0
+ li_a3 TOK_NEWLINE
+ la_br &psn_done
+ bne_a2,a3
+ addi_t0,t0,32
+ st_t0,a0,0
+ la_br &psn_loop
+ b
+:psn_done
+ ret
+
## eval_expr_atom(a0=tok, a1=limit) -> void
## Outputs via globals:
## eval_after_pos = token one past the consumed atom (or one past ')' for
@@ -4680,10 +4825,11 @@ DEFINE OFF_expr_frames 0049410000000000
beqz_a0
# Paren-less 0-arg atom:
- # Take the macro-call branch if (tok+1 < limit AND (tok+1)->kind == TOK_LPAREN)
- # OR macro->param_count == 0. Otherwise fall through to int atom (unchanged).
+ # Take the macro-call branch if (tok+1 < limit AND (tok+1)->kind == TOK_LPAREN
+ # AND (tok+1)->tight) OR macro->param_count == 0. Otherwise fall through
+ # to int atom (unchanged).
ld_t0,sp,0
- addi_t0,t0,24
+ addi_t0,t0,32
ld_t1,sp,8
la_br &eea_check_zero_arg
blt_t1,t0
@@ -4693,6 +4839,9 @@ DEFINE OFF_expr_frames 0049410000000000
li_a3 TOK_LPAREN
la_br &eea_check_zero_arg
bne_t2,a3
+ ld_t2,t0,24
+ la_br &eea_check_zero_arg
+ beqz_t2
la_br &eea_do_macro
b
@@ -4766,7 +4915,7 @@ DEFINE OFF_expr_frames 0049410000000000
# eval_after_pos = tok + 24
ld_t0,sp,0
- addi_t0,t0,24
+ addi_t0,t0,32
la_a0 &eval_after_pos
st_t0,a0,0
@@ -4904,7 +5053,7 @@ DEFINE OFF_expr_frames 0049410000000000
:eer_lparen
# pos++
- addi_t0,t0,24
+ addi_t0,t0,32
st_t0,sp,0
# skip_expr_newlines
ld_a0,sp,0
@@ -4956,7 +5105,7 @@ DEFINE OFF_expr_frames 0049410000000000
st_t0,a1,0
# pos++ (skip operator token)
ld_t0,sp,0
- addi_t0,t0,24
+ addi_t0,t0,32
st_t0,sp,0
la_br &eer_loop
b
@@ -4993,7 +5142,7 @@ DEFINE OFF_expr_frames 0049410000000000
addi_t0,t0,neg1
st_t0,a1,0
ld_t0,sp,0
- addi_t0,t0,24
+ addi_t0,t0,32
st_t0,sp,0
li_t0 %1 %0
st_t0,sp,32
@@ -5005,7 +5154,7 @@ DEFINE OFF_expr_frames 0049410000000000
# TOK_STRING atom, not a recursive expression.
# pos++ past the "strlen" operator word.
ld_t0,sp,0
- addi_t0,t0,24
+ addi_t0,t0,32
st_t0,sp,0
# skip_expr_newlines(pos, end)
ld_a0,sp,0
@@ -5038,7 +5187,7 @@ DEFINE OFF_expr_frames 0049410000000000
addi_a1,a1,neg2
st_a1,sp,16
# pos++
- addi_t0,t0,24
+ addi_t0,t0,32
st_t0,sp,0
# skip_expr_newlines(pos, end)
ld_a0,sp,0
@@ -5057,7 +5206,7 @@ DEFINE OFF_expr_frames 0049410000000000
la_br &err_bad_macro_header
bne_t2,a3
# pos++
- addi_t0,t0,24
+ addi_t0,t0,32
st_t0,sp,0
# have_value = 1
li_t0 %1 %0
@@ -5187,7 +5336,7 @@ DEFINE OFF_expr_frames 0049410000000000
call
# ehv_token.kind = TOK_STRING; ehv_token.text_ptr = text_ptr;
- # ehv_token.text_len = 2 + 2 * ehv_bytes
+ # ehv_token.text_len = 2 + 2 * ehv_bytes; ehv_token.tight = 0.
la_a2 &ehv_token
li_a3 TOK_STRING
st_a3,a2,0
@@ -5197,6 +5346,8 @@ DEFINE OFF_expr_frames 0049410000000000
shli_a1,a1,1
addi_a1,a1,2
st_a1,a2,16
+ li_a1 %0 %0
+ st_a1,a2,24
# emit_token(&ehv_token)
la_a0 &ehv_token
@@ -5237,7 +5388,7 @@ DEFINE OFF_expr_frames 0049410000000000
st_a0,a2,0
# lparen = builtin_tok + 24; if (lparen >= stream->end) fatal
- addi_t0,a1,24
+ addi_t0,a1,32
ld_t1,a0,8 # stream->end
la_br &err_bad_macro_header
beq_t0,t1
@@ -5558,13 +5709,13 @@ DEFINE OFF_expr_frames 0049410000000000
la_a1 &ebc_arg0_end
st_t0,a1,0
- # require arg0_end - arg0_start == 24 (exactly one token)
+ # require arg0_end - arg0_start == 32 (exactly one token)
la_a0 &ebc_arg0_start
ld_t0,a0,0
la_a1 &ebc_arg0_end
ld_t1,a1,0
sub_t2,t1,t0
- li_a2 %24 %0
+ li_a2 %32 %0
la_br &err_bad_macro_header
bne_t2,a2
@@ -5631,7 +5782,7 @@ DEFINE OFF_expr_frames 0049410000000000
la_br &append_text
call
- # ebc_str_token = { TOK_STRING, text_ptr, out_len }
+ # ebc_str_token = { TOK_STRING, text_ptr, out_len, tight=0 }
la_a2 &ebc_str_token
li_a3 TOK_STRING
st_a3,a2,0
@@ -5639,6 +5790,8 @@ DEFINE OFF_expr_frames 0049410000000000
la_a1 &ebc_str_out_len
ld_a1,a1,0
st_a1,a2,16
+ li_a1 %0 %0
+ st_a1,a2,24
# stream->pos = ebc_call_end_pos; stream->line_start = 0
la_a0 &ebc_stream
@@ -5970,6 +6123,8 @@ ZERO8
ZERO8
:lex_punct_kind
ZERO8
+:lex_saw_separator
+ZERO8
:proc_pos
ZERO8
:proc_line_start
@@ -5984,6 +6139,8 @@ ZERO8
ZERO8
:def_body_line_start
ZERO8
+:psc_stream_end
+ZERO8
:err_saved_msg
ZERO8
:err_saved_len
@@ -6210,8 +6367,8 @@ ZERO8
## Builtin scratch.
## emit_hex_value: ehv_value/bytes hold the args; ehv_scratch is a 24-byte
## buffer (max 18 chars used: 2 quotes + 16 hex chars; rounded up to keep
-## the next slot 8-byte aligned); ehv_token is a synthesized 24-byte
-## Token { kind, text_ptr, text_len }.
+## the next slot 8-byte aligned); ehv_token is a synthesized 32-byte
+## Token { kind, text_ptr, text_len, tight }.
:ehv_value
ZERO8
:ehv_bytes
@@ -6219,7 +6376,7 @@ ZERO8
:ehv_scratch
ZERO8 ZERO8 ZERO8
:ehv_token
-ZERO8 ZERO8 ZERO8
+ZERO8 ZERO8 ZERO8 ZERO8
## expand_builtin_call: snapshots the stream pointer, the post-call resume
## position, the byte count for !@%$, the eval_expr_range result, the chosen
@@ -6250,15 +6407,15 @@ ZERO8
## %str builtin scratch. ebc_str_orig_len / ebc_str_out_len spill the
## argument text length and its +2 output length across append_text;
-## ebc_str_token is the synthesized TOK_STRING { kind, text_ptr, text_len }
-## handed to emit_token; ebc_str_scratch is a 256-byte assembly buffer
-## (matches paste_scratch / M0's quoted-literal cap).
+## ebc_str_token is the synthesized TOK_STRING { kind, text_ptr, text_len,
+## tight } handed to emit_token; ebc_str_scratch is a 256-byte assembly
+## buffer (matches paste_scratch / M0's quoted-literal cap).
:ebc_str_orig_len
ZERO8
:ebc_str_out_len
ZERO8
:ebc_str_token
-ZERO8 ZERO8 ZERO8
+ZERO8 ZERO8 ZERO8 ZERO8
## arg_starts[16] / arg_ends[16]: 16 × 8 = 128 bytes each, i.e. 4 ZERO32.
## Written by parse_args; read by expand_macro_tokens and expand_builtin_call.
diff --git a/P1/P1-aarch64.M1 b/P1/P1-aarch64.M1
@@ -124,6 +124,7 @@ DEFINE sub_a3,t0,a2 230102CB
DEFINE sub_a3,t0,a3 230103CB
DEFINE sub_a3,t0,t1 23010ACB
DEFINE sub_a3,t1,a2 430102CB
+DEFINE sub_a3,t1,a3 430103CB
DEFINE sub_a3,t1,t0 430109CB
DEFINE sub_a3,t2,t1 63010ACB
DEFINE sub_t0,a1,a2 290002CB
@@ -156,9 +157,9 @@ DEFINE rem_a2,a2,a3 500CC39A028A039B
## ---- Immediate Arithmetic
DEFINE addi_a0,a0,neg1 000400D1
DEFINE addi_a0,a0,1 00040091
-DEFINE addi_a0,a0,24 00600091
+DEFINE addi_a0,a0,32 00800091
DEFINE addi_a1,a1,neg48 21C000D1
-DEFINE addi_a1,a1,neg24 216000D1
+DEFINE addi_a1,a1,neg32 218000D1
DEFINE addi_a1,a1,neg2 210800D1
DEFINE addi_a1,a1,1 21040091
DEFINE addi_a1,a1,2 21080091
@@ -175,27 +176,29 @@ DEFINE addi_a2,a2,48 42C00091
DEFINE addi_a2,t0,1 22050091
DEFINE addi_a2,t2,neg48 62C100D1
DEFINE addi_a3,a3,1 63040091
-DEFINE addi_a3,a3,24 63600091
-DEFINE addi_t0,a1,neg24 296000D1
-DEFINE addi_t0,a1,24 29600091
+DEFINE addi_a3,a3,32 63800091
+DEFINE addi_a3,t0,32 23810091
+DEFINE addi_a3,t1,32 43810091
+DEFINE addi_t0,a1,neg32 298000D1
+DEFINE addi_t0,a1,32 29800091
DEFINE addi_t0,t0,neg1 290500D1
DEFINE addi_t0,t0,1 29050091
DEFINE addi_t0,t0,2 29090091
-DEFINE addi_t0,t0,24 29610091
-DEFINE addi_t0,t0,48 29C10091
-DEFINE addi_t1,t0,24 2A610091
-DEFINE addi_t1,t1,neg24 4A6100D1
+DEFINE addi_t0,t0,32 29810091
+DEFINE addi_t1,t0,32 2A810091
+DEFINE addi_t1,t1,neg32 4A8100D1
DEFINE addi_t1,t1,neg1 4A0500D1
DEFINE addi_t1,t1,1 4A050091
DEFINE addi_t1,t1,16 4A410091
-DEFINE addi_t1,t1,24 4A610091
-DEFINE addi_t2,a1,neg24 2B6000D1
-DEFINE addi_t2,t0,neg24 2B6100D1
-DEFINE addi_t2,t0,24 2B610091
+DEFINE addi_t1,t1,32 4A810091
+DEFINE addi_t2,a1,neg32 2B8000D1
+DEFINE addi_t2,t0,neg32 2B8100D1
+DEFINE addi_t2,t0,32 2B810091
DEFINE addi_t2,t1,2 4B090091
DEFINE addi_t2,t2,neg1 6B0500D1
DEFINE addi_t2,t2,1 6B050091
DEFINE addi_t2,t2,24 6B610091
+DEFINE addi_t2,t2,32 6B810091
DEFINE andi_a2,a0,15 F00180D20200108A
DEFINE andi_a2,a1,15 F00180D22200108A
DEFINE andi_a2,a2,15 F00180D24200108A
@@ -223,32 +226,35 @@ DEFINE ld_a0,a2,0 400040F9
DEFINE ld_a0,a3,0 600040F9
DEFINE ld_a0,a3,8 600440F9
DEFINE ld_a0,a3,16 600840F9
+DEFINE ld_a0,a3,24 600C40F9
DEFINE ld_a0,t0,0 200140F9
DEFINE ld_a0,sp,0 E00B40F9
DEFINE ld_a0,sp,8 E00F40F9
DEFINE ld_a0,sp,24 E01740F9
DEFINE ld_a1,a0,0 010040F9
DEFINE ld_a1,a0,8 010440F9
-DEFINE ld_a1,a0,24 010C40F9
DEFINE ld_a1,a1,0 210040F9
DEFINE ld_a1,a2,8 410440F9
DEFINE ld_a1,a3,8 610440F9
DEFINE ld_a1,t0,0 210140F9
DEFINE ld_a1,t0,8 210540F9
DEFINE ld_a1,t0,16 210940F9
+DEFINE ld_a1,t0,24 210D40F9
DEFINE ld_a1,t1,0 410140F9
+DEFINE ld_a1,t1,24 410D40F9
DEFINE ld_a1,t2,16 610940F9
DEFINE ld_a1,sp,8 E10F40F9
DEFINE ld_a2,a0,0 020040F9
DEFINE ld_a2,a0,8 020440F9
DEFINE ld_a2,a0,16 020840F9
+DEFINE ld_a2,a0,24 020C40F9
DEFINE ld_a2,a1,0 220040F9
DEFINE ld_a2,a1,8 220440F9
DEFINE ld_a2,a2,0 420040F9
DEFINE ld_a2,t0,0 220140F9
DEFINE ld_a2,t0,8 220540F9
DEFINE ld_a2,t0,16 220940F9
-DEFINE ld_a2,t1,neg24 42815EF8
+DEFINE ld_a2,t1,neg32 42015EF8
DEFINE ld_a2,t1,0 420140F9
DEFINE ld_a2,t2,0 620140F9
DEFINE ld_a2,sp,16 E21340F9
@@ -262,6 +268,7 @@ DEFINE ld_a3,a3,0 630040F9
DEFINE ld_a3,t0,0 230140F9
DEFINE ld_a3,t0,8 230540F9
DEFINE ld_a3,t0,16 230940F9
+DEFINE ld_a3,t0,24 230D40F9
DEFINE ld_a3,t1,8 430540F9
DEFINE ld_t0,a0,0 090040F9
DEFINE ld_t0,a0,8 090440F9
@@ -272,10 +279,10 @@ DEFINE ld_t0,a1,16 290840F9
DEFINE ld_t0,a1,24 290C40F9
DEFINE ld_t0,a1,32 291040F9
DEFINE ld_t0,a2,0 490040F9
-DEFINE ld_t0,a3,0 690040F9
DEFINE ld_t0,t0,0 290140F9
DEFINE ld_t0,t1,8 490540F9
DEFINE ld_t0,t1,16 490940F9
+DEFINE ld_t0,t2,0 690140F9
DEFINE ld_t0,t2,16 690940F9
DEFINE ld_t0,sp,0 E90B40F9
DEFINE ld_t0,sp,8 E90F40F9
@@ -286,6 +293,7 @@ DEFINE ld_t0,sp,40 E91F40F9
DEFINE ld_t1,a0,0 0A0040F9
DEFINE ld_t1,a0,8 0A0440F9
DEFINE ld_t1,a0,16 0A0840F9
+DEFINE ld_t1,a0,24 0A0C40F9
DEFINE ld_t1,a1,0 2A0040F9
DEFINE ld_t1,a1,8 2A0440F9
DEFINE ld_t1,a1,16 2A0840F9
@@ -304,6 +312,7 @@ DEFINE ld_t2,a0,0 0B0040F9
DEFINE ld_t2,a1,0 2B0040F9
DEFINE ld_t2,a3,16 6B0840F9
DEFINE ld_t2,t0,0 2B0140F9
+DEFINE ld_t2,t0,24 2B0D40F9
DEFINE ld_t2,t1,0 4B0140F9
DEFINE ld_t2,t2,0 6B0140F9
DEFINE ld_t2,sp,16 EB1340F9
@@ -311,6 +320,7 @@ DEFINE st_a0,a1,0 200000F9
DEFINE st_a0,a2,0 400000F9
DEFINE st_a0,a2,8 400400F9
DEFINE st_a0,a2,16 400800F9
+DEFINE st_a0,a2,24 400C00F9
DEFINE st_a0,a3,0 600000F9
DEFINE st_a0,a3,8 600400F9
DEFINE st_a0,a3,16 600800F9
@@ -324,14 +334,17 @@ DEFINE st_a0,sp,16 E01300F9
DEFINE st_a1,a0,0 010000F9
DEFINE st_a1,a2,0 410000F9
DEFINE st_a1,a2,16 410800F9
+DEFINE st_a1,a2,24 410C00F9
DEFINE st_a1,a3,0 610000F9
DEFINE st_a1,a3,8 610400F9
DEFINE st_a1,t0,0 210100F9
DEFINE st_a1,t0,8 210500F9
DEFINE st_a1,t0,16 210900F9
+DEFINE st_a1,t0,24 210D00F9
DEFINE st_a1,t1,0 410100F9
DEFINE st_a1,t1,8 410500F9
DEFINE st_a1,t1,16 410900F9
+DEFINE st_a1,t1,24 410D00F9
DEFINE st_a1,t2,16 610900F9
DEFINE st_a1,sp,8 E10F00F9
DEFINE st_a1,sp,16 E11300F9
@@ -339,18 +352,22 @@ DEFINE st_a2,a0,0 020000F9
DEFINE st_a2,a1,0 220000F9
DEFINE st_a2,a3,0 620000F9
DEFINE st_a2,a3,16 620800F9
+DEFINE st_a2,a3,24 620C00F9
DEFINE st_a2,a3,32 621000F9
DEFINE st_a2,t0,0 220100F9
DEFINE st_a2,t0,16 220900F9
DEFINE st_a2,t1,0 420100F9
DEFINE st_a2,t2,0 620100F9
DEFINE st_a3,a0,0 030000F9
+DEFINE st_a3,a1,0 230000F9
DEFINE st_a3,a2,0 430000F9
DEFINE st_a3,t0,0 230100F9
+DEFINE st_a3,t0,24 230D00F9
DEFINE st_a3,t1,8 430500F9
DEFINE st_a3,t2,0 630100F9
DEFINE st_a3,t2,8 630500F9
DEFINE st_a3,t2,16 630900F9
+DEFINE st_a3,t2,24 630D00F9
DEFINE st_t0,a0,0 090000F9
DEFINE st_t0,a0,16 090800F9
DEFINE st_t0,a0,24 090C00F9
@@ -359,6 +376,7 @@ DEFINE st_t0,a2,0 490000F9
DEFINE st_t0,a3,0 690000F9
DEFINE st_t0,a3,8 690400F9
DEFINE st_t0,t1,0 490100F9
+DEFINE st_t0,t2,0 690100F9
DEFINE st_t0,sp,0 E90B00F9
DEFINE st_t0,sp,8 E90F00F9
DEFINE st_t0,sp,16 E91300F9
@@ -428,6 +446,7 @@ DEFINE beq_a0,t1 1F000AEB4100005420021FD6
DEFINE beq_a1,a2 3F0002EB4100005420021FD6
DEFINE beq_a2,a1 5F0001EB4100005420021FD6
DEFINE beq_a2,a3 5F0003EB4100005420021FD6
+DEFINE beq_a3,a1 7F0001EB4100005420021FD6
DEFINE beq_a3,a2 7F0002EB4100005420021FD6
DEFINE beq_a3,t0 7F0009EB4100005420021FD6
DEFINE beq_a3,t1 7F000AEB4100005420021FD6
@@ -444,13 +463,13 @@ DEFINE beq_t1,t2 5F010BEB4100005420021FD6
DEFINE beq_t2,a0 7F0100EB4100005420021FD6
DEFINE beq_t2,a2 7F0102EB4100005420021FD6
DEFINE beq_t2,a3 7F0103EB4100005420021FD6
-DEFINE beq_t2,t0 7F0109EB4100005420021FD6
DEFINE beq_t2,t1 7F010AEB4100005420021FD6
DEFINE bne_a0,t0 1F0009EB4000005420021FD6
DEFINE bne_a1,a2 3F0002EB4000005420021FD6
DEFINE bne_a1,t0 3F0009EB4000005420021FD6
DEFINE bne_a2,a3 5F0003EB4000005420021FD6
DEFINE bne_a3,a0 7F0000EB4000005420021FD6
+DEFINE bne_a3,a1 7F0001EB4000005420021FD6
DEFINE bne_a3,a2 7F0002EB4000005420021FD6
DEFINE bne_a3,t0 7F0009EB4000005420021FD6
DEFINE bne_t0,t1 3F010AEB4000005420021FD6
@@ -473,7 +492,6 @@ DEFINE blt_a2,t2 5F000BEB4A00005420021FD6
DEFINE blt_a3,a2 7F0002EB4A00005420021FD6
DEFINE blt_a3,t2 7F000BEB4A00005420021FD6
DEFINE blt_t0,t1 3F010AEB4A00005420021FD6
-DEFINE blt_t0,t2 3F010BEB4A00005420021FD6
DEFINE blt_t1,a1 5F0101EB4A00005420021FD6
DEFINE blt_t1,t0 5F0109EB4A00005420021FD6
DEFINE blt_t2,a3 7F0103EB4A00005420021FD6
diff --git a/P1/P1-amd64.M1 b/P1/P1-amd64.M1
@@ -124,6 +124,7 @@ DEFINE sub_a3,t0,a2 4C89D14829D1
DEFINE sub_a3,t0,a3 4989C94C89D14C29C9
DEFINE sub_a3,t0,t1 4C89D14C29D9
DEFINE sub_a3,t1,a2 4C89D94829D1
+DEFINE sub_a3,t1,a3 4989C94C89D94C29C9
DEFINE sub_a3,t1,t0 4C89D94C29D1
DEFINE sub_a3,t2,t1 4C89C14C29D9
DEFINE sub_t0,a1,a2 4989F24929D2
@@ -156,9 +157,9 @@ DEFINE rem_a2,a2,a3 4889D54989C94889D0489949F7F94889D04889EA4889C2
## ---- Immediate Arithmetic
DEFINE addi_a0,a0,neg1 4889FF4883C7FF
DEFINE addi_a0,a0,1 4889FF4883C701
-DEFINE addi_a0,a0,24 4889FF4883C718
+DEFINE addi_a0,a0,32 4889FF4883C720
DEFINE addi_a1,a1,neg48 4889F64883C6D0
-DEFINE addi_a1,a1,neg24 4889F64883C6E8
+DEFINE addi_a1,a1,neg32 4889F64883C6E0
DEFINE addi_a1,a1,neg2 4889F64883C6FE
DEFINE addi_a1,a1,1 4889F64883C601
DEFINE addi_a1,a1,2 4889F64883C602
@@ -175,27 +176,29 @@ DEFINE addi_a2,a2,48 4889D24883C230
DEFINE addi_a2,t0,1 4C89D24883C201
DEFINE addi_a2,t2,neg48 4C89C24883C2D0
DEFINE addi_a3,a3,1 4889C94883C101
-DEFINE addi_a3,a3,24 4889C94883C118
-DEFINE addi_t0,a1,neg24 4989F24983C2E8
-DEFINE addi_t0,a1,24 4989F24983C218
+DEFINE addi_a3,a3,32 4889C94883C120
+DEFINE addi_a3,t0,32 4C89D14883C120
+DEFINE addi_a3,t1,32 4C89D94883C120
+DEFINE addi_t0,a1,neg32 4989F24983C2E0
+DEFINE addi_t0,a1,32 4989F24983C220
DEFINE addi_t0,t0,neg1 4D89D24983C2FF
DEFINE addi_t0,t0,1 4D89D24983C201
DEFINE addi_t0,t0,2 4D89D24983C202
-DEFINE addi_t0,t0,24 4D89D24983C218
-DEFINE addi_t0,t0,48 4D89D24983C230
-DEFINE addi_t1,t0,24 4D89D34983C318
-DEFINE addi_t1,t1,neg24 4D89DB4983C3E8
+DEFINE addi_t0,t0,32 4D89D24983C220
+DEFINE addi_t1,t0,32 4D89D34983C320
+DEFINE addi_t1,t1,neg32 4D89DB4983C3E0
DEFINE addi_t1,t1,neg1 4D89DB4983C3FF
DEFINE addi_t1,t1,1 4D89DB4983C301
DEFINE addi_t1,t1,16 4D89DB4983C310
-DEFINE addi_t1,t1,24 4D89DB4983C318
-DEFINE addi_t2,a1,neg24 4989F04983C0E8
-DEFINE addi_t2,t0,neg24 4D89D04983C0E8
-DEFINE addi_t2,t0,24 4D89D04983C018
+DEFINE addi_t1,t1,32 4D89DB4983C320
+DEFINE addi_t2,a1,neg32 4989F04983C0E0
+DEFINE addi_t2,t0,neg32 4D89D04983C0E0
+DEFINE addi_t2,t0,32 4D89D04983C020
DEFINE addi_t2,t1,2 4D89D84983C002
DEFINE addi_t2,t2,neg1 4D89C04983C0FF
DEFINE addi_t2,t2,1 4D89C04983C001
DEFINE addi_t2,t2,24 4D89C04983C018
+DEFINE addi_t2,t2,32 4D89C04983C020
DEFINE andi_a2,a0,15 4889FA4883E20F
DEFINE andi_a2,a1,15 4889F24883E20F
DEFINE andi_a2,a2,15 4889D24883E20F
@@ -223,32 +226,35 @@ DEFINE ld_a0,a2,0 488B7A00
DEFINE ld_a0,a3,0 488B7900
DEFINE ld_a0,a3,8 488B7908
DEFINE ld_a0,a3,16 488B7910
+DEFINE ld_a0,a3,24 488B7918
DEFINE ld_a0,t0,0 498B7A00
DEFINE ld_a0,sp,0 488B7C2410
DEFINE ld_a0,sp,8 488B7C2418
DEFINE ld_a0,sp,24 488B7C2428
DEFINE ld_a1,a0,0 488B7700
DEFINE ld_a1,a0,8 488B7708
-DEFINE ld_a1,a0,24 488B7718
DEFINE ld_a1,a1,0 488B7600
DEFINE ld_a1,a2,8 488B7208
DEFINE ld_a1,a3,8 488B7108
DEFINE ld_a1,t0,0 498B7200
DEFINE ld_a1,t0,8 498B7208
DEFINE ld_a1,t0,16 498B7210
+DEFINE ld_a1,t0,24 498B7218
DEFINE ld_a1,t1,0 498B7300
+DEFINE ld_a1,t1,24 498B7318
DEFINE ld_a1,t2,16 498B7010
DEFINE ld_a1,sp,8 488B742418
DEFINE ld_a2,a0,0 488B5700
DEFINE ld_a2,a0,8 488B5708
DEFINE ld_a2,a0,16 488B5710
+DEFINE ld_a2,a0,24 488B5718
DEFINE ld_a2,a1,0 488B5600
DEFINE ld_a2,a1,8 488B5608
DEFINE ld_a2,a2,0 488B5200
DEFINE ld_a2,t0,0 498B5200
DEFINE ld_a2,t0,8 498B5208
DEFINE ld_a2,t0,16 498B5210
-DEFINE ld_a2,t1,neg24 498B53E8
+DEFINE ld_a2,t1,neg32 498B53E0
DEFINE ld_a2,t1,0 498B5300
DEFINE ld_a2,t2,0 498B5000
DEFINE ld_a2,sp,16 488B542420
@@ -262,6 +268,7 @@ DEFINE ld_a3,a3,0 488B4900
DEFINE ld_a3,t0,0 498B4A00
DEFINE ld_a3,t0,8 498B4A08
DEFINE ld_a3,t0,16 498B4A10
+DEFINE ld_a3,t0,24 498B4A18
DEFINE ld_a3,t1,8 498B4B08
DEFINE ld_t0,a0,0 4C8B5700
DEFINE ld_t0,a0,8 4C8B5708
@@ -272,10 +279,10 @@ DEFINE ld_t0,a1,16 4C8B5610
DEFINE ld_t0,a1,24 4C8B5618
DEFINE ld_t0,a1,32 4C8B5620
DEFINE ld_t0,a2,0 4C8B5200
-DEFINE ld_t0,a3,0 4C8B5100
DEFINE ld_t0,t0,0 4D8B5200
DEFINE ld_t0,t1,8 4D8B5308
DEFINE ld_t0,t1,16 4D8B5310
+DEFINE ld_t0,t2,0 4D8B5000
DEFINE ld_t0,t2,16 4D8B5010
DEFINE ld_t0,sp,0 4C8B542410
DEFINE ld_t0,sp,8 4C8B542418
@@ -286,6 +293,7 @@ DEFINE ld_t0,sp,40 4C8B542438
DEFINE ld_t1,a0,0 4C8B5F00
DEFINE ld_t1,a0,8 4C8B5F08
DEFINE ld_t1,a0,16 4C8B5F10
+DEFINE ld_t1,a0,24 4C8B5F18
DEFINE ld_t1,a1,0 4C8B5E00
DEFINE ld_t1,a1,8 4C8B5E08
DEFINE ld_t1,a1,16 4C8B5E10
@@ -304,6 +312,7 @@ DEFINE ld_t2,a0,0 4C8B4700
DEFINE ld_t2,a1,0 4C8B4600
DEFINE ld_t2,a3,16 4C8B4110
DEFINE ld_t2,t0,0 4D8B4200
+DEFINE ld_t2,t0,24 4D8B4218
DEFINE ld_t2,t1,0 4D8B4300
DEFINE ld_t2,t2,0 4D8B4000
DEFINE ld_t2,sp,16 4C8B442420
@@ -311,6 +320,7 @@ DEFINE st_a0,a1,0 48897E00
DEFINE st_a0,a2,0 48897A00
DEFINE st_a0,a2,8 48897A08
DEFINE st_a0,a2,16 48897A10
+DEFINE st_a0,a2,24 48897A18
DEFINE st_a0,a3,0 48897900
DEFINE st_a0,a3,8 48897908
DEFINE st_a0,a3,16 48897910
@@ -324,14 +334,17 @@ DEFINE st_a0,sp,16 48897C2420
DEFINE st_a1,a0,0 48897700
DEFINE st_a1,a2,0 48897200
DEFINE st_a1,a2,16 48897210
+DEFINE st_a1,a2,24 48897218
DEFINE st_a1,a3,0 48897100
DEFINE st_a1,a3,8 48897108
DEFINE st_a1,t0,0 49897200
DEFINE st_a1,t0,8 49897208
DEFINE st_a1,t0,16 49897210
+DEFINE st_a1,t0,24 49897218
DEFINE st_a1,t1,0 49897300
DEFINE st_a1,t1,8 49897308
DEFINE st_a1,t1,16 49897310
+DEFINE st_a1,t1,24 49897318
DEFINE st_a1,t2,16 49897010
DEFINE st_a1,sp,8 4889742418
DEFINE st_a1,sp,16 4889742420
@@ -339,18 +352,22 @@ DEFINE st_a2,a0,0 48895700
DEFINE st_a2,a1,0 48895600
DEFINE st_a2,a3,0 48895100
DEFINE st_a2,a3,16 48895110
+DEFINE st_a2,a3,24 48895118
DEFINE st_a2,a3,32 48895120
DEFINE st_a2,t0,0 49895200
DEFINE st_a2,t0,16 49895210
DEFINE st_a2,t1,0 49895300
DEFINE st_a2,t2,0 49895000
DEFINE st_a3,a0,0 48894F00
+DEFINE st_a3,a1,0 48894E00
DEFINE st_a3,a2,0 48894A00
DEFINE st_a3,t0,0 49894A00
+DEFINE st_a3,t0,24 49894A18
DEFINE st_a3,t1,8 49894B08
DEFINE st_a3,t2,0 49894800
DEFINE st_a3,t2,8 49894808
DEFINE st_a3,t2,16 49894810
+DEFINE st_a3,t2,24 49894818
DEFINE st_t0,a0,0 4C895700
DEFINE st_t0,a0,16 4C895710
DEFINE st_t0,a0,24 4C895718
@@ -359,6 +376,7 @@ DEFINE st_t0,a2,0 4C895200
DEFINE st_t0,a3,0 4C895100
DEFINE st_t0,a3,8 4C895108
DEFINE st_t0,t1,0 4D895300
+DEFINE st_t0,t2,0 4D895000
DEFINE st_t0,sp,0 4C89542410
DEFINE st_t0,sp,8 4C89542418
DEFINE st_t0,sp,16 4C89542420
@@ -428,6 +446,7 @@ DEFINE beq_a0,t1 4C39DF750341FFE7
DEFINE beq_a1,a2 4839D6750341FFE7
DEFINE beq_a2,a1 4839F2750341FFE7
DEFINE beq_a2,a3 4839CA750341FFE7
+DEFINE beq_a3,a1 4839F1750341FFE7
DEFINE beq_a3,a2 4839D1750341FFE7
DEFINE beq_a3,t0 4C39D1750341FFE7
DEFINE beq_a3,t1 4C39D9750341FFE7
@@ -444,13 +463,13 @@ DEFINE beq_t1,t2 4D39C3750341FFE7
DEFINE beq_t2,a0 4939F8750341FFE7
DEFINE beq_t2,a2 4939D0750341FFE7
DEFINE beq_t2,a3 4939C8750341FFE7
-DEFINE beq_t2,t0 4D39D0750341FFE7
DEFINE beq_t2,t1 4D39D8750341FFE7
DEFINE bne_a0,t0 4C39D7740341FFE7
DEFINE bne_a1,a2 4839D6740341FFE7
DEFINE bne_a1,t0 4C39D6740341FFE7
DEFINE bne_a2,a3 4839CA740341FFE7
DEFINE bne_a3,a0 4839F9740341FFE7
+DEFINE bne_a3,a1 4839F1740341FFE7
DEFINE bne_a3,a2 4839D1740341FFE7
DEFINE bne_a3,t0 4C39D1740341FFE7
DEFINE bne_t0,t1 4D39DA740341FFE7
@@ -473,7 +492,6 @@ DEFINE blt_a2,t2 4C39C27D0341FFE7
DEFINE blt_a3,a2 4839D17D0341FFE7
DEFINE blt_a3,t2 4C39C17D0341FFE7
DEFINE blt_t0,t1 4D39DA7D0341FFE7
-DEFINE blt_t0,t2 4D39C27D0341FFE7
DEFINE blt_t1,a1 4939F37D0341FFE7
DEFINE blt_t1,t0 4D39D37D0341FFE7
DEFINE blt_t2,a3 4939C87D0341FFE7
diff --git a/P1/P1-riscv64.M1 b/P1/P1-riscv64.M1
@@ -124,6 +124,7 @@ DEFINE sub_a3,t0,a2 B386C240
DEFINE sub_a3,t0,a3 B386D240
DEFINE sub_a3,t0,t1 B3866240
DEFINE sub_a3,t1,a2 B306C340
+DEFINE sub_a3,t1,a3 B306D340
DEFINE sub_a3,t1,t0 B3065340
DEFINE sub_a3,t2,t1 B3866340
DEFINE sub_t0,a1,a2 B382C540
@@ -156,9 +157,9 @@ DEFINE rem_a2,a2,a3 3366D602
## ---- Immediate Arithmetic
DEFINE addi_a0,a0,neg1 1305F5FF
DEFINE addi_a0,a0,1 13051500
-DEFINE addi_a0,a0,24 13058501
+DEFINE addi_a0,a0,32 13050502
DEFINE addi_a1,a1,neg48 938505FD
-DEFINE addi_a1,a1,neg24 938585FE
+DEFINE addi_a1,a1,neg32 938505FE
DEFINE addi_a1,a1,neg2 9385E5FF
DEFINE addi_a1,a1,1 93851500
DEFINE addi_a1,a1,2 93852500
@@ -175,27 +176,29 @@ DEFINE addi_a2,a2,48 13060603
DEFINE addi_a2,t0,1 13861200
DEFINE addi_a2,t2,neg48 138603FD
DEFINE addi_a3,a3,1 93861600
-DEFINE addi_a3,a3,24 93868601
-DEFINE addi_t0,a1,neg24 938285FE
-DEFINE addi_t0,a1,24 93828501
+DEFINE addi_a3,a3,32 93860602
+DEFINE addi_a3,t0,32 93860202
+DEFINE addi_a3,t1,32 93060302
+DEFINE addi_t0,a1,neg32 938205FE
+DEFINE addi_t0,a1,32 93820502
DEFINE addi_t0,t0,neg1 9382F2FF
DEFINE addi_t0,t0,1 93821200
DEFINE addi_t0,t0,2 93822200
-DEFINE addi_t0,t0,24 93828201
-DEFINE addi_t0,t0,48 93820203
-DEFINE addi_t1,t0,24 13838201
-DEFINE addi_t1,t1,neg24 130383FE
+DEFINE addi_t0,t0,32 93820202
+DEFINE addi_t1,t0,32 13830202
+DEFINE addi_t1,t1,neg32 130303FE
DEFINE addi_t1,t1,neg1 1303F3FF
DEFINE addi_t1,t1,1 13031300
DEFINE addi_t1,t1,16 13030301
-DEFINE addi_t1,t1,24 13038301
-DEFINE addi_t2,a1,neg24 938385FE
-DEFINE addi_t2,t0,neg24 938382FE
-DEFINE addi_t2,t0,24 93838201
+DEFINE addi_t1,t1,32 13030302
+DEFINE addi_t2,a1,neg32 938305FE
+DEFINE addi_t2,t0,neg32 938302FE
+DEFINE addi_t2,t0,32 93830202
DEFINE addi_t2,t1,2 93032300
DEFINE addi_t2,t2,neg1 9383F3FF
DEFINE addi_t2,t2,1 93831300
DEFINE addi_t2,t2,24 93838301
+DEFINE addi_t2,t2,32 93830302
DEFINE andi_a2,a0,15 1376F500
DEFINE andi_a2,a1,15 13F6F500
DEFINE andi_a2,a2,15 1376F600
@@ -223,32 +226,35 @@ DEFINE ld_a0,a2,0 03350600
DEFINE ld_a0,a3,0 03B50600
DEFINE ld_a0,a3,8 03B58600
DEFINE ld_a0,a3,16 03B50601
+DEFINE ld_a0,a3,24 03B58601
DEFINE ld_a0,t0,0 03B50200
DEFINE ld_a0,sp,0 03350101
DEFINE ld_a0,sp,8 03358101
DEFINE ld_a0,sp,24 03358102
DEFINE ld_a1,a0,0 83350500
DEFINE ld_a1,a0,8 83358500
-DEFINE ld_a1,a0,24 83358501
DEFINE ld_a1,a1,0 83B50500
DEFINE ld_a1,a2,8 83358600
DEFINE ld_a1,a3,8 83B58600
DEFINE ld_a1,t0,0 83B50200
DEFINE ld_a1,t0,8 83B58200
DEFINE ld_a1,t0,16 83B50201
+DEFINE ld_a1,t0,24 83B58201
DEFINE ld_a1,t1,0 83350300
+DEFINE ld_a1,t1,24 83358301
DEFINE ld_a1,t2,16 83B50301
DEFINE ld_a1,sp,8 83358101
DEFINE ld_a2,a0,0 03360500
DEFINE ld_a2,a0,8 03368500
DEFINE ld_a2,a0,16 03360501
+DEFINE ld_a2,a0,24 03368501
DEFINE ld_a2,a1,0 03B60500
DEFINE ld_a2,a1,8 03B68500
DEFINE ld_a2,a2,0 03360600
DEFINE ld_a2,t0,0 03B60200
DEFINE ld_a2,t0,8 03B68200
DEFINE ld_a2,t0,16 03B60201
-DEFINE ld_a2,t1,neg24 033683FE
+DEFINE ld_a2,t1,neg32 033603FE
DEFINE ld_a2,t1,0 03360300
DEFINE ld_a2,t2,0 03B60300
DEFINE ld_a2,sp,16 03360102
@@ -262,6 +268,7 @@ DEFINE ld_a3,a3,0 83B60600
DEFINE ld_a3,t0,0 83B60200
DEFINE ld_a3,t0,8 83B68200
DEFINE ld_a3,t0,16 83B60201
+DEFINE ld_a3,t0,24 83B68201
DEFINE ld_a3,t1,8 83368300
DEFINE ld_t0,a0,0 83320500
DEFINE ld_t0,a0,8 83328500
@@ -272,10 +279,10 @@ DEFINE ld_t0,a1,16 83B20501
DEFINE ld_t0,a1,24 83B28501
DEFINE ld_t0,a1,32 83B20502
DEFINE ld_t0,a2,0 83320600
-DEFINE ld_t0,a3,0 83B20600
DEFINE ld_t0,t0,0 83B20200
DEFINE ld_t0,t1,8 83328300
DEFINE ld_t0,t1,16 83320301
+DEFINE ld_t0,t2,0 83B20300
DEFINE ld_t0,t2,16 83B20301
DEFINE ld_t0,sp,0 83320101
DEFINE ld_t0,sp,8 83328101
@@ -286,6 +293,7 @@ DEFINE ld_t0,sp,40 83328103
DEFINE ld_t1,a0,0 03330500
DEFINE ld_t1,a0,8 03338500
DEFINE ld_t1,a0,16 03330501
+DEFINE ld_t1,a0,24 03338501
DEFINE ld_t1,a1,0 03B30500
DEFINE ld_t1,a1,8 03B38500
DEFINE ld_t1,a1,16 03B30501
@@ -304,6 +312,7 @@ DEFINE ld_t2,a0,0 83330500
DEFINE ld_t2,a1,0 83B30500
DEFINE ld_t2,a3,16 83B30601
DEFINE ld_t2,t0,0 83B30200
+DEFINE ld_t2,t0,24 83B38201
DEFINE ld_t2,t1,0 83330300
DEFINE ld_t2,t2,0 83B30300
DEFINE ld_t2,sp,16 83330102
@@ -311,6 +320,7 @@ DEFINE st_a0,a1,0 23B0A500
DEFINE st_a0,a2,0 2330A600
DEFINE st_a0,a2,8 2334A600
DEFINE st_a0,a2,16 2338A600
+DEFINE st_a0,a2,24 233CA600
DEFINE st_a0,a3,0 23B0A600
DEFINE st_a0,a3,8 23B4A600
DEFINE st_a0,a3,16 23B8A600
@@ -324,14 +334,17 @@ DEFINE st_a0,sp,16 2330A102
DEFINE st_a1,a0,0 2330B500
DEFINE st_a1,a2,0 2330B600
DEFINE st_a1,a2,16 2338B600
+DEFINE st_a1,a2,24 233CB600
DEFINE st_a1,a3,0 23B0B600
DEFINE st_a1,a3,8 23B4B600
DEFINE st_a1,t0,0 23B0B200
DEFINE st_a1,t0,8 23B4B200
DEFINE st_a1,t0,16 23B8B200
+DEFINE st_a1,t0,24 23BCB200
DEFINE st_a1,t1,0 2330B300
DEFINE st_a1,t1,8 2334B300
DEFINE st_a1,t1,16 2338B300
+DEFINE st_a1,t1,24 233CB300
DEFINE st_a1,t2,16 23B8B300
DEFINE st_a1,sp,8 233CB100
DEFINE st_a1,sp,16 2330B102
@@ -339,18 +352,22 @@ DEFINE st_a2,a0,0 2330C500
DEFINE st_a2,a1,0 23B0C500
DEFINE st_a2,a3,0 23B0C600
DEFINE st_a2,a3,16 23B8C600
+DEFINE st_a2,a3,24 23BCC600
DEFINE st_a2,a3,32 23B0C602
DEFINE st_a2,t0,0 23B0C200
DEFINE st_a2,t0,16 23B8C200
DEFINE st_a2,t1,0 2330C300
DEFINE st_a2,t2,0 23B0C300
DEFINE st_a3,a0,0 2330D500
+DEFINE st_a3,a1,0 23B0D500
DEFINE st_a3,a2,0 2330D600
DEFINE st_a3,t0,0 23B0D200
+DEFINE st_a3,t0,24 23BCD200
DEFINE st_a3,t1,8 2334D300
DEFINE st_a3,t2,0 23B0D300
DEFINE st_a3,t2,8 23B4D300
DEFINE st_a3,t2,16 23B8D300
+DEFINE st_a3,t2,24 23BCD300
DEFINE st_t0,a0,0 23305500
DEFINE st_t0,a0,16 23385500
DEFINE st_t0,a0,24 233C5500
@@ -359,6 +376,7 @@ DEFINE st_t0,a2,0 23305600
DEFINE st_t0,a3,0 23B05600
DEFINE st_t0,a3,8 23B45600
DEFINE st_t0,t1,0 23305300
+DEFINE st_t0,t2,0 23B05300
DEFINE st_t0,sp,0 23385100
DEFINE st_t0,sp,8 233C5100
DEFINE st_t0,sp,16 23305102
@@ -428,6 +446,7 @@ DEFINE beq_a0,t1 6314650067800F00
DEFINE beq_a1,a2 6394C50067800F00
DEFINE beq_a2,a1 6314B60067800F00
DEFINE beq_a2,a3 6314D60067800F00
+DEFINE beq_a3,a1 6394B60067800F00
DEFINE beq_a3,a2 6394C60067800F00
DEFINE beq_a3,t0 6394560067800F00
DEFINE beq_a3,t1 6394660067800F00
@@ -444,13 +463,13 @@ DEFINE beq_t1,t2 6314730067800F00
DEFINE beq_t2,a0 6394A30067800F00
DEFINE beq_t2,a2 6394C30067800F00
DEFINE beq_t2,a3 6394D30067800F00
-DEFINE beq_t2,t0 6394530067800F00
DEFINE beq_t2,t1 6394630067800F00
DEFINE bne_a0,t0 6304550067800F00
DEFINE bne_a1,a2 6384C50067800F00
DEFINE bne_a1,t0 6384550067800F00
DEFINE bne_a2,a3 6304D60067800F00
DEFINE bne_a3,a0 6384A60067800F00
+DEFINE bne_a3,a1 6384B60067800F00
DEFINE bne_a3,a2 6384C60067800F00
DEFINE bne_a3,t0 6384560067800F00
DEFINE bne_t0,t1 6384620067800F00
@@ -473,7 +492,6 @@ DEFINE blt_a2,t2 6354760067800F00
DEFINE blt_a3,a2 63D4C60067800F00
DEFINE blt_a3,t2 63D4760067800F00
DEFINE blt_t0,t1 63D4620067800F00
-DEFINE blt_t0,t2 63D4720067800F00
DEFINE blt_t1,a1 6354B30067800F00
DEFINE blt_t1,t0 6354530067800F00
DEFINE blt_t2,a3 63D4D30067800F00
diff --git a/docs/scheme-shell-todo.md b/docs/scheme-shell-todo.md
@@ -8,45 +8,9 @@ run the suite to confirm it fails for the expected reason, then
implement until green. Multi-arch suite (`make test SUITE=scheme1`)
must stay clean before moving on.
-## Checklist (original plan)
-
-- [x] **1. Reader: `;` comments, `'datum`, `'()`, `#xNN` hex literals.**
- Hook `;` into `skip_ws` (skip until LF/EOF). Add `'` to `parse_one` as a prefix that reads the next datum and wraps in `(quote …)`. Intern `quote` at startup. Extend the `#`-cascade with `x`/`X` → hex parse.
-
-- [x] **2. Top-level `define`.**
- `eval_define` → `sym_set_global`. Recognize `(define (f a b . rest) body…)` sugar by rewriting to `(define f (lambda (a b . rest) body…))`. Returns UNSPEC.
-
-- [x] **3. Variadic `.`-tail.**
- Reader: `parse_list` recognizes `.` followed by ws inside a list as the dotted-pair separator; `parse_atom` rejects a bare `.`. `bind_params`: when `params` is a non-pair, bind `params` to the remaining args list and stop.
-
-- [x] **4. `cond` and the `let` family.**
- `cond` driver walks clauses; intern `else` at startup and pointer-compare it. `let` / `let*` / `letrec` / named `let`. `letrec` pre-binds names to UNSPEC, evaluates inits in the new env, then writes each value via `%st(val, pair, 7)` on the binding cell — no new primitive required.
-
-- [x] **5. Arith + list primitives.**
- Batch in: `cons`, `car`, `cdr`, `null?`, `pair?`, `zero?`, `not`, `eq?`, `+`, `-`, `*`, `=`, `<`, `bit-and`, `bit-or`, `arithmetic-shift`, `apply`. Each is a leaf with the `prim_sys_exit_entry` shape; `register_primitives` grows a table-style init.
-
-- [x] **6. Bytevectors.**
- Encode length in the upper 56 bits of the header (`hdr = (len << 8) | HDR.BV`). Allocator + `make-bytevector` (1- and 2-arg), `bytevector-length`, `bytevector-u8-ref`, `bytevector-u8-set!`, `bytevector-copy` (3-arg), `bytevector-copy!` (5-arg).
-
-- [x] **7. `define-record-type`.**
- Desugar at eval time by constructing the equivalent s-expression (a `begin` of `define`s for the td, ctor, predicate, and accessors/mutators) and recursing into `eval`. Cleaner than hand-rolling closures and reuses every existing path. Add the internal `%record-*` primitives. Depends on (1) for the quoted type-name symbol.
-
-- [x] **8. Syscall primitives.**
- Wrap libp1pp's `sys_read/write/close/openat/exit/clone/execve/waitid` macros as scheme primitives returning the `(#t . val)` / `(#f . errno)` convention. Promote argv to a BSS slot at startup so `sys-argv` can rebuild a list of bytevectors on demand. Add `EOF` as a sixth `IMM` and expose `eof-object` / `eof-object?`.
-
-- [x] **9. Prelude.**
- Embed `lisp/prelude.scm` as a bytevector literal in `scheme1.P1pp`. Parse-and-eval it before the user file at startup. Contents per LISP-C.md §Prelude: `list`, `length`, `reverse`, `append`, `list-ref`, `map`, `for-each`.
-
-- [x] **10. Port `shell.scm` into the prelude.**
- Concatenate `lisp/shell.scm` (or a shell-prelude variant) into the embedded source after the base prelude. Add an end-to-end test that uses `spawn` / `run` / a file-I/O round-trip.
-
----
-
# Audit: deviations and known issues
-The boxes above are checked because each task's tests pass on all three
-arches, but several items diverge from the original plan or from the
-LISP.md / LISP-C.md spec. Everything below is a real bug, hack, or spec
+Everything below is a real bug, hack, or spec
gap that must be addressed before calling scheme1 shippable.
## Open bugs
@@ -61,20 +25,24 @@ gap that must be addressed before calling scheme1 shippable.
redefining `spawn` at user level. Until this is understood, the
prelude's `spawn` and `run` are effectively unverified.
-- [ ] **No heap-exhaustion check.** `cons` and `alloc_hdr` bump
- `heap_next` past the 64 KiB heap arena without a runtime test. Past
- `&ELF_end + 0x20000` writes silently corrupt the symtab arena. The
- comment at `:cons` references "LISP-C.md milestone 3" but never adds
- the check.
-
-- [ ] **No symtab-name copy bound.** `intern` allocates a fresh copy of
- every name into the heap. A program that interns lots of unique
- symbols will exhaust the heap (see above).
-
-- [ ] **Bytevector-u8-set! / -ref / -copy / -copy! have no bounds check.**
- Out-of-range index silently corrupts adjacent heap memory. LISP.md
- says "primitive failure is undefined behavior", but no abort path is
- installed.
+- [x] **No heap-exhaustion check.** `cons`, `alloc_hdr`, and
+ `alloc_bytes` now compare `heap_next + bytes` against `:heap_end`
+ (initialized to `heap_buf_ptr + HEAP_CAP_BYTES` at startup) and abort
+ via `runtime_error` on overflow. `load_source` and `eval_prelude`
+ reject sources that would overrun `READBUF_CAP_BYTES`.
+
+- [x] **No symtab-name copy bound.** Name copies still go through
+ `alloc_bytes`, but that path now errors cleanly when the heap arena
+ is exhausted instead of silently scribbling into the symtab.
+ `intern`'s 1024-slot count check remains and routes through the same
+ `runtime_error`.
+
+- [x] **Bytevector-u8-set! / -ref / -copy / -copy! have no bounds
+ check.** All four now check `0 <= idx < length` (or
+ `0 <= start <= end <= length`, plus the dst-side range for
+ `bytevector-copy!`) and abort via `runtime_error`. `make-bytevector`
+ and `bytevector-grow!` reject negative arguments through the same
+ path.
- [ ] **`car` / `cdr` of non-pair, `quotient`/`remainder` of zero, etc.,
are silent UB** — same policy as above, no abort path.
@@ -110,22 +78,12 @@ Per LISP.md and LISP-C.md, but not implemented:
`record-type-of`, no way to inspect a TD from user code. Spec is
ambiguous on this; LISP-C.md's example uses a generated `<point-td>`
binding.
-- [ ] **`sys-execve` always passes `envp = NULL`.** shell.scm's contract
- ("parent env is inherited by the child") is not satisfied.
-- [ ] **`sys-clone` is hard-wired to `SIGCHLD` only.** No `CLONE_VM`,
- thread support, etc.
-- [ ] **No `sys-pipe`, `sys-dup2`, `sys-fcntl`, `sys-fork`** — needed
- for any real shell pipeline / redirection.
-- [ ] **`sys-wait` adapter only handles `CLD_EXITED` and signal kill;
- `CLD_STOPPED` and `CLD_CONTINUED` collapse into the kill branch.**
- [ ] **shell.scm's port record-type, `stdin`/`stdout`/`stderr` ports,
`open-input` / `open-output` / `read-line` / `read-bytes` /
`read-all` / `bv-concat-reverse` / `write-bytes` / `write-line` are
NOT in the prelude.** Only the process-management half of shell.scm
is ported.
-- [ ] **Prelude is a strict subset of `lisp/prelude.scm`.** The embedded
- copy has only `list / length / reverse / append-pair / append /
- list-ref / map / for-each` plus the shell helpers; it's missing
+- [ ] **Prelude is a strict subset of `lisp/prelude.scm`.** Missing:
`<= >= zero? positive? negative? abs caar cadr cdar cddr caddr
list? assoc member filter fold equal?` and the vector helpers
(`vector->list`, `list->vector`, `equal?-vector`, etc.). The
@@ -180,34 +138,11 @@ These work today but are easy to break.
args in `a0`. All existing primitives ignore `a1`, but any future
primitive that uses `a1` for anything else will silently break.
-- [ ] **Recursive helpers, host-stack-bound depth.**
- - `eval_args` recurses once per arg; very long arg lists (>~10k)
- blow the host stack.
- - `apply_build_args` recurses once per prepended arg.
- - `bind_params` is iterative (good).
-
-- [ ] **No tail-call optimization across the named-let body.**
- `eval_let_named` ends with `%tail(&apply)`, so the apply itself is a
- tail call. Inside the body the closure can recurse via the bound
- name only because every tail position in the interpreter uses
- `%tail`. Worth confirming with a deep-recursion test.
-
- [ ] **Symbol-table linear scan.** `intern` walks the table from idx 0
on every call. LISP-C.md describes a 16384-slot open-addressing
hash; we have a 1024-slot linear scan that exits with code 5 on
overflow.
-- [ ] **No GC.** Bump allocator only. The static arenas are 64 KiB
- each (heap, symtab, readbuf) — easy to exhaust, no diagnostic.
-
-- [ ] **`m1pp` text_buf was bumped from 256 KiB to 512 KiB and shifted
- into the previously-unused gap before `OFF_source_tokens`.** This is
- a toolchain change, not a scheme1 change. Anyone rebuilding `m1pp`
- from an older `M1pp/M1pp.P1` will overflow text_buf again.
- Downstream consumers of `m1pp` (M1pp / pokem / hello tests) all
- pass, but the change should be called out in
- `vendor/`/`docs/M1.md` as well.
-
## Test suite caveats
Issues *in* the test files themselves that need fixing or revisiting
@@ -293,9 +228,6 @@ before the suite can be considered authoritative.
`%tail`/`%tailr`, but nothing recurses thousands of times to confirm
no host-stack growth.
-- [ ] **No multi-arg `+` / `-` / `*` / `=` / `<` test** because they
- don't support more than 2 args.
-
- [ ] **No `(define x …)` followed by `(set! x …)` test** because
`set!` doesn't exist.
@@ -318,32 +250,20 @@ before the suite can be considered authoritative.
UNSPEC (or whatever the policy is — currently it does, but
unspecified by spec).
-## Toolchain side-effects
-
-- [ ] **`M1pp/M1pp.P1` was patched** to grow `M1PP_TEXT_CAP` from
- 256 KiB to 512 KiB and shift `OFF_text_buf` from 0x100680 to
- 0xc0680. Both tests/M1pp and tests/P1 still pass, but downstream
- documentation (`docs/M1.md` if any, vendor/seed README, etc.)
- doesn't mention this and a future pruner walking the m1pp build
- could undo the change.
-
## Suggested next steps before shipping
In rough priority order:
1. Track down and fix the prelude `spawn`-via-`run` bug; remove the
workaround in test 45.
-2. Add heap, readbuf, and symtab overflow checks; surface them through
- `error`.
3. Implement `error`, `display`, `write`, `format` so the abort paths
- produce something readable.
+ produce something readable. Plumb them through `runtime_error` so
+ the prefix stays consistent with the existing error messages.
4. Fill in the spec-required primitives (`equal?`, `eqv?`, `set-car!`,
`set-cdr!`, the comparison family, the bytevector family, the
number/string converters).
-5. Variadic arithmetic.
-6. `set!`, `and`, `or`, `pmatch`.
-7. Reader: `"…"` strings, `#\char`, source locations.
-8. Port shell.scm's port record + I/O wrappers.
-9. Add `sys-pipe` / `sys-dup2` / `sys-fork` for real shell pipelines.
-10. Replace the 1024-slot linear-scan symtab with an open-addressing
- hash per LISP-C.md.
+5. `set!`, `and`, `or`, `pmatch`.
+6. Reader: `"…"` strings, `#\char`, source locations.
+7. Port shell.scm's port record + I/O wrappers.
+8. Replace the 1024-slot linear-scan symtab with an open-addressing
+ hash per LISP-C.md.
diff --git a/scheme1/scheme1.P1pp b/scheme1/scheme1.P1pp
@@ -64,6 +64,10 @@
65536
%endm
+%macro HEAP_CAP_BYTES()
+0x10000
+%endm
+
# =========================================================================
# Tag idioms
# =========================================================================
@@ -132,12 +136,11 @@
%add(rd, rd, scratch)
%endm
-# Print msg_label, exit with `code`. Never returns.
-%macro die(msg, code)
+# Print msg_label and abort. Never returns. Routes through runtime_error
+# so every error path lands in one place (stderr + exit 1).
+%macro die(msg)
%la(a0, & ## msg)
-%call(&print_cstr)
-%li(a0, code)
-%call(&sys_exit)
+%call(&runtime_error)
%endm
# Intern a special-form name and stash the tagged-symbol value in a
@@ -170,6 +173,70 @@
%tail(handler)
%endm
+# Branch to `target` if `val` holds the NIL immediate. `scratch` is
+# clobbered.
+%macro if_nil(scratch, val, target)
+%li(scratch, %imm_val(%IMM.NIL))
+%beq(val, scratch, target)
+%endm
+
+# Advance a list cursor parked at sp[slot] to its cdr. t0 is the implicit
+# scratch register; callers must ensure it's free.
+%macro advance_walk(slot)
+%ld(t0, sp, slot)
+%cdr(t0, t0)
+%st(t0, sp, slot)
+%endm
+
+# Bind a global from registers a0 (value) and t0 (tagged sym). Untags t0,
+# rearranges into the (idx, val) ABI, and calls sym_set_global. Used at
+# the tail of every define-style binder.
+%macro bind_global_from_t0()
+%untag_sym(t0, t0)
+%mov(a1, a0)
+%mov(a0, t0)
+%call(&sym_set_global)
+%endm
+
+# car-and-untag-fixnum: rd = car(list) >> 3.
+%macro car_fix(rd, list)
+%car(rd, list)
+%sari(rd, rd, 3)
+%endm
+
+# car-then-load-bytevector-data-pointer: rd = (car(list)).data_ptr.
+%macro car_bvdata(rd, list)
+%car(rd, list)
+%ld(rd, rd, 5)
+%endm
+
+# Positional list-arg extraction. r_n receives the nth element of `list`;
+# the last destination register doubles as the in-flight rest cursor
+# during extraction (its final value is the last argument).
+%macro args2(r0, r1, list)
+%car(r0, list)
+%cdr(r1, list)
+%car(r1, r1)
+%endm
+
+%macro args3(r0, r1, r2, list)
+%car(r0, list)
+%cdr(r2, list)
+%car(r1, r2)
+%cdr(r2, r2)
+%car(r2, r2)
+%endm
+
+%macro args4(r0, r1, r2, r3, list)
+%car(r0, list)
+%cdr(r3, list)
+%car(r1, r3)
+%cdr(r3, r3)
+%car(r2, r3)
+%cdr(r3, r3)
+%car(r3, r3)
+%endm
+
# =========================================================================
# p1_main -- runtime spine
# =========================================================================
@@ -216,6 +283,16 @@
%la(t1, &heap_next)
%st(t0, t1, 0)
+ # heap_end = heap_buf_ptr + HEAP_CAP_BYTES. cons / alloc_hdr /
+ # alloc_bytes test (heap_next + bytes <= heap_end) on every
+ # allocation and abort via runtime_error on overflow.
+ %la(t0, &heap_buf_ptr)
+ %ld(t0, t0, 0)
+ %li(t1, %HEAP_CAP_BYTES)
+ %add(t0, t0, t1)
+ %la(t1, &heap_end)
+ %st(t0, t1, 0)
+
# Reserve special-form symbol indices, then bind built-in primitives.
%call(&intern_special_forms)
%call(®ister_primitives)
@@ -253,6 +330,18 @@
})
# =========================================================================
+# Runtime error -- single abort entry point
+# =========================================================================
+#
+# runtime_error(msg_cstr=a0) -> never returns. Every overflow / bounds /
+# unbound / type-failure path lands here so error reporting (and the
+# eventual user-facing `error` primitive) only have to be implemented
+# once. Today we tail into libp1pp's `panic`, which writes msg + LF to
+# stderr and sys_exits 1.
+:runtime_error
+ %tail(&panic)
+
+# =========================================================================
# Source loading -- argv[1] -> readbuf, length stored in readbuf_len
# =========================================================================
@@ -263,6 +352,15 @@
%call(&read_file)
%bltz(a0, &::fail)
+ # If the read filled (or would have filled) the buffer, the source
+ # is at least cap bytes; refuse rather than silently truncate.
+ # read_file does a single sys_read so n == cap is the only saturation
+ # signal we have. We treat n >= cap as overflow defensively.
+ %li(t0, %READBUF_CAP_BYTES)
+ %bltu(a0, t0, &::ok)
+ %die(msg_readbuf_full)
+
+ ::ok
%la(t0, &readbuf_len)
%st(a0, t0, 0)
%li(a0, 0)
@@ -271,7 +369,7 @@
%eret
::fail
- %die(msg_load_fail, 3)
+ %die(msg_load_fail)
})
# eval_prelude -- copy the embedded prelude bytes into readbuf, run the
@@ -281,6 +379,13 @@
%la(a1, &prelude_src)
%la(a2, &prelude_src_end)
%sub(a2, a2, a1)
+
+ # Defensive: prelude is fixed at compile time, but the readbuf cap
+ # isn't. If somebody trims the readbuf below the prelude size, we
+ # need to surface it instead of overrunning into the heap arena.
+ %li(t0, %READBUF_CAP_BYTES)
+ %bltu(t0, a2, &::too_big)
+
%la(t0, &readbuf_buf_ptr)
%ld(a0, t0, 0)
%call(&memcpy)
@@ -307,6 +412,10 @@
%b(&::loop)
::done
+ %eret
+
+ ::too_big
+ %die(msg_readbuf_full)
})
# =========================================================================
@@ -314,32 +423,63 @@
# =========================================================================
#
# Both are call-free leaves: bump heap_next, write fields, return tagged
-# pointer. Heap exhaustion crashes via a deliberate read past the arena
-# end -- runtime check arrives when error reporting does (LISP-C.md
-# milestone 3).
+# pointer. Each allocation tests (new_next <= heap_end) and aborts via
+# runtime_error if the bump would overflow the heap arena. heap_next is
+# kept 8-byte aligned so every PAIR/HEAP tag bit is exact: cons always
+# bumps by a multiple of 8 (16); alloc_hdr / alloc_bytes round their
+# argument up via %alignup(_,_,8,_).
-# cons(car=a0, cdr=a1) -> tagged pair (a0)
+# cons(car=a0, cdr=a1) -> tagged pair (a0). Allocates 16 bytes.
:cons
+%scope cons
%la(t2, &heap_next)
%ld(t0, t2, 0)
+ %addi(t1, t0, 16)
+ %la(a3, &heap_end)
+ %ld(a3, a3, 0)
+ %bltu(a3, t1, &::oom)
%st(a0, t0, 0)
%st(a1, t0, 8)
- %addi(t1, t0, 16)
%st(t1, t2, 0)
%addi(a0, t0, 1)
%ret
+ ::oom
+ %die(msg_heap_full)
+%endscope
# alloc_hdr(bytes=a0, hdr_word=a1) -> tagged heap obj (a0)
# Rounds bytes up to a multiple of 8 and writes hdr_word at offset 0.
:alloc_hdr
+%scope alloc_hdr
%alignup(a0, a0, 8, t0)
%la(t2, &heap_next)
%ld(t0, t2, 0)
%add(t1, t0, a0)
+ %la(a3, &heap_end)
+ %ld(a3, a3, 0)
+ %bltu(a3, t1, &::oom)
%st(t1, t2, 0)
%st(a1, t0, 0)
%addi(a0, t0, 3)
%ret
+ ::oom
+ %die(msg_heap_full)
+%endscope
+
+# list_length(list=a0) -> count (a0). Linear walk; clobbers a0 (used as
+# the cursor). Callers that need the list afterward must save it first.
+:list_length
+%scope list_length
+ %li(t0, 0)
+ ::loop
+ %if_nil(t1, a0, &::done)
+ %addi(t0, t0, 1)
+ %cdr(a0, a0)
+ %b(&::loop)
+ ::done
+ %mov(a0, t0)
+ %ret
+%endscope
# =========================================================================
# Symbol intern -- linear scan, append on miss
@@ -393,7 +533,7 @@
%ld(t0, sp, 16)
%li(t1, %SYMTAB_CAP_SLOTS)
%bltu(t0, t1, &::append_ok)
- %die(msg_symtab_full, 5)
+ %die(msg_symtab_full)
::append_ok
# Copy the name into a stable heap buffer. The caller-provided ptr
@@ -521,7 +661,7 @@
%tail(&parse_list)
::rparen
- %die(msg_unexp_rparen, 6)
+ %die(msg_unexp_rparen)
::hash
# Consume '#' plus its type byte; dispatch on the type byte.
@@ -543,7 +683,7 @@
%beqz(a1, &::hex_lit)
%addi(a1, a0, -88) ; 'X'
%beqz(a1, &::hex_lit)
- %die(msg_bad_hash, 6)
+ %die(msg_bad_hash)
::true_lit
%li(a0, %imm_val(%IMM.TRUE))
@@ -606,7 +746,7 @@
%tail(&cons)
::eof
- %die(msg_unexp_eof, 6)
+ %die(msg_unexp_eof)
})
# parse_list() -> tagged list value in a0. Cursor sits past '(' on entry;
@@ -701,7 +841,7 @@
%eret
::eof
- %die(msg_unterm_list, 6)
+ %die(msg_unterm_list)
})
# parse_atom() -> tagged value (fixnum or symbol) in a0.
@@ -844,8 +984,7 @@
# return cdr(binding); on NIL, fall back to the symbol's global slot.
# a0 still holds the tagged sym; a1 still holds env.
::env_walk
- %li(t0, %imm_val(%IMM.NIL))
- %beq(a1, t0, &::env_miss)
+ %if_nil(t0, a1, &::env_miss)
%car(t1, a1) ; t1 = (sym . val)
%car(t2, t1) ; t2 = sym in binding
%beq(t2, a0, &::env_hit)
@@ -864,7 +1003,7 @@
%eret
::unbound
- %die(msg_unbound, 7)
+ %die(msg_unbound)
::pair
# Special-form dispatch: pointer-compare head against the cached
@@ -929,33 +1068,53 @@
})
# eval_args(args=a0, env=a1) -> evaluated args list (cons-built).
-# Recursion depth = arg count, so very long arg lists could blow the
-# stack. Iterative tail-build is a future tightening.
-%fn(eval_args, 24, {
- %li(t0, %imm_val(%IMM.NIL))
- %beq(a0, t0, &::nil)
-
+# Iterative head/tail-cdr build: each iteration evals one arg, allocates
+# a (val . NIL) cell, and either seeds head/tail or set-cdr!s onto the
+# previous tail. Host stack stays O(1) regardless of arg-list length;
+# eval order is left-to-right.
+#
+# Frame: 32 bytes
+# +0 args (advances)
+# +8 env
+# +16 head (NIL until first val is appended)
+# +24 tail (most recent cell; set-cdr! target)
+%fn(eval_args, 32, {
%st(a0, sp, 0)
%st(a1, sp, 8)
+ %li(t0, %imm_val(%IMM.NIL))
+ %st(t0, sp, 16)
+ %st(t0, sp, 24)
+
+ ::loop
+ %ld(t0, sp, 0)
+ %if_nil(t1, t0, &::done)
# val = eval(car(args), env)
- %car(a0, a0)
+ %car(a0, t0)
+ %ld(a1, sp, 8)
%call(&eval)
+
+ # cell = cons(val, NIL); append to head/tail.
+ %li(a1, %imm_val(%IMM.NIL))
+ %call(&cons)
+
+ %ld(t0, sp, 16)
+ %if_nil(t1, t0, &::first)
+ %ld(t0, sp, 24)
+ %st(a0, t0, 7)
+ %st(a0, sp, 24)
+ %b(&::advance)
+
+ ::first
%st(a0, sp, 16)
+ %st(a0, sp, 24)
- # rest = eval_args(cdr(args), env)
- %ld(a0, sp, 0)
- %cdr(a0, a0)
- %ld(a1, sp, 8)
- %call(&eval_args)
+ ::advance
+ %advance_walk(0)
+ %b(&::loop)
- # cons(val, rest) -- tail call
- %mov(a1, a0)
+ ::done
%ld(a0, sp, 16)
- %tail(&cons)
-
- ::nil
- %li(a0, %imm_val(%IMM.NIL))
})
# apply(fn=a0, args=a1) -> result (a0)
@@ -978,7 +1137,7 @@
%beq(t0, t1, &::closure)
::not_proc
- %die(msg_not_proc, 8)
+ %die(msg_not_proc)
::prim
# Primitives are called with args list in a0 and the prim ptr itself
@@ -1130,10 +1289,7 @@
%ld(t0, sp, 0)
%car(t0, t0)
- %untag_sym(t0, t0)
- %mov(a1, a0)
- %mov(a0, t0)
- %call(&sym_set_global)
+ %bind_global_from_t0()
%li(a0, %imm_val(%IMM.UNSPEC))
%eret
@@ -1151,10 +1307,7 @@
%ld(t0, sp, 0)
%car(t0, t0)
%car(t0, t0) ; name
- %untag_sym(t0, t0)
- %mov(a1, a0)
- %mov(a0, t0)
- %call(&sym_set_global)
+ %bind_global_from_t0()
%li(a0, %imm_val(%IMM.UNSPEC))
})
@@ -1172,8 +1325,7 @@
::loop
%ld(t0, sp, 0)
- %li(t1, %imm_val(%IMM.NIL))
- %beq(t0, t1, &::no_match)
+ %if_nil(t1, t0, &::no_match)
%car(t1, t0) ; clause = (test body...)
%car(t2, t1) ; test_expr
@@ -1198,9 +1350,7 @@
%tail(&eval_body)
::next
- %ld(t0, sp, 0)
- %cdr(t0, t0)
- %st(t0, sp, 0)
+ %advance_walk(0)
%b(&::loop)
::no_match
@@ -1237,8 +1387,7 @@
::loop
%ld(t0, sp, 16)
- %li(t1, %imm_val(%IMM.NIL))
- %beq(t0, t1, &::done)
+ %if_nil(t1, t0, &::done)
%car(t1, t0) ; pair = (name init)
%cdr(t2, t1)
@@ -1262,9 +1411,7 @@
%call(&cons)
%st(a0, sp, 24)
- %ld(t0, sp, 16)
- %cdr(t0, t0)
- %st(t0, sp, 16)
+ %advance_walk(16)
%b(&::loop)
::done
@@ -1296,8 +1443,7 @@
::loop
%ld(t0, sp, 16)
- %li(t1, %imm_val(%IMM.NIL))
- %beq(t0, t1, &::done)
+ %if_nil(t1, t0, &::done)
%car(t1, t0)
%cdr(t2, t1)
@@ -1319,9 +1465,7 @@
%call(&cons)
%st(a0, sp, 24)
- %ld(t0, sp, 16)
- %cdr(t0, t0)
- %st(t0, sp, 16)
+ %advance_walk(16)
%b(&::loop)
::done
@@ -1354,8 +1498,7 @@
::phase1
%ld(t0, sp, 16)
- %li(t1, %imm_val(%IMM.NIL))
- %beq(t0, t1, &::p1_done)
+ %if_nil(t1, t0, &::p1_done)
%car(t1, t0)
%car(t2, t1) ; name
@@ -1366,9 +1509,7 @@
%call(&cons)
%st(a0, sp, 24)
- %ld(t0, sp, 16)
- %cdr(t0, t0)
- %st(t0, sp, 16)
+ %advance_walk(16)
%b(&::phase1)
::p1_done
@@ -1378,8 +1519,7 @@
::phase2
%ld(t0, sp, 16)
- %li(t1, %imm_val(%IMM.NIL))
- %beq(t0, t1, &::p2_done)
+ %if_nil(t1, t0, &::p2_done)
%car(t1, t0)
%cdr(t2, t1)
@@ -1404,9 +1544,7 @@
%car(a1, t1)
%st(a0, a1, 7) ; set-cdr! binding val
- %ld(t0, sp, 16)
- %cdr(t0, t0)
- %st(t0, sp, 16)
+ %advance_walk(16)
%b(&::phase2)
::p2_done
@@ -1457,8 +1595,7 @@
::p1_loop
%ld(t0, sp, 32)
- %li(t1, %imm_val(%IMM.NIL))
- %beq(t0, t1, &::p1_done)
+ %if_nil(t1, t0, &::p1_done)
%car(t1, t0)
%car(t2, t1) ; name
@@ -1467,8 +1604,7 @@
%call(&cons) ; cell = (name . NIL)
%ld(t0, sp, 40)
- %li(t1, %imm_val(%IMM.NIL))
- %beq(t0, t1, &::p1_first)
+ %if_nil(t1, t0, &::p1_first)
%ld(t0, sp, 48)
%st(a0, t0, 7)
%st(a0, sp, 48)
@@ -1479,9 +1615,7 @@
%st(a0, sp, 48)
::p1_advance
- %ld(t0, sp, 32)
- %cdr(t0, t0)
- %st(t0, sp, 32)
+ %advance_walk(32)
%b(&::p1_loop)
::p1_done
@@ -1499,8 +1633,7 @@
::p2_loop
%ld(t0, sp, 32)
- %li(t1, %imm_val(%IMM.NIL))
- %beq(t0, t1, &::p2_done)
+ %if_nil(t1, t0, &::p2_done)
%car(t1, t0)
%cdr(t2, t1)
@@ -1513,8 +1646,7 @@
%call(&cons) ; cell = (val . NIL)
%ld(t0, sp, 40)
- %li(t1, %imm_val(%IMM.NIL))
- %beq(t0, t1, &::p2_first)
+ %if_nil(t1, t0, &::p2_first)
%ld(t0, sp, 48)
%st(a0, t0, 7)
%st(a0, sp, 48)
@@ -1525,9 +1657,7 @@
%st(a0, sp, 48)
::p2_advance
- %ld(t0, sp, 32)
- %cdr(t0, t0)
- %st(t0, sp, 32)
+ %advance_walk(32)
%b(&::p2_loop)
::p2_done
@@ -1586,12 +1716,8 @@
%st(a0, sp, 16)
# advance params and args
- %ld(t0, sp, 0)
- %cdr(t0, t0)
- %st(t0, sp, 0)
- %ld(t0, sp, 8)
- %cdr(t0, t0)
- %st(t0, sp, 8)
+ %advance_walk(0)
+ %advance_walk(8)
%b(&::loop)
::rest_bind
@@ -1621,8 +1747,7 @@
# If cdr(body) is NIL, body's car is the last form.
%cdr(t0, a0)
- %li(t1, %imm_val(%IMM.NIL))
- %beq(t0, t1, &::last)
+ %if_nil(t1, t0, &::last)
# Non-last form: eval and discard, advance.
%car(a0, a0)
@@ -1745,10 +1870,9 @@
:prim_nullq_entry
%scope prim_nullq
%car(t0, a0)
- %li(t1, %imm_val(%IMM.NIL))
- %li(a0, %imm_val(%IMM.FALSE))
- %bne(t0, t1, &::end)
%li(a0, %imm_val(%IMM.TRUE))
+ %if_nil(t1, t0, &::end)
+ %li(a0, %imm_val(%IMM.FALSE))
::end
%ret
%endscope
@@ -1902,13 +2026,20 @@
# alloc_bytes(size=a0) -> raw addr (a0). Untagged data buffer; size is
# rounded up to 8 to keep the next bump 8-byte-aligned.
:alloc_bytes
+%scope alloc_bytes
%alignup(a0, a0, 8, t0)
%la(t2, &heap_next)
%ld(t1, t2, 0)
%add(t0, t1, a0)
+ %la(a3, &heap_end)
+ %ld(a3, a3, 0)
+ %bltu(a3, t0, &::oom)
%st(t0, t2, 0)
%mov(a0, t1)
%ret
+ ::oom
+ %die(msg_heap_full)
+%endscope
# bv_capacity_for(n=a0) -> smallest power-of-two strictly greater than n,
# minimum 16 (so capacity > length and the byte at index `length` is the
@@ -2007,24 +2138,22 @@
# +16 wrapper (saved across fill loop)
%st(a0, sp, 0)
- %cdr(t0, a0)
- %li(t1, %imm_val(%IMM.NIL))
%li(t2, 0)
- %beq(t0, t1, &::no_fill)
+ %cdr(t0, a0)
+ %if_nil(t1, t0, &::no_fill)
%car(t0, t0)
%sari(t2, t0, 3)
::no_fill
%st(t2, sp, 8)
%ld(a0, sp, 0)
- %car(a0, a0)
- %sari(a0, a0, 3)
+ %car_fix(a0, a0)
+ %bltz(a0, &::bad_len)
%call(&bv_alloc)
%st(a0, sp, 16)
%ld(t0, sp, 0)
- %car(t0, t0)
- %sari(t0, t0, 3) ; raw_len
+ %car_fix(t0, t0) ; raw_len
%ld(t1, sp, 8) ; fill
%ld(a1, sp, 16)
%ld(t2, a1, 5) ; data_ptr
@@ -2039,6 +2168,10 @@
::fill_done
%ld(a0, sp, 16)
+ %eret
+
+ ::bad_len
+ %die(msg_bv_oob)
})
:prim_bv_length_entry
@@ -2048,25 +2181,35 @@
%ret
:prim_bv_u8_ref_entry
- %car(t0, a0)
- %cdr(t1, a0)
- %car(t1, t1)
- %sari(t1, t1, 3)
+%scope prim_bv_u8_ref
+ %args2(t0, t1, a0) ; bv, tagged idx
+ %sari(t1, t1, 3) ; raw idx
+ %bltz(t1, &::oob)
+ %ld(a0, t0, -3)
+ %shri(a0, a0, 8) ; length
+ %bltu(t1, a0, &::ok)
+ ::oob
+ %die(msg_bv_oob)
+ ::ok
%ld(t2, t0, 5)
%add(t2, t2, t1)
%lb(a0, t2, 0)
%mkfix(a0, a0)
%ret
+%endscope
:prim_bv_u8_set_entry
%scope prim_bv_u8_set
- %car(t0, a0)
- %cdr(t1, a0)
- %car(t2, t1)
- %sari(t2, t2, 3)
- %cdr(t1, t1)
- %car(t1, t1)
- %sari(t1, t1, 3)
+ %args3(t0, t2, t1, a0) ; bv, idx, val
+ %sari(t2, t2, 3) ; raw idx
+ %sari(t1, t1, 3) ; raw val
+ %bltz(t2, &::oob)
+ %ld(a0, t0, -3)
+ %shri(a0, a0, 8) ; length
+ %bltu(t2, a0, &::ok)
+ ::oob
+ %die(msg_bv_oob)
+ ::ok
%ld(a0, t0, 5)
%add(a0, a0, t2)
%sb(t1, a0, 0)
@@ -2075,6 +2218,7 @@
%endscope
# (bytevector-copy src start end) -> fresh bv of length end-start.
+# Bounds: 0 <= start <= end <= src.length.
#
# Frame: 24 bytes
# +0 args
@@ -2083,15 +2227,19 @@
%fn(prim_bv_copy_entry, 24, {
%st(a0, sp, 0)
- %car(t0, a0)
+ %args3(t0, t2, t1, a0) ; src, start, end
%st(t0, sp, 8)
-
- %cdr(t1, a0)
- %car(t2, t1)
%sari(t2, t2, 3) ; raw start
- %cdr(t1, t1)
- %car(t1, t1)
%sari(t1, t1, 3) ; raw end
+
+ # Bounds: start >= 0; end >= start (signed catches negative end since
+ # start is now non-negative); src.length >= end.
+ %bltz(t2, &::oob)
+ %blt(t1, t2, &::oob)
+ %ld(a0, t0, -3)
+ %shri(a0, a0, 8) ; src.length
+ %blt(a0, t1, &::oob)
+
%sub(a0, t1, t2) ; count
%call(&bv_alloc)
%st(a0, sp, 16)
@@ -2099,8 +2247,7 @@
# Recompute src ptr at start; dst ptr at 0; count from new bv's hdr.
%ld(t0, sp, 0)
%cdr(t0, t0)
- %car(t0, t0)
- %sari(t0, t0, 3) ; raw start
+ %car_fix(t0, t0) ; raw start
%ld(t1, sp, 8)
%ld(t2, t1, 5)
%add(t2, t2, t0) ; src ptr
@@ -2120,42 +2267,90 @@
::copy_done
%ld(a0, sp, 16)
+ %eret
+
+ ::oob
+ %die(msg_bv_oob)
})
# (bytevector-grow! bv min-cap) -> bv. Forwards to bv_grow; min-cap is
# untagged at the boundary. Useful when a builder pattern wants to
# pre-extend capacity, but also makes the doubling path testable.
:prim_bv_grow_entry
- %car(t0, a0)
- %cdr(t1, a0)
- %car(t1, t1)
+%scope prim_bv_grow
+ %args2(t0, t1, a0)
%sari(t1, t1, 3)
+ %bltz(t1, &::bad)
%mov(a0, t0)
%mov(a1, t1)
%b(&bv_grow)
+ ::bad
+ %die(msg_bv_oob)
+%endscope
-# (bytevector-copy! dst dst-start src src-start src-end)
-:prim_bv_copy_bang_entry
-%scope prim_bv_copy_bang
- %car(t0, a0) ; dst
- %cdr(t1, a0)
- %car(t2, t1)
- %sari(t2, t2, 3) ; dst-start
+# (bytevector-copy! dst dst-start src src-start src-end). Bounds:
+# 0 <= src-start <= src-end <= src.length and
+# 0 <= dst-start && dst-start + (src-end-src-start) <= dst.length.
+#
+# Frame: 40 bytes (five raw args parked across the bounds checks).
+# +0 dst (tagged)
+# +8 dst-start (raw)
+# +16 src (tagged)
+# +24 src-start (raw)
+# +32 src-end (raw)
+%fn(prim_bv_copy_bang_entry, 40, {
+ %car(t0, a0)
+ %st(t0, sp, 0) ; dst
+ %cdr(a0, a0)
+ %car(t0, a0)
+ %sari(t0, t0, 3)
+ %st(t0, sp, 8) ; dst-start
+ %cdr(a0, a0)
+ %car(t0, a0)
+ %st(t0, sp, 16) ; src
+ %cdr(a0, a0)
+ %car(t0, a0)
+ %sari(t0, t0, 3)
+ %st(t0, sp, 24) ; src-start
+ %cdr(a0, a0)
+ %car(t0, a0)
+ %sari(t0, t0, 3)
+ %st(t0, sp, 32) ; src-end
- %cdr(t1, t1)
- %car(a1, t1) ; src
- %cdr(t1, t1)
- %car(a2, t1)
- %sari(a2, a2, 3) ; src-start
- %cdr(t1, t1)
- %car(a3, t1)
- %sari(a3, a3, 3) ; src-end
+ # src-start >= 0
+ %ld(t0, sp, 24)
+ %bltz(t0, &::oob)
+ # src-end >= src-start (signed catches negative src-end)
+ %ld(t1, sp, 32)
+ %blt(t1, t0, &::oob)
+ # src-end <= src.length
+ %ld(t2, sp, 16)
+ %ld(a0, t2, -3)
+ %shri(a0, a0, 8)
+ %blt(a0, t1, &::oob)
+ # dst-start >= 0
+ %ld(t2, sp, 8)
+ %bltz(t2, &::oob)
+ # dst-start + count <= dst.length
+ %sub(a0, t1, t0) ; count = src-end - src-start
+ %add(a0, a0, t2) ; dst-start + count
+ %ld(t1, sp, 0)
+ %ld(t2, t1, -3)
+ %shri(t2, t2, 8)
+ %blt(t2, a0, &::oob)
+ # Set up copy. dst ptr = dst.data + dst-start; src ptr = src.data +
+ # src-start; count = src-end - src-start.
+ %ld(t0, sp, 0)
%ld(t0, t0, 5)
- %add(t0, t0, t2) ; dst ptr
+ %ld(a1, sp, 8)
+ %add(t0, t0, a1) ; dst ptr
+ %ld(a1, sp, 16)
%ld(a1, a1, 5)
- %add(a1, a1, a2) ; src ptr
- %sub(a3, a3, a2) ; count
+ %ld(a2, sp, 24)
+ %add(a1, a1, a2) ; src ptr
+ %ld(a3, sp, 32)
+ %sub(a3, a3, a2) ; count
::loop
%beqz(a3, &::done)
@@ -2168,8 +2363,11 @@
::done
%li(a0, %imm_val(%IMM.UNSPEC))
- %ret
-%endscope
+ %eret
+
+ ::oob
+ %die(msg_bv_oob)
+})
# (apply fn rest...) -- the trailing element of `rest` is a list; any
# leading elements get prepended to it. apply_build_args walks `rest` and
@@ -2185,24 +2383,60 @@
%tail(&apply)
})
-%fn(apply_build_args, 16, {
+# apply_build_args(rest=a0) -> assembled args list.
+# `rest` is (a1 a2 ... aN listargs); the trailing element is itself a list
+# whose elements get appended after the leading aₖ's. Iterative
+# head/tail-cdr build: walk every cell whose cdr isn't NIL into a fresh
+# (aₖ . NIL) cons; the final element (the trailing list) becomes the
+# tail's cdr (or the result itself if there are no leading elements).
+#
+# Frame: 24 bytes
+# +0 walk (advances; current cell of rest)
+# +8 head (NIL until first leading arg appended)
+# +16 tail (most recent cell; set-cdr! target)
+%fn(apply_build_args, 24, {
%st(a0, sp, 0)
+ %li(t0, %imm_val(%IMM.NIL))
+ %st(t0, sp, 8)
+ %st(t0, sp, 16)
- %cdr(t0, a0)
- %li(t1, %imm_val(%IMM.NIL))
- %beq(t0, t1, &::single)
+ ::loop
+ %ld(t0, sp, 0)
+ %cdr(t1, t0)
+ %if_nil(t2, t1, &::last)
- %ld(a0, sp, 0)
- %cdr(a0, a0)
- %call(&apply_build_args)
- %mov(a1, a0)
- %ld(a0, sp, 0)
- %car(a0, a0)
- %tail(&cons)
+ # cell = cons(car(walk), NIL); append to head/tail.
+ %car(a0, t0)
+ %li(a1, %imm_val(%IMM.NIL))
+ %call(&cons)
- ::single
- %ld(a0, sp, 0)
- %car(a0, a0)
+ %ld(t0, sp, 8)
+ %if_nil(t1, t0, &::first)
+ %ld(t0, sp, 16)
+ %st(a0, t0, 7)
+ %st(a0, sp, 16)
+ %b(&::advance)
+
+ ::first
+ %st(a0, sp, 8)
+ %st(a0, sp, 16)
+
+ ::advance
+ %advance_walk(0)
+ %b(&::loop)
+
+ ::last
+ # car(walk) is the trailing list. If head is NIL there were no leading
+ # args -- return the trailing list directly. Otherwise splice it onto
+ # the tail and return head.
+ %car(a0, t0)
+ %ld(t1, sp, 8)
+ %if_nil(t2, t1, &::done)
+ %ld(t1, sp, 16)
+ %st(a0, t1, 7)
+ %ld(a0, sp, 8)
+
+ ::done
})
# Records: TDs (type descriptors) and instances. A TD is a 24-byte heap
@@ -2248,19 +2482,10 @@
%fn(prim_make_record_entry, 24, {
%st(a0, sp, 0)
- # Count fields = length(args) - 1. We'll use the args walker for the
- # actual fill, just count to size the allocation.
- %cdr(t0, a0)
- %li(t1, 0)
- ::count_loop
- %li(t2, %imm_val(%IMM.NIL))
- %beq(t0, t2, &::count_done)
- %addi(t1, t1, 1)
- %cdr(t0, t0)
- %b(&::count_loop)
- ::count_done
-
- %shli(a0, t1, 3)
+ # Count fields = length(args) - 1.
+ %cdr(a0, a0)
+ %call(&list_length)
+ %shli(a0, a0, 3)
%addi(a0, a0, 16)
%li(a1, %HDR.REC)
%call(&alloc_hdr)
@@ -2277,8 +2502,7 @@
%addi(t1, a0, 13)
::fill_loop
- %li(t2, %imm_val(%IMM.NIL))
- %beq(t0, t2, &::fill_done)
+ %if_nil(t2, t0, &::fill_done)
%car(t2, t0)
%st(t2, t1, 0)
%addi(t1, t1, 8)
@@ -2355,17 +2579,8 @@
%st(t0, sp, 8)
# Count = length(args).
- %ld(t1, sp, 0)
- %li(t2, 0)
- ::count_loop
- %li(a2, %imm_val(%IMM.NIL))
- %beq(t1, a2, &::count_done)
- %addi(t2, t2, 1)
- %cdr(t1, t1)
- %b(&::count_loop)
- ::count_done
-
- %shli(a0, t2, 3)
+ %call(&list_length)
+ %shli(a0, a0, 3)
%addi(a0, a0, 16)
%li(a1, %HDR.REC)
%call(&alloc_hdr)
@@ -2378,8 +2593,7 @@
%addi(t1, a0, 13)
::fill_loop
- %li(t2, %imm_val(%IMM.NIL))
- %beq(t0, t2, &::fill_done)
+ %if_nil(t2, t0, &::fill_done)
%car(t2, t0)
%st(t2, t1, 0)
%addi(t1, t1, 8)
@@ -2449,21 +2663,14 @@
%st(a0, sp, 0)
%st(a1, sp, 8)
- # clauses = cdddr(rest); count them while we're at it.
- %ld(t0, sp, 0)
- %cdr(t0, t0)
- %cdr(t0, t0)
- %cdr(t0, t0)
- %st(t0, sp, 24)
- %li(t1, 0)
- ::count_loop
- %li(t2, %imm_val(%IMM.NIL))
- %beq(t0, t2, &::count_done)
- %addi(t1, t1, 1)
- %cdr(t0, t0)
- %b(&::count_loop)
- ::count_done
- %st(t1, sp, 40)
+ # clauses = cdddr(rest); count them via list_length.
+ %ld(a0, sp, 0)
+ %cdr(a0, a0)
+ %cdr(a0, a0)
+ %cdr(a0, a0)
+ %st(a0, sp, 24)
+ %call(&list_length)
+ %st(a0, sp, 40)
# td = alloc_hdr(24, HDR.TD); td.name = type-name; td.nfields = nfields.
%li(a0, 24)
@@ -2484,10 +2691,7 @@
%cdr(t0, t0)
%car(t0, t0)
%car(t0, t0)
- %untag_sym(t0, t0)
- %mov(a1, a0)
- %mov(a0, t0)
- %call(&sym_set_global)
+ %bind_global_from_t0()
# pred-prim = make_param_prim(prim_predicate_entry, td); bind pred.
%la(a0, &prim_predicate_entry)
@@ -2497,10 +2701,7 @@
%cdr(t0, t0)
%cdr(t0, t0)
%car(t0, t0)
- %untag_sym(t0, t0)
- %mov(a1, a0)
- %mov(a0, t0)
- %call(&sym_set_global)
+ %bind_global_from_t0()
# Iterate clauses: bind accessor + optional mutator per clause.
%li(t0, 0)
@@ -2508,8 +2709,7 @@
::clause_loop
%ld(t0, sp, 24)
- %li(t1, %imm_val(%IMM.NIL))
- %beq(t0, t1, &::done)
+ %if_nil(t1, t0, &::done)
# accessor-prim with data = tagged idx; bind cadr(clause).
%ld(a1, sp, 32)
@@ -2521,18 +2721,14 @@
%car(t0, t0)
%cdr(t0, t0)
%car(t0, t0)
- %untag_sym(t0, t0)
- %mov(a1, a0)
- %mov(a0, t0)
- %call(&sym_set_global)
+ %bind_global_from_t0()
# Mutator? If cddr(clause) is a pair, bind it.
%ld(t0, sp, 24)
%car(t0, t0)
%cdr(t0, t0)
%cdr(t0, t0)
- %li(t1, %imm_val(%IMM.NIL))
- %beq(t0, t1, &::no_mutator)
+ %if_nil(t1, t0, &::no_mutator)
%ld(a1, sp, 32)
%shli(a1, a1, 3)
@@ -2544,15 +2740,10 @@
%cdr(t0, t0)
%cdr(t0, t0)
%car(t0, t0)
- %untag_sym(t0, t0)
- %mov(a1, a0)
- %mov(a0, t0)
- %call(&sym_set_global)
+ %bind_global_from_t0()
::no_mutator
- %ld(t0, sp, 24)
- %cdr(t0, t0)
- %st(t0, sp, 24)
+ %advance_walk(24)
%ld(t0, sp, 32)
%addi(t0, t0, 1)
%st(t0, sp, 32)
@@ -2649,19 +2840,10 @@
# +16 array ptr (raw)
%fn(build_execve_argv, 24, {
%st(a0, sp, 0)
+ %call(&list_length) ; clobbers a0 -> count
+ %st(a0, sp, 8)
- %mov(t0, a0)
- %li(t1, 0)
- ::count_loop
- %li(t2, %imm_val(%IMM.NIL))
- %beq(t0, t2, &::count_done)
- %addi(t1, t1, 1)
- %cdr(t0, t0)
- %b(&::count_loop)
- ::count_done
- %st(t1, sp, 8)
-
- %addi(a0, t1, 1)
+ %addi(a0, a0, 1)
%shli(a0, a0, 3)
%call(&alloc_bytes)
%st(a0, sp, 16)
@@ -2670,8 +2852,7 @@
%ld(t1, sp, 16)
::fill_loop
- %li(t2, %imm_val(%IMM.NIL))
- %beq(t0, t2, &::fill_done)
+ %if_nil(t2, t0, &::fill_done)
%car(a3, t0)
%ld(a2, a3, 5)
%st(a2, t1, 0)
@@ -2688,15 +2869,10 @@
# (sys-read fd buf count)
%fn(prim_sys_read_entry, 0, {
- %car(t0, a0)
- %sari(t0, t0, 3)
- %cdr(t1, a0)
- %car(t1, t1)
- %ld(t1, t1, 5)
- %cdr(t2, a0)
- %cdr(t2, t2)
- %car(t2, t2)
- %sari(t2, t2, 3)
+ %args3(t0, t1, t2, a0)
+ %sari(t0, t0, 3) ; fd
+ %ld(t1, t1, 5) ; buf data ptr
+ %sari(t2, t2, 3) ; count
%mov(a0, t0)
%mov(a1, t1)
%mov(a2, t2)
@@ -2706,15 +2882,10 @@
# (sys-write fd buf count)
%fn(prim_sys_write_entry, 0, {
- %car(t0, a0)
- %sari(t0, t0, 3)
- %cdr(t1, a0)
- %car(t1, t1)
- %ld(t1, t1, 5)
- %cdr(t2, a0)
- %cdr(t2, t2)
- %car(t2, t2)
- %sari(t2, t2, 3)
+ %args3(t0, t1, t2, a0)
+ %sari(t0, t0, 3) ; fd
+ %ld(t1, t1, 5) ; buf data ptr
+ %sari(t2, t2, 3) ; count
%mov(a0, t0)
%mov(a1, t1)
%mov(a2, t2)
@@ -2724,30 +2895,21 @@
# (sys-close fd)
%fn(prim_sys_close_entry, 0, {
- %car(a0, a0)
- %sari(a0, a0, 3)
+ %car_fix(a0, a0)
%call(&sys_close)
%tail(&wrap_syscall_result)
})
# (sys-openat dirfd path flags mode)
-%fn(prim_sys_openat_entry, 16, {
- # +0 flags raw (parked across the path/mode extraction)
- %car(t0, a0)
- %sari(t0, t0, 3) ; dirfd raw
- %cdr(a0, a0)
- %car(t1, a0)
+%fn(prim_sys_openat_entry, 0, {
+ %args4(t0, t1, t2, a3, a0)
+ %sari(t0, t0, 3) ; dirfd
%ld(t1, t1, 5) ; path data_ptr
- %cdr(a0, a0)
- %car(t2, a0)
- %sari(t2, t2, 3)
- %st(t2, sp, 0) ; flags raw
- %cdr(a0, a0)
- %car(a3, a0)
- %sari(a3, a3, 3) ; mode raw
+ %sari(t2, t2, 3) ; flags
+ %sari(a3, a3, 3) ; mode
%mov(a0, t0)
%mov(a1, t1)
- %ld(a2, sp, 0)
+ %mov(a2, t2)
%call(&sys_openat)
%tail(&wrap_syscall_result)
})
@@ -2760,15 +2922,12 @@
# (sys-execve path argv-list)
%fn(prim_sys_execve_entry, 16, {
- %st(a0, sp, 0)
- %cdr(a0, a0)
- %car(a0, a0)
+ %args2(t0, a0, a0) ; t0 = path bv, a0 = argv-list
+ %st(t0, sp, 0)
%call(&build_execve_argv)
- %mov(t0, a0)
+ %mov(a1, a0)
%ld(a0, sp, 0)
- %car(a0, a0)
- %ld(a0, a0, 5)
- %mov(a1, t0)
+ %ld(a0, a0, 5) ; path data ptr
%li(a2, 0)
%call(&sys_execve)
%tail(&wrap_syscall_result)
@@ -2776,17 +2935,11 @@
# (sys-waitid idtype id infop options)
%fn(prim_sys_waitid_entry, 0, {
- %car(t0, a0)
- %sari(t0, t0, 3)
- %cdr(a0, a0)
- %car(t1, a0)
- %sari(t1, t1, 3)
- %cdr(a0, a0)
- %car(t2, a0)
- %ld(t2, t2, 5)
- %cdr(a0, a0)
- %car(a3, a0)
- %sari(a3, a3, 3)
+ %args4(t0, t1, t2, a3, a0)
+ %sari(t0, t0, 3) ; idtype
+ %sari(t1, t1, 3) ; id
+ %ld(t2, t2, 5) ; infop bv data ptr
+ %sari(a3, a3, 3) ; options
%mov(a0, t0)
%mov(a1, t1)
%mov(a2, t2)
@@ -2843,8 +2996,7 @@
%call(&cons)
%ld(t0, sp, 16)
- %li(t1, %imm_val(%IMM.NIL))
- %beq(t0, t1, &::first)
+ %if_nil(t1, t0, &::first)
%ld(t0, sp, 24)
%st(a0, t0, 7)
%st(a0, sp, 24)
@@ -3068,6 +3220,9 @@
:msg_unterm_list "scheme1: unterminated list" '0a' '00'
:msg_unbound "scheme1: unbound variable" '0a' '00'
:msg_not_proc "scheme1: not a procedure" '0a' '00'
+:msg_heap_full "scheme1: heap exhausted" '0a' '00'
+:msg_readbuf_full "scheme1: source buffer overflow" '0a' '00'
+:msg_bv_oob "scheme1: bytevector index out of range" '0a' '00'
# =========================================================================
# BSS pointer-init table
@@ -3088,6 +3243,10 @@
# heap_next: bump pointer; written once by p1_main, then by cons/alloc_hdr.
:heap_next $(0)
+# heap_end: one byte past the last valid heap address (= heap_buf_ptr +
+# HEAP_CAP_BYTES). Read on every allocation.
+:heap_end $(0)
+
# Source-buffer cursor and slurped length.
:readbuf_pos $(0)
:readbuf_len $(0)