boot2

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

commit 1d5aeed1c3804b5b2140324a64c89dd437a4cc33
parent a27e22c08bc27e3140f0cdc334e852447ab5c92e
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Fri,  1 May 2026 19:55:41 -0700

merge: cap variadic args at 16 + boundary tests

Diffstat:
Mcc/cc.scm | 42+++++++++++++++++++++++++++++++++++-------
Atests/cc/320-vararg-cap.c | 36++++++++++++++++++++++++++++++++++++
Atests/cc/320-vararg-cap.expected-exit | 1+
Atests/cc/321-vararg-mixed-many.c | 35+++++++++++++++++++++++++++++++++++
Atests/cc/321-vararg-mixed-many.expected-exit | 1+
5 files changed, 108 insertions(+), 7 deletions(-)

diff --git a/cc/cc.scm b/cc/cc.scm @@ -2958,6 +2958,13 @@ (bv-cat (list "%st(a0, sp, " (%cg-slot-expr cg ss) ")\n"))))) (else (%cg-fn-set! cg '%fn-sret-slot #f)))) + ;; Variadic save area is capped at 16 incoming-arg slots; reject + ;; variadic definitions whose named-arg count would already fill or + ;; exceed it (no room left for variadic reads). + (cond + ((and variadic? (> (length params) 16)) + (die #f "cg-fn-begin: variadic function exceeds 16-arg save-area cap" + name (length params)))) ;; With sret, explicit arg i lives at ABI position (i+1): args 0..2 ;; in a1..a3, args 3+ in slot (i-3). (let* ((sret-shift (if (%cg-fn-get cg '%fn-sret?) 1 0)) @@ -3708,6 +3715,24 @@ (sret? (and has-result? (or (eq? rk 'struct) (eq? rk 'union)) (> (ctype-size rty) 16))) + ;; If the callee is variadic, the callee's save area caps total + ;; incoming args at 16. Reject silent miscompiles up front. + (callee-fty (cond + ((eq? (ctype-kind fty) 'fn) fty) + ((and (eq? (ctype-kind fty) 'ptr) + (eq? (ctype-kind (ctype-ext fty)) 'fn)) + (ctype-ext fty)) + (else #f))) + (callee-variadic? (and callee-fty + (let ((ext (ctype-ext callee-fty))) + (and (pair? ext) (pair? (cdr ext)) + (pair? (cddr ext)) + (car (cddr ext)))))) + (_cap-check (cond + ((and callee-variadic? (> arity 16)) + (die #f "cg-call: variadic call exceeds 16-arg save-area cap" + arity)) + (else 0))) (sret-shift (if sret? 1 0)) (recv-slot (cond (sret? @@ -3858,19 +3883,22 @@ (%cg-emit-many cg (list "%continue(" tag ")\n"))) ;; -------------------------------------------------------------------- -;; Variadic receive (§G.2). Layout: cg-fn-begin/v reserves a 4-slot -;; saved-register area at known frame offsets; va_start sets ap to the -;; address of the first slot past the named-arg count; va_arg reads -;; *ap, advances ap by 8, and pushes the value as the requested type. +;; Variadic receive (§G.2). Layout: cg-fn-begin/v reserves a 16-slot +;; (8 bytes each) save area at known frame offsets, populating each +;; slot from the appropriate ABI source — a-register for indices 0..3, +;; LDARG for indices 4..15. va_start sets ap to the address of the +;; first slot past the named-arg count; va_arg reads *ap, advances ap +;; by 8, and pushes the value as the requested type. ;; ;; ap is an lval (typically a `va_list` local). cg-va-start pops it, ;; computes the address, stores into *ap (or the slot directly), and ;; pushes nothing. cg-va-arg pops ap-lval, loads ap, dereferences for ;; the value, advances ap, stores back, pushes the loaded value. ;; -;; Limitation: only first 4 incoming args (named + variadic) live in -;; the save area; variadic args at index >= 4 need LDARG and are not -;; yet supported. +;; Cap: total incoming args (named + variadic) must fit in the 16-slot +;; save area. Variadic call sites exceeding this die in cg-call; +;; variadic definitions whose named-arg count exceeds it die in +;; cg-fn-begin/v. ;; -------------------------------------------------------------------- (define (%cg-vararg-first-slot cg) (let ((s (%cg-fn-get cg '%fn-vararg-first-slot))) diff --git a/tests/cc/320-vararg-cap.c b/tests/cc/320-vararg-cap.c @@ -0,0 +1,36 @@ +/* Variadic save area is capped at 16 incoming-arg slots (named + + * variadic). cg should reject calls that exceed this cap with a clear + * compile-time error rather than silently miscompile by writing past + * the save area in the callee's prologue. + * + * This test exercises the boundary: 1 named + 15 variadic = 16 args + * total, which is the maximum supported. Sum 1..15 = 120. */ + +#ifndef CCSCM +#include <stdarg.h> +#else +typedef char *va_list; +#define va_start(ap, n) __builtin_va_start(ap, n) +#define va_arg(ap, t) __builtin_va_arg(ap, t) +#define va_end(ap) __builtin_va_end(ap) +#endif + +int sum(int n, ...) { + va_list ap; + int total; + int i; + va_start(ap, n); + total = 0; + i = 0; + while (i < n) { + total = total + va_arg(ap, int); + i = i + 1; + } + va_end(ap); + return total; +} + +int main(void) { + /* 15 variadic ints; n itself is named arg 0; total 16 args. */ + return sum(15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); +} diff --git a/tests/cc/320-vararg-cap.expected-exit b/tests/cc/320-vararg-cap.expected-exit @@ -0,0 +1 @@ +120 diff --git a/tests/cc/321-vararg-mixed-many.c b/tests/cc/321-vararg-mixed-many.c @@ -0,0 +1,35 @@ +/* Mixed-boundary variadic: 2 named + several variadic spanning the + * a-register / LDARG boundary. Probes that: + * - named arg 0 (lives in a0/save-slot 0) is preserved + * - named arg 1 (lives in a1/save-slot 1) is preserved + * - variadic args at idx >= 4 (LDARG-loaded) are read correctly + * + * Sum = base + tag + 1+2+3+4+5+6 = 100 + 7 + 21 = 128. */ + +#ifndef CCSCM +#include <stdarg.h> +#else +typedef char *va_list; +#define va_start(ap, n) __builtin_va_start(ap, n) +#define va_arg(ap, t) __builtin_va_arg(ap, t) +#define va_end(ap) __builtin_va_end(ap) +#endif + +int sum_with(int base, int n, ...) { + va_list ap; + int total; + int i; + va_start(ap, n); + total = base; + i = 0; + while (i < n) { + total = total + va_arg(ap, int); + i = i + 1; + } + va_end(ap); + return total; +} + +int main(void) { + return sum_with(100, 7, 1, 2, 3, 4, 5, 6, 7); +} diff --git a/tests/cc/321-vararg-mixed-many.expected-exit b/tests/cc/321-vararg-mixed-many.expected-exit @@ -0,0 +1 @@ +128