From 367aec23ed00cbff7a2878c26701e8610c8b3559 Mon Sep 17 00:00:00 2001 From: Vasilev Dmitrii Date: Tue, 18 Aug 2026 19:24:40 +0700 Subject: [PATCH] =?UTF-8?q?fix(specs):=20close=2027=20unterminated=20test?= =?UTF-8?q?=20blocks=20=E2=80=94=20the=20cross-target=20proof=20was=20neve?= =?UTF-8?q?r=20running?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit emit-bitexact has been red since 2026-08-14. The cause is one character. A bulk edit appended 'test _w339_batch_depth_invariant_2 {' to 27 spec files and omitted the closing brace in every one, so the parser hits the next 'test' keyword while still inside the block. Three layers then reported three different things, and only the innermost was true: CI gate IGLA RACE CROSS-TARGET MISMATCH nothing was compared verify_igla_race.py FAIL: C backend failed to build the backend never ran t27c parse error in fn '...' near line 1815: unexpected token after expression statement: KwTest <- correct verify_igla_race.py runs its builds with capture_output=True and inspects only returncode, so the compiler's message -- which named the file, the function, the line and the token -- was collected and discarded. A diagnostic that names the wrong subsystem costs more than no diagnostic: 'the C and Rust backends diverge' is a much more alarming claim than 'a spec has a typo', and it is where four days of reading went. There was no cross-target divergence. With the braces closed: OK ternary_mul/mac: C == reference BIT-EXACT over 800 vectors OK ternary_mul/mac: Rust == reference BIT-EXACT over 800 vectors OK systolic_ternary_pe: C == reference BIT-EXACT over 800 vectors (i16 psum) OK systolic_ternary_pe: Rust == reference BIT-EXACT over 800 vectors (i16 psum) #2184 is corrected accordingly. The i16 + i8 no-cast gap in gen_rust is real and documented in the script, but it is worked around there and was never the cause. The 27 sites were edited mechanically only after confirming all 27 share one shape -- comment, assert(true), blank line -- and the one already-correct site was left alone. Each file's parse was then checked individually. tools/check_specs_parse.py asks what the three layers did not, prints the compiler's own message, and carries a negative control that plants an unclosed block and proves the gate rejects it. It runs ahead of every other step because it is their precondition. Reported, not fixed: closing the braces reveals 15 of 27 igla specs still failing on unrelated parser features (unknown cast target type f32, Unexpected top-level token KwModule), overlapping #2174. Closes #2185 Refs #2184 Co-Authored-By: Claude Opus 5 --- .github/workflows/emit-bitexact-gate.yml | 10 ++ docs/NOW.md | 14 +++ specs/igla/coder/arch.t27 | 2 +- specs/igla/coder/bench_proxy.t27 | 2 +- specs/igla/coder/benchmark.t27 | 2 +- specs/igla/coder/dataset.t27 | 2 +- specs/igla/coder/eval.t27 | 2 +- specs/igla/coder/pipeline.t27 | 2 +- specs/igla/coder/prm.t27 | 2 +- specs/igla/coder/tokenizer.t27 | 2 +- specs/igla/coder/training.t27 | 2 +- specs/igla/coder/weights.t27 | 2 +- specs/igla/race/adder_tree.t27 | 2 +- specs/igla/race/backend.t27 | 2 +- specs/igla/race/bram_weights.t27 | 2 +- specs/igla/race/cordic.t27 | 2 +- specs/igla/race/cordic_fixed.t27 | 2 +- specs/igla/race/cordic_top.t27 | 2 +- specs/igla/race/eda.t27 | 2 +- specs/igla/race/formal.t27 | 2 +- specs/igla/race/gemm.t27 | 2 +- specs/igla/race/opcodes.t27 | 2 +- specs/igla/race/rtl.t27 | 2 +- specs/igla/race/systolic_array.t27 | 2 +- specs/igla/race/systolic_ternary.t27 | 2 +- specs/igla/race/ternary_gemm.t27 | 2 +- specs/igla/race/ternary_inference.t27 | 2 +- specs/igla/race/ternary_mac.t27 | 2 +- specs/igla/race/yosys.t27 | 2 +- tools/check_specs_parse.py | 129 +++++++++++++++++++++++ 30 files changed, 180 insertions(+), 27 deletions(-) create mode 100755 tools/check_specs_parse.py diff --git a/.github/workflows/emit-bitexact-gate.yml b/.github/workflows/emit-bitexact-gate.yml index 8f620b29a..98d8250e6 100644 --- a/.github/workflows/emit-bitexact-gate.yml +++ b/.github/workflows/emit-bitexact-gate.yml @@ -20,6 +20,7 @@ on: # rewrites the C emitter merges with the cross-target proof never running. - "bootstrap/**" - "cli/**" + - "tools/check_specs_parse.py" - "tools/gft_backprop_microcode.py" - "tools/verify_emit_bitexact.py" - "tools/verify_multitarget.py" @@ -54,6 +55,15 @@ jobs: - name: Build t27c run: cargo build --release -p t27c + # Precondition for everything below. A spec that does not parse produces no + # backend output at all, and the downstream scripts report that as "the C + # backend failed to build" -- which sent four days of readers to the wrong + # subsystem. Ask the parser first, and print its own message. + - name: Required specs parse (negative control) + run: python3 tools/check_specs_parse.py --self-check + - name: Required specs parse + run: python3 tools/check_specs_parse.py + - name: Prove generated RTL == GF-T model (bit-exact) + synthesizes run: python3 tools/verify_emit_bitexact.py diff --git a/docs/NOW.md b/docs/NOW.md index 552d43126..db529269a 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -1,3 +1,17 @@ +# NOW -- one missing brace, and three layers that named the wrong subsystem (2026-08-18) + +Last updated: 2026-08-18 + +## specs: close 27 unterminated test blocks; the cross-target proof was never running (Closes #2185) + +- **`emit-bitexact` was red since 2026-08-14 because of one character.** A bulk edit appended `test _w339_batch_depth_invariant_2 {` to 27 spec files and omitted the closing `}` in every one, so the parser hits the next `test` keyword while still inside the block +- **Three layers each reported something else, and only the innermost was true.** The CI gate said `IGLA RACE CROSS-TARGET MISMATCH`; `verify_igla_race.py` said `FAIL: C backend failed to build/run`; `t27c` said `parse error in fn 'ternary_mac_w339_batch_depth_invariant_2' near line 1815: unexpected token after expression statement: KwTest` -- file, function, line and token, exactly right. The script runs its builds with `capture_output=True` and inspects only `returncode`, so that message was collected and thrown away +- **A diagnostic that names the wrong subsystem costs more than none.** "The C and Rust backends diverge" is a far more alarming claim than "a spec has a typo", and it is where four days of reading went +- **There was no cross-target divergence.** With the braces closed, `verify_igla_race.py` exits 0: C and Rust each match the reference bit-exact over 800 vectors, for both `ternary_mul/mac` and `systolic_ternary_pe` with its i16 accumulator. #2184 is corrected accordingly -- the `i16 + i8` no-cast gap in `gen_rust` is real, is documented in the script, and is worked around there; it was never what made the gate red +- **Gate:** `tools/check_specs_parse.py` asks the question the three layers did not -- does `t27c` accept the file -- and prints the compiler's own message. Negative control plants an unclosed block and proves the gate rejects it. Wired in ahead of every other step, because it is their precondition +- **Reported, not fixed:** closing the braces reveals 15 of 27 igla specs still failing on unrelated parser features (`unknown cast target type f32`, `Unexpected top-level token: KwModule`), overlapping #2174. Different class, left alone +- The `paths:` edit silently failed on an indentation mismatch for the second time today. Caught by a negative control over the filter, not by reading it + # NOW -- the bit-exactness gate could not see the compiler (2026-08-18) Last updated: 2026-08-18 diff --git a/specs/igla/coder/arch.t27 b/specs/igla/coder/arch.t27 index 1b5ad9ca0..367a43f75 100644 --- a/specs/igla/coder/arch.t27 +++ b/specs/igla/coder/arch.t27 @@ -2329,7 +2329,7 @@ test arch_w339_batch_depth_invariant_1 { test arch_w339_batch_depth_invariant_2 { // Verify baseline properties for 15-variable accumulation assert(true) - +} // Wave Loop 340 — CODER depth +1 (72→73) test arch_w340_batch_depth_invariant_1 { /* verify baseline */ } test arch_w340_batch_depth_invariant_2 { /* verify baseline */ } diff --git a/specs/igla/coder/bench_proxy.t27 b/specs/igla/coder/bench_proxy.t27 index ef8bbb15f..71d5d96dc 100644 --- a/specs/igla/coder/bench_proxy.t27 +++ b/specs/igla/coder/bench_proxy.t27 @@ -1231,7 +1231,7 @@ test bench_proxy_w339_batch_depth_invariant_1 { test bench_proxy_w339_batch_depth_invariant_2 { // Verify baseline properties for 15-variable accumulation assert(true) - +} // Wave Loop 340 — CODER depth +1 (72→73) test bench_proxy_w340_batch_depth_invariant_1 { /* verify baseline */ } test bench_proxy_w340_batch_depth_invariant_2 { /* verify baseline */ } diff --git a/specs/igla/coder/benchmark.t27 b/specs/igla/coder/benchmark.t27 index 5ec007f87..5bb026046 100644 --- a/specs/igla/coder/benchmark.t27 +++ b/specs/igla/coder/benchmark.t27 @@ -4617,7 +4617,7 @@ test benchmark_w339_batch_depth_invariant_1 { test benchmark_w339_batch_depth_invariant_2 { // Verify baseline properties for 15-variable accumulation assert(true) - +} // Wave Loop 340 — CODER depth +1 (73→74) test benchmark_w340_batch_depth_invariant_1 { /* verify baseline */ } test benchmark_w340_batch_depth_invariant_2 { /* verify baseline */ } diff --git a/specs/igla/coder/dataset.t27 b/specs/igla/coder/dataset.t27 index efd1672dc..64d1bcc73 100644 --- a/specs/igla/coder/dataset.t27 +++ b/specs/igla/coder/dataset.t27 @@ -2450,7 +2450,7 @@ test dataset_w339_batch_depth_invariant_1 { test dataset_w339_batch_depth_invariant_2 { // Verify baseline properties for 15-variable accumulation assert(true) - +} // Wave Loop 340 — CODER depth +1 (72→73) test dataset_w340_batch_depth_invariant_1 { /* verify baseline */ } test dataset_w340_batch_depth_invariant_2 { /* verify baseline */ } diff --git a/specs/igla/coder/eval.t27 b/specs/igla/coder/eval.t27 index 8e6bc20ac..fe6dfb98c 100644 --- a/specs/igla/coder/eval.t27 +++ b/specs/igla/coder/eval.t27 @@ -3631,7 +3631,7 @@ test eval_w339_batch_depth_invariant_1 { test eval_w339_batch_depth_invariant_2 { // Verify baseline properties for 15-variable accumulation assert(true) - +} // Wave Loop 340 — CODER depth +1 (74→75) test eval_w340_batch_depth_invariant_1 { /* verify baseline */ } test eval_w340_batch_depth_invariant_2 { /* verify baseline */ } diff --git a/specs/igla/coder/pipeline.t27 b/specs/igla/coder/pipeline.t27 index 42a3bac3d..7c3a995b9 100644 --- a/specs/igla/coder/pipeline.t27 +++ b/specs/igla/coder/pipeline.t27 @@ -2253,7 +2253,7 @@ test pipeline_w339_batch_depth_invariant_1 { test pipeline_w339_batch_depth_invariant_2 { // Verify baseline properties for 15-variable accumulation assert(true) - +} // Wave Loop 340 — CODER depth +1 (73→74) test pipeline_w340_batch_depth_invariant_1 { /* verify baseline */ } test pipeline_w340_batch_depth_invariant_2 { /* verify baseline */ } diff --git a/specs/igla/coder/prm.t27 b/specs/igla/coder/prm.t27 index 4b9c4f09c..8cbbc3f9c 100644 --- a/specs/igla/coder/prm.t27 +++ b/specs/igla/coder/prm.t27 @@ -1559,7 +1559,7 @@ test prm_w339_batch_depth_invariant_1 { test prm_w339_batch_depth_invariant_2 { // Verify baseline properties for 15-variable accumulation assert(true) - +} // Wave Loop 340 — CODER depth +1 (73→74) test prm_w340_batch_depth_invariant_1 { /* verify baseline */ } test prm_w340_batch_depth_invariant_2 { /* verify baseline */ } diff --git a/specs/igla/coder/tokenizer.t27 b/specs/igla/coder/tokenizer.t27 index 03eeabea6..9c7935f2a 100644 --- a/specs/igla/coder/tokenizer.t27 +++ b/specs/igla/coder/tokenizer.t27 @@ -1381,7 +1381,7 @@ test tokenizer_w339_batch_depth_invariant_1 { test tokenizer_w339_batch_depth_invariant_2 { // Verify baseline properties for 15-variable accumulation assert(true) - +} // Wave Loop 340 — CODER depth +1 (73→74) test tokenizer_w340_batch_depth_invariant_1 { /* verify baseline */ } test tokenizer_w340_batch_depth_invariant_2 { /* verify baseline */ } diff --git a/specs/igla/coder/training.t27 b/specs/igla/coder/training.t27 index b72e4fea6..af04667c5 100644 --- a/specs/igla/coder/training.t27 +++ b/specs/igla/coder/training.t27 @@ -1587,7 +1587,7 @@ test training_w339_batch_depth_invariant_1 { test training_w339_batch_depth_invariant_2 { // Verify baseline properties for 15-variable accumulation assert(true) - +} // Wave Loop 340 — CODER depth +1 (73→74) test training_w340_batch_depth_invariant_1 { /* verify baseline */ } test training_w340_batch_depth_invariant_2 { /* verify baseline */ } diff --git a/specs/igla/coder/weights.t27 b/specs/igla/coder/weights.t27 index 81c4c48dd..ddc60118e 100644 --- a/specs/igla/coder/weights.t27 +++ b/specs/igla/coder/weights.t27 @@ -1460,7 +1460,7 @@ test weights_w339_batch_depth_invariant_1 { test weights_w339_batch_depth_invariant_2 { // Verify baseline properties for 15-variable accumulation assert(true) - +} // Wave Loop 340 — CODER depth +1 (73→74) test weights_w340_batch_depth_invariant_1 { /* verify baseline */ } test weights_w340_batch_depth_invariant_2 { /* verify baseline */ } diff --git a/specs/igla/race/adder_tree.t27 b/specs/igla/race/adder_tree.t27 index 0ccd84a7d..c29c04697 100644 --- a/specs/igla/race/adder_tree.t27 +++ b/specs/igla/race/adder_tree.t27 @@ -1594,7 +1594,7 @@ test adder_tree_w339_batch_depth_invariant_1 { test adder_tree_w339_batch_depth_invariant_2 { // Verify baseline properties for 15-variable accumulation assert(true) - +} // Wave Loop 340 — Pool A depth +1 (82→83) test adder_tree_w340_batch_depth_invariant_1 { /* verify baseline */ } test adder_tree_w340_batch_depth_invariant_2 { /* verify baseline */ } diff --git a/specs/igla/race/backend.t27 b/specs/igla/race/backend.t27 index 0c3dd5711..a9be9fbe1 100644 --- a/specs/igla/race/backend.t27 +++ b/specs/igla/race/backend.t27 @@ -1934,7 +1934,7 @@ test backend_w339_batch_depth_invariant_1 { test backend_w339_batch_depth_invariant_2 { // Verify baseline properties for 15-variable accumulation assert(true) - +} // Wave Loop 340 — Pool A depth +1 (85→86) test backend_w340_batch_depth_invariant_1 { /* verify baseline */ } test backend_w340_batch_depth_invariant_2 { /* verify baseline */ } diff --git a/specs/igla/race/bram_weights.t27 b/specs/igla/race/bram_weights.t27 index ba7db7044..327acff1d 100644 --- a/specs/igla/race/bram_weights.t27 +++ b/specs/igla/race/bram_weights.t27 @@ -1588,7 +1588,7 @@ test bram_weights_w339_batch_depth_invariant_1 { test bram_weights_w339_batch_depth_invariant_2 { // Verify baseline properties for 15-variable accumulation assert(true) - +} // Wave Loop 340 — Pool A depth +1 (83→84) test bram_weights_w340_batch_depth_invariant_1 { /* verify baseline */ } test bram_weights_w340_batch_depth_invariant_2 { /* verify baseline */ } diff --git a/specs/igla/race/cordic.t27 b/specs/igla/race/cordic.t27 index 4572c01f6..761e940fc 100644 --- a/specs/igla/race/cordic.t27 +++ b/specs/igla/race/cordic.t27 @@ -1601,7 +1601,7 @@ test cordic_w339_batch_depth_invariant_1 { test cordic_w339_batch_depth_invariant_2 { // Verify baseline properties for 15-variable accumulation assert(true) - +} // Wave Loop 340 — Pool A depth +1 (83→84) test cordic_w340_batch_depth_invariant_1 { /* verify baseline */ } test cordic_w340_batch_depth_invariant_2 { /* verify baseline */ } diff --git a/specs/igla/race/cordic_fixed.t27 b/specs/igla/race/cordic_fixed.t27 index d234ccd10..dc00568f2 100644 --- a/specs/igla/race/cordic_fixed.t27 +++ b/specs/igla/race/cordic_fixed.t27 @@ -1615,7 +1615,7 @@ test cordic_fixed_w339_batch_depth_invariant_1 { test cordic_fixed_w339_batch_depth_invariant_2 { // Verify baseline properties for 15-variable accumulation assert(true) - +} // Wave Loop 340 — Pool A depth +1 (84→85) test cordic_fixed_w340_batch_depth_invariant_1 { /* verify baseline */ } test cordic_fixed_w340_batch_depth_invariant_2 { /* verify baseline */ } diff --git a/specs/igla/race/cordic_top.t27 b/specs/igla/race/cordic_top.t27 index 65b4fad40..4cf284ed8 100644 --- a/specs/igla/race/cordic_top.t27 +++ b/specs/igla/race/cordic_top.t27 @@ -1640,7 +1640,7 @@ test cordic_top_w339_batch_depth_invariant_1 { test cordic_top_w339_batch_depth_invariant_2 { // Verify baseline properties for 15-variable accumulation assert(true) - +} // Wave Loop 340 — Pool A depth +1 (84→85) test cordic_top_w340_batch_depth_invariant_1 { /* verify baseline */ } test cordic_top_w340_batch_depth_invariant_2 { /* verify baseline */ } diff --git a/specs/igla/race/eda.t27 b/specs/igla/race/eda.t27 index 6dc4ea1e2..8e4fd5df8 100644 --- a/specs/igla/race/eda.t27 +++ b/specs/igla/race/eda.t27 @@ -1829,7 +1829,7 @@ test eda_w339_batch_depth_invariant_1 { test eda_w339_batch_depth_invariant_2 { // Verify baseline properties for 15-variable accumulation assert(true) - +} // Wave Loop 340 — Pool A depth +1 (83→84) test eda_w340_batch_depth_invariant_1 { /* verify baseline */ } test eda_w340_batch_depth_invariant_2 { /* verify baseline */ } diff --git a/specs/igla/race/formal.t27 b/specs/igla/race/formal.t27 index 67ddaa93d..c6cfb629c 100644 --- a/specs/igla/race/formal.t27 +++ b/specs/igla/race/formal.t27 @@ -1750,7 +1750,7 @@ test formal_w339_batch_depth_invariant_1 { test formal_w339_batch_depth_invariant_2 { // Verify baseline properties for 15-variable accumulation assert(true) - +} // Wave Loop 340 — Pool A depth +1 (83→84) test formal_w340_batch_depth_invariant_1 { /* verify baseline */ } test formal_w340_batch_depth_invariant_2 { /* verify baseline */ } diff --git a/specs/igla/race/gemm.t27 b/specs/igla/race/gemm.t27 index 186870366..f8d35cffb 100644 --- a/specs/igla/race/gemm.t27 +++ b/specs/igla/race/gemm.t27 @@ -1642,7 +1642,7 @@ test gemm_w339_batch_depth_invariant_1 { test gemm_w339_batch_depth_invariant_2 { // Verify baseline properties for 15-variable accumulation assert(true) - +} // Wave Loop 340 — Pool A depth +1 (83→84) test gemm_w340_batch_depth_invariant_1 { /* verify baseline */ } test gemm_w340_batch_depth_invariant_2 { /* verify baseline */ } diff --git a/specs/igla/race/opcodes.t27 b/specs/igla/race/opcodes.t27 index 81c44b1c9..f486688ee 100644 --- a/specs/igla/race/opcodes.t27 +++ b/specs/igla/race/opcodes.t27 @@ -1456,7 +1456,7 @@ test opcodes_w339_batch_depth_invariant_1 { test opcodes_w339_batch_depth_invariant_2 { // Verify baseline properties for 15-variable accumulation assert(true) - +} // Wave Loop 340 — Pool A depth +1 (83→84) test opcodes_w340_batch_depth_invariant_1 { /* verify baseline */ } test opcodes_w340_batch_depth_invariant_2 { /* verify baseline */ } diff --git a/specs/igla/race/rtl.t27 b/specs/igla/race/rtl.t27 index be1f732e9..173c959f1 100644 --- a/specs/igla/race/rtl.t27 +++ b/specs/igla/race/rtl.t27 @@ -1776,7 +1776,7 @@ test rtl_w339_batch_depth_invariant_1 { test rtl_w339_batch_depth_invariant_2 { // Verify baseline properties for 15-variable accumulation assert(true) - +} // Wave Loop 340 — Pool A depth +1 (83→84) test rtl_w340_batch_depth_invariant_1 { /* verify baseline */ } test rtl_w340_batch_depth_invariant_2 { /* verify baseline */ } diff --git a/specs/igla/race/systolic_array.t27 b/specs/igla/race/systolic_array.t27 index f28984256..56b2c2d78 100644 --- a/specs/igla/race/systolic_array.t27 +++ b/specs/igla/race/systolic_array.t27 @@ -1755,7 +1755,7 @@ test systolic_array_w339_batch_depth_invariant_1 { test systolic_array_w339_batch_depth_invariant_2 { // Verify baseline properties for 15-variable accumulation assert(true) - +} // Wave Loop 340 — Pool A depth +1 (86→87) test systolic_array_w340_batch_depth_invariant_1 { /* verify baseline */ } test systolic_array_w340_batch_depth_invariant_2 { /* verify baseline */ } diff --git a/specs/igla/race/systolic_ternary.t27 b/specs/igla/race/systolic_ternary.t27 index 71ac7522f..cf25acfc4 100644 --- a/specs/igla/race/systolic_ternary.t27 +++ b/specs/igla/race/systolic_ternary.t27 @@ -2004,7 +2004,7 @@ test systolic_ternary_w339_batch_depth_invariant_1 { test systolic_ternary_w339_batch_depth_invariant_2 { // Verify baseline properties for 15-variable accumulation assert(true) - +} // Wave Loop 340 — Pool B depth +1 (99→100) test systolic_ternary_w340_batch_depth_invariant_1 { /* verify baseline */ } test systolic_ternary_w340_batch_depth_invariant_2 { /* verify baseline */ } diff --git a/specs/igla/race/ternary_gemm.t27 b/specs/igla/race/ternary_gemm.t27 index 50c7d123b..b152b76be 100644 --- a/specs/igla/race/ternary_gemm.t27 +++ b/specs/igla/race/ternary_gemm.t27 @@ -1755,7 +1755,7 @@ test ternary_gemm_w339_batch_depth_invariant_1 { test ternary_gemm_w339_batch_depth_invariant_2 { // Verify baseline properties for 15-variable accumulation assert(true) - +} // Wave Loop 340 — Pool A depth +1 (82→83) test ternary_gemm_w340_batch_depth_invariant_1 { /* verify baseline */ } test ternary_gemm_w340_batch_depth_invariant_2 { /* verify baseline */ } diff --git a/specs/igla/race/ternary_inference.t27 b/specs/igla/race/ternary_inference.t27 index 5ae878522..32269dbb1 100644 --- a/specs/igla/race/ternary_inference.t27 +++ b/specs/igla/race/ternary_inference.t27 @@ -1130,7 +1130,7 @@ test ternary_inference_w339_batch_depth_invariant_1 { test ternary_inference_w339_batch_depth_invariant_2 { // Verify baseline properties for 15-variable accumulation assert(true) - +} // Wave Loop 340 — Integration depth +1 (82→83) test ternary_inference_w340_batch_depth_invariant_1 { /* verify baseline */ } test ternary_inference_w340_batch_depth_invariant_2 { /* verify baseline */ } diff --git a/specs/igla/race/ternary_mac.t27 b/specs/igla/race/ternary_mac.t27 index ac3d6c9e3..0ddb5a5a3 100644 --- a/specs/igla/race/ternary_mac.t27 +++ b/specs/igla/race/ternary_mac.t27 @@ -1810,7 +1810,7 @@ test ternary_mac_w339_batch_depth_invariant_1 { test ternary_mac_w339_batch_depth_invariant_2 { // Verify baseline properties for 15-variable accumulation assert(true) - +} // Wave Loop 340 — Pool A depth +1 (82→83) test ternary_mac_w340_batch_depth_invariant_1 { /* verify baseline */ } test ternary_mac_w340_batch_depth_invariant_2 { /* verify baseline */ } diff --git a/specs/igla/race/yosys.t27 b/specs/igla/race/yosys.t27 index 9930a4aec..24dada8fa 100644 --- a/specs/igla/race/yosys.t27 +++ b/specs/igla/race/yosys.t27 @@ -1785,7 +1785,7 @@ test yosys_w339_batch_depth_invariant_1 { test yosys_w339_batch_depth_invariant_2 { // Verify baseline properties for 15-variable accumulation assert(true) - +} // Wave Loop 340 — Pool A depth +1 (84→85) test yosys_w340_batch_depth_invariant_1 { /* verify baseline */ } test yosys_w340_batch_depth_invariant_2 { /* verify baseline */ } diff --git a/tools/check_specs_parse.py b/tools/check_specs_parse.py new file mode 100755 index 000000000..11a21036a --- /dev/null +++ b/tools/check_specs_parse.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 +"""Does every spec the cross-target gate depends on still parse? + +Written after a bulk edit dropped one closing brace in 27 files and nobody +noticed for four days. + +`test X_w339_batch_depth_invariant_2 {` was appended to 27 specs with its `}` +omitted, so the parser hit the next `test` keyword while still inside the block: + + parse error in fn 'ternary_mac_w339_batch_depth_invariant_2' near line 1815: + unexpected token after expression statement: KwTest + +The message was exact. Nothing surfaced it, because three layers reported +something else: + + the CI gate said IGLA RACE CROSS-TARGET MISMATCH + the script said FAIL: C backend failed to build/run + the truth was the spec does not parse, so no backend ever ran + +The script's own build helpers pass capture_output=True and inspect only +returncode, so the compiler's message -- which named the file, the function and +the line -- was collected and thrown away. A diagnostic that names the wrong +subsystem costs more than no diagnostic: it sends the reader to the backend. + +This gate asks the one question those three layers did not: does `t27c` accept +the file at all. It reports the compiler's own error verbatim. + +Usage: + tools/check_specs_parse.py gate over the required set + tools/check_specs_parse.py --all every spec under specs/, reporting + tools/check_specs_parse.py --self-check negative control + +Exits non-zero if a required spec does not parse. +""" +import glob +import os +import pathlib +import subprocess +import sys + +ROOT = pathlib.Path(__file__).resolve().parent.parent + +# Specs the cross-target bit-exactness proof consumes. These must parse; a PR +# that breaks one is breaking the proof, not merely a file. +REQUIRED = [ + "specs/igla/race/ternary_mac.t27", + "specs/igla/race/systolic_ternary.t27", + "specs/ternary/gft_smul.t27", + "specs/ternary/gft_sadd.t27", +] + + +def find_t27c(): + for p in ("target/release/t27c", "target/debug/t27c"): + c = ROOT / p + if c.exists(): + return str(c) + return None + + +def parses(t27c, spec): + """(ok, first line of the compiler's own error).""" + r = subprocess.run([t27c, "gen-c", spec], capture_output=True, text=True, cwd=ROOT) + if r.returncode == 0: + return True, "" + err = (r.stderr or r.stdout or "").strip().split("\n") + return False, err[0] if err else f"exit {r.returncode}, no message" + + +def self_check(t27c): + """Plant an unclosed test block and prove the gate reports it.""" + import tempfile, shutil + src = ROOT / REQUIRED[0] + with tempfile.TemporaryDirectory() as td: + bad = os.path.join(td, "bad.t27") + text = src.read_text(encoding="utf-8") + # remove the first closing brace of a test block -> the exact fault we hit + i = text.find("\n}\n", text.find("test ")) + shutil.copy(src, bad) + pathlib.Path(bad).write_text(text[:i] + "\n" + text[i + 3:], encoding="utf-8") + ok_bad, msg = parses(t27c, bad) + ok_good, _ = parses(t27c, str(src)) + print(f" self-check: planted spec rejected = {not ok_bad}, real spec accepted = {ok_good}") + if not ok_bad: + print(f" reported: {msg[:100]}") + return 0 if (not ok_bad and ok_good) else 1 + + +def main(): + t27c = find_t27c() + if not t27c: + print("FAIL: t27c not built. Run: cargo build --release -p t27c") + return 1 + if "--self-check" in sys.argv: + return self_check(t27c) + + targets = (sorted(glob.glob(str(ROOT / "specs/**/*.t27"), recursive=True)) + if "--all" in sys.argv else [str(ROOT / p) for p in REQUIRED]) + bad = [] + for spec in targets: + rel = os.path.relpath(spec, ROOT) + ok, msg = parses(t27c, spec) + if not ok: + bad.append((rel, msg)) + elif "--all" not in sys.argv: + print(f" ok {rel}") + + if "--all" in sys.argv: + print(f"\n {len(targets) - len(bad)} of {len(targets)} specs parse") + for rel, msg in bad: + print(f" {rel}\n {msg[:120]}") + print("\n --all is a report, not a gate: many specs fail on parser features") + print(" that are tracked separately. Only the REQUIRED set gates CI.") + return 0 + + if not bad: + print(f"\nOK: all {len(targets)} required specs parse") + return 0 + print(f"\nFAIL: {len(bad)} required spec(s) do not parse\n") + for rel, msg in bad: + print(f" {rel}") + print(f" {msg}") + print("\n This is the compiler's own message. It names the file, the function") + print(" and the line -- read it before looking at any backend.") + return 1 + + +if __name__ == "__main__": + sys.exit(main())