From f864e9f9bcc9fb69203064239628bea3fd028c27 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Mon, 22 Jun 2026 08:37:57 +0900 Subject: [PATCH] test: retire 15 T2+T1-covered byte-id .c twins (fold-6 batch-1) These test/wcc/*_run.c carriers were migrated to test/lang @test packages (value-asserting under test-lang/T1) and now have a byte-id home under test-lang-byteid/T2. With T1 wired into make test + test-commit (prior commit), union(T1 runtime value-asserts + T2 .s byte-id) covers everything each .c proved, so the C twins are pure-deletion redundant. Each retired family below names the T1 twin that now carries its value obligation; every twin was verified to READ the value/slot and ASSERT it (not merely compile), including the pre-zeroed-slot families (it poisons cap!=len / spoils the base so a wrong data word fails the read-back, which byte-id alone cannot catch). 989_gunsigned_run -> gunsigned_test (global unsigned opcode select) 989_chainidx_run -> chainidx_test (chained m[i][k] header load) 989_idxarg_run -> idxarg_test (indexed elem call-arg header push) 906_callret_unsigned_arith -> callret_unsigned_test (call-return unsigned arm) 912_sar_shr_run -> sar_shr_test (signed >> emits SAR) 793_widen_pad_zero_run -> widen_pad_test (#227 high-pad zeroing; reads pad) 932_str_elem_cap_run -> str_elem_cap_test (str-elem .cap full 24B load) 957_size_type_run -> size_type_test (`size` type-position bind) 933_str_field_cap_run -> str_field_cap_test (str field .cap read) 934_str_chained_field_cap_run-> str_chained_field_cap_test (chained field .cap) 935_str_tuple_elem_cap_run -> str_tuple_elem_cap_test (tuple-elem str .cap) 936_str_arrfield_cap_run -> str_arrfield_cap_test (array-field str .cap) 937_str_arrfield_store_cap_run -> str_arrfield_store_cap_test (array-field store) 938_str_chainfield_store_cap_run-> str_chainfield_store_cap_test (chain-field store) 939_str_massign_store_cap_run -> str_massign_store_cap_test (multi-assign store) 110_uniesc_run.c is KEPT: it carries a .wwi round-trip sep-build (exit 42) the uniesc_test twin does not replicate (HAS-UNIQUE-MODE, deferred). --- Makefile | 115 --------- test/wcc/793_widen_pad_zero_run.c | 215 ---------------- test/wcc/906_callret_unsigned_arith_run.c | 263 -------------------- test/wcc/912_sar_shr_run.c | 221 ---------------- test/wcc/932_str_elem_cap_run.c | 173 ------------- test/wcc/933_str_field_cap_run.c | 197 --------------- test/wcc/934_str_chained_field_cap_run.c | 168 ------------- test/wcc/935_str_tuple_elem_cap_run.c | 169 ------------- test/wcc/936_str_arrfield_cap_run.c | 234 ----------------- test/wcc/937_str_arrfield_store_cap_run.c | 225 ----------------- test/wcc/938_str_chainfield_store_cap_run.c | 217 ---------------- test/wcc/939_str_massign_store_cap_run.c | 211 ---------------- test/wcc/957_size_type_run.c | 137 ---------- test/wcc/989_chainidx_run.c | 196 --------------- test/wcc/989_gunsigned_run.c | 190 -------------- test/wcc/989_idxarg_run.c | 204 --------------- 16 files changed, 3135 deletions(-) delete mode 100644 test/wcc/793_widen_pad_zero_run.c delete mode 100644 test/wcc/906_callret_unsigned_arith_run.c delete mode 100644 test/wcc/912_sar_shr_run.c delete mode 100644 test/wcc/932_str_elem_cap_run.c delete mode 100644 test/wcc/933_str_field_cap_run.c delete mode 100644 test/wcc/934_str_chained_field_cap_run.c delete mode 100644 test/wcc/935_str_tuple_elem_cap_run.c delete mode 100644 test/wcc/936_str_arrfield_cap_run.c delete mode 100644 test/wcc/937_str_arrfield_store_cap_run.c delete mode 100644 test/wcc/938_str_chainfield_store_cap_run.c delete mode 100644 test/wcc/939_str_massign_store_cap_run.c delete mode 100644 test/wcc/957_size_type_run.c delete mode 100644 test/wcc/989_chainidx_run.c delete mode 100644 test/wcc/989_gunsigned_run.c delete mode 100644 test/wcc/989_idxarg_run.c diff --git a/Makefile b/Makefile index e5cbf771..cbefd632 100644 --- a/Makefile +++ b/Makefile @@ -264,9 +264,7 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \ $(BIN)/test_taggedcompoundderef_reject \ $(BIN)/test_taggedcompoundplace_reject \ $(BIN)/test_libprecond_abort \ - $(BIN)/test_idxarg_run \ $(BIN)/test_ffivariadic_run \ - $(BIN)/test_chainidx_run \ $(BIN)/test_m1mangle_run \ $(BIN)/test_m1mangle_sym \ $(BIN)/test_m1usehint_run \ @@ -292,7 +290,6 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \ $(BIN)/test_zerotest_run \ $(BIN)/test_ampfncollide_run \ $(BIN)/test_trycallcollide_run \ - $(BIN)/test_gunsigned_run \ $(BIN)/test_taggedidx_run \ $(BIN)/test_fnptrcollide_run \ $(BIN)/test_tagnorm_run \ @@ -383,14 +380,6 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \ $(BIN)/test_match_slice_variant \ $(BIN)/test_match_slice_variant_run \ $(BIN)/test_str_abi_run \ - $(BIN)/test_str_elem_cap_run \ - $(BIN)/test_str_field_cap_run \ - $(BIN)/test_str_chained_field_cap_run \ - $(BIN)/test_str_tuple_elem_cap_run \ - $(BIN)/test_str_arrfield_cap_run \ - $(BIN)/test_str_arrfield_store_cap_run \ - $(BIN)/test_str_chainfield_store_cap_run \ - $(BIN)/test_str_massign_store_cap_run \ $(BIN)/test_slice_store_cap_run \ $(BIN)/test_subslice_cap_run \ $(BIN)/test_subslice_ptresz_run \ @@ -528,7 +517,6 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \ $(BIN)/test_tryprop_multisuccess \ $(BIN)/test_append_place \ $(BIN)/test_struct_tuple_field_slot \ - $(BIN)/test_widen_pad_zero_run \ $(BIN)/test_named_ptr_alias_variant_widen \ $(BIN)/test_single_field_struct_zeroinit \ $(BIN)/test_structvariant_largeunion_return \ @@ -602,8 +590,6 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \ $(BIN)/test_oddstruct_byval_ret_run \ $(BIN)/test_chained_dot_struct_copy_run \ $(BIN)/test_continue_run \ - $(BIN)/test_callret_unsigned_arith_run \ - $(BIN)/test_sar_shr_run \ $(BIN)/test_def_mangle_run \ $(BIN)/test_arr_u16_store_run \ $(BIN)/test_arr_module_index_run \ @@ -629,7 +615,6 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \ $(BIN)/test_structparam_run \ $(BIN)/test_structret_run \ $(BIN)/test_floats_run \ - $(BIN)/test_size_type_run \ $(BIN)/test_types_sizelim_run \ $(BIN)/test_types_intlim_run \ $(BIN)/test_opaque_decl_run \ @@ -819,17 +804,6 @@ $(BIN)/test_taggedcompoundplace_reject: test/wcc/989_taggedcompoundplace_reject. $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< -# 989_idxarg_run (F7-c2, #45/#46): an indexed slice/str element passed as -# a call arg must push its full multi-word header. Builds+runs each fixture -# on BOTH the cstage `ww` and wwstage `ww_ww` drivers (rule-10), so it needs -# the full cstage + wwstage tool sets plus libwwrt for the link. -$(BIN)/test_idxarg_run: test/wcc/989_idxarg_run.c \ - $(BIN)/ww $(BIN)/ww_ww \ - $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ - $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - # 989_ffivariadic_run (C1, catB-54): a ww caller of a C variadic fn must set # the SysV AL register to the count of XMM regs used for the variadic float # args — a RUNTIME-only correctness property (byte-id is blind to AL). The cc @@ -853,16 +827,6 @@ $(BIN)/test_ffivariadic_run: test/wcc/989_ffivariadic_run.c \ $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< -# 989_chainidx_run (F7-c3, #22): a chained index `m[i][k]` whose element is -# a str/slice must load the full 24B/16B header. Builds+runs each fixture on -# BOTH the cstage `ww` and wwstage `ww_ww` drivers (rule-10). -$(BIN)/test_chainidx_run: test/wcc/989_chainidx_run.c \ - $(BIN)/ww $(BIN)/ww_ww \ - $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ - $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - # 989_m1mangle_run / _sym (M1, #22 + #32): path-qualified symbol mangling # and root-unit entry detection. _run builds+runs a tiny package tree on # BOTH stages (rule-10); _sym asserts the emitted symbol names + cs.s==ww.s. @@ -1168,16 +1132,6 @@ $(BIN)/test_trycallcollide_run: test/wcc/989_trycallcollide_run.c \ $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< -# 989_gunsigned_run (F7-c5, #25): a module-global unsigned ident on the -# divide/shift/relational path must pick the unsigned opcode. Builds+runs on -# BOTH driver twins (rule-10). CLASS-M — see the test header. -$(BIN)/test_gunsigned_run: test/wcc/989_gunsigned_run.c \ - $(BIN)/ww $(BIN)/ww_ww \ - $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ - $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - # 989_taggedidx_run (F7-c6, #23): a tagged-union element indexed off a call # or slice base must load the full tag+payload cursor. Builds+runs on BOTH # driver twins (rule-10). @@ -1943,12 +1897,6 @@ $(BIN)/test_spread_variant_match: test/wcc/792_spread_variant_match.c \ $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_widen_pad_zero_run: test/wcc/793_widen_pad_zero_run.c \ - $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ - $(BIN)/w6c_ww \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - # #55: bare value-ident in an imported module must resolve same-module- # preferred (exprtype N_IDENT scopelookup -> scopelookupprefer). cstage # driver build + run pins routing; the ww-sep build (w6c_ww) must ACCEPT @@ -2467,54 +2415,6 @@ $(BIN)/test_str_abi_run: test/wcc/928_str_abi_run.c \ $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_str_elem_cap_run: test/wcc/932_str_elem_cap_run.c \ - $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ - $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - -$(BIN)/test_str_field_cap_run: test/wcc/933_str_field_cap_run.c \ - $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ - $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - -$(BIN)/test_str_chained_field_cap_run: test/wcc/934_str_chained_field_cap_run.c \ - $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ - $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - -$(BIN)/test_str_tuple_elem_cap_run: test/wcc/935_str_tuple_elem_cap_run.c \ - $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ - $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - -$(BIN)/test_str_arrfield_cap_run: test/wcc/936_str_arrfield_cap_run.c \ - $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ - $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - -$(BIN)/test_str_arrfield_store_cap_run: test/wcc/937_str_arrfield_store_cap_run.c \ - $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ - $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - -$(BIN)/test_str_chainfield_store_cap_run: test/wcc/938_str_chainfield_store_cap_run.c \ - $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ - $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - -$(BIN)/test_str_massign_store_cap_run: test/wcc/939_str_massign_store_cap_run.c \ - $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ - $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - $(BIN)/test_str_forrange_loopvar_run: test/wcc/940_str_forrange_loopvar_run.c \ $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ @@ -3489,17 +3389,6 @@ $(BIN)/test_continue_run: test/wcc/911_continue_run.c $(BIN)/ww \ $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_sar_shr_run: test/wcc/912_sar_shr_run.c $(BIN)/ww \ - $(BIN)/w6c $(BIN)/w6c_ww $(BIN)/w6a $(BIN)/w6l \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - -$(BIN)/test_callret_unsigned_arith_run: \ - test/wcc/906_callret_unsigned_arith_run.c $(BIN)/ww \ - $(BIN)/w6c $(BIN)/w6c_ww $(BIN)/w6a $(BIN)/w6l \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - $(BIN)/test_def_mangle_run: test/wcc/913_def_mangle_run.c $(BIN)/ww \ $(BIN)/w6c $(BIN)/w6c_ww $(BIN)/w6a $(BIN)/w6l \ $(LIB)/libwwrt.a | $(BIN) @@ -3563,10 +3452,6 @@ $(BIN)/test_floats_run: test/wcc/952_floats_run.c $(BIN)/ww $(BIN)/w6c \ $(BIN)/w6a $(BIN)/w6l $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_size_type_run: test/wcc/957_size_type_run.c $(BIN)/ww $(BIN)/w6c \ - $(BIN)/w6a $(BIN)/w6l $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - $(BIN)/test_types_sizelim_run: test/wcc/958_types_sizelim_run.c $(BIN)/ww \ $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< diff --git a/test/wcc/793_widen_pad_zero_run.c b/test/wcc/793_widen_pad_zero_run.c deleted file mode 100644 index a73f4e1e..00000000 --- a/test/wcc/793_widen_pad_zero_run.c +++ /dev/null @@ -1,215 +0,0 @@ -/* - * 793_widen_pad_zero_run — widening a NARROW scalar/float variant into a - * tagged-union slot whose payload is WIDER than one word must zero the - * high pad words (slot+16, slot+24), not leave them at whatever the - * frame slot last held. Project #227. - * - * cg_widen_tagged_store (cmd/w6c/cgen.c) and the wwstage twin - * cgwidentaggedstorebp (selfhost/cmd/wcc/cgenutil.ww) wrote only the tag - * (slot+0) and the value (slot+8) in their scalar and float arms; the - * remaining slot words were left uninitialised. For a >16B union (e.g. - * `(i64 | str)`, whose str variant makes the slot 32B = 4 words) a - * passthrough return / slot copy / `*u8` reinterpret of the now - * scalar-tagged value then read stack garbage at slot+16 / slot+24. - * - * Both stages were wrong the SAME way, so the 990-997 byte-id gates and - * the cs==ww asm gate were GREEN while the runtime was wrong — the bug - * is dead in the bootstrap corpus (the fmt `field` spread-union scalar - * widen is the first real consumer). The fix adds, in BOTH stages' - * scalar and float arms, an unconditional (size>16) tail-zero of - * slot+16..slot_sz that mirrors the existing tagged-subset / struct - * tail-zeros — so the two stages stay byte-identical (rule 10) and the - * full destination payload width is always defined. - * - * Observability: each row first widens a STR into the union (which fills - * slot+16/+24 with the str's len/cap), then reassigns a SCALAR / FLOAT - * into the SAME slot, then reads slot+16/+24 back through a `*u8` - * reinterpret (the bit-pinning idiom from 715_tagged_widen_f64). Pre-fix - * the reassign left the str's stale len/cap in the pad; the row returns - * 2 (pad1 nonzero) on a broken compiler and 0 when the pad is zeroed. - * Confirmed: pre-fix both `ww` and `ww_ww` exit 2 on row 0; post-fix - * both exit 0; and the emitted .s is byte-identical across stages. - * - * Rows (each its own single-module program, built + run under cstage - * `ww` and, when present, wwstage `ww_ww`; want = 0): - * scalar_i64_after_str — reassign i64 over a str-occupied (i64|str) - * slot; assert value survives and pad == 0. - * float_f64_after_str — reassign f64 over a str-occupied (f64|str) - * slot; the float arm path. Asserts the f64 - * bits survive at slot+8 and pad == 0. - * scalar_let_fresh — a plain `let r:(i64|str) = n;` (no prior str) - * in a deliberately dirtied frame; pad == 0. - */ -#include -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -struct row { const char *label; const char *src; int want; }; - -static const struct row rows[] = { - /* Reassign a scalar i64 over a str-occupied (i64|str) slot. The - * str write fills slot+16 (len) and slot+24 (cap); the scalar - * reassign must zero them. payload bits = 123, tag = 0 (i64). */ - { "scalar_i64_after_str", - "export fn main() i32 = {\n" - " let a: (i64 | str) = \"abcdefgh\";\n" - " a = 123i64;\n" - " let pp: *(i64 | str) = &a;\n" - " let pu: *u8 = pp: *u8;\n" - " let tagp: *i64 = pu: *i64;\n" - " let valp: *i64 = (pu + 8u64): *i64;\n" - " let pad1: *i64 = (pu + 16u64): *i64;\n" - " let pad2: *i64 = (pu + 24u64): *i64;\n" - " if (*valp != 123i64) { return 1; };\n" - " if (*pad1 != 0i64) { return 2; };\n" - " if (*pad2 != 0i64) { return 3; };\n" - " if (*tagp != 0i64) { return 4; };\n" - " return 0;\n" - "};\n", - 0 }, - /* Reassign an f64 over a str-occupied (f64|str) slot — the float - * arm. f64 1.0 == 0x3FF0000000000000 == 4607182418800017408. - * tag = 0 (f64 is variant 0). */ - { "float_f64_after_str", - "export fn main() i32 = {\n" - " let a: (f64 | str) = \"abcdefgh\";\n" - " a = 1.0;\n" - " let pp: *(f64 | str) = &a;\n" - " let pu: *u8 = pp: *u8;\n" - " let tagp: *i64 = pu: *i64;\n" - " let valp: *u64 = (pu + 8u64): *u64;\n" - " let pad1: *i64 = (pu + 16u64): *i64;\n" - " let pad2: *i64 = (pu + 24u64): *i64;\n" - " if (*valp != 4607182418800017408u64) { return 1; };\n" - " if (*pad1 != 0i64) { return 2; };\n" - " if (*pad2 != 0i64) { return 3; };\n" - " if (*tagp != 0i64) { return 4; };\n" - " return 0;\n" - "};\n", - 0 }, - /* Reassign a scalar over a str TWICE — exercises the scalar widen - * arm on a slot whose pad currently holds a str's len/cap, then - * again, confirming the tail-zero is emitted on every scalar widen - * (not just a first-write). Reads pad after the second reassign. */ - { "scalar_after_str_twice", - "export fn main() i32 = {\n" - " let a: (i64 | str) = \"firstone\";\n" - " a = 11i64;\n" - " a = \"secondxx\";\n" - " a = 222i64;\n" - " let pp: *(i64 | str) = &a;\n" - " let pu: *u8 = pp: *u8;\n" - " let valp: *i64 = (pu + 8u64): *i64;\n" - " let pad1: *i64 = (pu + 16u64): *i64;\n" - " let pad2: *i64 = (pu + 24u64): *i64;\n" - " if (*valp != 222i64) { return 1; };\n" - " if (*pad1 != 0i64) { return 2; };\n" - " if (*pad2 != 0i64) { return 3; };\n" - " return 0;\n" - "};\n", - 0 }, -}; - -static int -run_driver(const char *driver, const struct row *r, int i) -{ - char src[96], tmpdir[96], cmd[1024]; - snprintf(src, sizeof src, "/tmp/wpz_%d_%d.ww", getpid(), i); - snprintf(tmpdir, sizeof tmpdir, "/tmp/wpz_%d_d_%d", getpid(), i); - - FILE *f = fopen(src, "wb"); - if (!f) return -1; - fputs(r->src, f); - fclose(f); - - mkdir(tmpdir, 0755); - snprintf(cmd, sizeof cmd, "cd %s && %s build %s > /dev/null 2>&1", - tmpdir, driver, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: build via %s failed\n", - r->label, driver); - unlink(src); - snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir); - runwait(cmd); - return -1; - } - - const char *base = strrchr(src, '/'); - base = base ? base + 1 : src; - char outbin[160]; - snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); - char *dot = strrchr(outbin, '.'); - if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; - int got = runwait(outbin); - - unlink(src); - snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir); - runwait(cmd); - return got; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - char absbin[512]; - if (bin[0] != '/') { - char cwd[256]; - if (getcwd(cwd, sizeof cwd) == NULL) return 1; - snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); - bin = absbin; - } - - char cdrv[640], wdrv[640]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - - struct { const char *name; const char *path; int gated_on_existence; } - drivers[] = { - { "cstage", cdrv, 0 }, - { "wwstage", wdrv, 1 }, - { NULL, NULL, 0 }, - }; - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - for (int d = 0; drivers[d].name; d++) { - if (drivers[d].gated_on_existence - && access(drivers[d].path, X_OK) != 0) { - fprintf(stderr, "widen_pad_zero_run: skip %s (no %s)\n", - drivers[d].name, drivers[d].path); - continue; - } - for (int i = 0; i < n; i++) { - int got = run_driver(drivers[d].path, &rows[i], i); - total++; - if (got != rows[i].want) { - fprintf(stderr, - "widen_pad_zero_run[%s][%s]: exit=%d want=%d\n", - drivers[d].name, rows[i].label, - got, rows[i].want); - fail++; - } - } - } - if (fail) { - fprintf(stderr, - "widen_pad_zero_run: %d/%d fixtures failed\n", fail, total); - return 1; - } - printf("widen_pad_zero_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/906_callret_unsigned_arith_run.c b/test/wcc/906_callret_unsigned_arith_run.c deleted file mode 100644 index a3820dfc..00000000 --- a/test/wcc/906_callret_unsigned_arith_run.c +++ /dev/null @@ -1,263 +0,0 @@ -/* - * 906_callret_unsigned_arith_run — runtime + byte-id net for #168, the - * N_CALL twin of #134. The cgen signedness classifier must treat a - * div/mod/shift whose operand is a *call result* (`f() / d`, `f() % d`, - * `f() >> n`) as unsigned iff the callee's return type is unsigned — - * picking DIVQ/SHRQ over IDIVQ/SARQ. - * - * The wwstage `nodeisunsigned` (cgenutil.ww) walked surface nodes but - * had no N_CALL arm, so a call-result operand fell through to - * `return false` (signed) → signed IDIVQ/SARQ on an unsigned-returning - * call → silent wrong arithmetic. The cstage was already correct: it - * reads `type_isunsigned(n->lhs->type)` on the operand's stamped tinfo - * (cgen.c TK_SLASH/TK_PERCENT/TK_RSHIFT), and the N_CALL result type is - * stamped at check.c N_CALL (`n->type = u->ret`). #168 brings the - * wwstage arm up to read the same stamp — exactly as #134 did for the - * N_INDEX arm (which also touched no C). - * - * Gate-blind: the bootstrap never divides/shifts a call result by an - * unsigned type (strconv dodged it via local-bind, which lands on the - * N_IDENT arm). So only a direct call-result probe catches it — a - * `let a = f();` bind would hide the bug. - * - * Discrimination: - * - byte-id (.s cmp, the primary #168 gate): for ANY unsigned- - * returning call, w6c picks DIVQ/SHRQ but pre-fix w6c_ww picks - * IDIVQ/SARQ → .s DIFFER. Holds for every unsigned width (the - * classifier diverges regardless of the runtime value). - * - cstage runtime: validates the unsigned *semantics*. Only the - * machine-word rows (u64/uint, high bit set) differ at runtime - * between signed/unsigned div on the 64-bit register; the u32 row - * is a byte-id discriminator + runtime control (zero-extends to a - * positive 64-bit value, so the value alone can't tell the ops - * apart — the .s cmp does). - * - signed rows (i64/i32/int) guard against an over-broad fix: a - * signed-returning call must STILL pick IDIVQ/SARQ in both stages. - * - * Same dual-driver shape as 912_sar_shr_run.c: each row is (a) cstage - * `ww build` + run asserting exit 42, and (b) w6c vs w6c_ww `.s` cmp - * (rule-10 byte-id). Hard-fails if w6c_ww is missing so the wwstage - * side — the side that carried the bug — can never silently skip. - */ -#include -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -struct row { const char *label; const char *src; int want_exit; }; - -static const struct row rows[] = { - /* ---- unsigned: call result drives DIVQ/SHRQ ------------------- */ - /* u64 div: 0x8000000000000001 / 2. unsigned = 0x4000000000000000; - * signed IDIVQ (CQO sign-extends the high-bit-set dividend) = - * 0xC000000000000001. */ - { "u64_div_callret", - "package main;\n" - "fn uval() u64 = { return 0x8000000000000001u64; };\n" - "export fn main() i32 = {\n" - " if (uval() / 2u64 == 0x4000000000000000u64) { return 42; };\n" - " return 0;\n" - "};\n", 42 }, - /* u64 mod: unsigned rem = 1; signed rem = -1. */ - { "u64_mod_callret", - "package main;\n" - "fn uval() u64 = { return 0x8000000000000001u64; };\n" - "export fn main() i32 = {\n" - " if (uval() % 2u64 == 1u64) { return 42; };\n" - " return 0;\n" - "};\n", 42 }, - /* u64 shr: SHRQ (logical) = 0x4000000000000000; SARQ (arithmetic, - * sign-fills the set MSB) = 0xC000000000000000. */ - { "u64_shr_callret", - "package main;\n" - "fn uval() u64 = { return 0x8000000000000001u64; };\n" - "export fn main() i32 = {\n" - " if (uval() >> 1u64 == 0x4000000000000000u64) { return 42; };\n" - " return 0;\n" - "};\n", 42 }, - /* u32 div: byte-id discriminator (w6c DIVQ vs pre-fix w6c_ww - * IDIVQ); runtime control — 0x80000001 zero-extends to a positive - * 64-bit value, so signed/unsigned div agree at runtime. */ - { "u32_div_callret", - "package main;\n" - "fn uval() u32 = { return 0x80000001u32; };\n" - "export fn main() i32 = {\n" - " if (uval() / 2u32 == 0x40000000u32) { return 42; };\n" - " return 0;\n" - "};\n", 42 }, - /* uint (machine word): exercises the TY_UINT arm of typeisunsigned - * on a call result. High bit set → discriminates like u64. */ - { "uint_div_callret", - "package main;\n" - "fn uval() uint = { return (0x8000000000000001u64): uint; };\n" - "export fn main() i32 = {\n" - " if (uval() / 2 == (0x4000000000000000u64): uint) { return 42; };\n" - " return 0;\n" - "};\n", 42 }, - - /* ---- signed guards: call result STAYS IDIVQ/SARQ -------------- */ - /* i64 div: -100 / 7 = -14 (toward zero). If wrongly unsigned, -100 - * as u64 is huge → quotient huge ≠ -14. */ - { "i64_div_callret", - "package main;\n" - "fn sval() i64 = { return -100i64; };\n" - "export fn main() i32 = {\n" - " if (sval() / 7i64 == -14i64) { return 42; };\n" - " return 0;\n" - "};\n", 42 }, - /* i64 shr: -8 >> 1 = -4 via SARQ (sign-fill). SHRQ would zero-fill - * → large positive. */ - { "i64_shr_callret", - "package main;\n" - "fn sval() i64 = { return -8i64; };\n" - "export fn main() i32 = {\n" - " if (sval() >> 1i64 == -4i64) { return 42; };\n" - " return 0;\n" - "};\n", 42 }, - /* i32 div: -100 / 7 = -14. */ - { "i32_div_callret", - "package main;\n" - "fn sval() i32 = { return -100i32; };\n" - "export fn main() i32 = {\n" - " if (sval() / 7i32 == -14i32) { return 42; };\n" - " return 0;\n" - "};\n", 42 }, - /* int (machine word, signed): -100 / 7 = -14; exercises the signed - * machine-word path on a call result. */ - { "int_div_callret", - "package main;\n" - "fn sval() int = { return -100; };\n" - "export fn main() i32 = {\n" - " if (sval() / 7 == -14) { return 42; };\n" - " return 0;\n" - "};\n", 42 }, - { NULL, NULL, 0 } -}; - -static int -slurp_eq(const char *a, const char *b) -{ - FILE *fa = fopen(a, "rb"); - FILE *fb = fopen(b, "rb"); - if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; } - int rc = 0; - for (;;) { - int ca = fgetc(fa); - int cb = fgetc(fb); - if (ca != cb) { rc = -1; break; } - if (ca == EOF) break; - } - fclose(fa); fclose(fb); - return rc; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - char absbin[1024]; - if (bin[0] != '/') { - char cwd[1024]; - if (getcwd(cwd, sizeof cwd) == NULL) return 1; - snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); - bin = absbin; - } - - char w6c[1100], w6c_ww[1100]; - snprintf(w6c, sizeof w6c, "%s/w6c", bin); - snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin); - if (access(w6c_ww, X_OK) != 0) { - fprintf(stderr, "callret_unsigned: w6c_ww missing — cannot run " - "the cs==ww byte-id gate (the whole point of this test)\n"); - return 1; - } - - int n = 0, fail = 0; - for (int i = 0; rows[i].src; i++, n++) { - char src[64]; - snprintf(src, sizeof src, "/tmp/wwcr_%d_%d.ww", getpid(), i); - FILE *f = fopen(src, "wb"); - if (f == NULL) { fail++; continue; } - fputs(rows[i].src, f); - fclose(f); - - char tmpdir[64]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/wwcr_%d_d_%d", - getpid(), i); - mkdir(tmpdir, 0755); - - char cmd[2048]; - snprintf(cmd, sizeof cmd, "cd %s && %s/ww build %s", - tmpdir, bin, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: cstage build failed\n", - rows[i].label); - fail++; - unlink(src); rmdir(tmpdir); - continue; - } - - char outbin[128]; - const char *base = strrchr(src, '/'); - base = base ? base + 1 : src; - snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); - char *dot = strrchr(outbin, '.'); - if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; - - int got = runwait(outbin); - if (got != rows[i].want_exit) { - fprintf(stderr, "row[%s]: cstage exit %d, want %d\n", - rows[i].label, got, rows[i].want_exit); - fail++; - } - unlink(outbin); rmdir(tmpdir); - - char cs_s[64], ws_s[64]; - snprintf(cs_s, sizeof cs_s, "/tmp/wwcr_%d_%d_cs.s", - getpid(), i); - snprintf(ws_s, sizeof ws_s, "/tmp/wwcr_%d_%d_ww.s", - getpid(), i); - - snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", - w6c, cs_s, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label); - fail++; unlink(src); continue; - } - snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", - w6c_ww, ws_s, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: w6c_ww failed\n", - rows[i].label); - fail++; unlink(src); unlink(cs_s); continue; - } - if (slurp_eq(cs_s, ws_s) != 0) { - fprintf(stderr, - "row[%s]: cstage/wwstage .s DIFFER (rule-10 " - "byte-id violation)\n", rows[i].label); - fail++; - } - unlink(src); unlink(cs_s); unlink(ws_s); - } - - if (fail) { - fprintf(stderr, "%d/%d callret_unsigned tests failed\n", - fail, n); - return 1; - } - printf("callret_unsigned: %d/%d ok (cstage run + cs==ww byte-id)\n", - n, n); - return 0; -} diff --git a/test/wcc/912_sar_shr_run.c b/test/wcc/912_sar_shr_run.c deleted file mode 100644 index 6a898794..00000000 --- a/test/wcc/912_sar_shr_run.c +++ /dev/null @@ -1,221 +0,0 @@ -/* - * 912_sar_shr_run — runtime + byte-id net for #136: signed right-shift - * (both plain `>>` and `>>=`) must use SAR (arithmetic, sign-extends - * MSB), not SHR (logical, zero-fill). Pre-fix BOTH stages emitted SHRQ - * for signed RSHIFT because A_SARQ wasn't in the w6a opcode table, so - * `let i: i32 = -200; i >>= 2;` produced i = 0x3FFFFFCE (1073741774) - * instead of -50. cs==ww held → gate-blind. #133-expanded shipped with - * this pre-existing concern documented (parity with cgen.c:4145 deref- - * lvalue compound site); #136 closes it. - * - * Fix: add A_SARQ to w6a + w6c + w6a_ww opcode tables (encoding REX.W - * D3 /7, parallel to SHR's D3 /5), then sweep cgen sites in both - * stages to dispatch SARQ vs SHRQ on the operand signedness. Sites - * covered (per-stage 6 each): - * - plain `>>` binop (cgbin TK_RSHIFT) - * - chained-ptr-field compound RSHIFTEQ (#133-expanded site 1) - * - N_INDEX-lhs compound RSHIFTEQ (#133-expanded site 2) - * - deref-target compound RSHIFTEQ (pre-existing) - * - top-level let compound RSHIFTEQ - * - IDENT-local compound RSHIFTEQ - * - * The bootstrap audit at design time showed zero current signed-RSHIFT - * callers in lib/+selfhost/, so 990-997 byte-id stays GREEN; the only - * shifts are on the previously-broken paths. - * - * Rows cover the operator + signedness × storage matrix: - * - i32_neg_rshifteq: lead's repro pattern, -200 >>= 2 = -50 (was - * 1073741774 pre-fix on SHRQ) - * - i64_neg_rshifteq: same shape, i64 - * - i32_pos_rshifteq: control, SARQ and SHRQ produce same result on - * positives; asserts no regression - * - u32_rshifteq: unsigned control, must still use SHRQ (no change) - * - i32_neg_rshift_binop: plain `>>` (not compound) on negative i32, - * covers the cgen 2596 / 3176 TK_RSHIFT N_BIN site - * - * Each row carries (a) cstage `ww build` + run asserting the exit - * code and (b) w6c vs w6c_ww `.s` cmp (rule-10 byte-id). Exit codes - * use small absolute values + 256 to encode signed expectations: - * -50 → 206 = 256-50 (Unix exit is 8-bit unsigned). i64 row clamped - * similarly. - */ -#include -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -struct row { const char *label; const char *src; int want_exit; }; - -static const struct row rows[] = { - /* The #136 repro: signed i32 >>=. Pre-fix produced wrong large - * positive; post-fix produces -50. Exit code = -50 as u8 = 206. */ - { "i32_neg_rshifteq", - "package main;\n" - "export fn main() i32 = {\n" - " let i: i32 = -200;\n" - " i >>= 2;\n" - " return i;\n" - "};\n", 206 }, - /* signed i64 >>=. Same shape, wider type. Result -50 as exit u8 - * = 206. */ - { "i64_neg_rshifteq", - "package main;\n" - "export fn main() i32 = {\n" - " let i: i64 = -200i64;\n" - " i >>= 2i64;\n" - " return i: i32;\n" - "};\n", 206 }, - /* Positive control: SARQ and SHRQ agree. 200 >> 2 = 50. */ - { "i32_pos_rshifteq", - "package main;\n" - "export fn main() i32 = {\n" - " let i: i32 = 200;\n" - " i >>= 2;\n" - " return i;\n" - "};\n", 50 }, - /* Unsigned control: SHRQ unchanged. 200u32 >> 2 = 50. */ - { "u32_rshifteq", - "package main;\n" - "export fn main() i32 = {\n" - " let i: u32 = 200u32;\n" - " i >>= 2u32;\n" - " return i: i32;\n" - "};\n", 50 }, - /* Plain `>>` (not compound) on signed negative: covers cgbin - * TK_RSHIFT site (cgen.c:2596 / cgenexpr.ww:3176). */ - { "i32_neg_rshift_binop", - "package main;\n" - "export fn main() i32 = {\n" - " let i: i32 = -200;\n" - " let r: i32 = i >> 2;\n" - " return r;\n" - "};\n", 206 }, - { NULL, NULL, 0 } -}; - -static int -slurp_eq(const char *a, const char *b) -{ - FILE *fa = fopen(a, "rb"); - FILE *fb = fopen(b, "rb"); - if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; } - int rc = 0; - for (;;) { - int ca = fgetc(fa); - int cb = fgetc(fb); - if (ca != cb) { rc = -1; break; } - if (ca == EOF) break; - } - fclose(fa); fclose(fb); - return rc; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - char absbin[1024]; - if (bin[0] != '/') { - char cwd[1024]; - if (getcwd(cwd, sizeof cwd) == NULL) return 1; - snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); - bin = absbin; - } - - char w6c[1100], w6c_ww[1100]; - snprintf(w6c, sizeof w6c, "%s/w6c", bin); - snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin); - if (access(w6c_ww, X_OK) != 0) { - fprintf(stderr, "sarshr: w6c_ww missing — cannot run the " - "cs==ww byte-id gate (the whole point of this test)\n"); - return 1; - } - - int n = 0, fail = 0; - for (int i = 0; rows[i].src; i++, n++) { - char src[64]; - snprintf(src, sizeof src, "/tmp/wwsar_%d_%d.ww", getpid(), i); - FILE *f = fopen(src, "wb"); - if (f == NULL) { fail++; continue; } - fputs(rows[i].src, f); - fclose(f); - - char tmpdir[64]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/wwsar_%d_d_%d", - getpid(), i); - mkdir(tmpdir, 0755); - - char cmd[2048]; - snprintf(cmd, sizeof cmd, "cd %s && %s/ww build %s", - tmpdir, bin, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: cstage build failed\n", - rows[i].label); - fail++; - unlink(src); rmdir(tmpdir); - continue; - } - - char outbin[128]; - const char *base = strrchr(src, '/'); - base = base ? base + 1 : src; - snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); - char *dot = strrchr(outbin, '.'); - if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; - - int got = runwait(outbin); - if (got != rows[i].want_exit) { - fprintf(stderr, "row[%s]: cstage exit %d, want %d\n", - rows[i].label, got, rows[i].want_exit); - fail++; - } - unlink(outbin); rmdir(tmpdir); - - char cs_s[64], ws_s[64]; - snprintf(cs_s, sizeof cs_s, "/tmp/wwsar_%d_%d_cs.s", - getpid(), i); - snprintf(ws_s, sizeof ws_s, "/tmp/wwsar_%d_%d_ww.s", - getpid(), i); - - snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", - w6c, cs_s, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label); - fail++; unlink(src); continue; - } - snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", - w6c_ww, ws_s, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: w6c_ww failed\n", - rows[i].label); - fail++; unlink(src); unlink(cs_s); continue; - } - if (slurp_eq(cs_s, ws_s) != 0) { - fprintf(stderr, - "row[%s]: cstage/wwstage .s DIFFER (rule-10 " - "byte-id violation)\n", rows[i].label); - fail++; - } - unlink(src); unlink(cs_s); unlink(ws_s); - } - - if (fail) { - fprintf(stderr, "%d/%d sar/shr tests failed\n", fail, n); - return 1; - } - printf("sarshr: %d/%d ok (cstage run + cs==ww byte-id)\n", - n, n); - return 0; -} diff --git a/test/wcc/932_str_elem_cap_run.c b/test/wcc/932_str_elem_cap_run.c deleted file mode 100644 index 0d17553a..00000000 --- a/test/wcc/932_str_elem_cap_run.c +++ /dev/null @@ -1,173 +0,0 @@ -/* - * 932_str_elem_cap_run — runtime coverage for the F2 fold: a str-element - * VALUE read via N_INDEX must load the full 24B {ptr,len,cap} header, not - * just {ptr,len}. str is 24B since Phase 2 (#1); pre-F2 the N_INDEX - * str-element arms dropped the cap word. - * - * The existing byte-id gates (990-997) can't catch a no-op fold here: a - * shared 2-word miscompile passes byte-id silently. So this pins the - * *runtime* contract — build each fixture through both the cstage `ww` - * and the wwstage `ww_ww` driver and confirm the program's assertions - * hold (exit 0). - * - * Each row POISONS the element so cap != len (a `.cap =` pseudo-field - * write, no malloc / no import — keeps the fixture self-contained so - * ww_ww writes intermediates only next to the /tmp source, not lib/). - * A 2-word read leaves cap = len (Site A) or a stale slice-cap (Site B), - * so the read-back .cap mismatches the poisoned value and the row fails. - * - * Both N_INDEX base forms are covered: - * - Site A: N_IDENT base — `xs[0]` on a local `[N]str`. - * - Site B: fallback base — `b.items[0]` where the base `b.items` is - * an N_DOT (slice field), not a plain ident. - * - * The cap word is observed through `let e: str = ` (a 3-word - * copy into the slot) then `e.cap` (an N_IDENT pseudo-field read off the - * slot). A direct `.cap` would NOT observe it — the non-ident - * .cap N_DOT path only handles .ptr/.len today (a separate gap, out of - * F2's scope). - */ -#include -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -struct row { const char *label; const char *src; int want; }; - -static const struct row rows[] = { - /* Site A — N_IDENT base. Poison cap=8 (len=2), read back via a - * local [2]str. Broken 2-word read leaves cap = len = 2. */ - { "sitea_ident_base", - "export fn main() i32 = {\n" - " let p: str = \"hi\";\n" - " p.cap = 8i32;\n" - " let xs: [2]str;\n" - " xs[0] = p;\n" - " let e: str = xs[0];\n" - " if (e.cap: i32 != 8) { return 1; };\n" - " if (e.len: i32 != 2) { return 2; };\n" - " return 0;\n" - "};\n", - 0 }, - /* Site B — fallback base. Element behind an N_DOT slice field, so - * the N_INDEX base isn't a plain ident. Poison cap=9 (len=5). - * Broken read leaves cap = the slice header's own cap, not the - * element's. */ - { "siteb_fallback_base", - "type box = struct { items: []str };\n" - "export fn main() i32 = {\n" - " let q: str = \"world\";\n" - " q.cap = 9i32;\n" - " let arr: [2]str;\n" - " arr[0] = q;\n" - " let b: box;\n" - " b.items = arr[0:2];\n" - " let e: str = b.items[0];\n" - " if (e.cap: i32 != 9) { return 1; };\n" - " if (e.len: i32 != 5) { return 2; };\n" - " return 0;\n" - "};\n", - 0 }, -}; - -static int -run_driver(const char *driver, const struct row *r, int i) -{ - char src[96], tmpdir[96], cmd[1024]; - snprintf(src, sizeof src, "/tmp/strelemcap_%d_%d.ww", getpid(), i); - snprintf(tmpdir, sizeof tmpdir, "/tmp/strelemcap_%d_d_%d", getpid(), i); - - FILE *f = fopen(src, "wb"); - if (!f) return -1; - fputs(r->src, f); - fclose(f); - - mkdir(tmpdir, 0755); - snprintf(cmd, sizeof cmd, "cd %s && %s build %s", - tmpdir, driver, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: build via %s failed\n", - r->label, driver); - unlink(src); rmdir(tmpdir); - return -1; - } - - const char *base = strrchr(src, '/'); - base = base ? base + 1 : src; - char outbin[160]; - snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); - char *dot = strrchr(outbin, '.'); - if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; - int got = runwait(outbin); - - unlink(src); unlink(outbin); rmdir(tmpdir); - return got; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - char absbin[512]; - if (bin[0] != '/') { - char cwd[256]; - if (getcwd(cwd, sizeof cwd) == NULL) return 1; - snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); - bin = absbin; - } - - char cdrv[640]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - char wdrv[640]; - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - - struct { const char *name; const char *path; int gated_on_existence; } - drivers[] = { - { "cstage", cdrv, 0 }, - { "wwstage", wdrv, 1 }, - { NULL, NULL, 0 }, - }; - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - for (int d = 0; drivers[d].name; d++) { - if (drivers[d].gated_on_existence - && access(drivers[d].path, X_OK) != 0) { - fprintf(stderr, - "str_elem_cap_run: skip %s (no %s)\n", - drivers[d].name, drivers[d].path); - continue; - } - for (int i = 0; i < n; i++) { - int got = run_driver(drivers[d].path, &rows[i], i); - total++; - if (got != rows[i].want) { - fprintf(stderr, - "str_elem_cap_run[%s][%s]: exit=%d want=%d\n", - drivers[d].name, rows[i].label, - got, rows[i].want); - fail++; - } - } - } - - if (fail) { - fprintf(stderr, "str_elem_cap_run: %d/%d fixtures failed\n", - fail, total); - return 1; - } - printf("str_elem_cap_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/933_str_field_cap_run.c b/test/wcc/933_str_field_cap_run.c deleted file mode 100644 index 680df3b2..00000000 --- a/test/wcc/933_str_field_cap_run.c +++ /dev/null @@ -1,197 +0,0 @@ -/* - * 933_str_field_cap_run — runtime coverage for the C4.6 fold: reading a - * str-typed FIELD of a struct (N_DOT value read) must load the full 24B - * {ptr,len,cap} header, not just {ptr,len}. str is 24B since Phase 2 (#1); - * pre-C4.6 the N_DOT str-field arms loaded 2 words and dropped cap. - * - * The byte-id gates (990-997) can't catch a symmetric 2-word miscompile: - * if both stages drop cap identically, byte-id passes silently. So this - * pins the *runtime* contract — build each fixture through both the cstage - * `ww` and the wwstage `ww_ww` driver and confirm the assertions hold - * (exit 0). The global row is the load-bearing case: in ww the global - * struct-field load is a separate arm with no slice sibling (cstage folds - * local+global in one base_reg arm; ww splits them), so a cap-drop there - * would hide from the local-field rows. - * - * Each row POISONS the source str so cap != len (a `.cap =` pseudo-field - * write, no malloc / no import — self-contained, so ww_ww writes - * intermediates only next to the /tmp source). A 2-word field read leaves - * cap = stale, so the read-back .cap mismatches the poison and the row - * fails. The cap word is observed through `let s: str = ` (a 3-word - * copy into the slot) then `s.cap` (an N_IDENT pseudo-field read off the - * slot) — mirrors the 932 observation shape. - * - * Sites (C4.6 S1 + S2, case A): - * - s1local : `st.f` — direct struct local field (base BP). - * - s1global : `g.f` — direct struct global field (base CX). - * - s2ptr : `pst.f` — field via a *struct local (base BX, deref). - */ -#include -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -struct row { const char *label; const char *src; int want; }; - -static const struct row rows[] = { - /* S1 local — `st.f` direct struct local field. Poison cap=8 - * (len=2). spoil() interposes a call between the field store and - * the read: a call clobbers caller-saved CX, so a broken 2-word - * read leaves cap = spoil's leftover (44), not the lingering store - * value — without it CX would coincidentally still hold the poison - * the `st.f = p` store left behind and the row wouldn't discriminate. */ - { "s1local_field", - "type rec = struct { f: str };\n" - "fn spoil() i32 = {\n" - " let z: str = \"zzzz\";\n" - " z.cap = 44i32;\n" - " let w: str = z;\n" - " return w.cap: i32;\n" - "};\n" - "export fn main() i32 = {\n" - " let p: str = \"hi\";\n" - " p.cap = 8i32;\n" - " let st: rec;\n" - " st.f = p;\n" - " let junk: i32 = spoil();\n" - " let s: str = st.f;\n" - " if (s.cap: i32 != 8) { return 1; };\n" - " if (s.len: i32 != 2) { return 2; };\n" - " if (junk != 44) { return 3; };\n" - " return 0;\n" - "};\n", - 0 }, - /* S1 global — `g.f` direct struct global field (base CX path). - * The no-local-sibling arm in ww; poison cap=9 (len=5). */ - { "s1global_field", - "type rec = struct { f: str };\n" - "let g: rec;\n" - "export fn main() i32 = {\n" - " let p: str = \"world\";\n" - " p.cap = 9i32;\n" - " g.f = p;\n" - " let s: str = g.f;\n" - " if (s.cap: i32 != 9) { return 1; };\n" - " if (s.len: i32 != 5) { return 2; };\n" - " return 0;\n" - "};\n", - 0 }, - /* S2 case A — `pst.f` field via a *struct local (deref, base BX). - * Poison cap=7 (len=3). */ - { "s2ptr_field", - "type rec = struct { f: str };\n" - "export fn main() i32 = {\n" - " let p: str = \"abc\";\n" - " p.cap = 7i32;\n" - " let st: rec;\n" - " st.f = p;\n" - " let pst: *rec = &st;\n" - " let s: str = pst.f;\n" - " if (s.cap: i32 != 7) { return 1; };\n" - " if (s.len: i32 != 3) { return 2; };\n" - " return 0;\n" - "};\n", - 0 }, -}; - -static int -run_driver(const char *driver, const struct row *r, int i) -{ - char src[96], tmpdir[96], cmd[1024]; - snprintf(src, sizeof src, "/tmp/strfieldcap_%d_%d.ww", getpid(), i); - snprintf(tmpdir, sizeof tmpdir, "/tmp/strfieldcap_%d_d_%d", getpid(), i); - - FILE *f = fopen(src, "wb"); - if (!f) return -1; - fputs(r->src, f); - fclose(f); - - mkdir(tmpdir, 0755); - snprintf(cmd, sizeof cmd, "cd %s && %s build %s", - tmpdir, driver, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: build via %s failed\n", - r->label, driver); - unlink(src); rmdir(tmpdir); - return -1; - } - - const char *base = strrchr(src, '/'); - base = base ? base + 1 : src; - char outbin[160]; - snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); - char *dot = strrchr(outbin, '.'); - if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; - int got = runwait(outbin); - - unlink(src); unlink(outbin); rmdir(tmpdir); - return got; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - char absbin[512]; - if (bin[0] != '/') { - char cwd[256]; - if (getcwd(cwd, sizeof cwd) == NULL) return 1; - snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); - bin = absbin; - } - - char cdrv[640]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - char wdrv[640]; - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - - struct { const char *name; const char *path; int gated_on_existence; } - drivers[] = { - { "cstage", cdrv, 0 }, - { "wwstage", wdrv, 1 }, - { NULL, NULL, 0 }, - }; - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - for (int d = 0; drivers[d].name; d++) { - if (drivers[d].gated_on_existence - && access(drivers[d].path, X_OK) != 0) { - fprintf(stderr, - "str_field_cap_run: skip %s (no %s)\n", - drivers[d].name, drivers[d].path); - continue; - } - for (int i = 0; i < n; i++) { - int got = run_driver(drivers[d].path, &rows[i], i); - total++; - if (got != rows[i].want) { - fprintf(stderr, - "str_field_cap_run[%s][%s]: exit=%d want=%d\n", - drivers[d].name, rows[i].label, - got, rows[i].want); - fail++; - } - } - } - - if (fail) { - fprintf(stderr, "str_field_cap_run: %d/%d fixtures failed\n", - fail, total); - return 1; - } - printf("str_field_cap_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/934_str_chained_field_cap_run.c b/test/wcc/934_str_chained_field_cap_run.c deleted file mode 100644 index c8bdcb12..00000000 --- a/test/wcc/934_str_chained_field_cap_run.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * 934_str_chained_field_cap_run — runtime coverage for the C4.6-caseB fold: - * a CHAINED str-field read `o.p.f` (depth >= 2, where p is a *struct field of - * o and f is a str field of *p) must load the full 24B {ptr,len,cap} header, - * not just {ptr,len}. str is 24B since Phase 2 (#1); pre-caseB the chained - * *struct N_DOT str arm loaded 2 words (ptr in AX, len in BX) and dropped cap. - * - * This is the chained sibling of 933 (which covered the direct / single-deref - * S1/S2 arms). The site is cgen.c's "Chained N_DOT through a *struct field" - * branch (n->lhs is itself an N_DOT typed *struct): the lhs cgexpr leaves AX = - * the inner *struct pointer, then the field is read off AX. caseB folds the - * str arm onto the adjacent 3-word slice arm — cap loads from +16(AX) and the - * base (AX = ptr) is read LAST so the earlier index loads still see the base. - * - * The byte-id gates (990-997) can't catch a symmetric 2-word miscompile: if - * both stages drop cap identically, byte-id passes silently. So this pins the - * *runtime* contract — build through both the cstage `ww` and wwstage `ww_ww` - * driver and confirm the assertion holds (exit 0). - * - * DISCRIMINATION (heed the 933 lesson): a 2-word read leaves CX untouched, so - * the row could coincidentally pass if CX happened to still carry the poison - * cap. spoil() interposes a call between the field store and the chained read; - * a call clobbers caller-saved CX (spoil's own str copy leaves cap=44 in CX), - * so a broken 2-word read observes cap=44, not 8, and the row fails (return 1). - * Verified fail-before (reverted fold: exit 1) / pass-after (exit 0), both - * drivers. - */ -#include -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -struct row { const char *label; const char *src; int want; }; - -static const struct row rows[] = { - /* caseB — `o.p.f` chained read: o a struct holding p: *inr, inr - * holding a str field f. Base AX = the inner *struct pointer; the - * str field is read 3-word off AX (cap at +16). Poison cap=8 - * (len=2). spoil() interposes a CX-clobbering call between the - * `ist.f = p` store and the chained `o.p.f` read so a broken - * 2-word read cannot coincidentally pass on a stale CX. */ - { "caseb_chained_field", - "type inr = struct { f: str };\n" - "type otr = struct { p: *inr };\n" - "fn spoil() i32 = {\n" - " let z: str = \"zzzz\";\n" - " z.cap = 44i32;\n" - " let w: str = z;\n" - " return w.cap: i32;\n" - "};\n" - "export fn main() i32 = {\n" - " let p: str = \"hi\";\n" - " p.cap = 8i32;\n" - " let ist: inr;\n" - " ist.f = p;\n" - " let o: otr;\n" - " o.p = &ist;\n" - " let junk: i32 = spoil();\n" - " let s: str = o.p.f;\n" - " if (s.cap: i32 != 8) { return 1; };\n" - " if (s.len: i32 != 2) { return 2; };\n" - " if (junk != 44) { return 3; };\n" - " return 0;\n" - "};\n", - 0 }, -}; - -static int -run_driver(const char *driver, const struct row *r, int i) -{ - char src[96], tmpdir[96], cmd[1024]; - snprintf(src, sizeof src, "/tmp/strchainfieldcap_%d_%d.ww", getpid(), i); - snprintf(tmpdir, sizeof tmpdir, "/tmp/strchainfieldcap_%d_d_%d", getpid(), i); - - FILE *f = fopen(src, "wb"); - if (!f) return -1; - fputs(r->src, f); - fclose(f); - - mkdir(tmpdir, 0755); - snprintf(cmd, sizeof cmd, "cd %s && %s build %s", - tmpdir, driver, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: build via %s failed\n", - r->label, driver); - unlink(src); rmdir(tmpdir); - return -1; - } - - const char *base = strrchr(src, '/'); - base = base ? base + 1 : src; - char outbin[160]; - snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); - char *dot = strrchr(outbin, '.'); - if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; - int got = runwait(outbin); - - unlink(src); unlink(outbin); rmdir(tmpdir); - return got; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - char absbin[512]; - if (bin[0] != '/') { - char cwd[256]; - if (getcwd(cwd, sizeof cwd) == NULL) return 1; - snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); - bin = absbin; - } - - char cdrv[640]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - char wdrv[640]; - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - - struct { const char *name; const char *path; int gated_on_existence; } - drivers[] = { - { "cstage", cdrv, 0 }, - { "wwstage", wdrv, 1 }, - { NULL, NULL, 0 }, - }; - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - for (int d = 0; drivers[d].name; d++) { - if (drivers[d].gated_on_existence - && access(drivers[d].path, X_OK) != 0) { - fprintf(stderr, - "str_chained_field_cap_run: skip %s (no %s)\n", - drivers[d].name, drivers[d].path); - continue; - } - for (int i = 0; i < n; i++) { - int got = run_driver(drivers[d].path, &rows[i], i); - total++; - if (got != rows[i].want) { - fprintf(stderr, - "str_chained_field_cap_run[%s][%s]: exit=%d want=%d\n", - drivers[d].name, rows[i].label, - got, rows[i].want); - fail++; - } - } - } - - if (fail) { - fprintf(stderr, "str_chained_field_cap_run: %d/%d fixtures failed\n", - fail, total); - return 1; - } - printf("str_chained_field_cap_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/935_str_tuple_elem_cap_run.c b/test/wcc/935_str_tuple_elem_cap_run.c deleted file mode 100644 index ba8bf229..00000000 --- a/test/wcc/935_str_tuple_elem_cap_run.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * 935_str_tuple_elem_cap_run — runtime coverage for the C4.6-S3 fold: a - * tuple POSITIONAL str element read `t.N` (N_IDENT base, TY_TUPLE) must load - * the full 24B {ptr,len,cap} header, not just {ptr,len}. str is 24B since - * Phase 2 (#1); pre-S3 the tuple str-element arm loaded 2 words (ptr in AX, - * len in BX) and dropped cap. - * - * This is the "author-to-ABI" coda of C4.6: UNLIKE S1/S2/caseB there is no - * adjacent slice-element arm to fold onto, so the 3-word triple is authored - * directly to the canonical slice-header ABI (AX=ptr, BX=len, CX=cap). The - * base is BP (the frame, not a target reg), so the canonical ptr/len/cap - * order has no clobber subtlety. Sites: cgen.c's "tuple positional field - * access" branch and the cgenexpr.ww N_TTUPLE twin. - * - * The byte-id gates (990-997) can't catch a symmetric 2-word miscompile: if - * both stages drop cap identically, byte-id passes silently. So this pins the - * *runtime* contract — build through both the cstage `ww` and wwstage `ww_ww` - * driver and confirm the assertion holds (exit 0). - * - * The tuple is built by a (i64, str)-returning mk() so the poisoned cap=8 - * rides the 4-word return ABI (AX=scalar, DX=ptr, CX=len, R8=cap) into the - * tuple slot's +24 word; `t.1` then reads it back 3-word (cap at slot+8+16). - * - * DISCRIMINATION (heed the 933/934 lesson): a 2-word read leaves CX untouched, - * so the row could coincidentally pass on a stale CX. spoil() interposes a - * call between the `let t = mk()` build and the `t.1` read; a call clobbers - * caller-saved CX (spoil's own str copy leaves cap=44 in CX), so a broken - * 2-word read observes cap=44, not 8, and the row fails (return 1). Verified - * fail-before (reverted +16 cap load: exit 1) / pass-after (exit 0), both - * drivers. - */ -#include -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -struct row { const char *label; const char *src; int want; }; - -static const struct row rows[] = { - /* S3 — `t.N` positional str element. t a local (i64, str) tuple - * built by mk() so the poison cap=8 (len=2) rides the return ABI - * into the str element's slot. spoil() interposes a CX-clobbering - * call between the build and the `t.1` read so a broken 2-word read - * cannot coincidentally pass on a stale CX. */ - { "tuple_positional_str_elem", - "fn mk() (i64, str) = {\n" - " let p: str = \"hi\";\n" - " p.cap = 8i32;\n" - " return (5i64, p);\n" - "};\n" - "fn spoil() i32 = {\n" - " let z: str = \"zzzz\";\n" - " z.cap = 44i32;\n" - " let w: str = z;\n" - " return w.cap: i32;\n" - "};\n" - "export fn main() i32 = {\n" - " let t: (i64, str) = mk();\n" - " let junk: i32 = spoil();\n" - " let s: str = t.1;\n" - " if (s.cap: i32 != 8) { return 1; };\n" - " if (s.len: i32 != 2) { return 2; };\n" - " if (junk != 44) { return 3; };\n" - " return 0;\n" - "};\n", - 0 }, -}; - -static int -run_driver(const char *driver, const struct row *r, int i) -{ - char src[96], tmpdir[96], cmd[1024]; - snprintf(src, sizeof src, "/tmp/strtupleelemcap_%d_%d.ww", getpid(), i); - snprintf(tmpdir, sizeof tmpdir, "/tmp/strtupleelemcap_%d_d_%d", getpid(), i); - - FILE *f = fopen(src, "wb"); - if (!f) return -1; - fputs(r->src, f); - fclose(f); - - mkdir(tmpdir, 0755); - snprintf(cmd, sizeof cmd, "cd %s && %s build %s", - tmpdir, driver, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: build via %s failed\n", - r->label, driver); - unlink(src); rmdir(tmpdir); - return -1; - } - - const char *base = strrchr(src, '/'); - base = base ? base + 1 : src; - char outbin[160]; - snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); - char *dot = strrchr(outbin, '.'); - if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; - int got = runwait(outbin); - - unlink(src); unlink(outbin); rmdir(tmpdir); - return got; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - char absbin[512]; - if (bin[0] != '/') { - char cwd[256]; - if (getcwd(cwd, sizeof cwd) == NULL) return 1; - snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); - bin = absbin; - } - - char cdrv[640]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - char wdrv[640]; - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - - struct { const char *name; const char *path; int gated_on_existence; } - drivers[] = { - { "cstage", cdrv, 0 }, - { "wwstage", wdrv, 1 }, - { NULL, NULL, 0 }, - }; - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - for (int d = 0; drivers[d].name; d++) { - if (drivers[d].gated_on_existence - && access(drivers[d].path, X_OK) != 0) { - fprintf(stderr, - "str_tuple_elem_cap_run: skip %s (no %s)\n", - drivers[d].name, drivers[d].path); - continue; - } - for (int i = 0; i < n; i++) { - int got = run_driver(drivers[d].path, &rows[i], i); - total++; - if (got != rows[i].want) { - fprintf(stderr, - "str_tuple_elem_cap_run[%s][%s]: exit=%d want=%d\n", - drivers[d].name, rows[i].label, - got, rows[i].want); - fail++; - } - } - } - - if (fail) { - fprintf(stderr, "str_tuple_elem_cap_run: %d/%d fixtures failed\n", - fail, total); - return 1; - } - printf("str_tuple_elem_cap_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/936_str_arrfield_cap_run.c b/test/wcc/936_str_arrfield_cap_run.c deleted file mode 100644 index 75c6790d..00000000 --- a/test/wcc/936_str_arrfield_cap_run.c +++ /dev/null @@ -1,234 +0,0 @@ -/* - * 936_str_arrfield_cap_run — runtime coverage for the C4.6-arrfield fold: a - * str-typed FIELD of an INDEXED element `arr[i].f` (N_INDEX-rooted N_DOT) must - * load the full 24B {ptr,len,cap} header, not just {ptr,len}. str is 24B since - * Phase 2 (#1); pre-arrfield the `arr[i].f` str arm loaded 2 words (len in BX, - * ptr in AX) and dropped cap. - * - * This is the LAST member of the 3-word-value-read cluster. UNLIKE the other - * C4.6 arms there is no adjacent slice-element sibling at this leaf, so the - * 3-word triple is authored directly to the canonical slice-header ABI. By the - * leaf the element base is in AX, so the order is len->BX(foff+8), - * cap->CX(foff+16), ptr->AX(foff+0) LAST — matching the proven in-tree oracle - * cgslicehdr(D_AX) and the caseB chained-*struct slice arm. Sites: cgen.c's - * "arr[i].field" branch (N_DOT, N_INDEX lhs) and the cgenexpr.ww cgdot twin. - * - * The byte-id gates (990-997) can't catch a symmetric 2-word miscompile: if - * both stages drop cap identically, byte-id passes silently. So this pins the - * *runtime* contract — build through both the cstage `ww` and wwstage `ww_ww` - * driver and confirm the assertion holds (exit 0). - * - * Sub-cases (one leaf covers all; the differences — LEAQ vs MOVQ base, the - * pointer-element deref — are upstream of the leaf): - * A [N]S local array : `arr[i].f` (LEAQ base, value element). - * B []S local slice : `sl[i].f` (MOVQ slice.ptr base, value element). - * D [N]*S pointer-elem : `arr[i].f` (LEAQ base, MOVQ deref to the *S). - * - * POISONING: the store side of `arr[i].f = v` and whole-struct `arr[i] = st` - * are SEPARATE, still-broken store-side gaps (filed; out of scope for this - * read-side fold — they store only 1-2 words and drop cap). So the value - * elements (A, B) are poisoned through `&arr[i]` + a *struct field write - * (`let pr: *rec = &arr[i]; pr.f = p;` — both the &arr[i] index-address and - * the chained pointer-field store are landed/working), which lands a real - * cap into the element's +16 word. D points its element at a separately-built - * struct (`st.f = p`, the proven s1local 3-word field store). - * - * DISCRIMINATION (heed the 933/934/935 lesson): a 2-word read leaves CX - * holding whatever the index scale-multiply (`MOVQ $esz, CX; IMULQ`) left - * there, never the poison. spoil() additionally interposes a CX-clobbering - * call between the build and the `arr[i].f` read, so a broken 2-word read - * observes spoil's leftover (44), never the poison cap. Verified fail-before - * (stashed the +16 cap load: all rows exit 1 on both drivers) / pass-after - * (exit 0), both drivers. - */ -#include -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -struct row { const char *label; const char *src; int want; }; - -static const struct row rows[] = { - /* A — `arr[i].f` value element of a [N]S local array (LEAQ base). - * Poison cap=8 (len=2) via &arr[1] + a *struct field store. spoil() - * clobbers CX between the build and the read so a broken 2-word read - * cannot coincidentally pass on a stale CX. */ - { "arrfield_array_value", - "type rec = struct { f: str };\n" - "fn spoil() i32 = {\n" - " let z: str = \"zzzz\";\n" - " z.cap = 44i32;\n" - " let w: str = z;\n" - " return w.cap: i32;\n" - "};\n" - "export fn main() i32 = {\n" - " let p: str = \"hi\";\n" - " p.cap = 8i32;\n" - " let arr: [3]rec;\n" - " let pr: *rec = &arr[1];\n" - " pr.f = p;\n" - " let junk: i32 = spoil();\n" - " let s: str = arr[1].f;\n" - " if (s.cap: i32 != 8) { return 1; };\n" - " if (s.len: i32 != 2) { return 2; };\n" - " if (junk != 44) { return 3; };\n" - " return 0;\n" - "};\n", - 0 }, - /* B — `sl[i].f` value element of a []S local slice (MOVQ slice.ptr - * base). The slice views the same poisoned backing array. Poison - * cap=9 (len=5). */ - { "arrfield_slice_value", - "type rec = struct { f: str };\n" - "fn spoil() i32 = {\n" - " let z: str = \"zzzz\";\n" - " z.cap = 44i32;\n" - " let w: str = z;\n" - " return w.cap: i32;\n" - "};\n" - "export fn main() i32 = {\n" - " let p: str = \"world\";\n" - " p.cap = 9i32;\n" - " let arr: [3]rec;\n" - " let pr: *rec = &arr[1];\n" - " pr.f = p;\n" - " let sl: []rec = arr[0:3];\n" - " let junk: i32 = spoil();\n" - " let s: str = sl[1].f;\n" - " if (s.cap: i32 != 9) { return 1; };\n" - " if (s.len: i32 != 5) { return 2; };\n" - " if (junk != 44) { return 3; };\n" - " return 0;\n" - "};\n", - 0 }, - /* D — `arr[i].f` pointer element of a [N]*S array (LEAQ base, MOVQ - * deref to the *S, then the leaf field load). The element points at - * a separately-built struct so the poison rides the proven s1local - * 3-word field store, not the broken array-element store. Poison - * cap=7 (len=3). */ - { "arrfield_ptr_elem", - "type rec = struct { f: str };\n" - "fn spoil() i32 = {\n" - " let z: str = \"zzzz\";\n" - " z.cap = 44i32;\n" - " let w: str = z;\n" - " return w.cap: i32;\n" - "};\n" - "export fn main() i32 = {\n" - " let p: str = \"abc\";\n" - " p.cap = 7i32;\n" - " let st: rec;\n" - " st.f = p;\n" - " let arr: [3]*rec;\n" - " arr[1] = &st;\n" - " let junk: i32 = spoil();\n" - " let s: str = arr[1].f;\n" - " if (s.cap: i32 != 7) { return 1; };\n" - " if (s.len: i32 != 3) { return 2; };\n" - " if (junk != 44) { return 3; };\n" - " return 0;\n" - "};\n", - 0 }, -}; - -static int -run_driver(const char *driver, const struct row *r, int i) -{ - char src[96], tmpdir[96], cmd[1024]; - snprintf(src, sizeof src, "/tmp/strarrfieldcap_%d_%d.ww", getpid(), i); - snprintf(tmpdir, sizeof tmpdir, "/tmp/strarrfieldcap_%d_d_%d", getpid(), i); - - FILE *f = fopen(src, "wb"); - if (!f) return -1; - fputs(r->src, f); - fclose(f); - - mkdir(tmpdir, 0755); - snprintf(cmd, sizeof cmd, "cd %s && %s build %s", - tmpdir, driver, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: build via %s failed\n", - r->label, driver); - unlink(src); rmdir(tmpdir); - return -1; - } - - const char *base = strrchr(src, '/'); - base = base ? base + 1 : src; - char outbin[160]; - snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); - char *dot = strrchr(outbin, '.'); - if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; - int got = runwait(outbin); - - unlink(src); unlink(outbin); rmdir(tmpdir); - return got; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - char absbin[512]; - if (bin[0] != '/') { - char cwd[256]; - if (getcwd(cwd, sizeof cwd) == NULL) return 1; - snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); - bin = absbin; - } - - char cdrv[640]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - char wdrv[640]; - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - - struct { const char *name; const char *path; int gated_on_existence; } - drivers[] = { - { "cstage", cdrv, 0 }, - { "wwstage", wdrv, 1 }, - { NULL, NULL, 0 }, - }; - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - for (int d = 0; drivers[d].name; d++) { - if (drivers[d].gated_on_existence - && access(drivers[d].path, X_OK) != 0) { - fprintf(stderr, - "str_arrfield_cap_run: skip %s (no %s)\n", - drivers[d].name, drivers[d].path); - continue; - } - for (int i = 0; i < n; i++) { - int got = run_driver(drivers[d].path, &rows[i], i); - total++; - if (got != rows[i].want) { - fprintf(stderr, - "str_arrfield_cap_run[%s][%s]: exit=%d want=%d\n", - drivers[d].name, rows[i].label, - got, rows[i].want); - fail++; - } - } - } - - if (fail) { - fprintf(stderr, "str_arrfield_cap_run: %d/%d fixtures failed\n", - fail, total); - return 1; - } - printf("str_arrfield_cap_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/937_str_arrfield_store_cap_run.c b/test/wcc/937_str_arrfield_store_cap_run.c deleted file mode 100644 index bea97996..00000000 --- a/test/wcc/937_str_arrfield_store_cap_run.c +++ /dev/null @@ -1,225 +0,0 @@ -/* - * 937_str_arrfield_store_cap_run — runtime coverage for the G1 fold: STORING a - * str into a FIELD of an INDEXED element `arr[i].f = v` must write the full 24B - * {ptr,len,cap} header, not just {ptr,len}. str is 24B since Phase 2 (#1); the - * write-side mirror of the landed arrfield READ (936) previously stored only 2 - * words (AX=ptr@foff+0, BX=len@foff+8) and silently DROPPED cap. - * - * This is only meaningful now that the arrfield READ is 3-word (936): before - * that fix the store+read were 2-word-symmetric and cap was untouched at both - * ends, so a dropped store-cap was invisible. Now the read returns the real - * +16 word, so a 2-word store is observable. - * - * Sites: cgen.c's `arr[i].field = v` str branch (N_DOT lhs, N_INDEX lhs.lhs, - * N_IDENT idxbase) and the cgenexpr.ww cgassign twin. The composed mechanic: - * the rhs str leaves AX=ptr/BX=len/CX=cap, all three spilled across the - * index/address computation (the index scale `MOVQ $esz, CX; IMULQ` would - * clobber cap), the element address staged in DX off the str AX/BX/CX - * convention (mirroring the s.f=v store), then the full triple stored at - * foff+0/+8/+16. - * - * Rows mirror the 936 arrfield READ rows: - * A [N]S local array : `arr[i].f = v` (LEAQ base, value element). - * B []S local slice : `sl[i].f = v` (MOVQ slice.ptr base, value elem). - * D [N]*S pointer-elem : `arr[i].f = v` (LEAQ base, MOVQ deref to the *S). - * - * RHS cap!=len: each test str is a literal whose .cap is mutated to a value - * DISTINCT from its len (NOT a bare literal — literals carry cap==len, which - * would hide a dropped cap; #12: global literal .cap reads 0). cap and len are - * both nonzero and unequal so a 2-word store that drops cap is detectable. - * - * PRE-POISON (B != A, both nonzero, != len): before the G1 store under test, - * all three slot words are seeded with a DIFFERENT str (ptr='q', len=4, - * cap=5=A) via a PROVEN already-3-word store path — never the G1 store itself - * (if G1 is broken its own poison write would also drop cap, leaving +16 - * uninit rather than a controlled poison). A/B poison via `&arr[i]` + a - * *struct field store (`pr.f = q`); D poisons the pointee via the proven - * s1local field store (`st.f = q`). The G1 store then writes the test str - * (ptr='h', len=2, cap=8=B). A broken 2-word store never touches +16, so the - * 3-word read-back observes the poison cap 5, never 8 — deterministic - * discrimination with no reliance on a stale register. - * - * FULL-TRIPLE ASSERT: a register-reallocation slip in the 3-word store could - * clobber ptr or len while wiring cap, and a cap-only assert would miss it. So - * each row reads the value back (landed 3-word arrfield read) and checks all - * three words: ptr (first byte through it — 'h'=104), len (2), cap (8). The - * 2-word store writes ptr/len correctly (they ARE the two words it keeps), so - * cap is the fail-before discriminator; ptr/len guard the fix. - * - * Verified fail-before (stashed the store edit on BOTH stages → all rows exit - * 1, cap reads the poison 5) / pass-after (exit 0), both the cstage `ww` and - * wwstage `ww_ww` drivers. - */ -#include -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -struct row { const char *label; const char *src; int want; }; - -static const struct row rows[] = { - /* A — `arr[i].f = v` into a [N]S local array (LEAQ base, value - * element). Poison the slot (cap=5,len=4,'q') via &arr[1] + a - * *struct field store; then the G1 store lands the test str - * (cap=8,len=2,'h'). */ - { "arrfield_store_array_value", - "type rec = struct { f: str };\n" - "export fn main() i32 = {\n" - " let q: str = \"qqqq\"; q.cap = 5i32;\n" - " let p: str = \"hi\"; p.cap = 8i32;\n" - " let arr: [3]rec;\n" - " let pr: *rec = &arr[1];\n" - " pr.f = q;\n" - " arr[1].f = p;\n" - " let s: str = arr[1].f;\n" - " if (s.cap: i32 != 8) { return 1; };\n" - " if (s.len: i32 != 2) { return 2; };\n" - " if (s[0] != 104u8) { return 3; };\n" - " return 0;\n" - "};\n", - 0 }, - /* B — `sl[i].f = v` into a []S local slice (MOVQ slice.ptr base, - * value element). The slice views the same backing array; poison the - * element through the array pointer, store the test str through the - * slice, read it back through the slice. */ - { "arrfield_store_slice_value", - "type rec = struct { f: str };\n" - "export fn main() i32 = {\n" - " let q: str = \"qqqq\"; q.cap = 5i32;\n" - " let p: str = \"hi\"; p.cap = 8i32;\n" - " let arr: [3]rec;\n" - " let pr: *rec = &arr[1];\n" - " pr.f = q;\n" - " let sl: []rec = arr[0:3];\n" - " sl[1].f = p;\n" - " let s: str = sl[1].f;\n" - " if (s.cap: i32 != 8) { return 1; };\n" - " if (s.len: i32 != 2) { return 2; };\n" - " if (s[0] != 104u8) { return 3; };\n" - " return 0;\n" - "};\n", - 0 }, - /* D — `arr[i].f = v` into a [N]*S pointer element (LEAQ base, MOVQ - * deref to the *S, then store at the field). The element points at a - * separately-built struct poisoned via the proven s1local field store - * (`st.f = q`); the G1 store derefs arr[1] and overwrites st.f. */ - { "arrfield_store_ptr_elem", - "type rec = struct { f: str };\n" - "export fn main() i32 = {\n" - " let q: str = \"qqqq\"; q.cap = 5i32;\n" - " let p: str = \"hi\"; p.cap = 8i32;\n" - " let st: rec;\n" - " st.f = q;\n" - " let arr: [3]*rec;\n" - " arr[1] = &st;\n" - " arr[1].f = p;\n" - " let s: str = arr[1].f;\n" - " if (s.cap: i32 != 8) { return 1; };\n" - " if (s.len: i32 != 2) { return 2; };\n" - " if (s[0] != 104u8) { return 3; };\n" - " return 0;\n" - "};\n", - 0 }, -}; - -static int -run_driver(const char *driver, const struct row *r, int i) -{ - char src[96], tmpdir[96], cmd[1024]; - snprintf(src, sizeof src, "/tmp/strarrfieldstore_%d_%d.ww", getpid(), i); - snprintf(tmpdir, sizeof tmpdir, "/tmp/strarrfieldstore_%d_d_%d", getpid(), i); - - FILE *f = fopen(src, "wb"); - if (!f) return -1; - fputs(r->src, f); - fclose(f); - - mkdir(tmpdir, 0755); - snprintf(cmd, sizeof cmd, "cd %s && %s build %s", - tmpdir, driver, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: build via %s failed\n", - r->label, driver); - unlink(src); rmdir(tmpdir); - return -1; - } - - const char *base = strrchr(src, '/'); - base = base ? base + 1 : src; - char outbin[160]; - snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); - char *dot = strrchr(outbin, '.'); - if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; - int got = runwait(outbin); - - unlink(src); unlink(outbin); rmdir(tmpdir); - return got; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - char absbin[512]; - if (bin[0] != '/') { - char cwd[256]; - if (getcwd(cwd, sizeof cwd) == NULL) return 1; - snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); - bin = absbin; - } - - char cdrv[640]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - char wdrv[640]; - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - - struct { const char *name; const char *path; int gated_on_existence; } - drivers[] = { - { "cstage", cdrv, 0 }, - { "wwstage", wdrv, 1 }, - { NULL, NULL, 0 }, - }; - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - for (int d = 0; drivers[d].name; d++) { - if (drivers[d].gated_on_existence - && access(drivers[d].path, X_OK) != 0) { - fprintf(stderr, - "str_arrfield_store_cap_run: skip %s (no %s)\n", - drivers[d].name, drivers[d].path); - continue; - } - for (int i = 0; i < n; i++) { - int got = run_driver(drivers[d].path, &rows[i], i); - total++; - if (got != rows[i].want) { - fprintf(stderr, - "str_arrfield_store_cap_run[%s][%s]: exit=%d want=%d\n", - drivers[d].name, rows[i].label, - got, rows[i].want); - fail++; - } - } - } - - if (fail) { - fprintf(stderr, "str_arrfield_store_cap_run: %d/%d fixtures failed\n", - fail, total); - return 1; - } - printf("str_arrfield_store_cap_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/938_str_chainfield_store_cap_run.c b/test/wcc/938_str_chainfield_store_cap_run.c deleted file mode 100644 index e829ce1d..00000000 --- a/test/wcc/938_str_chainfield_store_cap_run.c +++ /dev/null @@ -1,217 +0,0 @@ -/* - * 938_str_chainfield_store_cap_run — runtime coverage for the G2 fold: STORING a - * str into a FIELD reached through a *struct-VALUED EXPRESSION (`r.sym.f = v`, - * the chained-N_DOT store) must write the full 24B {ptr,len,cap} header, not - * just {ptr,len}. str is 24B since Phase 2 (#1); the chained-store arm - * previously stored only 2 words (AX=ptr@foff+0, BX=len@foff+8) and silently - * DROPPED cap — the second STORE-cluster fold, after the arrfield store (937). - * - * This is the write-side mirror of the landed chained *struct str-field READ - * (caseB, #11) and is only meaningful now that read is 3-word: before that the - * store+read were 2-word-symmetric and cap was untouched at both ends, so a - * dropped store-cap was invisible. Now the read returns the real +16 word, so a - * 2-word store is observable. - * - * Sites: cgen.c's chained-N_DOT `.field = v` str branch (n->lhs is N_DOT, - * n->lhs->lhs is N_DOT/non-IDENT evaluating to a *struct) and the cgenexpr.ww - * cgassign twin (strict gate: chain root is a LOCAL ident, every dot resolves - * through a *struct). The mechanic is a direct transfer of G1 (arrfield store): - * the rhs str leaves AX=ptr/BX=len/CX=cap, all three spilled (PUSHQ CX/BX/AX) - * across the base-expr eval (which may clobber any register), the resolved - * *struct ptr staged in DX off the str AX/BX/CX convention (mirroring the - * s.f=v store), then the full triple stored at foff+0/+8/+16. The base here is - * a single-word *struct pointer, so it does not contend for the spilled slots. - * - * Rows exercise the CHAINED arm at two depths (a direct-field `s.f=` probe would - * MISS this arm and test the already-3-word IDENT-base path): - * A depth-2 `r.sym.f = v` (r: outer, outer.sym: *inner, inner.f: str). - * B depth-3 `r.a.b.f = v` (two pointer hops; deeper base eval clobbers - * more registers, stressing the spill). - * - * RHS cap!=len: each test str is a literal whose .cap is mutated to a value - * DISTINCT from its len (NOT a bare literal — literals carry cap==len, which - * would hide a dropped cap; a str sub-slice was rejected too: ww yields cap==len - * for `buf[0:2]`, equally hiding the drop). cap=8 and len=2 are both nonzero and - * unequal so a 2-word store that drops cap is detectable. - * - * PRE-POISON (cap=5 != 8, both nonzero, != len): before the G2 store under test, - * all three slot words are seeded with a DIFFERENT str (ptr='q', len=4, cap=5) - * via the PROVEN already-3-word DIRECT field store (`st.f = q`, the s.f=v - * oracle, N_IDENT base) — never the G2 chained store itself (if G2 is broken its - * own poison write would also drop cap, leaving +16 uninit rather than a - * controlled poison). The pointee is a local struct; the chain's leaf points at - * it, so the G2 store overwrites the poisoned field. The G2 store then writes - * the test str (ptr='h', len=2, cap=8). A broken 2-word store never touches +16, - * so the 3-word read-back observes the poison cap 5, never 8 — deterministic - * discrimination with no reliance on a stale register. - * - * FULL-TRIPLE ASSERT: a register-reallocation slip in the 3-word store could - * clobber ptr or len while wiring cap, and a cap-only assert would miss it. So - * each row reads the value back (landed 3-word chained read, caseB) and checks - * all three words: ptr (first byte through it — 'h'=104), len (2), cap (8). The - * 2-word store writes ptr/len correctly (they ARE the two words it keeps), so - * cap is the fail-before discriminator; ptr/len guard the fix. - * - * Verified fail-before (stashed the store edit on BOTH stages → all rows exit 1, - * cap reads the poison 5) / pass-after (exit 0), both the cstage `ww` and - * wwstage `ww_ww` drivers. - */ -#include -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -struct row { const char *label; const char *src; int want; }; - -static const struct row rows[] = { - /* A — depth-2 `r.sym.f = v`. r is a local outer; r.sym is a *inner - * pointing at a local struct st. Poison st.f (cap=5,len=4,'q') via the - * DIRECT s.f=v 3-word store; then the G2 chained store lands the test - * str (cap=8,len=2,'h') through r.sym. */ - { "chainfield_store_depth2", - "type inner = struct { f: str };\n" - "type outer = struct { sym: *inner };\n" - "export fn main() i32 = {\n" - " let q: str = \"qqqq\"; q.cap = 5i32;\n" - " let p: str = \"hi\"; p.cap = 8i32;\n" - " let st: inner;\n" - " st.f = q;\n" - " let r: outer;\n" - " r.sym = &st;\n" - " r.sym.f = p;\n" - " let s: str = r.sym.f;\n" - " if (s.cap: i32 != 8) { return 1; };\n" - " if (s.len: i32 != 2) { return 2; };\n" - " if (s[0] != 104u8) { return 3; };\n" - " return 0;\n" - "};\n", - 0 }, - /* B — depth-3 `r.a.b.f = v`. Two pointer hops (r.a: *mid, mid.b: - * *inner). Poison the leaf via the DIRECT store (`leaf.f = q`); the G2 - * chained store derefs r.a then .b and overwrites leaf.f. The deeper - * base eval clobbers more registers, confirming the spilled triple - * survives an arbitrary base-formation. */ - { "chainfield_store_depth3", - "type inner = struct { f: str };\n" - "type mid = struct { b: *inner };\n" - "type outer = struct { a: *mid };\n" - "export fn main() i32 = {\n" - " let q: str = \"qqqq\"; q.cap = 5i32;\n" - " let p: str = \"hi\"; p.cap = 8i32;\n" - " let leaf: inner;\n" - " leaf.f = q;\n" - " let m: mid;\n" - " m.b = &leaf;\n" - " let r: outer;\n" - " r.a = &m;\n" - " r.a.b.f = p;\n" - " let s: str = r.a.b.f;\n" - " if (s.cap: i32 != 8) { return 1; };\n" - " if (s.len: i32 != 2) { return 2; };\n" - " if (s[0] != 104u8) { return 3; };\n" - " return 0;\n" - "};\n", - 0 }, -}; - -static int -run_driver(const char *driver, const struct row *r, int i) -{ - char src[96], tmpdir[96], cmd[1024]; - snprintf(src, sizeof src, "/tmp/strchainfieldstore_%d_%d.ww", getpid(), i); - snprintf(tmpdir, sizeof tmpdir, "/tmp/strchainfieldstore_%d_d_%d", getpid(), i); - - FILE *f = fopen(src, "wb"); - if (!f) return -1; - fputs(r->src, f); - fclose(f); - - mkdir(tmpdir, 0755); - snprintf(cmd, sizeof cmd, "cd %s && %s build %s", - tmpdir, driver, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: build via %s failed\n", - r->label, driver); - unlink(src); rmdir(tmpdir); - return -1; - } - - const char *base = strrchr(src, '/'); - base = base ? base + 1 : src; - char outbin[160]; - snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); - char *dot = strrchr(outbin, '.'); - if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; - int got = runwait(outbin); - - unlink(src); unlink(outbin); rmdir(tmpdir); - return got; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - char absbin[512]; - if (bin[0] != '/') { - char cwd[256]; - if (getcwd(cwd, sizeof cwd) == NULL) return 1; - snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); - bin = absbin; - } - - char cdrv[640]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - char wdrv[640]; - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - - struct { const char *name; const char *path; int gated_on_existence; } - drivers[] = { - { "cstage", cdrv, 0 }, - { "wwstage", wdrv, 1 }, - { NULL, NULL, 0 }, - }; - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - for (int d = 0; drivers[d].name; d++) { - if (drivers[d].gated_on_existence - && access(drivers[d].path, X_OK) != 0) { - fprintf(stderr, - "str_chainfield_store_cap_run: skip %s (no %s)\n", - drivers[d].name, drivers[d].path); - continue; - } - for (int i = 0; i < n; i++) { - int got = run_driver(drivers[d].path, &rows[i], i); - total++; - if (got != rows[i].want) { - fprintf(stderr, - "str_chainfield_store_cap_run[%s][%s]: exit=%d want=%d\n", - drivers[d].name, rows[i].label, - got, rows[i].want); - fail++; - } - } - } - - if (fail) { - fprintf(stderr, "str_chainfield_store_cap_run: %d/%d fixtures failed\n", - fail, total); - return 1; - } - printf("str_chainfield_store_cap_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/939_str_massign_store_cap_run.c b/test/wcc/939_str_massign_store_cap_run.c deleted file mode 100644 index 6f6a12fc..00000000 --- a/test/wcc/939_str_massign_store_cap_run.c +++ /dev/null @@ -1,211 +0,0 @@ -/* - * 939_str_massign_store_cap_run — runtime coverage for the G3 fold: a - * tuple-destructure REASSIGNMENT `a, s = call()` (N_MASSIGN) whose str element - * must write the full 24B {ptr,len,cap} header into the (already 24B) str slot, - * not just {ptr}. str is 24B since Phase 2 (#1); the N_MASSIGN arm previously - * stored the str element with the bare scalar path (PUSHQ DX; ...; MOVQ DX, - * slot) — one word, dropping len/cap. This is the third and final STORE-cluster - * fold (after arrfield 937 and chainfield 938), closing the store cluster on - * the tuple-destructure REASSIGN shape. - * - * N_MASSIGN vs N_MLET: `let a, s = call()` is N_MLET (fresh bindings, slots - * sized from each binding's type) and ALREADY destructures 3-word. `a, s = - * call()` with a, s PRE-DECLARED is N_MASSIGN (reassignment, slots already - * exist). The fix mirrors N_MLET's (DX,CX,R8)->(.ptr,.len,.cap) routing at the - * N_MASSIGN arm but localfinds the existing slot instead of allocating it. - * The fixture MUST pre-declare a and s then reassign WITHOUT `let`, or the - * parser emits N_MLET and this arm is never reached. - * - * Sites: cgen.c's N_MASSIGN case (str-XOR branch, l0/l1 N_IDENT, str-ness from - * the checker-stamped l->type) and the cgenstmt.ww cgmassign twin (wwstage has - * no checker, so str-ness comes from the called fn's return-type tuple element, - * exactly as cgmlet derives it). ONE-STR ONLY, matching N_MLET's coverage; the - * two-str destructure is a shared N_MLET/N_MASSIGN gap (task #22). - * - * Rows exercise BOTH XOR positions (the routing is position-agnostic, as in - * N_MLET): - * A `a, s = mk()` mk() returns (i64, str) — str is the 2nd element (l1). - * B `s, a = mk2()` mk2() returns (str, i64) — str is the 1st element (l0). - * - * RHS cap!=len: the returned str is a literal whose .cap is MUTATED to a value - * DISTINCT from its len (NOT a bare literal — literals carry cap==len, which - * would hide a dropped cap; a `buf[lo:hi]` sub-slice was rejected too: ww - * yields cap==len for it, task #20, equally hiding the drop). cap=8 and len=2 - * are both nonzero and unequal so a 1-word store that drops cap is detectable. - * - * PRE-POISON (cap=5 != 8, len=4 != 2, both nonzero): before the G3 store under - * test, all three slot words of `s` are seeded with a DIFFERENT str (ptr='q', - * len=4, cap=5) via `let s: str = q` — a PROVEN already-3-word let-init copy - * (the same path 935's spoil() relies on), NOT the G3 store itself (if G3 is - * broken its own poison write would also drop cap, leaving uninit words rather - * than a controlled poison). The let-init both DECLARES s (so the subsequent - * comma-assign is a reassignment = N_MASSIGN) and poisons its slot. A broken - * 1-word G3 store writes only s.ptr and never touches +8/+16, so the 3-word - * read-back observes the poison len 4 / cap 5, never 2 / 8 — deterministic - * discrimination with no reliance on a stale register. - * - * FULL-TRIPLE ASSERT: a register-reallocation slip in the 3-word store could - * clobber ptr while wiring len/cap, so each row reads s back (landed 3-word - * tuple/let reads) and checks ptr (first byte through it — 'h'=104), len (2), - * cap (8), plus the scalar side a (5) — confirming the XOR branch wires the - * scalar slot too. The broken 1-word store writes ptr correctly (it IS the one - * word it keeps), so len AND cap are the fail-before discriminators; ptr and a - * guard the fix. - * - * Verified fail-before (reverted the store edit on BOTH stages → all rows exit - * 1, cap reads the poison 5) / pass-after (exit 0), both the cstage `ww` and - * wwstage `ww_ww` drivers. Confirmed via emitted asm the fixture hits the - * N_MASSIGN arm (CALL ...; MOVQ {AX,DX,CX,R8} -> slots), not N_MLET. - */ -#include -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -struct row { const char *label; const char *src; int want; }; - -static const struct row rows[] = { - /* A — `a, s = mk()`, str the 2nd tuple element (l1). a and s are - * pre-declared (so the comma-assign is N_MASSIGN, not N_MLET); s is - * poisoned (cap=5,len=4,'q') by its let-init copy. The G3 store then - * lands the test str (cap=8,len=2,'h') via (DX,CX,R8)->s, AX->a. */ - { "massign_store_str_pos1", - "fn mk() (i64, str) = {\n" - " let p: str = \"hi\"; p.cap = 8i32;\n" - " return (5i64, p);\n" - "};\n" - "export fn main() i32 = {\n" - " let q: str = \"qqqq\"; q.cap = 5i32;\n" - " let a: i64 = 7i64;\n" - " let s: str = q;\n" - " a, s = mk();\n" - " if (s.cap: i32 != 8) { return 1; };\n" - " if (s.len: i32 != 2) { return 2; };\n" - " if (s[0] != 104u8) { return 3; };\n" - " if (a != 5) { return 4; };\n" - " return 0;\n" - "};\n", - 0 }, - /* B — `s, a = mk2()`, str the 1st tuple element (l0). Same poison and - * test values; exercises the s0_is_str routing (DX,CX,R8)->s, AX->a - * from the other XOR side. */ - { "massign_store_str_pos0", - "fn mk2() (str, i64) = {\n" - " let p: str = \"hi\"; p.cap = 8i32;\n" - " return (p, 5i64);\n" - "};\n" - "export fn main() i32 = {\n" - " let q: str = \"qqqq\"; q.cap = 5i32;\n" - " let s: str = q;\n" - " let a: i64 = 7i64;\n" - " s, a = mk2();\n" - " if (s.cap: i32 != 8) { return 1; };\n" - " if (s.len: i32 != 2) { return 2; };\n" - " if (s[0] != 104u8) { return 3; };\n" - " if (a != 5) { return 4; };\n" - " return 0;\n" - "};\n", - 0 }, -}; - -static int -run_driver(const char *driver, const struct row *r, int i) -{ - char src[96], tmpdir[96], cmd[1024]; - snprintf(src, sizeof src, "/tmp/strmassignstore_%d_%d.ww", getpid(), i); - snprintf(tmpdir, sizeof tmpdir, "/tmp/strmassignstore_%d_d_%d", getpid(), i); - - FILE *f = fopen(src, "wb"); - if (!f) return -1; - fputs(r->src, f); - fclose(f); - - mkdir(tmpdir, 0755); - snprintf(cmd, sizeof cmd, "cd %s && %s build %s", - tmpdir, driver, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: build via %s failed\n", - r->label, driver); - unlink(src); rmdir(tmpdir); - return -1; - } - - const char *base = strrchr(src, '/'); - base = base ? base + 1 : src; - char outbin[160]; - snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); - char *dot = strrchr(outbin, '.'); - if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; - int got = runwait(outbin); - - unlink(src); unlink(outbin); rmdir(tmpdir); - return got; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - char absbin[512]; - if (bin[0] != '/') { - char cwd[256]; - if (getcwd(cwd, sizeof cwd) == NULL) return 1; - snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); - bin = absbin; - } - - char cdrv[640]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - char wdrv[640]; - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - - struct { const char *name; const char *path; int gated_on_existence; } - drivers[] = { - { "cstage", cdrv, 0 }, - { "wwstage", wdrv, 1 }, - { NULL, NULL, 0 }, - }; - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - for (int d = 0; drivers[d].name; d++) { - if (drivers[d].gated_on_existence - && access(drivers[d].path, X_OK) != 0) { - fprintf(stderr, - "str_massign_store_cap_run: skip %s (no %s)\n", - drivers[d].name, drivers[d].path); - continue; - } - for (int i = 0; i < n; i++) { - int got = run_driver(drivers[d].path, &rows[i], i); - total++; - if (got != rows[i].want) { - fprintf(stderr, - "str_massign_store_cap_run[%s][%s]: exit=%d want=%d\n", - drivers[d].name, rows[i].label, - got, rows[i].want); - fail++; - } - } - } - - if (fail) { - fprintf(stderr, "str_massign_store_cap_run: %d/%d fixtures failed\n", - fail, total); - return 1; - } - printf("str_massign_store_cap_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/957_size_type_run.c b/test/wcc/957_size_type_run.c deleted file mode 100644 index 05d643e6..00000000 --- a/test/wcc/957_size_type_run.c +++ /dev/null @@ -1,137 +0,0 @@ -/* - * 957_size_type_run — runtime proof that the `size` type name is usable - * in TYPE position (#85 fold-2) and that it coexists with the `size(T)` - * size-of OPERATOR. The two are different paths — TYPE-position `size` - * resolves via the type-name resolver (lookup_builtin / tinfofornode's - * N_TNAME chain), while `size(T)` is a c.top SK_FN seed folded to an - * N_INTLIT — so a program may use BOTH in the same scope with no clash. - * Each row below exercises that coexistence; without the fold-2 resolver - * arm the `let _: size` rows fail to compile ("unknown type 'size'"), so - * a green row IS the proof the name binds. - * - * `size` mirrors uintptr: platform-width (8B amd64) unsigned int. The - * rows assert it is usable in arithmetic, comparison, and an i32 cast. - * - * Table-driven like 952_floats_run: each row is a self-contained ww - * program; the C-side cstage `ww build -I lib` compiles it, we run the - * binary and assert the exit code. cstage-only by design (mirrors 951/ - * 952/969): `ww_ww run` is broken (#95) and per-program wwstage byte-id - * is the 990-997 gates' job; the wwstage resolver arm is twinned for - * rule-10 symmetry and stays dead on the size-free selfhost corpus. - */ -#include -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return 1; -} - -struct row { const char *src; int want_exit; }; - -static const struct row rows[] = { - /* type-position `size` + the size(T) operator in one scope. x is a - * size-typed local; size(int)/size(i32) are folded operator results. - * Both must resolve and agree (8/4 on amd64). */ - { "package main;\n" - "export fn main() i32 = {\n" - " let x: size = 0;\n" - " if (x != 0) { return 1; };\n" - " let y: size = 42;\n" - " if (y != 42) { return 2; };\n" - " if (size(int) != 8) { return 3; };\n" - " if (size(i32) != 4) { return 4; };\n" - " return 0;\n" - "};\n", 0 }, - /* size arithmetic + i32 cast value propagation: a size-typed sum - * truncates to i32 and propagates (40 + 2 == 42). */ - { "package main;\n" - "export fn main() i32 = {\n" - " let x: size = 40;\n" - " let y: size = 2;\n" - " return (x + y): i32;\n" - "};\n", 42 }, - /* size as a struct field type: 8-byte slot, written and read back. */ - { "package main;\n" - "type box = struct { n: size };\n" - "export fn main() i32 = {\n" - " let b: box = box{ n = 7 };\n" - " return b.n: i32;\n" - "};\n", 7 }, - /* the size(T) operator value-propagated through an i32 cast (operator - * path, must keep working alongside the new type-name arm). */ - { "package main;\n" - "export fn main() i32 = {\n" - " return size(int): i32;\n" - "};\n", 8 }, - { NULL, 0 } -}; - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - char cwd[1024]; - if (getcwd(cwd, sizeof cwd) == NULL) return 1; - char absbin[1024]; - if (bin[0] != '/') { - snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); - bin = absbin; - } - - int n = 0, fail = 0; - for (int i = 0; rows[i].src; i++, n++) { - char src[64]; - snprintf(src, sizeof src, "/tmp/wwsz_%d_%d.ww", getpid(), i); - - FILE *f = fopen(src, "wb"); - if (f == NULL) { fail++; continue; } - fputs(rows[i].src, f); - fclose(f); - - char tmpdir[64]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/wwsz_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - - char cmd[2048]; - snprintf(cmd, sizeof cmd, "cd %s && %s/ww build -I %s/lib %s", - tmpdir, bin, cwd, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row %d: build failed\n src: %s\n", - i, rows[i].src); - fail++; - unlink(src); rmdir(tmpdir); - continue; - } - - char outbin[128]; - const char *base = strrchr(src, '/'); - base = base ? base + 1 : src; - snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); - char *dot = strrchr(outbin, '.'); - if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; - - int got = runwait(outbin); - if (got != rows[i].want_exit) { - fprintf(stderr, "row %d: exit %d, want %d\n src: %s\n", - i, got, rows[i].want_exit, rows[i].src); - fail++; - } - unlink(src); unlink(outbin); rmdir(tmpdir); - } - if (fail) { - fprintf(stderr, "%d/%d size_type tests failed\n", fail, n); - return 1; - } - printf("size_type: %d/%d ok\n", n, n); - return 0; -} diff --git a/test/wcc/989_chainidx_run.c b/test/wcc/989_chainidx_run.c deleted file mode 100644 index d3959fb5..00000000 --- a/test/wcc/989_chainidx_run.c +++ /dev/null @@ -1,196 +0,0 @@ -/* - * 989_chainidx_run — F7-c3 (#22): a CHAINED index `m[i][k]` whose element - * is a str/slice must load the full 24B/16B header, both stages. - * - * THE BUG (cat-A silent miscompile, gate-blind): cgindex - * (selfhost/cmd/wcc/cgenexpr.ww) has a dedicated arm for a chained index - * (the outer index's base is itself an N_INDEX). That arm read the - * checker-stamped element tinfo for esz/signedness/float-ness but NOT for - * elemisstr/elemisslice — so a chained index over [N][M]str / [N][M][]T - * loaded only the .ptr word and left .len/.cap as stale BX/CX. cstage's - * idx_eff path classifies the element uniformly (type_isstr/type_isslice), - * so it loaded the full header and ran correct — the cat-A divergence. - * 990-997 stay green because the bootstrap corpus never chains an index - * to a str/slice element; only a runtime row catches it. THE FIX: the - * chained-index arm stamps elemisstr/elemisslice off the SAME element - * tinfo it already reads for esz, aligning wwstage UP. - * - * NB: the [N][M] arrays are built by per-element store — the nested array - * literal `[[..],[..]]` is independently #270-1c-blocked (orthogonal). - * - * Rows (each builds+runs on cstage `ww` and, when present, wwstage `ww_ww`; - * rule-10 — both stages must agree AND hit want_exit): - * row | shape | want - * -------------------+------------------------------------+------ - * chain_str_read | let s = m[1][1]; len(s) m:[2][2]str | 5 [#22 bug] - * chain_str_call | take(m[1][1]) m:[2][2]str | 5 [#22+#46: - * | needs the c2 push AND this c3 read] - * chain_slice_read | let xs = m[1][1]; len(xs) m:[2][2][]int | 7 [#22 bug] - * chain_scalar_read | m[1][1] m:[2][2]int | 42 (control: - * | the scalar chained index the arm already handled — - * | c3 must not regress its byte-id) - */ -#include -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -struct row { - const char *label; - const char *src; - int want_exit; -}; - -static const struct row rows[] = { - /* (1) #22 — chained str element READ. m[1][1] = "ddddd", len 5. The - * chained-index arm dropped the .len load → garbage. */ - { "chain_str_read", - "package main;\n" - "export fn main() int = {\n" - " let m: [2][2]str;\n" - " m[0][0] = \"aa\"; m[0][1] = \"bbb\";\n" - " m[1][0] = \"c\"; m[1][1] = \"ddddd\";\n" - " let s: str = m[1][1];\n" - " return len(s): int;\n" - "};\n", - 5 }, - - /* (2) #22+#46 — chained str element as a CALL ARG. Needs both the c2 - * push-side recognizer (nodeisstr N_INDEX arm) AND this c3 read-side - * header load; pinned here where both halves are present. len 5. */ - { "chain_str_call", - "package main;\n" - "fn take(s: str) int = { return len(s): int; };\n" - "export fn main() int = {\n" - " let m: [2][2]str;\n" - " m[0][0] = \"aa\"; m[0][1] = \"bbb\";\n" - " m[1][0] = \"c\"; m[1][1] = \"ddddd\";\n" - " return take(m[1][1]);\n" - "};\n", - 5 }, - - /* (3) #22 — chained slice element READ. m[1][1] = b (len 7). The arm - * dropped the .len/.cap load for the 24B slice header. */ - { "chain_slice_read", - "package main;\n" - "export fn main() int = {\n" - " let a: []int = [1, 2];\n" - " let b: []int = [9, 9, 9, 9, 9, 9, 9];\n" - " let m: [2][2][]int;\n" - " m[0][0] = a; m[0][1] = a;\n" - " m[1][0] = a; m[1][1] = b;\n" - " let xs: []int = m[1][1];\n" - " return len(xs): int;\n" - "};\n", - 7 }, - - /* (4) control — chained SCALAR element. The arm already handled the - * scalar esz; c3 must not regress its byte-id. m[1][1] == 42. */ - { "chain_scalar_read", - "package main;\n" - "export fn main() int = {\n" - " let m: [2][2]int;\n" - " m[0][0] = 1; m[0][1] = 2;\n" - " m[1][0] = 3; m[1][1] = 42;\n" - " return m[1][1]: int;\n" - "};\n", - 42 }, -}; - -/* run_build — build+run `src` via `driver`; returns the binary's exit - * code, or -1 on a build failure. */ -static int -run_build(const char *driver, const struct row *r, int i) -{ - char src[64], tmpdir[64], cmd[1024]; - snprintf(src, sizeof src, "/tmp/chainidx_%d_%d.ww", getpid(), i); - snprintf(tmpdir, sizeof tmpdir, "/tmp/chainidx_%d_d_%d", getpid(), i); - - FILE *f = fopen(src, "wb"); - if (!f) return -2; - fputs(r->src, f); - fclose(f); - - mkdir(tmpdir, 0755); - snprintf(cmd, sizeof cmd, "cd %s && %s build %s 2>/dev/null", - tmpdir, driver, src); - int brc = runwait(cmd); - - const char *base = strrchr(src, '/'); - base = base ? base + 1 : src; - char outbin[128]; - snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); - char *dot = strrchr(outbin, '.'); - if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; - - int got = -1; - if (brc == 0) got = runwait(outbin); - - unlink(src); unlink(outbin); rmdir(tmpdir); - return brc == 0 ? got : -1; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - char absbin[1024]; - if (bin[0] != '/') { - char cwd[1024]; - if (getcwd(cwd, sizeof cwd) == NULL) return 1; - snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); - bin = absbin; - } - - char cdrv[1024], wdrv[1024]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - - struct { const char *name; const char *drv; int gated; } - drivers[] = { - { "cstage", cdrv, 0 }, - { "wwstage", wdrv, 1 }, - { NULL, NULL, 0 }, - }; - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - - for (int d = 0; drivers[d].name; d++) { - if (drivers[d].gated && access(drivers[d].drv, X_OK) != 0) { - fprintf(stderr, "chainidx_run: skip %s (no %s)\n", - drivers[d].name, drivers[d].drv); - continue; - } - for (int i = 0; i < n; i++) { - total++; - int got = run_build(drivers[d].drv, &rows[i], i); - if (got != rows[i].want_exit) { - fprintf(stderr, "chainidx_run[%s][%s]: exit=%d " - "want=%d\n", drivers[d].name, rows[i].label, - got, rows[i].want_exit); - fail++; - } - } - } - - if (fail) { - fprintf(stderr, "chainidx_run: %d/%d fixtures failed\n", - fail, total); - return 1; - } - printf("chainidx_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/989_gunsigned_run.c b/test/wcc/989_gunsigned_run.c deleted file mode 100644 index 04cec158..00000000 --- a/test/wcc/989_gunsigned_run.c +++ /dev/null @@ -1,190 +0,0 @@ -/* - * 989_gunsigned_run — F7-c5 (#25): a module-GLOBAL unsigned ident on the - * divide / shift / relational path must pick the UNSIGNED opcode. - * - * THE BUG (cat-A silent miscompile, gate-blind): nodeisunsigned - * (selfhost/cmd/wcc/cgenutil.ww) read the LOCAL's declared tnode and - * returned `false` (signed) for a module-global ident (localfindnode→nil - * → fell through to `return false`). So a `u64` global counter fed to - * `/ % >> >= >` got the signed opcode — IDIVQ/CQO, SARQ, JG/JGE — instead - * of the unsigned DIVQ, SHRQ, JA/JAE cstage emits (type_isunsigned reads - * the stamped n->type, cmd/w6c/cgen.c:2541). For a high-bit-set u64 - * global the two diverge at runtime (cs≠ww — the cat-A signature). - * THE FIX: the N_IDENT arm collapses onto the checker-stamped n.type_ - * (the same stamp the N_DOT/N_CAST/N_INDEX/N_CALL arms already read), - * aligning wwstage UP — the global's unsigned stamp now flows. - * - * CLASS-M: unlike c1-c4 this changes emission on a shape the bootstrap - * corpus DOES hit (module-global unsigned counters on the divide/shift - * path), so the self-compile .s MOVES; the bind verifies every move is - * toward cstage (IDIV→DIV) and runtime-correct. - * - * Rows (cstage `ww` + gated wwstage `ww_ww`; rule-10 + absolute value): - * row | shape | want - * -----------------+-----------------------------+------ - * global_ushr | g:u64=1<<63; g >> 1 | 0 [bug: SAR] - * global_udiv | g:u64=1<<63; g / (1<<62) | 2 [bug: IDIV] - * global_ucmp | g:u64=1<<63; g > 1 | 7 [bug: JG] - * signed_global_ctl| s:i64=-8; s >> 1 | 252 (control: a SIGNED - * | global must STAY SARQ — c5 must not over-convert) - */ -#include -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -struct row { - const char *label; - const char *src; - int want_exit; -}; - -static const struct row rows[] = { - /* (1) #25 — u64 global >> 1: unsigned SHRQ (cs) vs signed SARQ (bug). - * 0x8000000000000000 >> 1 == 0x4000000000000000 unsigned → return 0. */ - { "global_ushr", - "package main;\n" - "let g: u64 = 0;\n" - "export fn main() int = {\n" - " g = 9223372036854775808u64;\n" - " let r: u64 = g >> 1;\n" - " if (r == 4611686018427387904u64) { return 0; };\n" - " return 1;\n" - "};\n", - 0 }, - - /* (2) #25 — u64 global / (1<<62): unsigned DIVQ (cs) vs signed IDIVQ. - * (1<<63) / (1<<62) == 2 unsigned; signed reads g as negative. */ - { "global_udiv", - "package main;\n" - "let g: u64 = 0;\n" - "export fn main() int = {\n" - " g = 9223372036854775808u64;\n" - " let r: u64 = g / 4611686018427387904u64;\n" - " return r: int;\n" - "};\n", - 2 }, - - /* (3) #25 — u64 global > 1: JA (cs) vs JG (bug). 1<<63 > 1 is true - * unsigned, false signed (1<<63 is negative as i64). */ - { "global_ucmp", - "package main;\n" - "let g: u64 = 0;\n" - "export fn main() int = {\n" - " g = 9223372036854775808u64;\n" - " if (g > 1u64) { return 7; };\n" - " return 9;\n" - "};\n", - 7 }, - - /* (4) control — a SIGNED i64 global must keep the signed shift (SARQ): - * -8 >> 1 == -4 (exit byte 252). Pins that c5 reads the stamp, not a - * blanket "global → unsigned" — no over-conversion. */ - { "signed_global_ctl", - "package main;\n" - "let s: i64 = 0;\n" - "export fn main() int = {\n" - " s = -8;\n" - " let r: i64 = s >> 1;\n" - " return r: int;\n" - "};\n", - 252 }, -}; - -/* run_build — build+run `src` via `driver`; returns the binary's exit - * code, or -1 on a build failure. */ -static int -run_build(const char *driver, const struct row *r, int i) -{ - char src[64], tmpdir[64], cmd[1024]; - snprintf(src, sizeof src, "/tmp/gun_%d_%d.ww", getpid(), i); - snprintf(tmpdir, sizeof tmpdir, "/tmp/gun_%d_d_%d", getpid(), i); - - FILE *f = fopen(src, "wb"); - if (!f) return -2; - fputs(r->src, f); - fclose(f); - - mkdir(tmpdir, 0755); - snprintf(cmd, sizeof cmd, "cd %s && %s build %s 2>/dev/null", - tmpdir, driver, src); - int brc = runwait(cmd); - - const char *base = strrchr(src, '/'); - base = base ? base + 1 : src; - char outbin[128]; - snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); - char *dot = strrchr(outbin, '.'); - if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; - - int got = -1; - if (brc == 0) got = runwait(outbin); - - unlink(src); unlink(outbin); rmdir(tmpdir); - return brc == 0 ? got : -1; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - char absbin[1024]; - if (bin[0] != '/') { - char cwd[1024]; - if (getcwd(cwd, sizeof cwd) == NULL) return 1; - snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); - bin = absbin; - } - - char cdrv[1024], wdrv[1024]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - - struct { const char *name; const char *drv; int gated; } - drivers[] = { - { "cstage", cdrv, 0 }, - { "wwstage", wdrv, 1 }, - { NULL, NULL, 0 }, - }; - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - - for (int d = 0; drivers[d].name; d++) { - if (drivers[d].gated && access(drivers[d].drv, X_OK) != 0) { - fprintf(stderr, "gunsigned_run: skip %s (no %s)\n", - drivers[d].name, drivers[d].drv); - continue; - } - for (int i = 0; i < n; i++) { - total++; - int got = run_build(drivers[d].drv, &rows[i], i); - if (got != rows[i].want_exit) { - fprintf(stderr, "gunsigned_run[%s][%s]: exit=%d " - "want=%d\n", drivers[d].name, rows[i].label, - got, rows[i].want_exit); - fail++; - } - } - } - - if (fail) { - fprintf(stderr, "gunsigned_run: %d/%d fixtures failed\n", - fail, total); - return 1; - } - printf("gunsigned_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/989_idxarg_run.c b/test/wcc/989_idxarg_run.c deleted file mode 100644 index 45d5784e..00000000 --- a/test/wcc/989_idxarg_run.c +++ /dev/null @@ -1,204 +0,0 @@ -/* - * 989_idxarg_run — F7-c2 (#45/#46): an INDEXED slice/str element passed as - * a call argument must push its full multi-word header, both stages. - * - * THE BUG (cat-A silent miscompile, gate-blind): pushargsrev sizes a - * call arg via nodeisslice/nodeisstr (selfhost/cmd/wcc/cgenutil.ww). A - * str arg takes 2 slots (ptr+len), a slice 3 (ptr+len+cap); a scalar - * takes 1. Pre-fix: - * - nodeisslice had NO N_INDEX arm (#45) → `take(rows[i])` where the - * element is a slice fell to the scalar default: one PUSHQ AX, the - * callee read .len/.cap from stack residue (garbage len). - * - nodeisstr's N_INDEX arm was a base-kind whitelist that only knew - * N_IDENT and N_DOT bases (#46) → a chained index `take(m[1][1])` - * (the outer index's base is itself an N_INDEX) fell through to - * `return false` → 1-word push, garbage .len. - * cstage is type-keyed (cmd/w6c/cgen.c node_isslice/node_isstr = - * type_isXXX(n->type)), so it pushed the full header and ran correct — - * the divergence is the cat-A signature. 990-997 stay green because the - * bootstrap corpus never feeds an indexed slice/str element as a call - * arg, so this value miscompile is invisible to byte-id; only a runtime - * row catches it. THE FIX: both N_INDEX arms read the checker-stamped - * element type n.type_ (exprtype N_INDEX, check.ww), aligning wwstage UP. - * - * Rows (every row builds+runs on cstage `ww` and, when present, wwstage - * `ww_ww`; rule-10 — both stages must agree AND hit want_exit): - * row | shape | want - * -------------------+------------------------------------+------ - * slice_elem_arg | take(rows[1]) rows:[2][]int | 2 [#45 bug] - * call_str_elem | take(getarr()[1]) getarr()->[]str | 5 [#46 bug: - * | the index's base is an N_CALL — non-ident/non-dot, - * | the shape the old base-kind whitelist missed. The - * | slice-base READ already loads the header, so this - * | isolates the PUSH-side fix (c2) cleanly.] - * ident_str_elem | take(arr[1]) arr:[3]str | 5 (#46 control: - * | the N_IDENT-base case the old whitelist DID handle — - * | must still work through the stamp read) - * slice_local_arg | take(xs) xs:[]int | 3 (control: the - * | N_IDENT-local arm, unchanged since c1) - * - * The chained-index `take(m[1][1])` shape (an N_INDEX base) is covered by - * the c3 sibling test (989_chainidx_run.c): its push side is fixed here, - * but it ALSO needs the cgindex read-side header load (#22/c3), so it is - * pinned where both halves are present. - */ -#include -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -struct row { - const char *label; - const char *src; - int want_exit; -}; - -static const struct row rows[] = { - /* (1) #45 — slice element `rows[1]` (a []int) as a call arg. Pre-fix - * nodeisslice had no N_INDEX arm → 1-word push, callee read garbage - * len. len(rows[1]) == len(b) == 2. */ - { "slice_elem_arg", - "package main;\n" - "fn take(xs: []int) int = { return len(xs): int; };\n" - "export fn main() int = {\n" - " let a: []int = [10, 20, 30, 40];\n" - " let b: []int = [1, 2];\n" - " let rows: [2][]int = [a, b];\n" - " return take(rows[1]);\n" - "};\n", - 2 }, - - /* (2) #46 — index whose base is an N_CALL (`getarr()[1]`): a non-ident/ - * non-dot base the old base-kind whitelist missed. The slice-base READ - * already loads the (ptr,len) header, so this isolates the PUSH-side - * fix (nodeisstr N_INDEX arm). len("ddddd") == 5. */ - { "call_str_elem", - "package main;\n" - "fn getarr() []str = {\n" - " let a: []str = [\"x\", \"ddddd\", \"zz\"];\n" - " return a;\n" - "};\n" - "fn take(s: str) int = { return len(s): int; };\n" - "export fn main() int = { return take(getarr()[1]); };\n", - 5 }, - - /* (3) #46 control — `arr[1]` with an N_IDENT base (the case the old - * whitelist DID handle): the stamp read must not regress it. */ - { "ident_str_elem", - "package main;\n" - "fn take(s: str) int = { return len(s): int; };\n" - "export fn main() int = {\n" - " let arr: [3]str = [\"x\", \"ddddd\", \"zz\"];\n" - " return take(arr[1]);\n" - "};\n", - 5 }, - - /* (4) control — a plain str/slice local arg (the c1 N_IDENT-local - * arm): pins that c2 leaves the non-indexed path alone. */ - { "slice_local_arg", - "package main;\n" - "fn take(xs: []int) int = { return len(xs): int; };\n" - "export fn main() int = {\n" - " let xs: []int = [7, 8, 9];\n" - " return take(xs);\n" - "};\n", - 3 }, -}; - -/* run_build — build+run `src` via `driver`; returns the binary's exit - * code, or -1 on a build failure. */ -static int -run_build(const char *driver, const struct row *r, int i) -{ - char src[64], tmpdir[64], cmd[1024]; - snprintf(src, sizeof src, "/tmp/idxarg_%d_%d.ww", getpid(), i); - snprintf(tmpdir, sizeof tmpdir, "/tmp/idxarg_%d_d_%d", getpid(), i); - - FILE *f = fopen(src, "wb"); - if (!f) return -2; - fputs(r->src, f); - fclose(f); - - mkdir(tmpdir, 0755); - snprintf(cmd, sizeof cmd, "cd %s && %s build %s 2>/dev/null", - tmpdir, driver, src); - int brc = runwait(cmd); - - const char *base = strrchr(src, '/'); - base = base ? base + 1 : src; - char outbin[128]; - snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); - char *dot = strrchr(outbin, '.'); - if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; - - int got = -1; - if (brc == 0) got = runwait(outbin); - - unlink(src); unlink(outbin); rmdir(tmpdir); - return brc == 0 ? got : -1; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - char absbin[1024]; - if (bin[0] != '/') { - char cwd[1024]; - if (getcwd(cwd, sizeof cwd) == NULL) return 1; - snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); - bin = absbin; - } - - char cdrv[1024], wdrv[1024]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - - struct { const char *name; const char *drv; int gated; } - drivers[] = { - { "cstage", cdrv, 0 }, - { "wwstage", wdrv, 1 }, - { NULL, NULL, 0 }, - }; - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - - for (int d = 0; drivers[d].name; d++) { - if (drivers[d].gated && access(drivers[d].drv, X_OK) != 0) { - fprintf(stderr, "idxarg_run: skip %s (no %s)\n", - drivers[d].name, drivers[d].drv); - continue; - } - for (int i = 0; i < n; i++) { - total++; - int got = run_build(drivers[d].drv, &rows[i], i); - if (got != rows[i].want_exit) { - fprintf(stderr, "idxarg_run[%s][%s]: exit=%d " - "want=%d\n", drivers[d].name, rows[i].label, - got, rows[i].want_exit); - fail++; - } - } - } - - if (fail) { - fprintf(stderr, "idxarg_run: %d/%d fixtures failed\n", - fail, total); - return 1; - } - printf("idxarg_run: %d/%d ok\n", total, total); - return 0; -}