kit

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

commit 0a7d4b2e190eaa8b05699f2d7a9474b853a4ef7f
parent 3ad7ea9ac8a11b814c0288fb0ec7f35cc4b20032
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Tue,  9 Jun 2026 08:59:39 -0700

doc/plan: add the Windows self-host roadmap

windows.md records the aarch64-windows self-host baseline (cross-built kit.exe
runs cc + JIT, the bring-up fixes, the 147/166 Toy AOT result) and the open
follow-ups: the self-host miscompile crash on ~17 cases (gates a Windows
bootstrap), JIT printf via libucrt static helpers, x86_64-windows parity, a
committed compile-on-VM test lane, distribution/sysroot, the 3-stage bootstrap,
and the dbg fault-guard/SEH gap. Indexed in doc/plan/README.md.

Diffstat:
Mdoc/plan/README.md | 1+
Adoc/plan/windows.md | 178+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 179 insertions(+), 0 deletions(-)

diff --git a/doc/plan/README.md b/doc/plan/README.md @@ -16,6 +16,7 @@ shrinks to whatever remains open. | [WASM.md](WASM.md) | Completing the Wasm object backend and remaining parser/validator coverage. | [../WASM.md](../WASM.md) | | [ARCH.md](ARCH.md) | Remaining native-backend completeness for x64/rv64 relative to the aa64 reference, and per-call cost follow-ups. | [../ARCH.md](../ARCH.md) | | [BOOTSTRAP.md](BOOTSTRAP.md) | The 3-stage self-build reproducibility goal and the open `-O1` issues blocking it. | [../BUILD.md](../BUILD.md) | +| [windows.md](windows.md) | Self-hosting kit on Windows: cross-built `kit.exe` runs `cc` + JIT on aarch64-windows; the open self-host miscompile crash, JIT printf, x64 parity, compile-on-VM lane, and the Windows 3-stage bootstrap. | [../WINDOWS.md](../WINDOWS.md) | | [IMAGE_INSPECT.md](IMAGE_INSPECT.md) | Extending object inspection to executables and shared libraries. COFF/PE image reader is the main remaining gap. | [../OBJ.md](../OBJ.md) | | [BUILD.md](BUILD.md) | A new content-addressed build coordinator (Bazel/Nix-style incremental builds layered on the CAS) — storage state machine, caching algorithm, recipe protocol. Distinct from `../BUILD.md` (kit's own Makefile build). | — (new subsystem) | | [BUILD_COMMANDS.md](BUILD_COMMANDS.md) | The kit-native `build-exe`/`build-lib`/`build-obj` verbs that replace `compile`: polyglot, in-memory compile+link with `--group` flag scoping and full link-flag control. Distinct from `BUILD.md` (the CAS coordinator). | [../DRIVER.md](../DRIVER.md) | diff --git a/doc/plan/windows.md b/doc/plan/windows.md @@ -0,0 +1,178 @@ +# Windows Self-Host (current state and roadmap) + +This roadmap tracks bringing kit up as a self-hosted **Windows** toolchain: +cross-compiling the kit binary into a PE/COFF `kit.exe` and running `kit cc` +(AOT) and `kit run` (JIT) natively on Windows. The hosted target profile, +sysroot mechanics, and VM are described in [../WINDOWS.md](../WINDOWS.md); the +ABI work is in [windows-abi context]; this document tracks the self-host goal, +the baseline already in tree, and the genuinely-open follow-ups. + +Targets are PE/COFF, 64-bit only: `aarch64-windows` (the reference, and what +runs natively on the Apple-silicon ARM64 Win11 VM) and `x86_64-windows` (runs +via the in-box x64 emulator on the same VM). The hosted profile is mingw-w64 +UCRT via llvm-mingw (not MSVC): kit advertises `__MINGW32__/__MINGW64__`, never +`_MSC_VER`. + +## Baseline (done — context, not planned work) + +Cross-build + native cc + native JIT all work on aarch64-windows, verified on +the VM. Landed in commits `12850478` (the bring-up) and `9242af27` + +follow-on (rt path resolution). + +- **Cross-build**: `scripts/windows_cross.sh aarch64` builds `kit.exe` + (PE32+ console, ~20 MB) with the host `build/kit` as the cross-compiler + against the llvm-mingw UCRT sysroot, overriding `HOST_OS=windows + HOST_ARCH=aarch64` so `mk/env.mk` selects `driver/env/windows.c`. (Windows + can't bootstrap natively — no seed C compiler in the VM — so the binary is + cross-produced on the dev host; contrast the native Linux/FreeBSD + bootstraps.) +- **`kit cc`**: compiles + links real C programs natively on the VM (`hello.c` + → a `hello.exe` that prints and returns the right exit code), compiling + `libkit_rt` on demand. +- **`kit run` (JIT)**: executes self-contained programs (compute, data/globals), + external calls (`rand`), and **libc I/O via dlsym** (`puts`). +- **Toy AOT corpus on the VM** (native `kit cc` + execute, compare `.expected`): + **147 pass / 0 miscompile / 17 crash / 2 skip** of 166 — see task 1. + +Fixes the bring-up required (all in tree): +- C frontend honors GCC asm-label renames on declarations (`Decl.asm_name`; + mingw `time`→`_time64`). +- COFF short-import `NameType` (NOPREFIX/UNDECORATE/**EXPORTAS**): the PE + hint/name table uses the real DLL export name (UCRT aliases local + `__msvcrt_assert`→export `_assert`); previously emitted the alias → load + failure 0xC0000139. +- `driver/lib/hosted.c` links `libwinpthread.a` (mingw `<time.h>` inline + wrappers reference `nanosleep64`/`clock_*64`; static, no DLL dep). +- `rt/include/setjmp.h` asm-renames `setjmp`/`longjmp` → mingw non-SEH + `__mingw_setjmp`/`__mingw_longjmp` (mingw exposes no bare `setjmp` symbol). +- `driver/env/windows.c`: SEH `__try` gated under `!__kit__` (VEH backstop); + JIT dlsym fallback for libucrt-static `__local_stdio_{printf,scanf}_options`; + JIT execmem dual-map runtime view mapped **`FILE_MAP_WRITE`** so writable/GOT + segments can be `VirtualProtect`'d RW (a `FILE_MAP_EXECUTE`-only view rejects + RW, err 87 — broke `kit run` of anything with an external call or writable + global). +- `driver/lib/runtime.c`: rt support-dir discovery accepts `\` separators and + falls back to the real image path (`driver_self_exe_path` / + `GetModuleFileNameW`) when `argv[0]` isn't a usable path (PATH invocation), + so `<bindir>/support/rt` resolves from any cwd. + +The items below are what is **not** yet done. + +## 1. Self-host miscompilation: kit.exe segfaults compiling ~17 toy cases (HIGH) + +The Toy AOT corpus on the VM is 147/166 with **zero exit-code mismatches** (no +miscompiles of the toy programs themselves), but **17 cases deterministically +crash kit.exe itself** with `0xC0000005` (silent access violation) during +compilation. + +What's known: +- The crash is in the **frontend/codegen** stage: `kit.exe cc -c case.toy` and + `kit.exe run case.toy` both crash; the linker is not involved. +- **Deterministic per case**: `01_return_const` (`return 7;`) crashes 12/12; + `03_bitwise_shift` / `08_recursion_fib` always succeed. Not correlated with + input size — the trivial case crashes, complex ones pass. +- The crashers (from a clean run): `01_return_const`, `02_arith_precedence`, + `05_if_else`, `06_while_sum`, `33_musttail_void`, `50_switch_statement`, + `70_labeled_switch_break`, `90_continue_through_switch`, + `96_data_relocations`, `97_let_pointer_pointee_assignment`, + `100_record_data_relocation`, `101_extern_threadlocal_decls`, + `117_many_enum_values`, `120_data_symdiff`, `122_data_entsize`, … (~17). +- **It is a self-host miscompilation, not a frontend bug**: the *same* inputs + cross-compile cleanly via the host `build/kit -target aarch64-windows` + (valid PE32+). So `build/kit`'s aarch64-windows `-O0` codegen miscompiled + some libkit frontend/CG function, and that miscompiled code in kit.exe faults + on specific control/data flow. + +Next steps: get the faulting function. Either run kit.exe under a Windows +debugger (WinDbg / `cdb`, or `Add-MpPreference` + a crash-dump) to get the +faulting address → map to a libkit symbol; or bisect by building kit.exe with +subsets at `-O0` vs a known-good path. A debug-instrumented cross build (asserts +already on; add `kit_debug_printf` tracing around the toy compile pipeline) +narrows the stage. This is the gating bug for a Windows 3-stage bootstrap +(task 6). + +## 2. printf-family via `kit run` (JIT) needs libucrt static helpers + +`kit run` of a printf program fails: `undefined reference to 'printf'`. mingw's +`<stdio.h>` printf wrapper resolves to the static `printf` (and +`__local_stdio_printf_options`) that live only in `libucrt.a` — not DLL +exports — and the JIT links no static archives (it resolves externs via dlsym +over loaded DLLs + in-image symbols). AOT `kit cc` printf works fully (it links +`libucrt.a`); `puts` and other direct ucrtbase exports work under JIT. + +Options: (a) feed the hosted profile's static archives to the JIT link so it +lazily pulls static members like `printf`/`__local_stdio_printf_options` (and +routes the short-import members to dlsym) — the general fix; or (b) extend the +`driver/env/windows.c` dlsym fallback (already used for the +`__local_stdio_*_options` helpers) to the small set of libucrt-static stdio +entry points, handing JIT'd code kit.exe's own statically-linked copies. + +## 3. x86_64-windows parity + +aarch64-windows is the reference. For x64: +- Build `rt-x86_64-pc-windows` (the variant exists in `mk/rt.mk` / + `driver/lib/runtime.c`; not built by default here). +- `scripts/windows_cross.sh x64` should produce an x64 `kit.exe`; verify it runs + via the VM's x64 emulator and re-run the Toy AOT corpus. +- Known x64-windows codegen gaps to expect (from the toy VM lanes): `36/37` + `*sret` tail-call crash at **-O1**, and `118_decl_extra_attrs` ADRP-range link + is aarch64-only. Re-confirm against a native x64 `kit.exe`. + +## 4. A committed "compile-on-VM" test lane + +`test/toy/vm.sh windows` today cross-compiles the toy cases on the *host* and +only executes on the VM. The native self-host path (kit.exe compiling on the +VM) was exercised ad hoc this session. Generalize it into a committed lane +(e.g. `test/toy/vm.sh windows --native` or a new harness) that ships the case +sources + a `<bindir>/support/rt` + the mingw sysroot to the VM, compiles with +`kit.exe` there, runs, and compares `.expected`. Gotchas to bake in: +- PowerShell `Start-Process -PassThru` *without* `-Wait` reports `.ExitCode = 0` + always — use a `[Diagnostics.Process]` with `WaitForExit(ms)` + `.ExitCode`, + or `& exe; $LASTEXITCODE` with a Defender path exclusion. +- macOS `tar` adds AppleDouble `._*` sidecars — ship case sources with + `COPYFILE_DISABLE=1` (same trap as the FreeBSD bootstrap). +- The mingw sysroot's `<arch>-w64-mingw32/include` is a symlink to + `generic-w64-mingw32/include`; dereference (`cp -RL`) before shipping. + +## 5. Distribution + default sysroot + +- Ship the Windows distribution as `<bindir>/support/rt` (what `kit install` + produces); rt now resolves from the real image path, so no cwd dependence. +- The Windows hosted profile **requires** `--sysroot`/`KIT_SYSROOT` (no + default). Windows ships the runtime DLLs (`ucrtbase.dll`, the + `api-ms-win-crt-*` api-sets, `kernel32.dll`) but **no headers or import + libs**, so a sysroot is mandatory. Consider: bundling the mingw UCRT + headers + import libs into the kit distribution (a baked sysroot), and/or + teaching the COFF linker to synthesize imports directly from a system DLL's + export table (drops the import-lib half of the sysroot, but the `crt2.o` + startup + `libmingwex` helpers — `__mingw_setjmp`, + `__local_stdio_printf_options`, … — still have no DLL home and would have to + move into `libkit_rt` for Windows). + +## 6. Windows 3-stage bootstrap + +The self-host milestone: use the cross-built `kit.exe` on the VM to compile +kit's own sources into a stage-2 `kit.exe`, then stage-3, and assert stage-2 == +stage-3 byte-for-byte (cf. [BOOTSTRAP.md](BOOTSTRAP.md), and the +native-bootstrap analogs `scripts/{linux,freebsd}_bootstrap.sh`). **Blocked on +task 1** — kit.exe crashes compiling some inputs, so it cannot yet compile the +full libkit/driver source set. A `scripts/windows_bootstrap.sh` would drive the +VM-side stages once task 1 is resolved. + +## 7. Debugger fault-guard / SEH on Windows + +`driver/env/windows.c`'s `driver_run_with_crash_guard` is a no-op on Windows +(the POSIX path uses `sigaction` + `sigsetjmp`); a crashing `kit run` program +takes down kit.exe instead of reporting `on_crash`. The dbg `guarded_copy` also +relies on the VEH backstop rather than `__try` (kit's C frontend has no SEH). +A proper vectored-exception-handler port would give `kit run`/`kit dbg` +fault isolation on Windows. + +## Operational notes + +- Build: `scripts/windows_cross.sh [aarch64|x64]` (needs `make bin`, the mingw + sysroot via `scripts/llvm_mingw_sysroot.sh prepare <arch>`, and the rt + variant). kit cc emits no `-MMD` depfiles, so the script wipes objects for a + correct full rebuild each run. +- VM: `scripts/windows_vm.sh boot|wait-ssh|run|ssh|stop` (see + [../WINDOWS.md](../WINDOWS.md)). One ARM64 VM serves both arches.