boot2

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

commit bbea27bd4c29996f1dc1ebc94d3ab798e4f9e731
parent c506d1cf9a1cfb2cffd0944f4a216b878d5c7940
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed, 29 Apr 2026 12:39:00 -0700

tests/cc: extended vararg test

Diffstat:
Atests/cc/131-vararg-mixed.c | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atests/cc/131-vararg-mixed.expected-exit | 1+
2 files changed, 116 insertions(+), 0 deletions(-)

diff --git a/tests/cc/131-vararg-mixed.c b/tests/cc/131-vararg-mixed.c @@ -0,0 +1,115 @@ +/* Variadic recv across mixed types and printf-style forwarding. + * + * Existing vararg coverage (015/067/076/079/097/116) only pulls `int` + * via __builtin_va_arg from the same function that called va_start. + * Real callers (printf-shaped) interleave pointers and 64-bit ints, + * AND forward `va_list` across function boundaries + * (printf -> vprintf -> vfprintf). cg-va-arg always loads 8 bytes + * and lets the caller cast, and doesn't require the enclosing + * function to be variadic — but that path was untested. + * + * This test pins down: + * - va_arg(ap, long) — full 64-bit value survives (past INT_MAX) + * - va_arg(ap, char *) — pointer round-trips intact + * - alternating types in one call + * - va_list passed as a parameter to a non-variadic helper which + * then calls va_arg (the vfprintf shape) + * - two-level forwarding: variadic wrapper -> v-helper -> reader + * (the printf -> vprintf -> vfprintf shape) + */ + +typedef char *va_list; + +static int streq(char *a, char *b) { + while (*a && *b) { + if (*a != *b) return 0; + a = a + 1; + b = b + 1; + } + return *a == *b; +} + +/* Each "record" is (long, char *, int). */ +static long check(int n, ...) { + va_list ap; + long sum = 0; + int i = 0; + long lv; + char *sv; + int iv; + __builtin_va_start(ap, n); + while (i < n) { + lv = __builtin_va_arg(ap, long); + sv = __builtin_va_arg(ap, char *); + iv = __builtin_va_arg(ap, int); + if (!streq(sv, "ok")) return -1; + sum = sum + lv + iv; + i = i + 1; + } + __builtin_va_end(ap); + return sum; +} + +/* Non-variadic reader. Receives an already-initialized va_list and a + * format-shaped char* + count. Mirrors vfprintf: ap was set up in a + * caller's frame, this function only consumes it. */ +static long vsum(char *tag, int n, va_list ap) { + long total = 0; + int i = 0; + if (!streq(tag, "go")) return -1; + while (i < n) { + total = total + __builtin_va_arg(ap, int); + i = i + 1; + } + return total; +} + +/* Mid-layer forwarder. Non-variadic; takes ap by value and hands it + * down. Mirrors vprintf -> vfprintf. */ +static long vsum_fwd(char *tag, int n, va_list ap) { + return vsum(tag, n, ap); +} + +/* Top-level variadic wrapper: va_start then forward through two + * non-variadic frames before any va_arg runs. Mirrors printf. */ +static long psum(char *tag, int n, ...) { + va_list ap; + long r; + __builtin_va_start(ap, n); + r = vsum_fwd(tag, n, ap); + __builtin_va_end(ap); + return r; +} + +int main(int argc, char **argv) { + /* --- Mixed-type direct va_arg (long / char* / int) ----------- */ + /* Two records. Longs chosen so the sum exceeds INT_MAX + * (2^31 - 1 = 2147483647); a 32-bit truncation in va_arg would + * corrupt the total. */ + long r = check(2, + 1000000000L, "ok", 5, + 2000000000L, "ok", 7); + if (r == -1) return 1; /* pointer arg lost or corrupted */ + if (r != 3000000012L) return 2; /* long arg truncated */ + + /* Single record, distinct pointer to confirm we don't latch the + * literal across calls. */ + long r2 = check(1, 4000000000L, "ok", 1); + if (r2 == -1) return 3; + if (r2 != 4000000001L) return 4; + + /* --- printf-style two-level va_list forwarding --------------- */ + /* psum -> vsum_fwd -> vsum. va_arg only runs in vsum, on an ap + * that was initialized two frames up. The single-int case + * matches printf("got %d\n", 42) at the ABI level. */ + long s1 = psum("go", 1, 42); + if (s1 != 42) return 5; + + /* Multi-arg forwarding to confirm ap advances correctly across + * frames (each va_arg increments the local copy; the originator's + * frame is irrelevant once values are read). */ + long s2 = psum("go", 4, 10, 20, 30, 40); + if (s2 != 100) return 6; + + return 0; +} diff --git a/tests/cc/131-vararg-mixed.expected-exit b/tests/cc/131-vararg-mixed.expected-exit @@ -0,0 +1 @@ +0