commit ce7dbc4c1ff6509f28aa721ebffdffda6b6bb051
parent a52dc7712af059af01946616204fd6aebefc7652
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Thu, 23 Apr 2026 16:48:36 -0700
m1pp/m1pp.M1: scrub evolutionary comments
Comments now describe current state and intent only. No phase numbers,
no track / layer / oracle references, no "this used to do X" narratives.
Section dividers renamed from "Phase N STUBS:" to topic-named blocks
(e.g. "Stream stack + expansion-pool lifetime", "Argument parsing").
Inline `Oracle: m1pp.c:NAME.` per-function refs deleted; oracle-anchored
prose ("matches C oracle", "the oracle spells xor as $", "the C oracle
uses 512-byte tmp") rephrased to state the rule directly. Stale sizing
notes (32 macros, 256 expand slots) updated to match current caps.
Behavior unchanged: all 11 m1pp oracle-parity fixtures and the P1 hello
fixture still pass.
Diffstat:
| M | m1pp/m1pp.M1 | | | 184 | +++++++++++++++++++++++++++++++++++-------------------------------------------- |
1 file changed, 82 insertions(+), 102 deletions(-)
diff --git a/m1pp/m1pp.M1 b/m1pp/m1pp.M1
@@ -1,21 +1,23 @@
-## m1pp.M1 — bootstrap M1 macro-expander, P1v2 port.
+## m1pp.M1 — bootstrap M1 macro-expander, P1v2.
##
## Runtime shape: m1pp input.M1 output.M1
##
## Pipeline:
-## _start argv/argc from kernel SP; openat+read into input_buf
-## -> call lex_source
-## -> call process_tokens
-## -> openat+write output_buf to argv[2]; exit
-## lex_source input_buf -> source_tokens[] (via append_text + push_source_token)
-## process_tokens source_tokens[] -> output_buf (via emit_token / emit_newline,
-## branching to define_macro at
-## line-start %macro). Phase 3
-## rewrites this into a stream-
-## driven loop that dispatches
-## the Phase 4+ stubs.
-## define_macro parse %macro header+body; record in macros[] + macro_body_tokens[];
-## consume through the %endm line without emitting output
+## _start argv/argc from kernel SP; openat+read into input_buf;
+## call lex_source, then process_tokens; openat+write
+## output_buf to argv[2]; exit.
+## lex_source input_buf -> source_tokens[] (via append_text +
+## push_source_token).
+## process_tokens Stream-driven loop. Pushes source_tokens as the initial
+## stream and walks it token-by-token, dispatching to
+## define_macro at line-start %macro, emit_newline /
+## emit_token for pass-through, expand_builtin_call for
+## !@%$ and %select, and expand_call for user macros.
+## Macro expansions and %select push fresh streams onto
+## streams[]; popping rewinds the expansion pool.
+## define_macro Parse %macro header+body; record in macros[] +
+## macro_body_tokens[]; consume through the %endm line
+## without emitting output.
##
## P1v2 ABI: a0..a3 arg/return, t0..t2 caller-saved temps, s0..s3 callee-saved
## (unused here). Non-leaf functions use enter_0 / leave. _start has no frame;
@@ -28,8 +30,8 @@ DEFINE M1PP_OUTPUT_CAP 0000010000000000
DEFINE M1PP_TEXT_CAP 0080000000000000
DEFINE M1PP_TOKENS_END 0080010000000000
## Macro record is 296 bytes: name (16) + param_count (8) + params[16]*16 (256)
-## + body_start (8) + body_end (8). 32 macros × 296 = 9472 bytes = MACROS_CAP.
-## Body-token arena: 256 × 24 = 6144 bytes = MACRO_BODY_CAP.
+## + body_start (8) + body_end (8). MACROS_CAP fits 256 records (75776 B).
+## Body-token arena fits 4096 tokens (98304 B = 0x18000).
DEFINE M1PP_MACRO_RECORD_SIZE 2801000000000000
DEFINE M1PP_MACRO_BODY_START_OFF 1801000000000000
DEFINE M1PP_MACRO_BODY_END_OFF 2001000000000000
@@ -52,7 +54,7 @@ DEFINE TOK_PASTE 0600000000000000
## Token record stride (kind + text_ptr + text_len). Advance a Token* by this.
DEFINE M1PP_TOK_SIZE 1800000000000000
-## --- Phase 3+ data structure sizes (stubbed functions reference these) -------
+## --- Stream / expansion-pool / expression-frame sizes ------------------------
## Stream record: 40 bytes. Fields (each 8 bytes):
## +0 start Token*
## +8 end Token* (exclusive)
@@ -68,7 +70,7 @@ DEFINE M1PP_STREAM_MARK_OFF 2000000000000000
## Stream stack cap: 16 streams × 40 = 640 bytes.
DEFINE M1PP_STREAM_STACK_CAP 8002000000000000
-## Expansion pool: 256 Token slots × 24 bytes = 6144 bytes.
+## Expansion pool fits 4096 Token slots × 24 bytes = 98304 bytes (0x18000).
DEFINE M1PP_EXPAND_CAP 0080010000000000
## ExprFrame record: 144 bytes. Fields:
@@ -769,9 +771,9 @@ DEFINE EXPR_INVALID 1100000000000000
ret
## --- Main processor ----------------------------------------------------------
-## Phase-3+ stream-driven loop. Pushes source_tokens as the initial stream, then
-## drives the streams[] stack until it empties. Per iteration:
-## pop the stream if exhausted, otherwise dispatch on the current token:
+## Stream-driven loop. Pushes source_tokens as the initial stream, then drives
+## the streams[] stack until it empties. Per iteration: pop the stream if
+## exhausted, otherwise dispatch on the current token:
## - line-start %macro -> shim into define_macro via proc_pos
## - TOK_NEWLINE -> emit_newline, advance, set line_start = 1
## - WORD + LPAREN follow + name in {! @ % $ %select}
@@ -1313,11 +1315,12 @@ DEFINE EXPR_INVALID 1100000000000000
ret
## ============================================================================
-## --- Phase 3 STUBS: stream stack + expansion-pool lifetime -------------------
+## --- Stream stack + expansion-pool lifetime ---------------------------------
## ============================================================================
-## Phase 3 isolates lifecycle plumbing with no semantic change: after Phase 3
-## lands, process_tokens is rewritten to be stream-driven but only exercises
-## the pass-through + %macro paths, so Phase 2 parity still holds.
+## process_tokens drives a stack of token streams. The source token array is
+## pushed first; each macro expansion or %select chosen-branch pushes a fresh
+## stream backed by a slice of expand_pool, popping rewinds pool_used to the
+## stream's pool_mark.
## push_stream_span(a0=start_tok, a1=end_tok, a2=pool_mark) -> void (fatal on overflow)
## Push Stream { start = pos = a0, end = a1, line_start = 1, pool_mark = a2 }
@@ -1326,7 +1329,7 @@ DEFINE EXPR_INVALID 1100000000000000
##
## stream_top is maintained as a byte offset into streams[] (count * 40),
## matching the running-tail-pointer pattern used by source_end / macros_end.
-## Reads/writes: streams, stream_top. Leaf. Oracle: m1pp.c:push_stream_span.
+## Reads/writes: streams, stream_top. Leaf.
:push_stream_span
# new_top = stream_top + STREAM_SIZE; if (cap < new_top) fatal
la_t0 &stream_top
@@ -1355,7 +1358,7 @@ DEFINE EXPR_INVALID 1100000000000000
## current_stream() -> a0 = &streams[stream_top-1], or 0 if empty. Leaf.
## stream_top is a byte offset, so &streams[top-1] = streams + stream_top - 40.
-## Reads: streams, stream_top. Oracle: m1pp.c:current_stream.
+## Reads: streams, stream_top.
:current_stream
la_a0 &stream_top
ld_t0,a0,0
@@ -1373,7 +1376,7 @@ DEFINE EXPR_INVALID 1100000000000000
## pop_stream() -> void. Leaf.
## Decrement stream_top. If the popped stream's pool_mark >= 0, restore
## pool_used = pool_mark (reclaim the expansion-pool space it used).
-## Reads/writes: streams, stream_top, pool_used. Oracle: m1pp.c:pop_stream.
+## Reads/writes: streams, stream_top, pool_used.
:pop_stream
la_a0 &stream_top
ld_t0,a0,0
@@ -1400,7 +1403,6 @@ DEFINE EXPR_INVALID 1100000000000000
## Append each 24-byte Token in [start, end) to expand_pool at pool_used,
## advancing pool_used accordingly.
## Reads/writes: expand_pool, pool_used. Leaf.
-## Oracle: m1pp.c:copy_span_to_pool.
:copy_span_to_pool
:cstp_loop
# if (start == end) done
@@ -1441,7 +1443,6 @@ DEFINE EXPR_INVALID 1100000000000000
## Otherwise push_stream_span(expand_pool+mark, expand_pool+pool_used, mark).
## Reads/writes: expand_pool, pool_used, streams, stream_top. Non-leaf:
## needs a frame so the call to push_stream_span doesn't clobber LR.
-## Oracle: m1pp.c:push_pool_stream_from_mark.
:push_pool_stream_from_mark
enter_0
# if (pool_used == mark) return
@@ -1463,7 +1464,7 @@ DEFINE EXPR_INVALID 1100000000000000
ret
## ============================================================================
-## --- Phase 4 STUB: argument parsing ------------------------------------------
+## --- Argument parsing -------------------------------------------------------
## ============================================================================
## parse_args(a0=lparen_tok, a1=limit_tok) -> void (fatal on unterminated/overflow)
@@ -1478,7 +1479,6 @@ DEFINE EXPR_INVALID 1100000000000000
## call_end_pos = one past the closing RPAREN
##
## Fatal on: > 16 args, reaching limit without matching RPAREN.
-## Oracle: m1pp.c:parse_args.
:parse_args
# tok = lparen + 1; arg_start = tok; depth = 1; arg_index = 0
addi_a0,a0,24
@@ -1648,13 +1648,13 @@ DEFINE EXPR_INVALID 1100000000000000
b
## ============================================================================
-## --- Phase 5 STUBS: macro lookup + call expansion ----------------------------
+## --- Macro lookup + call expansion ------------------------------------------
## ============================================================================
## find_macro(a0=tok) -> a0 = Macro* or 0. Leaf.
## Non-zero only if tok is TOK_WORD, text.len >= 2, text[0] == '%', and
## (text+1, len-1) equals macros[i].name for some i. First match wins.
-## Reads: macros, macros_end. Oracle: m1pp.c:find_macro.
+## Reads: macros, macros_end.
:find_macro
# if (tok.kind != TOK_WORD) return 0
ld_a1,a0,0
@@ -1731,7 +1731,6 @@ DEFINE EXPR_INVALID 1100000000000000
## find_param(a0=macro_ptr, a1=tok) -> a0 = (index+1) or 0. Leaf.
## Linear search over macro->params[0..param_count). Non-WORD tok -> 0, so
## callers can test the return against zero without pre-filtering.
-## Oracle: m1pp.c:find_param.
:find_param
# if (tok.kind != TOK_WORD) return 0
ld_a2,a1,0
@@ -1826,7 +1825,6 @@ DEFINE EXPR_INVALID 1100000000000000
## copy_arg_tokens_to_pool(a0=arg_start, a1=arg_end) -> void (fatal if empty)
## Non-leaf (calls copy_span_to_pool). Empty arg is an error.
-## Oracle: m1pp.c:copy_arg_tokens_to_pool.
:copy_arg_tokens_to_pool
enter_0
# if (arg_start == arg_end) fatal
@@ -1839,7 +1837,6 @@ DEFINE EXPR_INVALID 1100000000000000
## copy_paste_arg_to_pool(a0=arg_start, a1=arg_end) -> void (fatal unless len 1)
## Enforces the single-token-argument rule for params adjacent to ##.
-## Oracle: m1pp.c:copy_paste_arg_to_pool.
:copy_paste_arg_to_pool
enter_0
# if ((arg_end - arg_start) != 24) fatal
@@ -1864,7 +1861,6 @@ DEFINE EXPR_INVALID 1100000000000000
## emt_after_pos = token one past the matching ')' (= call_end_pos)
## emt_mark = pool_used as of entry (start of expansion slice)
##
-## Oracle: m1pp.c:expand_macro_tokens.
:expand_macro_tokens
enter_0
@@ -1905,10 +1901,8 @@ DEFINE EXPR_INVALID 1100000000000000
la_br &err_bad_macro_header
bne_t0,t1
- # Snapshot call_end_pos -> emt_after_pos now (matches C oracle: end_pos
- # is captured before the body walk, which is technically safe in this
- # implementation but spares us a re-read in case future helpers write
- # call_end_pos).
+ # Snapshot call_end_pos -> emt_after_pos before the body walk, so
+ # nothing in the substitution loop can clobber the resume position.
la_a0 &call_end_pos
ld_t0,a0,0
la_a1 &emt_after_pos
@@ -2108,7 +2102,6 @@ DEFINE EXPR_INVALID 1100000000000000
## Calls expand_macro_tokens for the call at stream->pos, sets
## stream->pos = emt_after_pos, stream->line_start = 0, and
## push_pool_stream_from_mark(emt_mark) to rescan the expansion.
-## Oracle: m1pp.c:expand_call.
:expand_call
enter_8
@@ -2146,17 +2139,15 @@ DEFINE EXPR_INVALID 1100000000000000
ret
## ============================================================================
-## --- Phase 6 STUBS: ## token paste compaction --------------------------------
+## --- ## token paste compaction ----------------------------------------------
## ============================================================================
## append_pasted_token(a0=dst_tok, a1=left_tok, a2=right_tok) -> void (fatal)
## Concatenate left->text and right->text into paste_scratch, then call
## append_text(&paste_scratch, total_len) for stable storage in text_buf,
-## and write *dst = { TOK_WORD, text_ptr, total_len }. The C oracle uses a
-## 512-byte tmp buffer; we use 256 to fit M0's quoted-literal cap. Fatal
-## (err_text_overflow) if combined length exceeds 256 bytes; append_text
-## handles its own text_buf overflow check.
-## Oracle: m1pp.c:append_pasted_token.
+## and write *dst = { TOK_WORD, text_ptr, total_len }. paste_scratch is
+## 256 bytes (M0's quoted-literal cap). Fatal err_text_overflow if combined
+## length exceeds 256 bytes; append_text handles its own text_buf overflow.
:append_pasted_token
enter_0
@@ -2257,7 +2248,6 @@ DEFINE EXPR_INVALID 1100000000000000
## 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.
-## Oracle: m1pp.c:paste_pool_range.
:paste_pool_range
enter_0
@@ -2393,14 +2383,13 @@ DEFINE EXPR_INVALID 1100000000000000
ret
## ============================================================================
-## --- Phase 7 STUBS: integer atoms + S-expression evaluator -------------------
+## --- Integer atoms + S-expression evaluator ---------------------------------
## ============================================================================
## parse_int_token(a0=tok) -> a0 = i64 (fatal on bad). Leaf.
-## Accepts decimal (optional leading '-') and 0x-prefixed hex. For positive
-## inputs the oracle goes through strtoull and reinterprets as i64, so u64
-## values with the high bit set wrap to negative i64.
-## Oracle: m1pp.c:parse_int_token.
+## Accepts decimal (optional leading '-') and 0x-prefixed hex. Positive
+## values are accumulated as u64 and reinterpreted as i64, so values with
+## the high bit set wrap to negative i64.
##
## Register usage (leaf, no calls):
## t0 = src ptr (cursor into text)
@@ -2558,12 +2547,10 @@ DEFINE EXPR_INVALID 1100000000000000
:pit_done
ret
-## expr_op_code(a0=tok) -> a0 = EXPR_ADD..EXPR_GE, or EXPR_INVALID. Leaf.
+## expr_op_code(a0=tok) -> a0 = EXPR_ADD..EXPR_GE, or EXPR_INVALID.
## Accepts operator tokens: + - * / % << >> & | $ ~ = == !=
-## < <= > >=. Note: the oracle spells XOR as "$" (not "^"). Keep parity
-## with the oracle unless docs/M1M-IMPL.md is updated. Non-WORD tok or
-## unknown operator -> EXPR_INVALID.
-## Oracle: m1pp.c:expr_op_code.
+## < <= > >=. XOR is spelled `$`, not `^`. Non-WORD tok or unknown
+## operator -> EXPR_INVALID.
##
## tok_eq_const is a leaf but clobbers a0..a3,t0..t2; spill tok to eoc_tok
## once, reload before each compare. Needs an enter_0 frame because it
@@ -2668,7 +2655,7 @@ DEFINE EXPR_INVALID 1100000000000000
la_br &eoc_or
bnez_a0
- # "$" -> EXPR_XOR (oracle spells xor as "$")
+ # "$" -> EXPR_XOR (XOR is spelled `$`, not `^`)
la_a0 &eoc_tok
ld_a0,a0,0
la_a1 &op_dollar
@@ -2832,14 +2819,14 @@ DEFINE EXPR_INVALID 1100000000000000
ret
## apply_expr_op(a0=op_code, a1=args_ptr, a2=argc) -> a0 = i64 result
-## Reduce args[0..argc) per op (see docs/M1M-IMPL.md "Operators"):
+## Reduce args[0..argc) per op:
## + * & | $ variadic, argc >= 1
## - argc >= 1 (argc == 1 is negate, else left-assoc subtract)
## / % binary, div-by-zero fatal
## << >> binary (>> is arithmetic)
## ~ unary
## = == != < <= > >= binary
-## Fatal on wrong argc or EXPR_INVALID. Oracle: m1pp.c:apply_expr_op.
+## Fatal on wrong argc or EXPR_INVALID.
##
## Calls aeo_require_* helpers via `call`, so it needs a frame.
## State held in BSS scratch (aeo_op/args/argc/acc/i) since loops trash registers.
@@ -3322,7 +3309,7 @@ DEFINE EXPR_INVALID 1100000000000000
## skip_expr_newlines(a0=pos, a1=end) -> a0 = new pos. Leaf.
## Advance pos past consecutive TOK_NEWLINE tokens so expressions may span
-## lines. Oracle: m1pp.c:skip_expr_newlines.
+## lines.
:skip_expr_newlines
:sen_loop
# if (pos == end) done
@@ -3355,7 +3342,6 @@ DEFINE EXPR_INVALID 1100000000000000
## CAVEAT: this path can recurse through eval_expr_range. Callers MUST
## snapshot eval_after_pos / eval_value into local stack slots (via
## enter_N) before any further call that might overwrite them.
-## Oracle: m1pp.c:eval_expr_atom.
##
## Stack-local layout (enter_40):
## sp+16 saved tok
@@ -3459,14 +3445,14 @@ DEFINE EXPR_INVALID 1100000000000000
ret
## eval_expr_range(a0=start_tok, a1=end_tok) -> a0 = i64 result (fatal on bad)
-## Main S-expression evaluator loop, driven by an explicit ExprFrame stack
-## (expr_frames[], expr_frame_top) — NOT by P1 recursion. See
-## docs/M1M-IMPL.md "Layer 7: expression evaluator" for the step-by-step
-## loop. Enforces exactly one top-level value and no trailing tokens.
+## Main S-expression evaluator loop, driven by the explicit ExprFrame stack
+## in expr_frames[] / expr_frame_top — NOT by P1 recursion (eval_expr_atom
+## can re-enter eval_expr_range through expand_macro_tokens, and a P1
+## recursion would defeat the bounded frame budget). Enforces exactly one
+## top-level value and no trailing tokens.
## Fatal on: unmatched parens, > 16 frames deep, > 16 args per frame,
## bad atom, bad operator.
## Reads/writes: expr_frames, expr_frame_top.
-## Oracle: m1pp.c:eval_expr_range.
##
## Stack-local layout (enter_56):
## sp+16 pos Token*
@@ -3700,7 +3686,7 @@ DEFINE EXPR_INVALID 1100000000000000
ret
## ============================================================================
-## --- Phase 8 STUB: hex emit for !@%$ -----------------------------------------
+## --- Hex emit for !@%$ ------------------------------------------------------
## ============================================================================
## emit_hex_value(a0=value_u64, a1=byte_count) -> void (fatal on overflow)
@@ -3710,7 +3696,6 @@ DEFINE EXPR_INVALID 1100000000000000
## treats it as a hex-byte string literal rather than parsing it as a
## decimal numeric token. Total emitted text length = 2 + 2 * byte_count;
## emitted as a TOK_STRING via append_text + emit_token.
-## Oracle: m1pp.c:emit_hex_value.
:emit_hex_value
enter_0
@@ -3822,20 +3807,20 @@ DEFINE EXPR_INVALID 1100000000000000
ret
## ============================================================================
-## --- Phase 8-9 STUB: builtin dispatcher ( ! @ % $ %select ) ------------------
+## --- Builtin dispatcher ( ! @ % $ %select ) ---------------------------------
## ============================================================================
## expand_builtin_call(a0=stream_ptr, a1=builtin_tok) -> void (fatal on bad)
## Requires builtin_tok+1 is TOK_LPAREN. Runs parse_args(lparen, stream->end),
## then dispatches on builtin_tok->text:
##
-## "!" "@" "%" "$" [Phase 8]
+## "!" "@" "%" "$"
## require arg_count == 1
## eval_expr_range(arg_starts[0], arg_ends[0]) -> value
## stream->pos = call_end_pos; stream->line_start = 0
## emit_hex_value(value, 1 / 2 / 4 / 8 respectively)
##
-## "%select" [Phase 9]
+## "%select"
## require arg_count == 3
## eval_expr_range(cond_arg) -> value
## chosen = (value != 0) ? arg1 : arg2
@@ -3845,7 +3830,6 @@ DEFINE EXPR_INVALID 1100000000000000
## The unchosen branch is NOT evaluated, validated, or expanded.
##
## Any other text under a builtin slot -> fatal "bad builtin".
-## Oracle: m1pp.c:expand_builtin_call.
:expand_builtin_call
enter_0
@@ -3881,12 +3865,10 @@ DEFINE EXPR_INVALID 1100000000000000
la_a1 &ebc_call_end_pos
st_t0,a1,0
- # dispatch on builtin_tok->text. We've lost a1 (builtin_tok) across calls,
- # so reload from stream: builtin_tok = lparen - 24 = (parse_args's a0 input)
- # — easier path: builtin_tok was passed in originally; it's not snapshotted.
- # Solution: snapshot builtin_tok at function entry too (but we don't have a
- # BSS slot for it). We DO know stream->pos still points at the builtin_tok
- # because expand_builtin_call hasn't moved it yet. Use that.
+ # dispatch on builtin_tok->text. a1 (builtin_tok) is gone after parse_args,
+ # but stream->pos still points at the builtin token (we don't advance it
+ # until the dispatched branch sets stream->pos = call_end_pos), so reload
+ # builtin_tok from stream->pos.
la_a0 &ebc_stream
ld_a0,a0,0
ld_t0,a0,16 # stream->pos -> builtin_tok
@@ -4263,9 +4245,9 @@ DEFINE EXPR_INVALID 1100000000000000
:const_select "%select"
## Operator strings for expr_op_code. Each is a raw byte literal; lengths
-## are passed separately to tok_eq_const. Note: XOR is "$" (oracle parity),
-## not "^". "==" must be tested before "=" so the longer match wins; same
-## for "<=" before "<" and ">=" before ">".
+## are passed separately to tok_eq_const. XOR is "$", not "^".
+## "==" must be tested before "=" so the longer match wins; same for
+## "<=" before "<" and ">=" before ">".
:op_plus "+"
:op_minus "-"
:op_star "*"
@@ -4359,9 +4341,9 @@ ZERO8
:err_saved_len
ZERO8
-## Phase 3+ scalars. Each is one u64 (ZERO8).
+## Stream / pool / arg / expression scalars. Each is one u64 (ZERO8).
## pool_used — byte offset into expand_pool (i.e. next write slot).
-## stream_top — stream stack depth (0 == empty).
+## stream_top — stream stack depth in bytes (count × 40; 0 == empty).
## arg_count — number of args produced by the most recent parse_args.
## call_end_pos — Token* one past the ')' of that call.
## expr_frame_top — ExprFrame stack depth inside eval_expr_range.
@@ -4389,9 +4371,9 @@ ZERO8
:eval_value
ZERO8
-## Phase 6 paste-pass spill slots. Both append_pasted_token and
-## paste_pool_range call other functions, so all locals must round-trip
-## through BSS across the call.
+## Paste-pass spill slots. Both append_pasted_token and paste_pool_range
+## call other functions, so all locals must round-trip through BSS
+## across the call.
## paste_dst_save — dst Token* spilled across append_text
## paste_left_ptr/_len, paste_right_ptr/_len — operand spans for the
## byte-copy loops in append_pasted_token
@@ -4425,16 +4407,14 @@ ZERO8
## paste_scratch — 256-byte working buffer for append_pasted_token.
## We assemble left.text ++ right.text here, then call
## append_text(&paste_scratch, total_len) to copy into the durable
-## text_buf arena. The cap is the M0 quoted-literal limit; the C oracle
-## uses 512.
+## text_buf arena. 256 bytes is M0's quoted-literal cap.
:paste_scratch
ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32
-## Track C scalars: parse_args + expand_macro_tokens + find_param locals
-## (P1v2 has no callee-save spill on enter, and find_param's inner byte
-## compare needs every caller-saved register; parse_args + expand_macro_tokens
-## write the parse_args output globals across iterations and across calls).
-## One u64 each (ZERO8).
+## parse_args + expand_macro_tokens + find_param spill slots (P1v2 has
+## no callee-save spill on enter, and find_param's inner byte compare
+## needs every caller-saved register; parse_args + expand_macro_tokens
+## carry state across iterations and nested calls). One u64 each (ZERO8).
:pa_pos
ZERO8
:pa_arg_start
@@ -4466,10 +4446,10 @@ ZERO8
:fp_idx
ZERO8
-## Phase 7 scratch slots (leaf-call BSS scratch — see expr_op_code and
-## apply_expr_op). expr_op_code spills its tok argument to eoc_tok across
-## tok_eq_const calls. apply_expr_op spills op/args/argc and uses acc/i
-## as the accumulator and loop induction var inside the variadic folds.
+## Expression-evaluator scratch slots. expr_op_code spills its tok
+## argument to eoc_tok across tok_eq_const calls. apply_expr_op spills
+## op/args/argc and uses acc/i as the accumulator and loop induction var
+## inside the variadic folds.
:eoc_tok
ZERO8
:aeo_op
@@ -4483,7 +4463,7 @@ ZERO8
:aeo_i
ZERO8
-## Layer 8 (Phase 8/9) scratch.
+## 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