From cb57cd795d8b71eaf5b4921e2c134913ebef0287 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Mon, 11 May 2026 02:36:46 +0900 Subject: [PATCH] build: split cstage/wwstage targets, document bootstrap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 10 step 8 (delete the C trees) is deferred to v1.0 — until the compiler stops churning we keep Cstage as the fresh-checkout entry point. Split the Makefile so the two stages are named, and add BOOTSTRAP.md describing the cstage → ww1 → ww2 → ww3 fixed-point flow. PLAN.md gets a status note pointing at it. --- BOOTSTRAP.md | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Makefile | 19 +++++++++++--- PLAN.md | 25 +++++++++++++++++++ 3 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 BOOTSTRAP.md diff --git a/BOOTSTRAP.md b/BOOTSTRAP.md new file mode 100644 index 00000000..d22e4f9a --- /dev/null +++ b/BOOTSTRAP.md @@ -0,0 +1,70 @@ +# BOOTSTRAP + +How `ww` builds itself. + +`ww` is self-hosted: the ww-side compiler/assembler/linker/driver live +under `selfhost/cmd/` and reach a byte-identical fixed point under +`make bootstrap`. But the *first* ww binary on a fresh checkout has to +come from somewhere. Today, that "somewhere" is a small C bootstrap +toolchain — the **Cstage**: + + cmd/wwc/ frontend library (lex, parse, check) → libwwc.a + cmd/6c/ amd64 compiler .ww → .s + cmd/6a/ amd64 assembler .s → .o + cmd/6l/ amd64 linker .o → static ELF + cmd/ww/ user-facing driver (orchestrates 6c → 6a → 6l) + +Cstage is built with `cc`. Once it exists, it compiles the +ww-rewritten tools (the **wwstage**) under `selfhost/cmd/`, which then +self-compile to a byte-identical fixed point. After that, no C is +involved. + +## Build flow + + cstage (cc → C tools, one-time) + │ + ├──→ ww1 wwstage tools, built by Cstage + │ │ + │ └──→ ww2 wwstage self-compiles + │ │ + │ └──→ ww3 ww2 self-compiles + │ │ + │ ↓ + │ cmp ww2 ww3 → fixed point ✓ + ↑ + └ from here on, no C is invoked + +## Make targets + + make builds cstage + wwstage + libs + make cstage Cstage only (cc → ww, 6c, 6a, 6l, wwdump, libs) + make wwstage wwstage only (assumes cstage) + make bootstrap three-stage build; gates on cmp ww2 == ww3 + make test runs all tests, including the 990–993 diffs + (ww-side tools vs C-side tools, byte-for-byte) + +## Status + +Phase 10 of `PLAN.md` is at its first exit criterion: the bootstrap is +green and `selfhost/cmd/{wwc,6a,6l,ww}` are byte-identical to their +Cstage counterparts. The second exit criterion — deleting the C trees +— is **deferred to v1.0**: until the compiler stops churning, the +Cstage stays as the fresh-checkout entry point. A `git clone` today +still requires `cc`. + +## What "no C" looks like + +When the compiler stabilises: + +1. Cross-compile or natively build a known-good wwstage on each + supported arch. +2. Ship those binaries under `bootstrap//{ww,6c,6a,6l}` in the + tree. They are the new stage 0. +3. Delete `cmd/wwc/`, `cmd/6c/`, `cmd/6a/`, `cmd/6l/`, `cmd/ww/`. +4. Move `selfhost/cmd/*` to `cmd/*`. +5. Build path: stage-0 binary → ww1 → ww2 → fixed point, same as + today, just without `cc`. + +This is the standard self-hosting story (Go, Rust, OCaml all do +variants of it). The bootstrap binaries become the new "trusting +trust" surface. diff --git a/Makefile b/Makefile index 566616f5..a7c12c22 100644 --- a/Makefile +++ b/Makefile @@ -40,11 +40,22 @@ L6_OBJ = $(L6_SRC:cmd/6l/%.c=$(OBJ)/6l/%.o) RT_S = rt/start.s rt/syscall.s rt/alloc.s rt/streq.s rt/abort.s RT_OBJ = $(RT_S:rt/%.s=$(OBJ)/rt/%.o) -BINS = $(BIN)/ww $(BIN)/6c $(BIN)/6a $(BIN)/6l $(BIN)/wwdump \ - $(BIN)/wwdump_ww $(BIN)/6a_ww $(BIN)/6l_ww $(BIN)/ww_ww +# Cstage: one-time C bootstrap (see BOOTSTRAP.md). Built by `cc`. +# Goes away at v1.0, replaced by a checked-in stage-0 binary. +CSTAGE_BINS = $(BIN)/ww $(BIN)/6c $(BIN)/6a $(BIN)/6l $(BIN)/wwdump + +# Wwstage: ww-rewritten toolchain. Built by Cstage today; will be the +# only toolchain after Phase 10 closeout. Byte-identical to Cstage +# (tests 990-993). +WWSTAGE_BINS = $(BIN)/wwdump_ww $(BIN)/6a_ww $(BIN)/6l_ww $(BIN)/ww_ww + +BINS = $(CSTAGE_BINS) $(WWSTAGE_BINS) LIBS = $(LIB)/libwwc.a $(LIB)/libwwrt.a -all: $(BINS) $(LIBS) +all: cstage wwstage + +cstage: $(CSTAGE_BINS) $(LIBS) +wwstage: $(WWSTAGE_BINS) # ---- libwwc.a ---------------------------------------------------------- $(LIB)/libwwc.a: $(WWC_OBJ) | $(LIB) @@ -266,4 +277,4 @@ bootstrap: all @echo " - cmd/6l/ : ww-6l (linker w/ archive support) — byte-identical to C 6l (test 992)" @echo " - cmd/ww/ : ww-driver (build/run/version) — byte-identical to C ww (test 993)" -.PHONY: all test install clean bootstrap +.PHONY: all cstage wwstage test install clean bootstrap diff --git a/PLAN.md b/PLAN.md index 05746bec..3f961a8a 100644 --- a/PLAN.md +++ b/PLAN.md @@ -291,6 +291,31 @@ Exit criteria: - The C `cmd/wwc/`, `cmd/6c/`, `cmd/6a/`, `cmd/6l/` C trees are deleted in this phase's final commit. +### Status (2026-05) + +Steps 1–6 done. The four ww-side tools live in `selfhost/cmd/`: + +- `wwc/` — ww-native lex/parse/check/cgen +- `6a/` — ww-native amd64 assembler +- `6l/` — ww-native amd64 linker +- `ww/` — ww-native driver + +Step 7 reaches its fixed point: `make bootstrap` produces ww1 → ww2 → +ww3 with `cmp ww2 ww3` byte-identical. Tests 990–993 confirm each +selfhost tool is byte-identical to its Cstage counterpart. + +Exit criterion 1 (bootstrap green) is met. Exit criterion 2 (delete +the C trees) is **deferred to v1.0**: removing `cmd/wwc/`, `cmd/6c/`, +`cmd/6a/`, `cmd/6l/`, `cmd/ww/` is a one-way door. Until the compiler +stops churning we keep Cstage as the canonical fresh-checkout entry +point and treat `selfhost/cmd/*` as the path forward. At v1.0, the +plan is to ship a checked-in stage-0 binary archive +(`bootstrap//{ww,6c,6a,6l}`), delete the C trees, and promote +`selfhost/cmd/*` to `cmd/*`. + +See `BOOTSTRAP.md` for the build flow and `make cstage`/`make wwstage` +targets. + ## Phase 11 — CSP Goal: channels and lightweight processes, without GC.