test: fold driver-parity byteid into 989_lib_byteid, retire 815/940/951
Wwstage-driver-leg byte identity gets one owner. The three carriers' content identity is already blanket-owned by their r815_/r940_/r951_ corpus twins via test-data-byteid; their only non-redundant assertion was the ww_ww driver leg, which 989_lib_byteid's 43-unit sweep covers apart from the zero-dep root-only edge — folded in as a new probe row (44 units). r940 fixture comments re-cited off the retired carrier.
This commit is contained in:
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* 815: residual separated-package cstage/wwstage assembly identity.
|
||||
* Runtime is owned by r815_*; -S stops both driver legs before assembly,
|
||||
* archive creation, and linking. The sorted package-assembly concatenation
|
||||
* remains the byte oracle.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
struct row { const char *label, *path; };
|
||||
static const struct row rows[] = {
|
||||
{ "bare_int", "test/wcc/data/r815_bare_int/case.ww" },
|
||||
{ "bare_int_neg", "test/wcc/data/r815_bare_int_neg/case.ww" },
|
||||
{ "bare_uint", "test/wcc/data/r815_bare_uint/case.ww" },
|
||||
{ "uint_highbit", "test/wcc/data/r815_uint_highbit/case.ww" },
|
||||
{ "int_printf", "test/wcc/data/r815_int_printf/case.ww" },
|
||||
{ "unaffected_i64", "test/wcc/data/r815_unaffected_i64/case.ww" },
|
||||
{ "unaffected_str", "test/wcc/data/r815_unaffected_str/case.ww" },
|
||||
{ "mixed", "test/wcc/data/r815_mixed/case.ww" },
|
||||
};
|
||||
|
||||
static int runwait(const char *cmd) {
|
||||
int rc = system(cmd);
|
||||
return rc != -1 && WIFEXITED(rc) ? WEXITSTATUS(rc) : -1;
|
||||
}
|
||||
|
||||
static int copyfile(const char *src, const char *dst) {
|
||||
char buf[8192]; size_t n; int rc = 0;
|
||||
FILE *in = fopen(src, "rb"), *out;
|
||||
if (!in) return -1;
|
||||
out = fopen(dst, "wb");
|
||||
if (!out) { fclose(in); return -1; }
|
||||
while ((n = fread(buf, 1, sizeof buf, in)) != 0)
|
||||
if (fwrite(buf, 1, n, out) != n) { rc = -1; break; }
|
||||
if (ferror(in)) rc = -1;
|
||||
if (fclose(out) != 0) rc = -1;
|
||||
fclose(in); return rc;
|
||||
}
|
||||
|
||||
static int samefile(const char *a, const char *b) {
|
||||
FILE *fa = fopen(a, "rb"), *fb = fopen(b, "rb"); int rc = 0;
|
||||
if (!fa || !fb) rc = -1;
|
||||
else for (;;) { int ca = fgetc(fa), cb = fgetc(fb); if (ca != cb) { rc = -1; break; } if (ca == EOF) break; }
|
||||
if (fa) fclose(fa); if (fb) fclose(fb); return rc;
|
||||
}
|
||||
|
||||
static int byteid(const char *cdrv, const char *wdrv, const char *cwd,
|
||||
const struct row *r, int seq) {
|
||||
char tmp[256], src[512], cs[512], ws[512], cmd[2048]; int rc = -1;
|
||||
snprintf(tmp, sizeof tmp, "/tmp/fic_%d_b_%d", getpid(), seq);
|
||||
snprintf(src, sizeof src, "%s/main815b.ww", tmp);
|
||||
snprintf(cs, sizeof cs, "%s/all_cs.s", tmp);
|
||||
snprintf(ws, sizeof ws, "%s/all_ww.s", tmp);
|
||||
/* an unowned path (stale dir, full /tmp) must not be built in — or
|
||||
* rm -rf'd — below. */
|
||||
if (mkdir(tmp, 0755) != 0) { perror(tmp); return -1; }
|
||||
if (copyfile(r->path, src) != 0) goto out;
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"cd %s && timeout 180 %s build -S -I %s/lib "
|
||||
"-o %s/main815b_c %s 2>/dev/null", tmp, cdrv, cwd, tmp, src);
|
||||
if (runwait(cmd) != 0) goto out;
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"cd %s && timeout 180 %s build -S -I %s/lib "
|
||||
"-o %s/main815b_w %s 2>/dev/null", tmp, wdrv, cwd, tmp, src);
|
||||
if (runwait(cmd) != 0) goto out;
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"cat %s/main815b_c.sepwork/*.s > %s 2>/dev/null", tmp, cs);
|
||||
if (runwait(cmd) != 0) goto out;
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"cat %s/main815b_w.sepwork/*.s > %s 2>/dev/null", tmp, ws);
|
||||
if (runwait(cmd) != 0) goto out;
|
||||
rc = samefile(cs, ws);
|
||||
out:
|
||||
if (rc != 0) fprintf(stderr, "fmt_int_run[%s]: byte mismatch/error\n", r->label);
|
||||
snprintf(cmd, sizeof cmd, "rm -rf %s", tmp);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "fmt_int_run[%s]: cleanup %s failed\n", r->label, tmp);
|
||||
if (rc == 0) rc = -1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
const char *bin = getenv("BIN"); char cwd[1024], absbin[1024];
|
||||
char cdrv[1024], wdrv[1024];
|
||||
if (!getcwd(cwd, sizeof cwd)) return 1;
|
||||
if (!bin) bin = "out/bin";
|
||||
if (bin[0] != '/') { snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); bin = absbin; }
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
if (access(wdrv, X_OK) != 0) { fprintf(stderr, "fmt_int_run: skip wwstage\n"); return 0; }
|
||||
int n = (int)(sizeof rows / sizeof rows[0]), fail = 0;
|
||||
for (int i = 0; i < n; i++) if (byteid(cdrv, wdrv, cwd, &rows[i], i) != 0) fail++;
|
||||
if (fail) { fprintf(stderr, "fmt_int_run: %d/%d byte rows failed\n", fail, n); return 1; }
|
||||
printf("fmt_int_run: %d/%d byte rows ok\n", n, n); return 0;
|
||||
}
|
||||
@@ -1,307 +0,0 @@
|
||||
/*
|
||||
* 940_global_sret_run — project #220. sret (>24B by-value struct
|
||||
* return) assigned into a GLOBAL lvalue must land the whole struct,
|
||||
* not a truncated 8-byte store.
|
||||
*
|
||||
* Gate-blind family (sibling of #211): pre-fix BOTH stages emitted the
|
||||
* SAME broken `MOVQ AX, g(SB)` (only the sret return pointer reached
|
||||
* the global; the struct body, written to a scratch temp, was lost), so
|
||||
* asm byte-id stayed GREEN while runtime was wrong — g.pos == 0 for the
|
||||
* #94 io vtable cgoutstream pattern. The runtime rows below are the net.
|
||||
*
|
||||
* Surfacing case: the eFinal FLIP repointed cgen.ww's per-fn body output
|
||||
* through a GLOBAL `let cgoutstream: memio.stream` (>24B) wired once via
|
||||
* `cgoutstream = memio.dynamic()`; the truncated store left pos==0 so
|
||||
* the buffered body never flushed and w6c_ww emitted prologue-only.
|
||||
*
|
||||
* The fix routes the sret dest pointer to the global's symbol address
|
||||
* (LEAQ g(SB), DI) — zero-copy, the same discipline the local case
|
||||
* already used (LEAQ off(BP), DI). Local rows pin no regression at the
|
||||
* shared cgassign / cglet sret-receive sites.
|
||||
*
|
||||
* Runtime behavior is carried by the r940_global_sret_* wwfixtures. This
|
||||
* wrapper retains the separate-build assembly artifacts and their byte
|
||||
* identity (cs.s == ww.s, rule 10).
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#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; int want; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* GLOBAL sret assign: `let g: quad;` at module scope, `g = mk()`
|
||||
* in main. Pre-fix the global got only `MOVQ AX, g(SB)` and the
|
||||
* field reads returned garbage. */
|
||||
{ "global_assign",
|
||||
"type quad = struct { a: i64, b: i64, c: i64, d: i64 };\n"
|
||||
"fn mk(x: i64) quad = {\n"
|
||||
" return quad { a = x, b = x + 1i64, c = x + 2i64, d = x + 3i64 };\n"
|
||||
"};\n"
|
||||
"let g: quad;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" g = mk(10i64);\n"
|
||||
" if (g.a != 10i64) { return 1; };\n"
|
||||
" if (g.b != 11i64) { return 2; };\n"
|
||||
" if (g.c != 12i64) { return 3; };\n"
|
||||
" if (g.d != 13i64) { return 4; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* GLOBAL sret with a BRANCHED callee (#105): the returned value
|
||||
* is selected at runtime so a constant-fold coincidence can't mask
|
||||
* the register/dest routing. */
|
||||
{ "global_branched",
|
||||
"type quad = struct { a: i64, b: i64, c: i64, d: i64 };\n"
|
||||
"fn mk1() quad = { return quad { a = 1i64, b = 2i64, c = 3i64, d = 4i64 }; };\n"
|
||||
"fn mk2() quad = { return quad { a = 50i64, b = 0i64, c = 0i64, d = 0i64 }; };\n"
|
||||
"fn pick(w: i64) quad = {\n"
|
||||
" if (w == 1i64) { return mk1(); };\n"
|
||||
" return mk2();\n"
|
||||
"};\n"
|
||||
"let g: quad;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" g = pick(1i64);\n"
|
||||
" if (g.a != 1i64) { return 1; };\n"
|
||||
" if (g.b != 2i64) { return 2; };\n"
|
||||
" if (g.c != 3i64) { return 3; };\n"
|
||||
" if (g.d != 4i64) { return 4; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* GLOBAL mutated THROUGH A POINTER after the sret assign — the io
|
||||
* vtable-callback shape that surfaced #220 (callback receives &g,
|
||||
* mutates a field). */
|
||||
{ "global_through_pointer",
|
||||
"type quad = struct { a: i64, b: i64, c: i64, d: i64 };\n"
|
||||
"fn mk(x: i64) quad = {\n"
|
||||
" return quad { a = x, b = x + 1i64, c = x + 2i64, d = x + 3i64 };\n"
|
||||
"};\n"
|
||||
"fn bump(p: *quad) void = { p.a += 100i64; };\n"
|
||||
"let g: quad;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" g = mk(7i64);\n"
|
||||
" bump(&g);\n"
|
||||
" if (g.a != 107i64) { return 1; };\n"
|
||||
" if (g.b != 8i64) { return 2; };\n"
|
||||
" if (g.c != 9i64) { return 3; };\n"
|
||||
" if (g.d != 10i64) { return 4; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* LOCAL decl+assign regression: `let g: quad; g = mk();` routes
|
||||
* through the same cgassign sret-receive site the fix touches. */
|
||||
{ "local_assign_regression",
|
||||
"type quad = struct { a: i64, b: i64, c: i64, d: i64 };\n"
|
||||
"fn mk(x: i64) quad = {\n"
|
||||
" return quad { a = x, b = x + 1i64, c = x + 2i64, d = x + 3i64 };\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let g: quad;\n"
|
||||
" g = mk(20i64);\n"
|
||||
" if (g.a != 20i64) { return 1; };\n"
|
||||
" if (g.b != 21i64) { return 2; };\n"
|
||||
" if (g.c != 22i64) { return 3; };\n"
|
||||
" if (g.d != 23i64) { return 4; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* LOCAL let-init regression: `let g: quad = mk();` routes through
|
||||
* cglet's sret-receive branch (the sister site). */
|
||||
{ "local_init_regression",
|
||||
"type quad = struct { a: i64, b: i64, c: i64, d: i64 };\n"
|
||||
"fn mk(x: i64) quad = {\n"
|
||||
" return quad { a = x, b = x + 1i64, c = x + 2i64, d = x + 3i64 };\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let g: quad = mk(30i64);\n"
|
||||
" if (g.a != 30i64) { return 1; };\n"
|
||||
" if (g.b != 31i64) { return 2; };\n"
|
||||
" if (g.c != 32i64) { return 3; };\n"
|
||||
" if (g.d != 33i64) { return 4; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
};
|
||||
|
||||
/* #94 sep layout: concat the per-package asm the cstage sep build emitted
|
||||
* (<dir>/gsret_c_<i>.sepwork) and the wwstage sep build emitted
|
||||
* (<dir>/gsret_w_<i>.sepwork), then assert byte-identical. Returns 0 ok,
|
||||
* 1 differ, -1 harness error. */
|
||||
static int
|
||||
byteid(const char *dir, int i)
|
||||
{
|
||||
char cs[256], ws[256], cmd[1024];
|
||||
int rc;
|
||||
snprintf(cs, sizeof cs, "%s/bid_cs_%d.s", dir, i);
|
||||
snprintf(ws, sizeof ws, "%s/bid_ww_%d.s", dir, i);
|
||||
|
||||
/* an unmatched sepwork glob must be a loud harness error — two
|
||||
* empty concatenations would otherwise cmp equal (vacuous green). */
|
||||
snprintf(cmd, sizeof cmd, "cat %s/gsret_c_%d.sepwork/*.s > %s 2>/dev/null",
|
||||
dir, i, cs);
|
||||
if (runwait(cmd) != 0) {
|
||||
rc = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "cat %s/gsret_w_%d.sepwork/*.s > %s 2>/dev/null",
|
||||
dir, i, ws);
|
||||
if (runwait(cmd) != 0) {
|
||||
rc = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
snprintf(cmd, sizeof cmd, "cmp -s %s %s", cs, ws);
|
||||
rc = runwait(cmd) == 0 ? 0 : 1;
|
||||
cleanup:
|
||||
if (unlink(cs) != 0 && errno != ENOENT) { perror(cs); if (rc == 0) rc = -1; }
|
||||
if (unlink(ws) != 0 && errno != ENOENT) { perror(ws); if (rc == 0) rc = -1; }
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Build `r` with `driver -o <dir>/<stem>` (per-package asm in
|
||||
* <dir>/<stem>.sepwork/), returning zero on build success. */
|
||||
static int
|
||||
run_driver(const char *driver, const struct row *r, const char *dir,
|
||||
int i, const char *stem)
|
||||
{
|
||||
char src[256], taggedstem[128], cmd[1024];
|
||||
snprintf(src, sizeof src, "%s/gsret_%d.ww", dir, i);
|
||||
snprintf(taggedstem, sizeof taggedstem, "%s_%d", stem, i);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) return -1;
|
||||
wwtest_fputs(r->src, f);
|
||||
int werr = ferror(f);
|
||||
if (fclose(f) != 0 || werr) {
|
||||
fprintf(stderr, "row[%s]: writing %s failed\n", r->label, src);
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"cd %s && %s build -S -o %s/%s %s",
|
||||
dir, driver, dir, taggedstem, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: build via %s failed\n",
|
||||
r->label, driver);
|
||||
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 cdrv[640], wdrv[640];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
char wwc[640];
|
||||
snprintf(wwc, sizeof wwc, "%s/w6c_ww", bin);
|
||||
int have_ww = (access(wdrv, X_OK) == 0);
|
||||
int have_wwc = (access(wwc, X_OK) == 0);
|
||||
|
||||
char dir[] = "/tmp/gsret_XXXXXX";
|
||||
if (mkdtemp(dir) == NULL) {
|
||||
perror("mkdtemp");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0, cleanfail = 0;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
/* cstage separate build → <dir>/gsret_c_<i>.sepwork/. */
|
||||
int gc = run_driver(cdrv, &rows[i], dir, i, "gsret_c");
|
||||
total++;
|
||||
if (gc != rows[i].want) {
|
||||
fprintf(stderr, "global_sret_run[cstage][%s]: "
|
||||
"build=%d want=%d\n", rows[i].label, gc,
|
||||
rows[i].want);
|
||||
fail++;
|
||||
}
|
||||
|
||||
/* wwstage separate build → <dir>/gsret_w_<i>.sepwork/. */
|
||||
if (have_ww) {
|
||||
int gw = run_driver(wdrv, &rows[i], dir, i, "gsret_w");
|
||||
total++;
|
||||
if (gw != rows[i].want) {
|
||||
fprintf(stderr, "global_sret_run[wwstage][%s]: "
|
||||
"build=%d want=%d\n", rows[i].label, gw,
|
||||
rows[i].want);
|
||||
fail++;
|
||||
}
|
||||
|
||||
/* byte-id (rule 10): the per-package w6c asm (cstage sep
|
||||
* build) == the w6c_ww asm (wwstage sep build). */
|
||||
if (have_wwc) {
|
||||
total++;
|
||||
int bid = byteid(dir, i);
|
||||
if (bid != 0) {
|
||||
fprintf(stderr, "global_sret_run[byteid][%s]: "
|
||||
"%s\n", rows[i].label,
|
||||
bid == 1 ? "cs.s != ww.s"
|
||||
: "harness error");
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char p[640];
|
||||
snprintf(p, sizeof p, "%s/gsret_%d.ww", dir, i);
|
||||
/* ENOENT = the row failed before writing the source. */
|
||||
if (unlink(p) != 0 && errno != ENOENT) {
|
||||
perror(p);
|
||||
cleanfail = 1;
|
||||
}
|
||||
snprintf(p, sizeof p, "rm -rf %s/gsret_c_%d.sepwork "
|
||||
"%s/gsret_w_%d.sepwork %s/gsret_c_%d %s/gsret_w_%d",
|
||||
dir, i, dir, i, dir, i, dir, i);
|
||||
if (runwait(p) != 0) {
|
||||
fprintf(stderr, "global_sret_run: cleanup row %d "
|
||||
"failed\n", i);
|
||||
cleanfail = 1;
|
||||
}
|
||||
}
|
||||
if (rmdir(dir) != 0) {
|
||||
perror(dir);
|
||||
cleanfail = 1;
|
||||
}
|
||||
|
||||
if (!have_ww)
|
||||
fprintf(stderr, "global_sret_run: skip wwstage (no %s)\n", wdrv);
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "global_sret_run: %d/%d checks failed\n",
|
||||
fail, total);
|
||||
return 1;
|
||||
}
|
||||
/* cleanup diagnostics already printed; must not stay green. */
|
||||
if (cleanfail)
|
||||
return 1;
|
||||
printf("global_sret_run: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,279 +0,0 @@
|
||||
/*
|
||||
* 951_defdim_struct_run — byte-id net for #141: a struct
|
||||
* field whose array dimension is a `def` (`[MAX]u8`, MAX a same-module
|
||||
* def), not an integer literal. BOTH stages were wrong, oppositely:
|
||||
*
|
||||
* cstage LOUD-rejected ("array length must be an integer literal").
|
||||
* resolve_type's N_TARRAY arm folded only N_INTLIT dims; a def-ref
|
||||
* fell through to the err. Root was a phase-ordering trap: def names
|
||||
* weren't bound in scope when resolve_typedecl walked the struct
|
||||
* body, so eval_def_const couldn't see MAX. Fix binds def-name stubs
|
||||
* before type-body resolution, then folds the dim via eval_def_const.
|
||||
*
|
||||
* wwstage SILENTLY mis-laid-out the struct. astsize sized the def-dim
|
||||
* field to 0 (only N_INTLIT dims were read), so the N_TSTRUCT
|
||||
* `off += astsize(field)` loop gave the NEXT field the array's offset
|
||||
* — the trailing scalar overlapped the array, reading/writing the
|
||||
* wrong bytes (ken pdefM4: end != written). Fix routes every N_TARRAY
|
||||
* length reader through a shared arrayelen() that folds the def.
|
||||
*
|
||||
* The TEETH row is the trailing scalar after the def-dim array: pre-fix
|
||||
* ww read a corrupted `end`; post-fix it round-trips and cs==ww byte-id.
|
||||
* Closes #13's def-dim half (the slice-repeat clause stays open).
|
||||
*
|
||||
* Runtime values live in r951_defdim_* fixtures. This carrier compares the
|
||||
* `ww build`-produced
|
||||
* .combined.ww, not the raw src: w6c does no import resolution, so the
|
||||
* cross-module row (C1, `import os`) only folds once concatenated, and a
|
||||
* same-module row combines to itself. The last row is the ACTUAL
|
||||
* path::buffer shape — a cross-module `os.PATH_MAX` def dimension (the
|
||||
* N_DOT fold arm). u8 array elements + i32 trailing scalars only (the
|
||||
* i32 return is MOVL/MOVSXD byte-id; see 949_dotbase_arr_run).
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.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[] = {
|
||||
/* TEETH: def-dim array FOLLOWED by a scalar; write both, read the
|
||||
* scalar back. Pre-fix ww laid `end` at the array's offset (array
|
||||
* sized 0) → end read garbage. Post-fix end at offset 4 → 42. */
|
||||
{ "m4_end",
|
||||
"package main;\n"
|
||||
"def M: i32 = 4;\n"
|
||||
"type t = struct { b: [M]u8, end: i32 };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let s: t;\n"
|
||||
" s.b[3] = 9u8;\n"
|
||||
" s.end = 42;\n"
|
||||
" return s.end;\n"
|
||||
"};\n" },
|
||||
/* the array element itself must survive too — read the last byte
|
||||
* (pre-fix the overlapping `end` write would clobber it). */
|
||||
{ "m4_belem",
|
||||
"package main;\n"
|
||||
"def M: i32 = 4;\n"
|
||||
"type t = struct { b: [M]u8, end: i32 };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let s: t;\n"
|
||||
" s.b[3] = 200u8;\n"
|
||||
" s.end = 7;\n"
|
||||
" return s.b[3]: i32;\n"
|
||||
"};\n" },
|
||||
/* the ~4KB path::buffer shape — def MAX=4095, read the trailing
|
||||
* scalar after a 4095-byte array. */
|
||||
{ "m4095_end",
|
||||
"package main;\n"
|
||||
"def MAX: i32 = 4095;\n"
|
||||
"type t = struct { b: [MAX]u8, end: i32 };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let s: t;\n"
|
||||
" s.b[4094] = 5u8;\n"
|
||||
" s.end = 99;\n"
|
||||
" return s.end;\n"
|
||||
"};\n" },
|
||||
/* two scalars after the def-dim array — pins the cumulative offset
|
||||
* (a+c read their written values: 11+22=33). */
|
||||
{ "twoafter",
|
||||
"package main;\n"
|
||||
"def M: i32 = 4;\n"
|
||||
"type t = struct { b: [M]u8, a: i32, c: i32 };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let s: t;\n"
|
||||
" s.a = 11;\n"
|
||||
" s.c = 22;\n"
|
||||
" return s.a + s.c;\n"
|
||||
"};\n" },
|
||||
/* C1 (rob-mandatory): the ACTUAL path::buffer shape — a CROSS-
|
||||
* MODULE def dimension. `def MAX = os.PATH_MAX - 1` folds an N_DOT
|
||||
* (os.PATH_MAX) const-ref through eval_def_const's cross-module arm,
|
||||
* which the same-module rows don't exercise. os.PATH_MAX is 4096, so
|
||||
* MAX is 4095. Byte-id runs on the ww-build combined.ww (w6c alone
|
||||
* does no import resolution, so it can't compile the raw `import os`
|
||||
* source). */
|
||||
{ "xmod_pathmax",
|
||||
"package main;\n"
|
||||
"import os;\n"
|
||||
"def MAX: i32 = os.PATH_MAX - 1;\n"
|
||||
"type t = struct { b: [MAX]u8, end: i32 };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let s: t;\n"
|
||||
" s.b[4094] = 7u8;\n"
|
||||
" s.end = 77;\n"
|
||||
" return s.end;\n"
|
||||
"};\n" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
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 wdrv[1100];
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
if (access(wdrv, X_OK) != 0) {
|
||||
fprintf(stderr, "defdim_struct: ww_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/wwdds_%d_%d.ww", getpid(), i);
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (f == NULL) { perror(src); fail++; continue; }
|
||||
fputs(rows[i].src, f);
|
||||
fclose(f);
|
||||
|
||||
char tmpdir[64];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwdds_%d_d_%d",
|
||||
getpid(), i);
|
||||
if (mkdir(tmpdir, 0755) != 0) {
|
||||
/* not owned — must not rm a path we failed to create */
|
||||
perror(tmpdir);
|
||||
fail++;
|
||||
if (unlink(src) != 0 && errno != ENOENT)
|
||||
perror(src);
|
||||
continue;
|
||||
}
|
||||
|
||||
char base[64];
|
||||
const char *b = strrchr(src, '/');
|
||||
b = b ? b + 1 : src;
|
||||
snprintf(base, sizeof base, "%s", b);
|
||||
char *dot = strrchr(base, '.');
|
||||
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
|
||||
|
||||
/* #94 sep layout: each driver's build emits the runnable
|
||||
* binary at <stem> + per-package asm under <stem>.sepwork/.
|
||||
* Distinct stems so the wwstage sepwork doesn't clobber cstage's;
|
||||
* pin all outputs stay under tmpdir. A
|
||||
* cross-module row's `import os` resolves per-package, a same-
|
||||
* module row is just __root — both compare via the sorted glob. */
|
||||
char cmd[2048], stem_c[160], stem_w[160];
|
||||
snprintf(stem_c, sizeof stem_c, "%s/%s_c", tmpdir, base);
|
||||
snprintf(stem_w, sizeof stem_w, "%s/%s_w", tmpdir, base);
|
||||
|
||||
char cs_s[64], ws_s[64];
|
||||
snprintf(cs_s, sizeof cs_s, "/tmp/wwdds_%d_%d_cs.s",
|
||||
getpid(), i);
|
||||
snprintf(ws_s, sizeof ws_s, "/tmp/wwdds_%d_%d_ww.s",
|
||||
getpid(), i);
|
||||
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"cd %s && %s/ww build -S -o %s %s",
|
||||
tmpdir, bin, stem_c, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: cstage build failed\n",
|
||||
rows[i].label);
|
||||
fail++;
|
||||
goto rowdone;
|
||||
}
|
||||
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"cd %s && %s/ww_ww build -S -o %s %s "
|
||||
">/dev/null 2>&1", tmpdir, bin, stem_w, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: ww_ww build failed\n",
|
||||
rows[i].label);
|
||||
fail++;
|
||||
} else {
|
||||
/* a failed cat (e.g. an empty sepwork glob) must fail
|
||||
* the row — two absent trees would compare equal-empty. */
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"cat %s.sepwork/*.s > %s 2>/dev/null", stem_c, cs_s);
|
||||
int catc = runwait(cmd);
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"cat %s.sepwork/*.s > %s 2>/dev/null", stem_w, ws_s);
|
||||
int catw = runwait(cmd);
|
||||
if (catc != 0 || catw != 0) {
|
||||
fprintf(stderr,
|
||||
"row[%s]: sepwork .s concat failed "
|
||||
"(cstage=%d wwstage=%d)\n",
|
||||
rows[i].label, catc, catw);
|
||||
fail++;
|
||||
} else 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++;
|
||||
}
|
||||
}
|
||||
|
||||
rowdone:
|
||||
/* a failed cleanup must fail the carrier, not leak silently;
|
||||
* ENOENT is fine — an early leg may exit before creating the
|
||||
* concat files. */
|
||||
snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir);
|
||||
int cleanfail = runwait(cmd) != 0;
|
||||
if (unlink(src) != 0 && errno != ENOENT) {
|
||||
perror(src);
|
||||
cleanfail = 1;
|
||||
}
|
||||
if (unlink(cs_s) != 0 && errno != ENOENT) {
|
||||
perror(cs_s);
|
||||
cleanfail = 1;
|
||||
}
|
||||
if (unlink(ws_s) != 0 && errno != ENOENT) {
|
||||
perror(ws_s);
|
||||
cleanfail = 1;
|
||||
}
|
||||
if (cleanfail) {
|
||||
fprintf(stderr, "row[%s]: cleanup failed\n",
|
||||
rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "%d/%d defdim-struct tests failed\n",
|
||||
fail, n);
|
||||
return 1;
|
||||
}
|
||||
printf("defdim_struct: %d/%d ok (cs==ww byte-id)\n",
|
||||
n, n);
|
||||
return 0;
|
||||
}
|
||||
@@ -52,7 +52,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
enum { M_ID, M_DIVERGE, M_WWREJECT, NENT_EXPECT = 43 };
|
||||
enum { M_ID, M_DIVERGE, M_WWREJECT, NENT_EXPECT = 44 };
|
||||
|
||||
struct ent {
|
||||
const char *fixture; /* repo-relative .ww; NULL → probe entry */
|
||||
@@ -141,6 +141,13 @@ static const struct ent ents[] = {
|
||||
.mode = M_ID, .sentinel = "fn rotl32", .moddir = "lib/crypto/math" },
|
||||
{ .probe = "package main;\nimport c.libc;\nfn main() i32 = { return 0; };\n",
|
||||
.mode = M_ID, .sentinel = "package libc;", .moddir = "lib/c/libc" },
|
||||
/* root-only, ZERO-dep build -S: the one driver-leg edge every
|
||||
* import probe misses (dep-count-0 unit composition). Folded in
|
||||
* from the retired 815/940/951 driver-parity carriers, whose
|
||||
* content byteid the corpus twins (r815_/r940_/r951_) own via
|
||||
* test-data-byteid. No sentinel: there is no dep unit to prove. */
|
||||
{ .probe = "package main;\nfn main() i32 = { return 0; };\n",
|
||||
.mode = M_ID },
|
||||
/* -------- documented-allowed cs≠ww (task #59) ------------ */
|
||||
{ .fixture = "lib/ascii/asciitest.ww",
|
||||
.mode = M_ID, .cite = "#59.1 graduated by #146 str==" },
|
||||
@@ -253,7 +260,8 @@ check_one(const char *bin, const char *cwd, const struct ent *e, int idx)
|
||||
if (!f) goto out;
|
||||
fputs(e->probe, f);
|
||||
fclose(f);
|
||||
label = e->moddir;
|
||||
if (e->moddir)
|
||||
label = e->moddir;
|
||||
}
|
||||
|
||||
/* dirname(fixture) leads the search path so bare same-module
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//ww:run
|
||||
// Runtime half of 940_global_sret_run.c global_assign.
|
||||
// #220 global-sret family: sret assign into a module-global lvalue.
|
||||
package main;
|
||||
type quad = struct { a: i64, b: i64, c: i64, d: i64 };
|
||||
fn mk(x: i64) quad = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//ww:run
|
||||
// Runtime half of 940_global_sret_run.c global_branched.
|
||||
// #220 global-sret family: runtime-branched callee (no constant-fold masking).
|
||||
package main;
|
||||
type quad = struct { a: i64, b: i64, c: i64, d: i64 };
|
||||
fn mk1() quad = { return quad { a = 1i64, b = 2i64, c = 3i64, d = 4i64 }; };
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//ww:run
|
||||
// Runtime half of 940_global_sret_run.c local_assign_regression.
|
||||
// #220 global-sret family: local decl+assign regression leg.
|
||||
package main;
|
||||
type quad = struct { a: i64, b: i64, c: i64, d: i64 };
|
||||
fn mk(x: i64) quad = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//ww:run
|
||||
// Runtime half of 940_global_sret_run.c local_init_regression.
|
||||
// #220 global-sret family: local let-init regression leg (cglet sret receive).
|
||||
package main;
|
||||
type quad = struct { a: i64, b: i64, c: i64, d: i64 };
|
||||
fn mk(x: i64) quad = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//ww:run
|
||||
// Runtime half of 940_global_sret_run.c global_through_pointer.
|
||||
// #220 global-sret family: global mutated through a pointer after the sret assign.
|
||||
package main;
|
||||
type quad = struct { a: i64, b: i64, c: i64, d: i64 };
|
||||
fn mk(x: i64) quad = {
|
||||
|
||||
Reference in New Issue
Block a user