From f56b056ed3324bbf603e1dcd33e9d23c935711f5 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Mon, 22 Jun 2026 07:56:20 +0900 Subject: [PATCH] make: add test-lang-byteid T2 cross-stage .s byte-id gate Compiles every test/lang/*_test.ww twice through the same cstage ww orchestrator swapping only the frontend (WW_W6C = w6c vs w6c_ww) and asserts every per-package .s (incl the synth-main __root.s) is byte-identical. This is the rule-10 stage-symmetry gate test/runww.ww reserves as T2. .s-only (Q3); .o/.wwi are 991/992's surface. Distinct WW_PKGCACHE per leg, both caches+sepworks wiped per file. Recipe is parameterized over an input-class (file-list + build-verb); only the test/lang class ships now, fold-6 slots in build --sep for selfhost/data. Wired into make test (pre-push) only, not test-commit/test-unit. --- Makefile | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index f2b286a3..9d85575e 100644 --- a/Makefile +++ b/Makefile @@ -3659,7 +3659,7 @@ sizelint: peellint: @sh tools/peellint -test: all sizelint peellint $(TESTS) +test: all sizelint peellint $(TESTS) test-lang-byteid @WW=$(BIN)/ww BIN=$(BIN) sh test/run test-unit: all sizelint peellint $(TESTS) @@ -3686,6 +3686,67 @@ test-lang: all echo "ww test $$f"; $(BIN)/ww test $$f; \ done; echo "test-lang: all ok" +# ---- test-lang-byteid: T2 cross-stage .s byte-id gate (fold 4) --------- +# For every test/lang/*_test.ww, compiles it TWICE through the SAME cstage +# `ww` orchestrator swapping ONLY the frontend (WW_W6C = w6c vs w6c_ww) and +# asserts every per-package .s (incl the synth-main __root.s) is +# byte-identical cs-vs-ww. This is the rule-10 stage-symmetry gate that +# test/runww.ww:9-15 reserves ("T2"); strictly more sensitive than re-running +# behavior. .s-ONLY by design: .o/.wwi are the assembler's surface (gated by +# 991/992) — diffing them here would false-attribute an assembler bug to cgen. +# WW_W6A/WW_W6L are pinned to the NON-_ww binaries on BOTH legs: the .s is the +# frontend's product (the assembler runs after), so the SOLE variable is +# WW_W6C. Distinct WW_PKGCACHE per leg is mandatory — a shared cache lets the +# ww leg HIT the cs-built package and never run w6c_ww (false GREEN). Depends +# on `all` so both frontends are at HEAD (stale-binary is the #1 false-pass). +# Pre-push (`make test`) ONLY — never test-commit/test-unit (byte-id is being +# demoted to pre-push; a content-keyed T2 would defeat that demotion). +# +# The recipe is parameterized over an input-class = (file-list, build-verb); +# only the test/lang class ships now. SEAM for fold-6: add a second class +# (`ww build --sep` over selfhost/data) plus //ww:error reject-routing (assert +# both legs fail) — no reject cases exist in test/lang today. +LANGBYTEID_DIR = $(OUT)/langbyteid +LANGBYTEID_FILES = $(wildcard test/lang/*_test.ww) +LANGBYTEID_VERB = test -c +LANGBYTEID_EXPECTED_MIN = 16 +$(if $(LANGBYTEID_FILES),,$(error test-lang-byteid: empty corpus)) +test-lang-byteid: all + @set -e; \ + mkdir -p $(LANGBYTEID_DIR); \ + n=0; \ + for f in $(LANGBYTEID_FILES); do \ + base=$(LANGBYTEID_DIR)/`basename $$f .ww`; \ + rm -rf $$base.cs.sepwork $$base.ww.sepwork $$base.cs.cache $$base.ww.cache; \ + WW_W6C=$(BIN)/w6c WW_W6A=$(BIN)/w6a WW_W6L=$(BIN)/w6l \ + WW_PKGCACHE=$$base.cs.cache \ + $(BIN)/ww $(LANGBYTEID_VERB) -o $$base.cs $$f; \ + WW_W6C=$(BIN)/w6c_ww WW_W6A=$(BIN)/w6a WW_W6L=$(BIN)/w6l \ + WW_PKGCACHE=$$base.ww.cache \ + $(BIN)/ww $(LANGBYTEID_VERB) -o $$base.ww $$f; \ + cs_set=`cd $$base.cs.sepwork && ls *.s | sort`; \ + ww_set=`cd $$base.ww.sepwork && ls *.s | sort`; \ + if [ "$$cs_set" != "$$ww_set" ]; then \ + echo "test-lang-byteid: FAIL $$f: .s set differs cs-vs-ww"; \ + echo " cs: $$cs_set"; echo " ww: $$ww_set"; exit 1; \ + fi; \ + echo "$$cs_set" | grep -qx __root.s || { \ + echo "test-lang-byteid: FAIL $$f: no __root.s emitted"; exit 1; }; \ + ns=0; \ + for s in $$cs_set; do \ + cmp -s $$base.cs.sepwork/$$s $$base.ww.sepwork/$$s || { \ + echo "test-lang-byteid: FAIL $$f: $$s differs cs-vs-ww (rule-10)"; \ + exit 1; }; \ + ns=`expr $$ns + 1`; \ + done; \ + test $$ns -ge 1 || { echo "test-lang-byteid: FAIL $$f: 0 .s compared"; exit 1; }; \ + echo "ww test-byteid $$f ($$ns .s cs==ww)"; \ + n=`expr $$n + 1`; \ + done; \ + test $$n -ge $(LANGBYTEID_EXPECTED_MIN) || { \ + echo "test-lang-byteid: corpus shrank ($$n < $(LANGBYTEID_EXPECTED_MIN))"; exit 1; }; \ + echo "test-lang-byteid: $$n files cs==ww" + # ---- test-commit: per-commit default (task #10 T1) --------------------- # Full suite, but content-key-SKIPS the expensive wwstage byte-id / # self-compile gates (950/989_lib_byteid/990-997) when their input set is @@ -3876,4 +3937,4 @@ nocc: @echo @echo "NOCC OK: $(STAGE0)/* reproduces itself from source. cc not invoked." -.PHONY: all cstage wwstage test test-unit test-commit test-run test-lang smoke sizelint peellint install clean bootstrap nocc bootstrap-snapshot +.PHONY: all cstage wwstage test test-unit test-commit test-run test-lang test-lang-byteid smoke sizelint peellint install clean bootstrap nocc bootstrap-snapshot