From edfc4273f6fd3cd89d6c7e7e2557374280ae0966 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Mon, 11 May 2026 11:51:34 +0900 Subject: [PATCH] nocc: stage-0 bootstrap path (binaries gitignored) Sets up the cc-free fresh-checkout flow without yet committing the stage-0 binaries. The pieces are in place; flipping the switch is one `git add -f` away when the compiler is judged stable. bootstrap//{ww,6c,6a,6l} stage-0 binaries (gitignored) bootstrap/README.md layout + workflow Two new targets: make bootstrap-snapshot populates bootstrap/$(ARCH)/ from the currently-built wwstage make nocc starts from bootstrap/$(ARCH)/, rebuilds the wwstage from source, gates on stage-0 == rebuilt byte-for-byte. Lands in $(OUT)/nocc/ so $(OUT)/ stays untouched. cc is never invoked. The fixed-point gate has the same shape as `make bootstrap`, just the entry point flipped: that target trusts cstage; nocc trusts the checked-in (or local-snapshot) stage-0 binaries. Implementation notes: - libwwrt.a is assembled with stage-0 6a, members ordered to match $(RT_OBJ) so the embedded symbol table reproduces the cstage build byte-for-byte. - The driver's default lib path is $self_dir/../../lib, which under $(NOCC_BIN) resolves to $(NOCC_OUT)/lib (libwwrt only). Each ww-build invocation passes -I $(CURDIR)/lib so `use os` etc. resolve to source. - Stage-0 binaries are duplicated under both natural names (ww/6c/ 6a/6l) and _ww-suffixed names so the wwstage driver's hard-coded join_path_lit(self_dir, "6c_ww") still finds them. When cmd/wwc/ gets deleted at v1.0, that duplication and the _ww suffix in the driver both go away. --- .gitignore | 7 +++++ BOOTSTRAP.md | 56 +++++++++++++++++++++++---------- Makefile | 76 ++++++++++++++++++++++++++++++++++++++++++++- bootstrap/README.md | 35 +++++++++++++++++++++ 4 files changed, 157 insertions(+), 17 deletions(-) create mode 100644 bootstrap/README.md diff --git a/.gitignore b/.gitignore index b5cbae80..0e1ce59a 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,10 @@ examples/**/*.o examples/**/*.s examples/**/*.combined.ww examples/snake/snake + +# Stage-0 binaries under bootstrap//. Untracked by default — +# committing them is the v1.0 lock per PLAN.md (the new trust +# surface). `make bootstrap-snapshot` populates the dir from the +# current wwstage; `make nocc` verifies they self-reproduce. +# To ship, `git add -f bootstrap/amd64/{ww,6c,6a,6l}` explicitly. +bootstrap/*/* diff --git a/BOOTSTRAP.md b/BOOTSTRAP.md index d22e4f9a..2d2c0fe7 100644 --- a/BOOTSTRAP.md +++ b/BOOTSTRAP.md @@ -36,32 +36,56 @@ involved. ## 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) + 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 990–995 (ww-side + tools vs C-side tools, byte-for-byte, plus + the wwstage rebuilding itself in 995) + +Without cc: + + make bootstrap-snapshot populate bootstrap/$(ARCH)/ from the + currently-built wwstage (gitignored + by default) + make nocc cold-start from bootstrap/$(ARCH)/, + rebuild the wwstage from source, gate + on `stage-0 == rebuilt` byte-for-byte. + cc is never invoked. ## 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`. +green and `selfhost/cmd/{wwc,6c,6a,6l,ww}` are byte-identical to their +Cstage counterparts. Test 995 pins the stronger property — the +wwstage rebuilds every one of its own tools through `ww_ww + 6c_ww + +6a_ww + 6l_ww`, byte-for-byte. + +`make nocc` is wired and verifies stage-0 self-reproduction locally; +the stage-0 binaries under `bootstrap/$(ARCH)/` are gitignored until +ready to commit. The second exit criterion — deleting the C trees — +is **still deferred to v1.0**: shipping the stage-0 binaries is a +one-way door (they become the project's new trust surface), and the +compiler is still churning. A `git clone` today still requires `cc` +unless the local working copy has already done `make +bootstrap-snapshot` once. ## What "no C" looks like +The pieces are already wired; only the binary commit is held back. 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. +1. On each supported arch, run `make wwstage && make bootstrap-snapshot` + to populate `bootstrap//{ww,6c,6a,6l}` from the current + wwstage. Verify with `make nocc`. +2. `git add -f bootstrap//{ww,6c,6a,6l}` — explicit because + the dir is gitignored. The binary SHAs become the new trust + surface. 3. Delete `cmd/wwc/`, `cmd/6c/`, `cmd/6a/`, `cmd/6l/`, `cmd/ww/`. -4. Move `selfhost/cmd/*` to `cmd/*`. + Drop `CC`/`AR` C-tool dependencies from the top-level Makefile. +4. Move `selfhost/cmd/*` to `cmd/*` and `make nocc` becomes the + default `make`. 5. Build path: stage-0 binary → ww1 → ww2 → fixed point, same as today, just without `cc`. diff --git a/Makefile b/Makefile index 8af30ee9..7e87d157 100644 --- a/Makefile +++ b/Makefile @@ -307,4 +307,78 @@ 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 cstage wwstage test install clean bootstrap +# ---- nocc: bootstrap from a checked-in stage-0 binary, no cc ----------- +# A fresh-checkout flow that never invokes cc. Mirrors `make bootstrap`'s +# fixed-point check but uses `bootstrap/$(ARCH)/{ww,6c,6a,6l}` as the +# starting point and lands in $(OUT)/nocc/ so the cstage build under +# $(OUT)/ stays untouched. See bootstrap/README.md and BOOTSTRAP.md. +# +# `make bootstrap-snapshot` populates bootstrap/$(ARCH)/ from the +# currently-built wwstage so you can verify locally before deciding +# to commit the binaries. +ARCH ?= amd64 +STAGE0 = bootstrap/$(ARCH) +NOCC_OUT = $(OUT)/nocc +NOCC_BIN = $(NOCC_OUT)/bin +NOCC_LIB = $(NOCC_OUT)/lib +NOCC_OBJ = $(NOCC_OUT)/obj + +bootstrap-snapshot: wwstage + @mkdir -p $(STAGE0) + @cp $(BIN)/ww_ww $(STAGE0)/ww + @cp $(BIN)/6c_ww $(STAGE0)/6c + @cp $(BIN)/6a_ww $(STAGE0)/6a + @cp $(BIN)/6l_ww $(STAGE0)/6l + @echo "snapshot: $(STAGE0)/{ww,6c,6a,6l} populated from $(BIN)/*_ww" + @echo " (gitignored — 'git add -f bootstrap/$(ARCH)/*' to ship)" + +nocc: + @for f in ww 6c 6a 6l; do \ + test -x $(STAGE0)/$$f || { \ + echo "$@: missing $(STAGE0)/$$f"; \ + echo "$@: run 'make bootstrap-snapshot' first, or read bootstrap/README.md"; \ + exit 1; \ + }; \ + done + @echo "=== nocc: starting from $(STAGE0) (no cc invoked) ===" + @rm -rf $(NOCC_OUT) + @mkdir -p $(NOCC_BIN) $(NOCC_LIB) $(NOCC_OBJ) + @# Stage 0: drop bootstrap binaries under both natural names and + @# the _ww-suffixed names the driver expects when shelling out. + @for f in ww 6c 6a 6l; do \ + cp $(STAGE0)/$$f $(NOCC_BIN)/$$f; \ + cp $(STAGE0)/$$f $(NOCC_BIN)/$${f}_ww; \ + done + @# Assemble libwwrt.a using stage-0 6a — the linker needs it for + @# every wwstage-built binary in the next stage. Member order + @# follows $(RT_S) so the embedded symbol table matches the + @# cstage-built libwwrt.a (cstage uses ar rcs $(RT_OBJ), same + @# order). + @objs=""; for s in $(RT_S); do \ + b=`basename $$s .s`; \ + $(NOCC_BIN)/6a -o $(NOCC_OBJ)/$$b.o $$s; \ + objs="$$objs $(NOCC_OBJ)/$$b.o"; \ + done; $(AR) rcs $(NOCC_LIB)/libwwrt.a $$objs + @echo "=== stage 1: stage-0 rebuilds the wwstage tools from source ===" + @# The driver's default lib path is $self_dir/../../lib — under + @# $(NOCC_BIN) that resolves to $(NOCC_OUT)/lib (libwwrt only). + @# Pass -I $(CURDIR)/lib so `use os` etc. resolve to source. + @cd $(NOCC_BIN) && ./ww build -I $(CURDIR)/selfhost/cmd/wwc \ + -I $(CURDIR)/lib $(CURDIR)/selfhost/cmd/6c/main.ww && mv main 6c_ww1 + @cd $(NOCC_BIN) && ./ww build -I $(CURDIR)/selfhost/cmd/6a \ + -I $(CURDIR)/selfhost/cmd/wwc -I $(CURDIR)/lib \ + $(CURDIR)/selfhost/cmd/6a/main.ww && mv main 6a_ww1 + @cd $(NOCC_BIN) && ./ww build -I $(CURDIR)/selfhost/cmd/6l \ + -I $(CURDIR)/selfhost/cmd/wwc -I $(CURDIR)/lib \ + $(CURDIR)/selfhost/cmd/6l/main.ww && mv main 6l_ww1 + @cd $(NOCC_BIN) && ./ww build -I $(CURDIR)/selfhost/cmd/wwc \ + -I $(CURDIR)/lib $(CURDIR)/selfhost/cmd/ww/main.ww && mv main ww_ww1 + @echo "=== fixed-point gates: rebuilt tools == stage-0 binaries? ===" + @cmp $(NOCC_BIN)/6c_ww1 $(STAGE0)/6c && echo " 6c ✓" + @cmp $(NOCC_BIN)/6a_ww1 $(STAGE0)/6a && echo " 6a ✓" + @cmp $(NOCC_BIN)/6l_ww1 $(STAGE0)/6l && echo " 6l ✓" + @cmp $(NOCC_BIN)/ww_ww1 $(STAGE0)/ww && echo " ww ✓" + @echo + @echo "NOCC OK: $(STAGE0)/* reproduces itself from source. cc not invoked." + +.PHONY: all cstage wwstage test install clean bootstrap nocc bootstrap-snapshot diff --git a/bootstrap/README.md b/bootstrap/README.md new file mode 100644 index 00000000..7478bb12 --- /dev/null +++ b/bootstrap/README.md @@ -0,0 +1,35 @@ +# bootstrap/ + +Stage-0 binaries for cold-start builds without `cc`. One subdirectory +per supported architecture; each holds four statically-linked ELF +executables that match the wwstage layout: + + bootstrap/amd64/ + ww driver, equivalent to out/bin/ww_ww + 6c compiler, out/bin/6c_ww + 6a assembler, out/bin/6a_ww + 6l linker, out/bin/6l_ww + +The binaries are intentionally **gitignored** until the compiler stops +churning enough to make the trust surface stable — see `PLAN.md` and +the v1.0 plan in `BOOTSTRAP.md`. To put them under your local working +copy, run `make bootstrap-snapshot` after a green `make wwstage`; to +ship them, `git add -f bootstrap/amd64/{ww,6c,6a,6l}` explicitly. + +## Workflow + +A fresh-checkout flow that never invokes `cc`: + + make nocc # uses bootstrap//* as the stage-0 entry + # and proves it reproduces itself byte-for-byte + +The non-nocc default (`make`) still drives `cc → cstage → wwstage`. +Both paths coexist — `make nocc` lands in `out/nocc/` so the cstage +build under `out/` is undisturbed. + +## When to refresh the snapshot + +The stage-0 binaries are frozen at the cgen state they were built +with. A `make nocc` failure usually means the live `selfhost/cmd/wwc` +cgen has drifted from the snapshot — regenerate with +`make bootstrap-snapshot` and re-verify.