diff --git a/Makefile b/Makefile index 06cb89da..389c7534 100644 --- a/Makefile +++ b/Makefile @@ -385,7 +385,8 @@ XMOD_WW_TARGETS = $(XMOD_WW_TESTS:%=wwtest/%) # w6c_ww per row — not identity gates — so they run under # test-compiler beside the surviving residual carriers. ASM_WW_TESTS = test/asm/modshadow_test.ww test/asm/sret_test.ww \ - test/asm/callarg_test.ww test/asm/chain_test.ww + test/asm/callarg_test.ww test/asm/chain_test.ww \ + test/asm/dataemit_test.ww ASM_WW_TARGETS = $(ASM_WW_TESTS:%=wwtest/%) BOOTSTRAP_WRAPPER_SOURCES = test/wcc/950_selfcheck.c \ test/wcc/991_w6a_ww.c \ diff --git a/test/asm/dataemit_test.ww b/test/asm/dataemit_test.ww new file mode 100644 index 00000000..6994ae7b --- /dev/null +++ b/test/asm/dataemit_test.ww @@ -0,0 +1,340 @@ +package dataemit_test; + +// Direct-w6c asm-shape gate over data/def emission and let lowering. +// Port of the retired native carriers test/wcc/719_signed_data_emit.c, +// 724_letdecl_zeroinit.c, 744_letcopy_struct.c and +// 746_strdef_inline.c; every assertion preserved. Runtime rows are +// owned elsewhere (test/lang/{signed_data_emit,local_zeroinit, +// void_error_singleton}_test.ww, test/wcc/data/r744_letcopy_*) — the +// directive-shape needles, zero-init/copy windows and count floors +// here are the unowned remainder; 744's load-count floor still +// catches a both-stages-identical partial copy that runtime checks +// and byte-id both miss. +// +// 719 (#19): the whole .s must carry the EXACT `DATAW (SB), +// ""` row — the payload rendered per emit_data_byte's +// printable/escape contract (printable ASCII raw, else \xNN) — so a +// future cgen refactor emitting the slot via a different directive +// cannot slip past green semantics. Whole-file needle as in the C. +// +// 724 (STATUS-3 #22): the bare `let x: T;` 8B slot zero-init +// `MOVQ $0, -` must appear inside [TEXT main.produce .. RET); the +// byte-id leg is the actual #22 fix pin. +// +// 744 (STATUS-6 #32): `MOVQ\t-` source-slot load count floors +// {1,3,4,4} over [TEXT main.produce .. next TEXT). +// +// 746 (#12): both str-def value-reference shapes must inline the +// strlit pair (LEAQ ._S_ + MOVQ $39,) and never the bogus +// `alpha.MSG(SB)` symbol load inside [TEXT sym .. RET). Per #49 the +// strlit label carries the USE-site module's prefix. +// +// Byte-id legs ride every row as in the C. Dropped C machinery, not +// assertions: w6c_ww-absent skip gates, getpid()-keyed /tmp names, +// slurp caps. The `package main;` prefix reproduces wwtest_fputs +// where the carrier injected it. + +import os; +import os.exec; +import strings; +import testenv; +import time; + +fn fail(label: str, why: str) void = { + let m: str = strings.concat("dataemit FAIL: ", label, " -- ", why, + "\n"); + os.write(2, m.ptr, m.len: u64); + assert(false); +}; + +fn tmo() time.duration = { + return (180i64 * (time.second: i64)): time.duration; +}; + +fn emitstage(td: str, label: str, stage: str, drv: str, + outname: str) void = { + let av: []str = []; + append(av, drv); + append(av, "-o"); + append(av, outname); + append(av, "src.ww"); + let co: testenv.commandout; + testenv.runcommand(td, td, stage, av, tmo(), &co); + let ok: bool = co.termination == exec.termination.EXIT && co.code == 0; + if (!ok) { fail(label, strings.concat(stage, " compile failed")); }; +}; + +// [first `sym` .. first `retneedle` after it), widened to EOF when +// retneedle is missing and mustret == 0. +fn asmwindow(label: str, stage: str, s: str, sym: str, retneedle: str, + mustret: i32) str = { + let fp: i32 = testenv.pos(s, sym); + if (fp < 0) { + fail(label, strings.concat(stage, ": no ", sym, " in .s")); + }; + let tail: str = strings.sub(s, fp, s.len); + let rp: i32 = testenv.pos(tail, retneedle); + if (rp < 0) { + if (mustret != 0) { + fail(label, strings.concat(stage, ": no RET inside ", + sym)); + }; + return tail; + }; + return strings.sub(tail, 0, rp); +}; + +// One 719 row: exact DATAW payload needle in both stages' .s, then +// byte-id. +fn datawrow(label: str, src: str, sym: str, bytes: str) void = { + let td: str = testenv.fresh(); + testenv.writefile(strings.concat(td, "/src.ww"), src); + emitstage(td, label, "cstage", testenv.driver("w6c"), "cs.s"); + emitstage(td, label, "wwstage", testenv.driver("w6c_ww"), "ws.s"); + let cs: str = testenv.readfile(strings.concat(td, "/cs.s")); + let ws: str = testenv.readfile(strings.concat(td, "/ws.s")); + let needle: str = strings.concat("DATAW ", sym, "(SB),\"", bytes, + "\""); + if (!testenv.has(cs, needle)) { + fail(label, strings.concat("cstage: ", needle, " missing")); + }; + if (!testenv.has(ws, needle)) { + fail(label, strings.concat("wwstage: ", needle, " missing")); + }; + if (!testenv.same(cs, ws)) { + fail(label, "cstage vs wwstage asm differs"); + }; + testenv.clean(td); +}; + +// 719: two's-complement little-endian payloads at the declared +// element width; 100 == 'd' and 56 == '8' stay raw per +// emit_data_byte's printable arm. +@test fn signeddataemit() void = { + datawrow("i8_neg", strings.concat( + "package main;\n", + "let x: i8 = -1i8;\n", + "fn main() i32 = { return 0; };\n"), + "main.x", "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff"); + datawrow("i16_neg", strings.concat( + "package main;\n", + "let x: i16 = -2i16;\n", + "fn main() i32 = { return 0; };\n"), + "main.x", "\\xfe\\xff\\xff\\xff\\xff\\xff\\xff\\xff"); + datawrow("i32_neg", strings.concat( + "package main;\n", + "let x: i32 = -100i32;\n", + "fn main() i32 = { return 0; };\n"), + "main.x", "\\x9c\\xff\\xff\\xff\\xff\\xff\\xff\\xff"); + datawrow("i64_neg", strings.concat( + "package main;\n", + "let x: i64 = -1000i64;\n", + "fn main() i32 = { return 0; };\n"), + "main.x", "\\x18\\xfc\\xff\\xff\\xff\\xff\\xff\\xff"); + datawrow("i8_arr", strings.concat( + "package main;\n", + "let a: [4]i8 = [1i8, -2i8, 3i8, -4i8];\n", + "fn main() i32 = { return 0; };\n"), + "main.a", "\\x01\\xfe\\x03\\xfc"); + datawrow("i16_arr", strings.concat( + "package main;\n", + "let a: [4]i16 = [1i16, -2i16, 3i16, -4i16];\n", + "fn main() i32 = { return 0; };\n"), + "main.a", "\\x01\\x00\\xfe\\xff\\x03\\x00\\xfc\\xff"); + datawrow("i32_arr", strings.concat( + "package main;\n", + "let a: [3]i32 = [10i32, -20i32, 30i32];\n", + "fn main() i32 = { return 0; };\n"), + "main.a", + "\\x0a\\x00\\x00\\x00\\xec\\xff\\xff\\xff\\x1e\\x00\\x00\\x00"); + datawrow("i64_arr", strings.concat( + "package main;\n", + "let a: [2]i64 = [100i64, -200i64];\n", + "fn main() i32 = { return 0; };\n"), + "main.a", strings.concat( + "d\\x00\\x00\\x00\\x00\\x00\\x00\\x00", + "8\\xff\\xff\\xff\\xff\\xff\\xff\\xff")); +}; + +// One 724 row: zero-init store inside the produce window on both +// stages, then the #22 fix-pin byte-id. +fn zeroinitrow(label: str, src: str) void = { + let td: str = testenv.fresh(); + testenv.writefile(strings.concat(td, "/src.ww"), src); + emitstage(td, label, "cstage", testenv.driver("w6c"), "cs.s"); + emitstage(td, label, "wwstage", testenv.driver("w6c_ww"), "ws.s"); + let cs: str = testenv.readfile(strings.concat(td, "/cs.s")); + let ws: str = testenv.readfile(strings.concat(td, "/ws.s")); + let cw: str = asmwindow(label, "cstage", cs, "TEXT main.produce", + "RET", 0); + let ww: str = asmwindow(label, "wwstage", ws, "TEXT main.produce", + "RET", 0); + if (!testenv.has(cw, "MOVQ\t$0, -")) { + fail(label, + "cstage: no `MOVQ $0, -K(BP)` zero-init in produce body"); + }; + if (!testenv.has(ww, "MOVQ\t$0, -")) { + fail(label, + "wwstage: no `MOVQ $0, -K(BP)` zero-init in produce body"); + }; + if (!testenv.same(cs, ws)) { + fail(label, "cstage vs wwstage asm differs"); + }; + testenv.clean(td); +}; + +@test fn letdeclzeroinit() void = { + // The utf8.invalid shape. + zeroinitrow("bang_void_letdecl", strings.concat( + "package main;\n", + "type invalid = !void;\n", + "export fn produce() (i32 | invalid) = {\n", + " let e: invalid;\n", + " return e;\n", + "};\n")); + // The utf8.done / utf8.more shape. + zeroinitrow("void_alias_letdecl", strings.concat( + "package main;\n", + "type done = void;\n", + "export fn produce() (i32 | done) = {\n", + " let d: done;\n", + " return d;\n", + "};\n")); +}; + +// One 744 row: `MOVQ\t-` load-count floor over [TEXT main.produce .. +// next TEXT) on both stages, then byte-id. +fn letcopyrow(label: str, src: str, minloads: i32) void = { + let td: str = testenv.fresh(); + testenv.writefile(strings.concat(td, "/src.ww"), src); + emitstage(td, label, "cstage", testenv.driver("w6c"), "cs.s"); + emitstage(td, label, "wwstage", testenv.driver("w6c_ww"), "ws.s"); + let cs: str = testenv.readfile(strings.concat(td, "/cs.s")); + let ws: str = testenv.readfile(strings.concat(td, "/ws.s")); + letcopycheck(label, "cstage", cs, minloads); + letcopycheck(label, "wwstage", ws, minloads); + if (!testenv.same(cs, ws)) { + fail(label, "cstage vs wwstage asm differs"); + }; + testenv.clean(td); +}; + +fn letcopycheck(label: str, stage: str, s: str, minloads: i32) void = { + let bp: i32 = testenv.pos(s, "TEXT main.produce"); + if (bp < 0) { + fail(label, strings.concat(stage, + ": no TEXT produce label in .s")); + }; + let tail: str = strings.sub(s, bp, s.len); + let ep: i32 = testenv.pos(strings.sub(tail, 1, tail.len), + "\nTEXT "); + let w: str = tail; + if (ep >= 0) { w = strings.sub(tail, 0, 1 + ep); }; + if (testenv.occurrences(w, "MOVQ\t-") < minloads) { + fail(label, strings.concat(stage, + ": too few `MOVQ -K(BP), AX` source loads in produce ", + "(copy missing)")); + }; +}; + +// 744 shapes per the #32 brief: p1 structlit-inits where a bare decl +// would hit the pre-existing #15/#26c non-8-multiple zero-init +// divergence; the tagged row checks only p2.a for the same reason. +@test fn letcopystruct() void = { + letcopyrow("tri_i32", strings.concat( + "package main;\n", + "type tri = struct { a: i32, b: i32, c: i32 };\n", + "export fn produce() i32 = {\n", + " let p1: tri = tri{a=11, b=22, c=33};\n", + " let p2: tri = p1;\n", + " if (p2.a != 11) { return 11; };\n", + " if (p2.b != 22) { return 12; };\n", + " if (p2.c != 33) { return 13; };\n", + " return 0;\n", + "};\n"), 1); + letcopyrow("field_str", strings.concat( + "package main;\n", + "type ws = struct { a: i32, s: str };\n", + "export fn produce() i32 = {\n", + " let p1: ws;\n", + " p1.a = 7; p1.s = \"hi\";\n", + " let p2: ws = p1;\n", + " if (p2.a != 7) { return 11; };\n", + " if (p2.s.len != 2) { return 12; };\n", + " return 0;\n", + "};\n"), 3); + letcopyrow("field_slice", strings.concat( + "package main;\n", + "type wsl = struct { a: i32, b: []u8 };\n", + "export fn produce() i32 = {\n", + " let raw: [3]u8 = [1: u8, 2: u8, 3: u8];\n", + " let p1: wsl;\n", + " p1.a = 9; p1.b = raw[0:3];\n", + " let p2: wsl = p1;\n", + " if (p2.a != 9) { return 11; };\n", + " if (p2.b.len != 3) { return 12; };\n", + " return 0;\n", + "};\n"), 4); + letcopyrow("field_tagged", strings.concat( + "package main;\n", + "type tag = (i32 | str);\n", + "type wtg = struct { a: i32, t: tag };\n", + "export fn produce() i32 = {\n", + " let p1: wtg = wtg{a=5, t=42: tag};\n", + " let p2: wtg = p1;\n", + " if (p2.a != 5) { return 11; };\n", + " return 0;\n", + "};\n"), 4); +}; + +// One 746 row: strlit-inline pair present, bogus SB symbol load +// absent, inside [TEXT sym .. RET) on both stages, then byte-id. +fn strdefrow(label: str, src: str, sym: str, wantlea: str, + bad: str) void = { + let td: str = testenv.fresh(); + testenv.writefile(strings.concat(td, "/src.ww"), src); + emitstage(td, label, "cstage", testenv.driver("w6c"), "cs.s"); + emitstage(td, label, "wwstage", testenv.driver("w6c_ww"), "ws.s"); + let cs: str = testenv.readfile(strings.concat(td, "/cs.s")); + let ws: str = testenv.readfile(strings.concat(td, "/ws.s")); + strdefcheck(label, "cstage", cs, sym, wantlea, bad); + strdefcheck(label, "wwstage", ws, sym, wantlea, bad); + if (!testenv.same(cs, ws)) { + fail(label, "cstage vs wwstage asm differs"); + }; + testenv.clean(td); +}; + +fn strdefcheck(label: str, stage: str, s: str, sym: str, wantlea: str, + bad: str) void = { + let w: str = asmwindow(label, stage, s, sym, "\tRET", 1); + if (!testenv.has(w, wantlea)) { + fail(label, strings.concat(stage, ": want_lea ", wantlea, + " missing inside ", sym)); + }; + if (!testenv.has(w, "MOVQ\t$39,")) { + fail(label, strings.concat(stage, + ": want_len MOVQ $39, missing inside ", sym)); + }; + if (testenv.has(w, bad)) { + fail(label, strings.concat(stage, ": bogus ", bad, + " symbol load inside ", sym)); + }; +}; + +@test fn strdefinline() void = { + strdefrow("bare_ident_strdef", strings.concat( + "package alpha;\n", + "def MSG: str = \"strdef_inline_pin_aaaaaaaaaaaaaaa_43chr\";\n", + "export fn afn() str = { return MSG; };\n", + "export fn main() i32 = { return 0; };\n"), + "TEXT alpha.afn", "LEAQ\talpha._S_", "alpha.MSG(SB)"); + strdefrow("mod_qualified_strdef", strings.concat( + "package alpha;\n", + "def MSG: str = \"strdef_inline_pin_aaaaaaaaaaaaaaa_43chr\";\n", + "package beta;\n", + "import alpha;\n", + "export fn bfn() str = { return alpha.MSG; };\n", + "export fn main() i32 = { return 0; };\n"), + "TEXT beta.bfn", "LEAQ\tbeta._S_", "alpha.MSG(SB)"); +}; diff --git a/test/wcc/719_signed_data_emit.c b/test/wcc/719_signed_data_emit.c deleted file mode 100644 index 2607ec35..00000000 --- a/test/wcc/719_signed_data_emit.c +++ /dev/null @@ -1,223 +0,0 @@ -/* - * 719_signed_data_emit — corpus-coverage-blind sentinel for #19. - * - * Confirms each cstage / wwstage `.s` for a top-level `let` of signed - * integer type contains a `DATAW (SB),"..."` row, *and* that the - * inline literal bytes encode the value's two's complement at the - * declared element width. Pre-fix: - * - cstage emit_lets dropped the row entirely on `let x: i8 = -1i8;` - * (link failed, no DATAW); the array arm dropped the whole row. - * - wwstage emitletdataw silently emitted zero bytes where the - * negative literal should have produced 0xFF... (link succeeded; - * runtime read 0). The array arm hit the same gap. - * - * Both test/lang/signed_data_emit_test.ww (the migrated runtime rows) - * and the broader bootstrap byte-id (995_self_rebuild) would catch a - * future regression, but this row - * pins the *asm shape* itself — a future cgen refactor that emits - * the slot via a different directive (e.g. via DATA + DATAR rather - * than DATAW) would silently divergent even with green semantics. - * - * Plus a cstage-vs-wwstage byte-id diff per row, mirroring 707's - * pattern. - */ -#include -#include -#include -#include -#include -#include -#include "wwtestpkg.h" - -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; - const char *sym; - const char *want_bytes; /* exact byte-payload inside the DATAW */ -}; - -/* The .s emits each byte either as a printable ASCII glyph (b is 0x20- - * 0x7e and not '"'/'\\') or as `\xNN`. emit_data_byte's rules: 0x22 + 0x5c - * → `\"` / `\\`; printable → raw; everything else → `\xNN`. For the - * sign-extended pads we'll see `\xff` × N as a contiguous run. */ -static const struct row rows[] = { - /* Scalar i8 = -1 → low byte 0xff, then 7 bytes of sign-extension. */ - { "i8_neg", "let x: i8 = -1i8;\n" - "fn main() i32 = { return 0; };\n", - "main.x", - "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff" }, - /* Scalar i16 = -2 → 0xfe 0xff then 6 byte sign extension. */ - { "i16_neg", "let x: i16 = -2i16;\n" - "fn main() i32 = { return 0; };\n", - "main.x", - "\\xfe\\xff\\xff\\xff\\xff\\xff\\xff\\xff" }, - /* Scalar i32 = -100 → 0x9C 0xFF 0xFF 0xFF then 4 byte sign extension. */ - { "i32_neg", "let x: i32 = -100i32;\n" - "fn main() i32 = { return 0; };\n", - "main.x", - "\\x9c\\xff\\xff\\xff\\xff\\xff\\xff\\xff" }, - /* Scalar i64 = -1000 → 0xfffffffffffffc18 little-endian. */ - { "i64_neg", "let x: i64 = -1000i64;\n" - "fn main() i32 = { return 0; };\n", - "main.x", - "\\x18\\xfc\\xff\\xff\\xff\\xff\\xff\\xff" }, - /* Array [4]i8 = [1, -2, 3, -4] → 0x01 0xfe 0x03 0xfc. */ - { "i8_arr", "let a: [4]i8 = [1i8, -2i8, 3i8, -4i8];\n" - "fn main() i32 = { return 0; };\n", - "main.a", - "\\x01\\xfe\\x03\\xfc" }, - /* Array [4]i16 = [1, -2, 3, -4] → LE16 each. */ - { "i16_arr", "let a: [4]i16 = [1i16, -2i16, 3i16, -4i16];\n" - "fn main() i32 = { return 0; };\n", - "main.a", - "\\x01\\x00\\xfe\\xff\\x03\\x00\\xfc\\xff" }, - /* Array [3]i32 = [10, -20, 30] → LE32 each. */ - { "i32_arr", "let a: [3]i32 = [10i32, -20i32, 30i32];\n" - "fn main() i32 = { return 0; };\n", - "main.a", - "\\x0a\\x00\\x00\\x00\\xec\\xff\\xff\\xff\\x1e\\x00\\x00\\x00" }, - /* Array [2]i64 = [100, -200] → LE64 each. Note 100 == 'd' and - * 56 == '8' are printable ASCII so emit_data_byte writes them - * raw rather than as `\xNN`. */ - { "i64_arr", "let a: [2]i64 = [100i64, -200i64];\n" - "fn main() i32 = { return 0; };\n", - "main.a", - "d\\x00\\x00\\x00\\x00\\x00\\x00\\x00" - "8\\xff\\xff\\xff\\xff\\xff\\xff\\xff" }, -}; - -static int -slurp(const char *path, char *buf, size_t cap) -{ - FILE *f = fopen(path, "rb"); - if (!f) return -1; - size_t n = fread(buf, 1, cap - 1, f); - fclose(f); - buf[n] = '\0'; - return (int)n; -} - -/* Check that the .s contains a DATAW row for `sym` whose payload - * matches `want_bytes`. Returns 0 on match. */ -static int -check_dataw(const char *spath, const struct row *r) -{ - char buf[1 << 16]; - if (slurp(spath, buf, sizeof buf) < 0) return -1; - char needle[256]; - snprintf(needle, sizeof needle, "DATAW %s(SB),\"%s\"", - r->sym, r->want_bytes); - return strstr(buf, needle) ? 0 : -1; -} - -static int -emit_s(const char *w6c, const struct row *r, int i, char *out_s, size_t cap) -{ - char src[64], cmd[1024]; - snprintf(src, sizeof src, "/tmp/sde_asm_%d_%d.ww", getpid(), i); - snprintf(out_s, cap, "/tmp/sde_asm_%d_%d_%s.s", - getpid(), i, w6c[strlen(w6c) - 1] == 'w' ? "ww" : "c"); - - FILE *f = fopen(src, "wb"); - if (!f) return -1; - wwtest_fputs(r->src, f); - fclose(f); - - snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, out_s, src); - int rc = runwait(cmd); - unlink(src); - return rc; -} - -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 w6c[640], w6c_ww[640]; - snprintf(w6c, sizeof w6c, "%s/w6c", bin); - snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin); - - int have_ww = (access(w6c_ww, X_OK) == 0); - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - - for (int i = 0; i < n; i++) { - char cs_path[128], ws_path[128]; - - /* cstage row: DATAW emit + byte payload. */ - if (emit_s(w6c, &rows[i], i, cs_path, sizeof cs_path) != 0) { - fprintf(stderr, - "signed_data_emit[cstage][%s]: w6c failed\n", - rows[i].label); - fail++; total++; continue; - } - total++; - if (check_dataw(cs_path, &rows[i]) != 0) { - fprintf(stderr, - "signed_data_emit[cstage][%s]: DATAW %s(SB),\"%s\" missing\n", - rows[i].label, rows[i].sym, rows[i].want_bytes); - fail++; - } - - if (!have_ww) { unlink(cs_path); continue; } - - /* wwstage row: same DATAW emit. */ - if (emit_s(w6c_ww, &rows[i], i, ws_path, sizeof ws_path) != 0) { - fprintf(stderr, - "signed_data_emit[wwstage][%s]: w6c_ww failed\n", - rows[i].label); - fail++; total++; - unlink(cs_path); - continue; - } - total++; - if (check_dataw(ws_path, &rows[i]) != 0) { - fprintf(stderr, - "signed_data_emit[wwstage][%s]: DATAW %s(SB),\"%s\" missing\n", - rows[i].label, rows[i].sym, rows[i].want_bytes); - fail++; - } - - /* Byte-id diff between stages. The bootstrap byte-id covers - * cross-stage drift globally; per-row diff here surfaces a - * focused regression in the emit_lets / emitletdataw arms. */ - total++; - char cmd[512]; - snprintf(cmd, sizeof cmd, "cmp -s %s %s", cs_path, ws_path); - if (runwait(cmd) != 0) { - fprintf(stderr, - "signed_data_emit[%s]: cstage vs wwstage asm differs\n", - rows[i].label); - fail++; - } - - unlink(cs_path); unlink(ws_path); - } - - if (fail) { - fprintf(stderr, - "signed_data_emit: %d/%d fixtures failed\n", fail, total); - return 1; - } - printf("signed_data_emit: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/724_letdecl_zeroinit.c b/test/wcc/724_letdecl_zeroinit.c deleted file mode 100644 index 0410f346..00000000 --- a/test/wcc/724_letdecl_zeroinit.c +++ /dev/null @@ -1,197 +0,0 @@ -/* - * 724_letdecl_zeroinit — sentinel for STATUS-3 #22. Bare `let x: T;` - * (no rhs) where T is `!void`, `void`, or a name-aliased type that - * resolves to either — sz==8 but `typeis8byteprimitive` returns false - * because there's no N_TBANG arm. Cstage at cmd/w6c/cgen.c:6381 emits - * `MOVQ $0, -K(BP)` for any 8B slot unconditionally; wwstage skipped - * the emit, leaving the slot uninit (stack residue). - * - * Filed STATUS-3 #22, previously byte-id drift only: 995_self_rebuild - * masked the divergence because the bootstrap main.combined.ww had no - * `let x: !void;` sites. utf8.next / utf8.utf8sz / utf8.validate - * introduced them with lib/encoding/utf8 landing; lib/strings's - * nested-if shapes (task #15) compound the drift past the per-rebuild - * threshold — promotes #22 from latent to bootstrap-blocking. - * - * Fix aligns wwstage DOWN to cstage (rule 10) by extending - * `typeis8byteprimitive` to recognise the same sz=8 default arms - * cstage's N_LET falls through (N_TBANG recurses on inner, plain - * `void`, alias chains via aliaslookup). Gating purely on sz==8 - * would over-zero sub-8B structs whose wwstage slot pads to 8 — the - * classifier-level fix is the narrow match. - * - * Rows pin asm-presence in both stages on the canonical shape and - * cmp -s byte-id between them. - */ -#include -#include -#include -#include -#include -#include -#include "wwtestpkg.h" - -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; }; - -static const struct row rows[] = { - /* `!void`-aliased let-decl — the utf8.invalid shape. */ - { "bang_void_letdecl", - "type invalid = !void;\n" - "export fn produce() (i32 | invalid) = {\n" - " let e: invalid;\n" - " return e;\n" - "};\n" }, - /* Plain-`void`-aliased let-decl — the utf8.done / utf8.more shape. */ - { "void_alias_letdecl", - "type done = void;\n" - "export fn produce() (i32 | done) = {\n" - " let d: done;\n" - " return d;\n" - "};\n" }, -}; - -static int -slurp(const char *path, char *buf, size_t cap) -{ - FILE *f = fopen(path, "rb"); - if (!f) return -1; - size_t n = fread(buf, 1, cap - 1, f); - fclose(f); - buf[n] = '\0'; - return (int)n; -} - -/* The let-decl's slot is the first local off BP; cstage emits - * `MOVQ $0, -8(BP)` (or the appropriate slot offset). We just look - * for the literal `MOVQ\t$0, -` substring inside the produce body - * (between the function label and the RET). */ -static int -check_movq_zero_present(const char *spath, const struct row *r) -{ - char buf[1 << 16]; - if (slurp(spath, buf, sizeof buf) < 0) return -1; - - const char *body = strstr(buf, "TEXT main.produce"); - if (!body) { - fprintf(stderr, "row[%s]: no TEXT produce label\n", r->label); - return -1; - } - const char *end = strstr(body, "RET"); - if (!end) end = buf + strlen(buf); - long bodylen = end - body; - if (bodylen <= 0 || (size_t)bodylen >= sizeof buf) return -1; - - char windowed[1 << 16]; - memcpy(windowed, body, bodylen); - windowed[bodylen] = '\0'; - - if (strstr(windowed, "MOVQ\t$0, -") == NULL) { - fprintf(stderr, - "row[%s]: no `MOVQ $0, -K(BP)` zero-init in produce body\n", - r->label); - return -1; - } - return 0; -} - -static int -emit_s(const char *w6c, const struct row *r, int i, char *out_s, size_t cap) -{ - char src[64], cmd[1024]; - snprintf(src, sizeof src, "/tmp/lzi_asm_%d_%d.ww", getpid(), i); - snprintf(out_s, cap, "/tmp/lzi_asm_%d_%d_%s.s", - getpid(), i, w6c[strlen(w6c) - 1] == 'w' ? "ww" : "c"); - - FILE *f = fopen(src, "wb"); - if (!f) return -1; - wwtest_fputs(r->src, f); - fclose(f); - - snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, out_s, src); - int rc = runwait(cmd); - unlink(src); - return rc; -} - -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 w6c[640], w6c_ww[640]; - snprintf(w6c, sizeof w6c, "%s/w6c", bin); - snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin); - - int have_ww = (access(w6c_ww, X_OK) == 0); - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - - for (int i = 0; i < n; i++) { - char cs_path[128], ws_path[128]; - - if (emit_s(w6c, &rows[i], i, cs_path, sizeof cs_path) != 0) { - fprintf(stderr, - "letdecl_zeroinit[cstage][%s]: w6c failed\n", - rows[i].label); - fail++; total++; continue; - } - total++; - if (check_movq_zero_present(cs_path, &rows[i]) != 0) { - fail++; - } - - if (!have_ww) { unlink(cs_path); continue; } - - if (emit_s(w6c_ww, &rows[i], i, ws_path, sizeof ws_path) != 0) { - fprintf(stderr, - "letdecl_zeroinit[wwstage][%s]: w6c_ww failed\n", - rows[i].label); - fail++; total++; - unlink(cs_path); - continue; - } - total++; - if (check_movq_zero_present(ws_path, &rows[i]) != 0) { - fail++; - } - - /* Byte-id between stages — the actual fix-pin. */ - total++; - char cmd[512]; - snprintf(cmd, sizeof cmd, "cmp -s %s %s", cs_path, ws_path); - if (runwait(cmd) != 0) { - fprintf(stderr, - "letdecl_zeroinit[%s]: cstage vs wwstage asm differs\n", - rows[i].label); - fail++; - } - - unlink(cs_path); unlink(ws_path); - } - - if (fail) { - fprintf(stderr, - "letdecl_zeroinit: %d/%d fixtures failed\n", fail, total); - return 1; - } - printf("letdecl_zeroinit: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/744_letcopy_struct.c b/test/wcc/744_letcopy_struct.c deleted file mode 100644 index b6a1bc4e..00000000 --- a/test/wcc/744_letcopy_struct.c +++ /dev/null @@ -1,265 +0,0 @@ -/* - * 744_letcopy_struct — sentinel for STATUS-6 #32. Class B-symmetric - * cgen miscompile: `let p2: T = p1;` where T is a struct >8B and rhs - * is a local ident silently zero-inits p2 (cstage emitted nothing for - * the copy; wwstage emitted a single MOVQ AX + stale BX from the - * sz==16 str-init tail). Reads after the let saw whatever the stack - * held — silent partial-copy / silent zero on a fresh frame. - * - * Both stages now byte-copy the source slot to the dest slot - * per-qword with a sized tail (MOVL/MOVB) for natural sizes not - * 8-aligned. Mirrors cg_widen_tagged_store's struct-ident payload - * copy. See cmd/w6c/cgen.c N_LET and selfhost/cmd/wcc/cgenstmt.ww - * cglet. - * - * Rows exercise the 4 struct shapes the task brief called out: - * (a) 3-field i32 (sz=12, MOVQ + MOVL tail). - * (b) i32 + str field (sz=24, 3 × MOVQ). - * (c) i32 + []u8 slice field (sz=32, 4 × MOVQ). - * (d) i32 + tagged (i32|str) field (sz=32, 4 × MOVQ). - * - * Each row pins: - * - asm-presence in both stages: produce body emits `min_loads` - * `MOVQ -K(BP), AX` source-slot loads (the bug pin — pre-fix - * cstage emitted 0; wwstage emitted 1 with garbage BX tail). - * - asm byte-id between stages (cmp -s on the produce-bearing .s). - * - runtime: produce() returns 0 (every field round-tripped). - */ -#include -#include -#include -#include -#include -#include "wwtestpkg.h" - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -static int -slurp(const char *path, char *buf, size_t cap) -{ - FILE *f = fopen(path, "rb"); - if (!f) return -1; - size_t n = fread(buf, 1, cap - 1, f); - fclose(f); - buf[n] = '\0'; - return (int)n; -} - -static int -count_substr(const char *start, const char *end, const char *needle) -{ - int n = 0; - size_t nlen = strlen(needle); - const char *p = start; - while (p + nlen <= end) { - if (memcmp(p, needle, nlen) == 0) { n++; p += nlen; } - else { p++; } - } - return n; -} - -struct row { - const char *label; - /* Direct-w6c source: bare fn produce, no package/imports. */ - const char *asm_src; - /* Runtime copies are owned by test/wcc/data/r744_letcopy_*. */ - /* Source-slot loads expected in produce: at least N for the copy. - * Pre-fix cstage emitted 0; pre-fix wwstage emitted 1 with stale BX. - * `MOVQ -K(BP), AX` is also emitted by p2.a / p2.s.len reads in the - * checks, so set min_loads to a value the copy alone surpasses. */ - int min_loads; -}; - -static const struct row rows[] = { - /* (a) 3-field i32 struct, sz=12. Copy = 1 × MOVQ + 1 × MOVL. - * p1 inits via structlit to sidestep the pre-existing no-rhs - * zero-init divergence for non-8-multiple struct sizes - * (cstage uses natural sz=12 → MOVQ+MOVL, wwstage uses slot - * sz=16 → MOVQ+MOVQ). Tracked as the #15/#26c rule-10 - * size-strategy convergence follow-up. */ - { "tri_i32", - "type tri = struct { a: i32, b: i32, c: i32 };\n" - "export fn produce() i32 = {\n" - " let p1: tri = tri{a=11, b=22, c=33};\n" - " let p2: tri = p1;\n" - " if (p2.a != 11) { return 11; };\n" - " if (p2.b != 22) { return 12; };\n" - " if (p2.c != 33) { return 13; };\n" - " return 0;\n" - "};\n", - 1 }, - /* (b) i32 + str field, sz=24. Copy = 3 × MOVQ. */ - { "field_str", - "type ws = struct { a: i32, s: str };\n" - "export fn produce() i32 = {\n" - " let p1: ws;\n" - " p1.a = 7; p1.s = \"hi\";\n" - " let p2: ws = p1;\n" - " if (p2.a != 7) { return 11; };\n" - " if (p2.s.len != 2) { return 12; };\n" - " return 0;\n" - "};\n", - 3 }, - /* (c) i32 + []u8 slice field, sz=32. Copy = 4 × MOVQ. */ - { "field_slice", - "type wsl = struct { a: i32, b: []u8 };\n" - "export fn produce() i32 = {\n" - " let raw: [3]u8 = [1: u8, 2: u8, 3: u8];\n" - " let p1: wsl;\n" - " p1.a = 9; p1.b = raw[0:3];\n" - " let p2: wsl = p1;\n" - " if (p2.a != 9) { return 11; };\n" - " if (p2.b.len != 3) { return 12; };\n" - " return 0;\n" - "};\n", - 4 }, - /* (d) i32 + tagged (i32|str) field, sz=32. Copy = 4 × MOVQ. - * Runtime checks only p2.a; the asm load-count (>=4) and the - * byte-id gate together prove the full 32B slot copied — match - * on p2.t avoided because wwstage's match scrutinee spill grows - * the frame and reorders ABI loads vs cstage (pre-existing - * #15/#26c rule-10 size-strategy / match-spill divergence). */ - { "field_tagged", - "type tag = (i32 | str);\n" - "type wtg = struct { a: i32, t: tag };\n" - "export fn produce() i32 = {\n" - " let p1: wtg = wtg{a=5, t=42: tag};\n" - " let p2: wtg = p1;\n" - " if (p2.a != 5) { return 11; };\n" - " return 0;\n" - "};\n", - 4 }, -}; - -/* Locate `TEXT produce` body in the asm file and count source-slot - * loads (`MOVQ\t-K(BP), AX`) inside it. Pre-fix cstage produce had 0; - * pre-fix wwstage produce had 1 (no tail). */ -static int -check_copy_loads(const char *spath, const struct row *r) -{ - static char buf[1 << 16]; - if (slurp(spath, buf, sizeof buf) < 0) return -1; - - const char *body = strstr(buf, "TEXT main.produce"); - if (!body) { - fprintf(stderr, "row[%s]: no TEXT produce label in %s\n", - r->label, spath); - return -1; - } - /* End at the next TEXT (or EOF). */ - const char *end = strstr(body + 1, "\nTEXT "); - if (!end) end = buf + strlen(buf); - - int loads = count_substr(body, end, "MOVQ\t-"); - if (loads < r->min_loads) { - fprintf(stderr, - "row[%s]: only %d `MOVQ -K(BP), AX` source loads in " - "produce; expected >= %d (copy missing)\n", - r->label, loads, r->min_loads); - return -1; - } - return 0; -} - -static int -emit_s(const char *w6c, const struct row *r, int i, int stage, - char *out_s, size_t cap) -{ - char src[64], cmd[1024]; - snprintf(src, sizeof src, "/tmp/lcs_asm_%d_%d_%d.ww", - getpid(), i, stage); - snprintf(out_s, cap, "/tmp/lcs_asm_%d_%d_%d.s", - getpid(), i, stage); - - FILE *f = fopen(src, "wb"); - if (!f) return -1; - wwtest_fputs(r->asm_src, f); - fclose(f); - - snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, out_s, src); - int rc = runwait(cmd); - unlink(src); - return rc; -} - -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 w6c[640], w6c_ww[640]; - snprintf(w6c, sizeof w6c, "%s/w6c", bin); - snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin); - - int have_ww = (access(w6c_ww, X_OK) == 0); - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - - for (int i = 0; i < n; i++) { - const struct row *r = &rows[i]; - char cs_path[160], ws_path[160]; - - /* cstage asm-presence. */ - if (emit_s(w6c, r, i, 0, cs_path, sizeof cs_path) != 0) { - fprintf(stderr, - "letcopy_struct[cstage][%s]: w6c failed\n", - r->label); - fail++; total++; continue; - } - total++; - if (check_copy_loads(cs_path, r) != 0) fail++; - - /* wwstage asm-presence + byte-id. */ - if (have_ww) { - if (emit_s(w6c_ww, r, i, 1, ws_path, sizeof ws_path) - != 0) { - fprintf(stderr, - "letcopy_struct[wwstage][%s]: " - "w6c_ww failed\n", r->label); - fail++; total++; - unlink(cs_path); - continue; - } - total++; - if (check_copy_loads(ws_path, r) != 0) fail++; - - total++; - char cmd[512]; - snprintf(cmd, sizeof cmd, "cmp -s %s %s", - cs_path, ws_path); - if (runwait(cmd) != 0) { - fprintf(stderr, - "letcopy_struct[%s]: cstage vs wwstage " - "asm differs\n", r->label); - fail++; - } - unlink(ws_path); - } - unlink(cs_path); - - } - - if (fail) { - fprintf(stderr, - "letcopy_struct: %d/%d fixtures failed\n", fail, total); - return 1; - } - printf("letcopy_struct: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/746_strdef_inline.c b/test/wcc/746_strdef_inline.c deleted file mode 100644 index b044d77a..00000000 --- a/test/wcc/746_strdef_inline.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - * 746_strdef_inline — sentinel for wwstage's str-def value-load shape - * (selfhost/cmd/wcc/cgenexpr.ww cgident bare-leaf + cgdot mod-qualified - * paths). Pins both reference shapes to emit the strlit-inline pair - * (LEAQ _S_(SB), AX; MOVQ $, BX) rather than the bogus DATAW - * symbol-load fallback (`MOVQ .(SB), AX` — the symbol is - * never defined because str defs aren't laid out at SB, they live as - * interned strlits the .ptr/.len fold and value-load consume). - * - * Pre-fix wwstage cgenexpr.ww:553 (bare ident → deflookup true branch) - * emitted `MOVQ .(SB), AX` — a load from a SB symbol - * that emit_data never writes. Same shape latent on the mod-qualified - * arm (cgenexpr.ww:1729 cgdot module-qualified leaf branch): bare- - * symbol fallback was reached even when the leaf was an Sdef-backed - * str. Both stages already had .ptr/.len field-fold via deflookuprhs - * (#4c); only the value-reference shape was broken. Surfaced by - * reviewer-def during #4c R3 — blocked cstage Sdef walks #1/#2 - * prefer-pass sentinels (#11, #13) from shipping because their cs-vs- - * ws byte-id rows could not pass while the wwstage emit shape was - * mismatched. - * - * Cstage emits the strlit-inline pair via Sdef walks #1 (N_IDENT bare - * load, cmd/w6c/cgen.c case N_IDENT non-local) and #2 (N_DOT mod- - * qualified, case N_DOT untyped-lhs). Both walks have already been in - * place; this commit aligns wwstage UP to match per rule 10. - * - * Class A silent miscompile — every bare/qualified `MSG` value - * reference where `def MSG: str = "..."` would have loaded garbage - * from a never-defined SB symbol at runtime (the linker would have - * rejected the asm, but in test isolation the symbol resolves to 0). - * Latent: no in-tree corpus referenced an str def as a value (only as - * `.ptr`/`.len` via cgdot field-fold) prior to lib/strings c3. - * - * Pin: 2 rows. Row 1 (bare ident, single module) sentinel-flips the - * cgenexpr.ww:555 deflookup-strlit-inline branch on wwstage — revert - * the branch and row 1 fails (`MOVQ\talpha.MSG(SB)` appears instead - * of `LEAQ\t._S_`). Row 2 (mod-qualified, two modules) sentinel- - * flips the cgenexpr.ww:1730 cgdot deflookup-strlit-inline branch — - * revert and row 2 fails the same way (`MOVQ\talpha.MSG(SB)` appears in - * beta.b). Asserts the strlit-inline pair lands inside the right - * TEXT sym (LEAQ\t._S_ + MOVQ\t$,) with the bad_movq - * anti-check on each stage plus cs-vs-ws byte-id per row. - */ -#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; - const char *textsym; /* TEXT sym containing the load */ - const char *want_lea; /* must appear: strlit address load */ - const char *want_len; /* must appear: strlit length immediate */ - const char *bad_movq; /* must NOT appear: bogus SB symbol load */ -}; - -/* Strlit length 39 (not aliased to common frame/offset immediates - * 0/8/16/24/32/40/48). Single source-order def per row so - * the choice of Sdef entry is unambiguous regardless of head-walk vs - * prefer-pass — this commit fixes the EMIT SHAPE, not the lookup - * ordering (see #11, #13 for the lookup-side prefer-pass graduations - * that #12 unblocks). */ -static const struct row rows[] = { - { "bare_ident_strdef", - "package alpha;\n" - "def MSG: str = \"strdef_inline_pin_aaaaaaaaaaaaaaa_43chr\";\n" - "export fn afn() str = { return MSG; };\n" - "export fn main() i32 = { return 0; };\n", - /* #49: strlit labels carry the interning module's path prefix - * (here alpha — MSG's def interns in alpha's compile). */ - "TEXT alpha.afn", "LEAQ\talpha._S_", "MOVQ\t$39,", "alpha.MSG(SB)" }, - { "mod_qualified_strdef", - "package alpha;\n" - "def MSG: str = \"strdef_inline_pin_aaaaaaaaaaaaaaa_43chr\";\n" - "package beta;\n" - "import alpha;\n" - "export fn bfn() str = { return alpha.MSG; };\n" - "export fn main() i32 = { return 0; };\n", - /* #49: the inlined sdef interns at the USE site (beta.bfn), so the - * strlit label carries beta's path prefix, not alpha's. */ - "TEXT beta.bfn", "LEAQ\tbeta._S_", "MOVQ\t$39,", "alpha.MSG(SB)" }, -}; - -static int -slurp(const char *path, char *buf, size_t cap) -{ - FILE *f = fopen(path, "rb"); - if (!f) return -1; - size_t n = fread(buf, 1, cap - 1, f); - fclose(f); - buf[n] = '\0'; - return (int)n; -} - -static int -emit_s(const char *w6c, const struct row *r, int i, char *out_s, size_t cap) -{ - char src[64], cmd[1024]; - snprintf(src, sizeof src, "/tmp/sdi_%d_%d.ww", getpid(), i); - snprintf(out_s, cap, "/tmp/sdi_%d_%d_%s.s", - getpid(), i, w6c[strlen(w6c) - 1] == 'w' ? "ww" : "c"); - - FILE *f = fopen(src, "wb"); - if (!f) return -1; - fputs(r->src, f); - fclose(f); - - snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, out_s, src); - int rc = runwait(cmd); - unlink(src); - return rc; -} - -/* Inside the named TEXT sym, before its first RET, want_lea AND - * want_len MUST appear and bad_movq MUST NOT. bad_movq flags pre-fix - * cgenexpr.ww:555 / :1730 falling through to the bogus DATAW symbol- - * load. */ -static int -check_emit(const char *spath, const struct row *r, const char *stage) -{ - char buf[1 << 14]; - if (slurp(spath, buf, sizeof buf) < 0) { - fprintf(stderr, "row[%s][%s]: cannot read %s\n", - r->label, stage, spath); - return -1; - } - const char *fn = strstr(buf, r->textsym); - if (!fn) { - fprintf(stderr, "row[%s][%s]: no %s in %s\n", - r->label, stage, r->textsym, spath); - return -1; - } - const char *ret = strstr(fn, "\tRET"); - if (!ret) { - fprintf(stderr, "row[%s][%s]: no RET inside %s\n", - r->label, stage, r->textsym); - return -1; - } - const char *lea = strstr(fn, r->want_lea); - if (!lea || lea >= ret) { - fprintf(stderr, - "row[%s][%s]: want_lea %s missing inside %s\n", - r->label, stage, r->want_lea, r->textsym); - return -1; - } - const char *len = strstr(fn, r->want_len); - if (!len || len >= ret) { - fprintf(stderr, - "row[%s][%s]: want_len %s missing inside %s\n", - r->label, stage, r->want_len, r->textsym); - return -1; - } - const char *bad = strstr(fn, r->bad_movq); - if (bad && bad < ret) { - fprintf(stderr, - "row[%s][%s]: bad_movq %s present inside %s — bogus SB load\n", - r->label, stage, r->bad_movq, r->textsym); - return -1; - } - return 0; -} - -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 w6c[640], w6c_ww[640]; - snprintf(w6c, sizeof w6c, "%s/w6c", bin); - snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin); - - int have_ww = (access(w6c_ww, X_OK) == 0); - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - - for (int i = 0; i < n; i++) { - char cs_path[128], ws_path[128]; - - if (emit_s(w6c, &rows[i], i, cs_path, sizeof cs_path) != 0) { - fprintf(stderr, - "strdef_inline[cstage][%s]: w6c failed\n", - rows[i].label); - fail++; total++; continue; - } - total++; - if (check_emit(cs_path, &rows[i], "cstage") != 0) fail++; - - if (!have_ww) { unlink(cs_path); continue; } - - if (emit_s(w6c_ww, &rows[i], i, ws_path, sizeof ws_path) != 0) { - fprintf(stderr, - "strdef_inline[wwstage][%s]: w6c_ww failed\n", - rows[i].label); - fail++; total++; - unlink(cs_path); continue; - } - total++; - if (check_emit(ws_path, &rows[i], "wwstage") != 0) fail++; - - total++; - char cmd[512]; - snprintf(cmd, sizeof cmd, "cmp -s %s %s", cs_path, ws_path); - if (runwait(cmd) != 0) { - fprintf(stderr, - "strdef_inline[%s]: cstage vs wwstage asm differs\n", - rows[i].label); - fail++; - } - - unlink(cs_path); unlink(ws_path); - } - - if (fail) { - fprintf(stderr, - "strdef_inline: %d/%d fixtures failed\n", fail, total); - return 1; - } - printf("strdef_inline: %d/%d ok\n", total, total); - return 0; -}