build: split cstage/wwstage targets, document bootstrap

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.
This commit is contained in:
2026-05-11 02:36:46 +09:00
parent 1657bdeda3
commit cb57cd795d
3 changed files with 110 additions and 4 deletions

70
BOOTSTRAP.md Normal file
View File

@@ -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 990993 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/<arch>/{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.

View File

@@ -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

25
PLAN.md
View File

@@ -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 16 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 990993 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/<arch>/{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.