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:
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.