boot2

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

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:
MM1pp/M1pp.P1 | 747++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
MP1/P1-aarch64.M1 | 54++++++++++++++++++++++++++++++++++++------------------
MP1/P1-amd64.M1 | 54++++++++++++++++++++++++++++++++++++------------------
MP1/P1-riscv64.M1 | 54++++++++++++++++++++++++++++++++++++------------------
Mdocs/scheme-shell-todo.md | 134++++++++++++++++---------------------------------------------------------------
Mscheme1/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 &macro_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 &macro_body_end ld_t1,a0,0 la_a2 &macro_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 &macro_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 &macro_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 &macros_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 &macro_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 &copy_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(&register_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)