kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit 9aa8d6e18cf373b4ec23486eeb96c824103b8997
parent a687ea88a10a85efa82c0d5caa8aebcded228003
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Tue, 16 Jun 2026 12:01:04 -0700

scripts: add coarse amalgamation cross-test lane

Diffstat:
Mdoc/plan/PORT.md | 27+++++++++++++++++----------
Mscripts/cross_test.sh | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Atest/cross/cases/c_frontback_amalgam.c | 249+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 335 insertions(+), 22 deletions(-)

diff --git a/doc/plan/PORT.md b/doc/plan/PORT.md @@ -84,8 +84,11 @@ Resolved centrally by `scripts/hosted.sh expand <selector> [--mode=cross|selfhos ### `DEPTH` -Uniform across both modes: +`test-cross` accepts: +- `coarse` — cross-compile and link the smoke artifacts across the expanded + support set, but do not execute them. This is the named form of + `make test-cross RUN=0`. - `smoke` (default) — two cases, building up platform coverage: 1. `exit` — an `#include`-free exit-code program (`test/cross/cases/exit.c`, returns 42): toolchain + link + crt + the exit-code path; on freestanding @@ -109,6 +112,9 @@ Uniform across both modes: (CLAUDE.md). `DEPTH=full` is the deliberate heavy run, usually with a scoped `TARGET`. +`test-selfhost` accepts `smoke` and `full`; it has no build-only `coarse` lane +because its purpose is to run the newly built compiler on the target. + ### `kit-cross` — cross-build the compiler itself `test-cross` asks "can kit cross-compile a *program* for a target?"; @@ -157,7 +163,8 @@ The Makefile reads it via `$(shell …)`; nothing else re-encodes the matrix. - `scripts/cross_test.sh <selector> [DEPTH]` — expand (mode=cross) → **fail-fast provisioning pre-flight** → compile/link all smoke artifacts → optional - execution phase (`RUN=0` / `KIT_CROSS_RUN=0` disables it) → aggregate report. + execution phase (`DEPTH=coarse`, `RUN=0`, or `KIT_CROSS_RUN=0` disables it) + → aggregate report. - `scripts/selfhost.sh <selector> [DEPTH]` — expand (mode=selfhost) → pre-flight → per-token self-host (dispatch by OS) → on-target corpus → report. @@ -237,14 +244,14 @@ compiled and ran a program there.* `cross`: -| Config | smoke | full | -|---|---|---| -| linux-{glibc,musl}-{aa64,x64,rv64} | ready | ready (toy X + parse E + libc) | -| macos-aarch64 | ready (native) | ready (native toy/parse) | -| freebsd-{aarch64,amd64,riscv64} | ready (VM) | ready (toy via VM) | -| windows-{aarch64,x64} | ready (VM) | ready (toy via VM) | -| freestanding-rv32 | ready (bare) | ready (toy X + parse E, bare) | -| freestanding-{aa64,x64,rv64} | ready (bare smoke) | **deferred** | +| Config | coarse | smoke | full | +|---|---|---|---| +| linux-{glibc,musl}-{aa64,x64,rv64} | ready | ready | ready (toy X + parse E + libc) | +| macos-aarch64 | ready | ready (native) | ready (native toy/parse) | +| freebsd-{aarch64,amd64,riscv64} | ready | ready (VM) | ready (toy via VM) | +| windows-{aarch64,x64} | ready | ready (VM) | ready (toy via VM) | +| freestanding-rv32 | ready | ready (bare) | ready (toy X + parse E, bare) | +| freestanding-{aa64,x64,rv64} | ready | ready (bare smoke) | **deferred** | `selfhost`: macos (native), linux musl/glibc aarch64 (container), linux x64/rv64 (container under emulation), freebsd aarch64/amd64 (VM), windows aa64/x64 (cross diff --git a/scripts/cross_test.sh b/scripts/cross_test.sh @@ -8,10 +8,11 @@ # cross_test.sh <selector> [DEPTH] # # selector hosted.sh grammar: all | <os> | linux-<libc> | <token> | a,b,c -# DEPTH smoke (default) — one hello program, build/link + optional run, +# DEPTH coarse — smoke cases plus compile+run front/back amalgam +# at -O0 and -O1 (execution follows RUN) +# smoke (default) — exit/hello smoke cases, build/link + optional run, # exit code (+ stdout) when execution is enabled -# full — also the toy + parse corpora (+ libc on linux), -# cross-compiled and run on the target +# full — coarse lane + toy/parse/libc corpora # # env: KIT (default build/kit), KIT_VM (default 1; 0 drops freebsd/windows), # KIT_CROSS_RUN (default 1; 0 disables execution), DEPTH (overridden by the @@ -33,11 +34,17 @@ DEPTH="${2:-${DEPTH:-smoke}}" export KIT_VM="${KIT_VM:-1}" KIT_CROSS_RUN="${KIT_CROSS_RUN:-${CROSS_RUN:-${RUN:-1}}}" case "$KIT_CROSS_RUN" in 0|false|FALSE|no|NO|off|OFF) KIT_CROSS_RUN=0 ;; *) KIT_CROSS_RUN=1 ;; esac +case "$DEPTH" in + coarse|smoke|full) ;; + *) echo "cross_test: bad DEPTH '$DEPTH' (want coarse|smoke|full)" >&2; exit 2 ;; +esac export KIT_CROSS_RUN [ -x "$KIT" ] || { echo "cross_test: kit not found at $KIT (run 'make bin')" >&2; exit 2; } mkdir -p "$BUILD_DIR" -RUN_LIST="$BUILD_DIR/.cross-run-list.$$" +RUN_LIST_SMOKE="$BUILD_DIR/.cross-run-list-smoke.$$" +RUN_LIST_COARSE="$BUILD_DIR/.cross-run-list-coarse.$$" +LIST_PATH="$RUN_LIST_SMOKE" # shellcheck source=../test/lib/kit_sh_report.sh . "$ROOT/test/lib/kit_sh_report.sh" @@ -117,8 +124,48 @@ smoke_build_token() { done } +coarse_build_token() { + local t="$1" os mode opt sfx cdir tag + os="$(tok_os "$t")" + tag="$($HOSTED tag "$t")" + cdir="$BUILD_DIR/$t"; mkdir -p "$cdir" + for opt in O0 O1; do + if [ "$os" = freestanding ]; then + local fobj="$cdir/c_frontback_amalgam.${opt}.o" + local out="$cdir/c_frontback_amalgam.${opt}.out" + local err="$cdir/c_frontback_amalgam.${opt}.err" + if ! "$HOSTED" cc "$t" -$opt -ffreestanding -c "$CASES/c_frontback_amalgam.c" -o "$fobj" \ + >"$cdir/c_frontback_amalgam.$opt.cc.out" 2>"$cdir/c_frontback_amalgam.$opt.cc.err"; then + kit_fail "frontback_amalgam/$t:$opt:cc" "cc failed"; sed 's/^/ | /' "$cdir/c_frontback_amalgam.$opt.cc.err" | head; return + fi + kit_pass "frontback_amalgam/$t:$opt:cc" + run_list_add target "frontback_amalgam/$t:$opt" "$tag" "$fobj" "$out" "$err" 0 - + else + for mode in $(link_modes "$t"); do + case "$mode" in static|dynamic) sfx=".$mode" ;; *) sfx="" ;; esac + local ext=""; [ "$os" = windows ] && ext=.exe + local fobj="$cdir/c_frontback_amalgam_${opt}${sfx}.o" + local fexe="$cdir/c_frontback_amalgam_${opt}${sfx}$ext" + if ! "$HOSTED" cc "$t" -$opt -c "$CASES/c_frontback_amalgam.c" -o "$fobj" \ + >"$cdir/c_frontback_amalgam_${opt}${sfx}.cc.out" 2>"$cdir/c_frontback_amalgam_${opt}${sfx}.cc.err"; then + kit_fail "frontback_amalgam/$t:$opt$sfx:cc" "cc failed"; sed 's/^/ | /' "$cdir/c_frontback_amalgam_${opt}${sfx}.cc.err" | head; return + fi + kit_pass "frontback_amalgam/$t:$opt$sfx:cc" + # shellcheck disable=SC2046 + if ! "$HOSTED" cc "$t" -$opt $(mode_flag "$mode") "$fobj" -o "$fexe" \ + >"$cdir/c_frontback_amalgam_${opt}${sfx}.ld.out" 2>"$cdir/c_frontback_amalgam_${opt}${sfx}.ld.err"; then + kit_fail "frontback_amalgam/$t:$opt$sfx:ld" "ld failed"; sed 's/^/ | /' "$cdir/c_frontback_amalgam_${opt}${sfx}.ld.err" | head; return + fi + kit_pass "frontback_amalgam/$t:$opt$sfx:ld" + run_list_add target "frontback_amalgam/$t:$opt$sfx" "$tag" "$fexe" \ + "$cdir/c_frontback_amalgam_${opt}${sfx}.out" "$cdir/c_frontback_amalgam_${opt}${sfx}.err" 0 - + done + fi + done +} + run_list_add() { # kind label tag exe out err expected want-file - printf '%s|%s|%s|%s|%s|%s|%s|%s\n' "$@" >> "$RUN_LIST" + printf '%s|%s|%s|%s|%s|%s|%s|%s\n' "$@" >> "$LIST_PATH" } # smoke_build_case TOKEN CASENAME MODE — compile and link one case in one link @@ -170,9 +217,10 @@ smoke_build_case() { fi } -smoke_run_all() { +cross_run_all() { + local list="$1" local kind label tag exe out err exp want_file rc got want barch - [ -s "$RUN_LIST" ] || { echo "cross: no linked smoke artifacts to execute"; return; } + [ -s "$list" ] || { echo "cross: no linked artifacts to execute"; return; } while IFS='|' read -r kind label tag exe out err exp want_file; do case "$kind" in bare) @@ -190,7 +238,7 @@ smoke_run_all() { else kit_pass "$label:run" fi - done < "$RUN_LIST" + done < "$list" } # ---- full lanes ------------------------------------------------------------ @@ -262,23 +310,32 @@ if [ "$missing" -eq 1 ]; then fi printf '\ncross: phase 1 — compile/link smoke cases\n' -: > "$RUN_LIST" +: > "$RUN_LIST_SMOKE" +: > "$RUN_LIST_COARSE" for t in $TOKENS; do + LIST_PATH="$RUN_LIST_SMOKE" smoke_build_token "$t" + if [ "$DEPTH" != smoke ]; then + LIST_PATH="$RUN_LIST_COARSE" + coarse_build_token "$t" + fi done if [ "$KIT_CROSS_RUN" -eq 1 ]; then printf '\ncross: phase 2 — execute smoke cases\n' - smoke_run_all + cross_run_all "$RUN_LIST_SMOKE" + if [ "$DEPTH" != smoke ]; then + printf '\ncross: phase 3 — execute front/back amalgam lane\n' + cross_run_all "$RUN_LIST_COARSE" + fi if [ "$DEPTH" = full ]; then - printf '\ncross: phase 3 — full execution lanes\n' + printf '\ncross: phase 4 — full execution lanes\n' for t in $TOKENS; do full_token "$t" done fi else printf '\ncross: phase 2 — execution disabled (KIT_CROSS_RUN=0)\n' - [ "$DEPTH" = full ] && printf 'cross: NOTE DEPTH=full lanes are execution lanes and were skipped\n' fi KIT_SKIP_IS_FAILURE=0 diff --git a/test/cross/cases/c_frontback_amalgam.c b/test/cross/cases/c_frontback_amalgam.c @@ -0,0 +1,249 @@ +#include <stdarg.h> +#include <stdint.h> + +/* --- Front/back smoke amalgamation -------------------------------------- */ + +#define FB_MUL2(X) ((X) * (X)) + +_Static_assert(FB_MUL2(3u) == 9u, "macro expansion sanity"); + +struct fb_small_struct { + int32_t a; + int16_t b; +}; + +struct fb_large_struct { + uint64_t a; + uint64_t b; + uint64_t c; + uint64_t d; +}; + +union fb_union_bits { + uint32_t raw; + struct { + uint32_t low:12; + uint32_t high:20; + } bits; +}; + +static uint32_t fb_mutable_data = 7; +static const uint32_t fb_ro_data = 0xA55A3A11u; +static uint32_t fb_bss[4]; + +static int fb_fail(const int rc) { + return rc; +} + +static int fb_macro_and_static_assert(void) { + int x = FB_MUL2(2 + 1); + return x != 9 ? fb_fail(1) : 0; +} + +static int fb_integer_promotions(void) { + int8_t s8 = -42; + uint8_t u8 = 17; + int32_t sum = (int32_t)s8 + (int32_t)u8; + return sum != -25 ? fb_fail(2) : 0; +} + +static int fb_signed_unsigned(void) { + int32_t si = -20; + uint32_t ui = 5u; + uint32_t mixed = si + ui; + return mixed != 4294967281u ? fb_fail(3) : 0; +} + +static int fb_shift_div_rem(void) { + uint32_t a = 0x10u; + uint32_t b = (a << 4) - 5u; + uint32_t c = b / 3u; + uint32_t d = b % 3u; + return (b != 0xFBu || c != 0x53u || d != 2u) ? fb_fail(4) : 0; +} + +static int fb_pointer_arith(void) { + int32_t values[5] = { 1, 2, 3, 4, 5 }; + int32_t* p = values; + int32_t x = p[2] + *(p + 2); + return (p + 4) - p != 4 || x != 6 ? fb_fail(5) : 0; +} + +static int fb_arrays_compound(void) { + int32_t arr[6] = { [0] = 1, [3] = 4, [5] = 9 }; + int32_t* tmp = (int32_t[]){ 10, 11, 12, 13 }; + if (arr[1] != 0 || arr[3] != 4 || arr[5] != 9) return fb_fail(6); + return (tmp[0] + tmp[3] == 23 && tmp[2] == 12) ? 0 : fb_fail(6); +} + +static int fb_struct_union_bitfields(void) { + struct fb_small_struct s = { .a = 7, .b = -2 }; + union fb_union_bits u; + struct { + uint32_t a; + uint32_t b; + uint32_t c; + } t; + u.raw = 0x1234u; + t.a = s.a; + t.b = (uint32_t)u.bits.low; + t.c = (uint32_t)u.bits.high; + if (t.a != 7u || t.b != 0x234u || t.c != 0x1u) return fb_fail(7); + return 0; +} + +static int fb_switches(void) { + int32_t dense = 0; + switch (2) { + case 0: dense = 1; break; + case 1: dense = 2; break; + case 2: dense = 3; break; + default: dense = 9; break; + } + if (dense != 3) return fb_fail(8); + + int32_t sparse = 0; + switch (100) { + case 1: sparse = 1; break; + case 100: sparse = 2; break; + case 200: sparse = 3; break; + default: sparse = 4; break; + } + return sparse == 2 ? 0 : fb_fail(8); +} + +static int fb_loops_goto(void) { + int32_t sum = 0; + int32_t i = 0; + while (i < 4) { + sum += i; + i += 1; + if (i == 3) goto fb_gate; + } +fb_gate: + return (sum == 3) ? 0 : fb_fail(9); +} + +static int fb_recursion(int32_t n) { + return n <= 1 ? n : fb_recursion(n - 1) + fb_recursion(n - 2); +} + +static int fb_many_args(int32_t a0, int32_t a1, int32_t a2, int32_t a3, int32_t a4, + int32_t a5, int32_t a6, int32_t a7, int32_t a8, int32_t a9, + int32_t a10) { + return a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10; +} + +static struct fb_small_struct fb_struct_by_value_small(struct fb_small_struct s) { + struct fb_small_struct out; + out.a = s.a + 1; + out.b = (int16_t)(s.b + 2); + return out; +} + +static struct fb_large_struct fb_struct_by_value_large(uint32_t seed) { + struct fb_large_struct r; + r.a = (uint64_t)seed + 1u; + r.b = (uint64_t)seed + 2u; + r.c = (uint64_t)seed + 3u; + r.d = (uint64_t)seed + 4u; + return r; +} + +static int fb_mixed_calls(int32_t i, float f, double d) { + int32_t mixed = (int32_t)d + (int32_t)f + i; + return mixed == 10 ? 0 : fb_fail(10); +} + +static int fb_varargs(const int count, ...) { + va_list ap; + int32_t sum; + int i; + va_start(ap, count); + sum = 0; + for (i = 0; i < count; ++i) { + sum += va_arg(ap, int32_t); + } + va_end(ap); + return sum == 10 ? 0 : fb_fail(11); +} + +static int fb_function_pointer(void) { + int32_t (*fp)(int32_t) = fb_recursion; + return (fp == 0) ? fb_fail(12) : (fp(2) != 1 ? fb_fail(12) : 0); +} + +static int fb_storage_and_memory(void) { + static uint32_t local_static; + struct fb_small_struct s = { 1, 2 }; + struct fb_small_struct t = fb_struct_by_value_small(s); + local_static += 3u; + fb_mutable_data += 1u; + if (t.a != 2 || local_static != 3u || fb_mutable_data != 8u) return fb_fail(13); + + if (fb_bss[0] != 0u || fb_bss[1] != 0u || fb_bss[2] != 0u || fb_bss[3] != 0u) { + return fb_fail(14); + } + fb_bss[2] = 0x1234u; + if (fb_bss[2] != 0x1234u || fb_ro_data != 0xA55A3A11u) return fb_fail(14); + return 0; +} + +static int fb_float_double(void) { + float f = 3.0f; + double d = 4.5; + float f2 = 0.5f; + double a = (double)f * f2; + double b = d - 0.5; + if (!(a == 1.5 && b == 4.0)) return fb_fail(15); + return 0; +} + +int main(void) { + int32_t rc; + + rc = fb_macro_and_static_assert(); + if (rc) return rc; + rc = fb_integer_promotions(); + if (rc) return rc; + rc = fb_signed_unsigned(); + if (rc) return rc; + rc = fb_shift_div_rem(); + if (rc) return rc; + rc = fb_pointer_arith(); + if (rc) return rc; + rc = fb_arrays_compound(); + if (rc) return rc; + rc = fb_struct_union_bitfields(); + if (rc) return rc; + rc = fb_switches(); + if (rc) return rc; + rc = fb_loops_goto(); + if (rc) return rc; + rc = fb_recursion(6) == 8 ? 0 : fb_fail(16); + if (rc) return rc; + rc = (fb_many_args(1,2,3,4,5,6,7,8,9,10,10) == 65) ? 0 : fb_fail(17); + if (rc) return rc; + { + struct fb_small_struct s = { 10, 20 }; + struct fb_small_struct p = fb_struct_by_value_small(s); + if (p.a != 11 || p.b != 22) return fb_fail(18); + } + { + struct fb_large_struct r = fb_struct_by_value_large(5); + uint64_t sum = r.a + r.b + r.c + r.d; + if (sum != 30u) return fb_fail(19); + } + rc = fb_mixed_calls(2, 3.0f, 5.0); + if (rc) return rc; + rc = fb_varargs(4, 1, 2, 3, 4); + if (rc) return rc; + rc = fb_function_pointer(); + if (rc) return rc; + rc = fb_storage_and_memory(); + if (rc) return rc; + rc = fb_float_double(); + if (rc) return rc; + + return 0; +}