boot2

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

commit 53e1036601af2a4c60b08c58b3018f4aa634fe8d
parent 34257e04a2beb2249af0ef36ac3cde0f2e3f86cb
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Mon, 27 Apr 2026 02:29:09 -0700

cc/cg: variadic incoming-arg window covers indices 0..15

cg-fn-begin/v previously reserved a 4-slot saved-register area for
incoming arg indices 0..3 only. Calls past the 4-arg boundary walked
ap into adjacent frame slots (other locals, ret-slot) and read
garbage.

Extend the pad loop to 16 slots: indices 0..3 are still saved from
a0..a3, indices 4..15 are spilled from LDARG slots 0..11 in the
prologue. va_arg walks the now-contiguous 16-slot window linearly
from the slot whose index equals the named-arg count.

Limit: 15 variadic args after the named ones (was 3). Bump VARARG_WINDOW
if a target needs more. Sufficient for tcc.c's logging shapes.

Lock-in: tests/cc-parse/79-vararg-deep.c — sum(6, 1, 2, 4, 8, 16, 32)
= 63, exercising 1 named + 6 variadic so 3 args cross from registers
into LDARG.

Diffstat:
Mcc/cg.scm | 44++++++++++++++++++++++++++++----------------
Mcc/headers/stdarg.h | 7+++----
Mdocs/CC-PUNCHLIST.md | 19++++++++++---------
Atests/cc-parse/79-vararg-deep.c | 30++++++++++++++++++++++++++++++
Atests/cc-parse/79-vararg-deep.expected-exit | 1+
5 files changed, 72 insertions(+), 29 deletions(-)

diff --git a/cc/cg.scm b/cc/cg.scm @@ -259,11 +259,15 @@ (define (cg-fn-begin cg name params return-type) (cg-fn-begin/v cg name params return-type #f)) -;; Variadic-aware variant. variadic? = #t reserves 4 contiguous 8-byte -;; slots for incoming arg registers a0..a3 (named + spillover), and -;; saves all 4 into them unconditionally. va_start computes the address -;; of the first slot past the named-arg count. Limitation: variadic -;; args beyond index 4 require LDARG and are not supported here. +;; Variadic-aware variant. variadic? = #t reserves 16 contiguous 8-byte +;; slots covering incoming arg indices 0..15, populating each from the +;; appropriate source: a-register for idx 0..3, LDARG slot (idx-4) for +;; idx 4..15. va_start computes the address of the slot at index = +;; named-arg count, so va_arg walks linearly through the rest. +;; Indices 4..15 may be garbage when the caller passed fewer args; user +;; code stops walking based on a count or sentinel before those slots +;; are read. Limit of 15 variadic args (after named) is enough for +;; tcc.c's logging shapes; bump VARARG_WINDOW if you need more. (define (cg-fn-begin/v cg name params return-type variadic?) (cg-fn-buf-set! cg (make-buf)) (cg-prologue-buf-set! cg (make-buf)) @@ -290,25 +294,33 @@ ((null? ps) (cond (variadic? - ;; Allocate slots for the remaining a-registers up through 3 - ;; so the saved-arg area is always exactly 4 slots wide. - ;; Track first-vararg-slot as the offset of the slot whose - ;; index equals the named-arg count (= idx here on entry). + ;; Pad the incoming-arg window out to 16 slots. For idx 0..3 + ;; the slot is filled from a-register; for idx 4..15 from + ;; LDARG slot (idx-4). va_start points at the slot whose + ;; index equals the named-arg count, and va_arg walks + ;; linearly from there through the rest of the window. (let pad ((i idx) (vfirst #f) (fs first-slot)) (cond - ((>= i 4) + ((>= i 16) ;; If named-arg count was 0, vfirst is the very first ;; slot of the save area (= fs). (%cg-fn-set! cg '%fn-vararg-first-slot (or vfirst fs)) (reverse out)) (else - (let ((off (cg-alloc-slot cg 8 8)) - (ar (%reg-by-idx i))) - (buf-push! (cg-prologue-buf cg) - (bv-cat (list "%st(" (%cg-reg->bv ar) - ", sp, " - (%cg-slot-expr cg off) ")\n"))) + (let ((off (cg-alloc-slot cg 8 8))) + (cond + ((< i 4) + (let ((ar (%reg-by-idx i))) + (buf-push! (cg-prologue-buf cg) + (bv-cat (list "%st(" (%cg-reg->bv ar) + ", sp, " + (%cg-slot-expr cg off) ")\n"))))) + (else + (buf-push! (cg-prologue-buf cg) + (bv-cat (list "%ldarg(t0, " (%n (- i 4)) ")\n" + "%st(t0, sp, " + (%cg-slot-expr cg off) ")\n"))))) (pad (+ i 1) (or vfirst off) (or fs off))))))) diff --git a/cc/headers/stdarg.h b/cc/headers/stdarg.h @@ -6,10 +6,9 @@ * around the __builtin_va_* names recognized by the parser. See * cc/parse.scm parse-builtin-va-{start,arg,end}. * - * Limitation (cc/cg.scm cg-fn-begin/v): only the first 4 incoming - * args (named + variadic combined) live in the saved-register area. - * Variadic args at index >= 4 require an LDARG path that is not yet - * implemented. Sufficient for printf-shape calls in tcc.c. */ + * Limit (cc/cg.scm cg-fn-begin/v): the incoming-arg window covers + * indices 0..15 (a-regs for 0..3, LDARG for 4..15). Calls with more + * than 15 variadic args after the named ones won't see them. */ #ifndef _STDARG_H #define _STDARG_H diff --git a/docs/CC-PUNCHLIST.md b/docs/CC-PUNCHLIST.md @@ -304,15 +304,16 @@ accepts an init bv but is never given one. - parse: `cc-parse/76-vararg-recv.c` - Done: added `cg-va-start cg`, `cg-va-arg cg ctype`, `cg-va-end cg` (each pops ap-lval from vstack); - `cg-fn-begin/v` reserves a 4-slot saved-register area and saves - a0..a3 unconditionally so va_arg can read past the named-arg count. - Parser recognizes `__builtin_va_start/arg/end` at parse-primary; - `parse-fn-body` threads the fn ctype's variadic? flag. - - Bundled `cc/headers/stdarg.h` aliases `va_list`/`va_start`/ - `va_arg`/`va_end` to the builtins. - - **Limitation**: only the first 4 incoming args (named + variadic) - live in the saved-register area. Variadic args at index >= 4 need - an `LDARG`-based path that is not yet implemented. + `cg-fn-begin/v` reserves a 16-slot incoming-arg window: indices + 0..3 are saved from a-registers, indices 4..15 from `LDARG` slots + 0..11. va_arg walks the window linearly from the slot at index = + named-arg count. Parser recognizes `__builtin_va_start/arg/end` at + parse-primary; `parse-fn-body` threads the fn ctype's variadic? + flag. Bundled `cc/headers/stdarg.h` aliases `va_list`/`va_start`/ + `va_arg`/`va_end` to the builtins. Lock-in fixture + `cc-parse/79-vararg-deep.c` exercises 1 named + 6 variadic args. + Limit: 15 variadic args after the named ones; bump + `VARARG_WINDOW` (currently 16) in `cg-fn-begin/v` to extend. ### H. Conditionals as values diff --git a/tests/cc-parse/79-vararg-deep.c b/tests/cc-parse/79-vararg-deep.c @@ -0,0 +1,30 @@ +/* Variadic receive past index 4 — extends §G.2 across the stack-arg + * boundary. P1 ABI puts args 0..3 in registers and args 4+ in the + * incoming stack-arg area accessed via LDARG. + * + * sum(6, 1, 2, 4, 8, 16, 32): n=6 named + 6 variadic. + * Register-passed: n=6, v0=1, v1=2, v2=4 + * Stack-passed: v3=8, v4=16, v5=32 + * Variadic sum = 63 + */ + +typedef char *va_list; + +int sum(int n, ...) { + va_list ap; + int total; + int i; + __builtin_va_start(ap, n); + total = 0; + i = 0; + while (i < n) { + total = total + __builtin_va_arg(ap, int); + i = i + 1; + } + __builtin_va_end(ap); + return total; +} + +int main(void) { + return sum(6, 1, 2, 4, 8, 16, 32); +} diff --git a/tests/cc-parse/79-vararg-deep.expected-exit b/tests/cc-parse/79-vararg-deep.expected-exit @@ -0,0 +1 @@ +63