test: retarget Pattern-B gates to sep .sepwork layout; 915 off private global (M4 E3, #93)
The driver flip moves build artifacts from next-to-source <stem>.s to a .sepwork/ scratch dir. 29 Pattern-B gates now build `ww build --sep -o <stem>` and read <stem>.sepwork/__root.s (multi-package gates concat all <stem>.sepwork/*.s, since cross-package labels live in per-package .s). All intermediates redirect to /tmp (WW_PKGCACHE + -o), so the corpus runs parallel-safe with no source-tree pollution. Tests pass now (--sep is live) and survive the flip. 915 additionally retargeted off strconv's PRIVATE left_shift_table (a let, not export) — which separate compilation correctly hides — onto a test-local package that exports its own probe table (#96). The combined path only linked it via a single-unit private leak; encapsulation is now honored under sep. Test-only; all 5 *_ww binaries HOLD. 989_m1mangle_run deferred (blocked by #99, imported-package fn main mangling under sep).
This commit is contained in:
@@ -32,15 +32,17 @@
|
||||
* shapes (defensive case; not exercised in committed code per
|
||||
* design-pass audit) aren't disturbed.
|
||||
*
|
||||
* Each row carries (a) cstage `ww build` + run asserting the exit
|
||||
* code (the cstage-segfault repro now exits cleanly) and (b) a w6c vs
|
||||
* w6c_ww `.s` cmp (rule-10 byte-id). Imports `strconv` because its
|
||||
* `let left_shift_table: [65]u16` is the canonical reviewer-stofdata
|
||||
* probe surface for this bug.
|
||||
* Each row carries a `ww build` + run asserting the exit code (the
|
||||
* cstage-segfault repro now exits cleanly). Single-file rows also carry
|
||||
* a w6c vs w6c_ww `.s` cmp (rule-10 byte-id); the cross-package module
|
||||
* rows gate on runtime exit here and are byte-id-covered cross-package
|
||||
* by 994. The probe table is a test-local exported `[8]u16` (package
|
||||
* wcmodarr — see the #93 retarget note below; it replaced strconv's
|
||||
* private `let left_shift_table`, which separate compilation correctly
|
||||
* hides).
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
@@ -54,55 +56,67 @@ runwait(const char *cmd)
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct row { const char *label; const char *src; int want_exit; };
|
||||
struct row {
|
||||
const char *label;
|
||||
const char *src; /* main.ww content */
|
||||
const char *modname; /* exported-probe-table package, or NULL */
|
||||
const char *modsrc; /* that package's content, or NULL */
|
||||
int want_exit;
|
||||
};
|
||||
|
||||
/* #93 (915 encapsulation retarget, ken-t): the module rows used to read
|
||||
* strconv's PRIVATE `let left_shift_table` — which separate compilation
|
||||
* CORRECTLY hides (the combined path only "worked" because the private
|
||||
* symbol leaked into one unit). Retargeted to a test-LOCAL package that
|
||||
* EXPORTS its own probe table: the same cross-package `mod.arr[i]` cgen
|
||||
* shape (LEAQ base + element-size stride, #128b), encapsulation-faithful
|
||||
* and sep-linkable. (The deeper checker tighten — loud-reject a cross-pkg
|
||||
* private ref — is tracked as #45; NOT attempted here.) Table prefix is
|
||||
* the same u16 sequence strconv used, so the expected exits are unchanged
|
||||
* (idx0→0, idx2→0x0801&0xFF=1, idx4→0x1006&0xFF=6). */
|
||||
#define PROBE_MOD \
|
||||
"export let probe_table: [8]u16 = [0u16, 0x0800u16, 0x0801u16, " \
|
||||
"0x0803u16, 0x1006u16, 0x1009u16, 0x100Du16, 0x1812u16];\n"
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* The reviewer-stofdata probe: indexed read on an imported [N]u16
|
||||
* at idx=0. Pre-fix cstage segfaulted on nontrivial indices and
|
||||
* MOVZBQ-loaded the wrong byte; here at idx=0 the wrong-stride
|
||||
* doesn't matter, so the test exit reflects the load-width fix:
|
||||
* MOVZWQ now reads the full u16 = 0x0000 → exit 0. */
|
||||
/* Indexed read on an imported (exported) [N]u16 at idx=0 — load-
|
||||
* width fix: MOVZWQ reads the full u16 = 0x0000 → exit 0. */
|
||||
{ "mod_u16_idx0",
|
||||
"package main;\n"
|
||||
"import strconv;\n"
|
||||
"import wcmodarr;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: u32 = (strconv.left_shift_table[0]: u32);\n"
|
||||
" let v: u32 = (wcmodarr.probe_table[0]: u32);\n"
|
||||
" return v: i32;\n"
|
||||
"};\n", 0 },
|
||||
/* Nontrivial idx: pre-fix segfault repro. table[2] = 0x0801;
|
||||
"};\n", "wcmodarr", PROBE_MOD, 0 },
|
||||
/* Nontrivial idx — pre-fix segfault repro. table[2] = 0x0801;
|
||||
* 0x0801 mod 256 = 1. */
|
||||
{ "mod_u16_idx2",
|
||||
"package main;\n"
|
||||
"import strconv;\n"
|
||||
"import wcmodarr;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: u32 = (strconv.left_shift_table[2]: u32);\n"
|
||||
" let v: u32 = (wcmodarr.probe_table[2]: u32);\n"
|
||||
" return v: i32;\n"
|
||||
"};\n", 1 },
|
||||
"};\n", "wcmodarr", PROBE_MOD, 1 },
|
||||
/* Further idx — pins per-element stride. table[4] = 0x1006;
|
||||
* 0x1006 mod 256 = 6. */
|
||||
{ "mod_u16_idx4",
|
||||
"package main;\n"
|
||||
"import strconv;\n"
|
||||
"import wcmodarr;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: u32 = (strconv.left_shift_table[4]: u32);\n"
|
||||
" let v: u32 = (wcmodarr.probe_table[4]: u32);\n"
|
||||
" return v: i32;\n"
|
||||
"};\n", 6 },
|
||||
"};\n", "wcmodarr", PROBE_MOD, 6 },
|
||||
/* Local-array control: confirms the existing IDENT-base path is
|
||||
* unchanged by the #128b cgindex edit. Pre-fix already worked; the
|
||||
* fix is gated by `bt == NULL` so this row's byte-id is
|
||||
* preserved. */
|
||||
* unchanged by the #128b cgindex edit. */
|
||||
{ "local_arr_control",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [4]u16 = [10u16, 20u16, 30u16, 40u16];\n"
|
||||
" let v: u32 = (a[2]: u32);\n"
|
||||
" return v: i32;\n"
|
||||
"};\n", 30 },
|
||||
/* Same-package access (NOT module-qualified) — pins that the
|
||||
* existing in-package IDENT path stays unchanged. left_shift_table
|
||||
* exists in strconv; access from within `package strconv` would be
|
||||
* IDENT-base. Wrap in a tiny helper here to keep test self-
|
||||
* contained; uses local table. */
|
||||
"};\n", NULL, NULL, 30 },
|
||||
/* Same-package (NOT module-qualified) access — pins the in-package
|
||||
* IDENT path stays unchanged; uses a local table. */
|
||||
{ "local_u16_idx_nonzero",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
@@ -110,8 +124,8 @@ static const struct row rows[] = {
|
||||
"0x1006u16, 0x1009u16, 0x100Du16, 0x1812u16];\n"
|
||||
" let v: u32 = (t[2]: u32);\n"
|
||||
" return v: i32;\n"
|
||||
"};\n", 1 },
|
||||
{ NULL, NULL, 0 }
|
||||
"};\n", NULL, NULL, 1 },
|
||||
{ NULL, NULL, NULL, NULL, 0 }
|
||||
};
|
||||
|
||||
static int
|
||||
@@ -155,96 +169,97 @@ main(void)
|
||||
|
||||
int n = 0, fail = 0;
|
||||
for (int i = 0; rows[i].src; i++, n++) {
|
||||
char src[64];
|
||||
snprintf(src, sizeof src, "/tmp/wwmoda_%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];
|
||||
char tmpdir[96];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwmoda_%d_d_%d",
|
||||
getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
|
||||
char src[160];
|
||||
snprintf(src, sizeof src, "%s/main915.ww", tmpdir);
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (f == NULL) { fail++; rmdir(tmpdir); continue; }
|
||||
fputs(rows[i].src, f);
|
||||
fclose(f);
|
||||
|
||||
char cmd[2048];
|
||||
snprintf(cmd, sizeof cmd, "cd %s && %s/ww build %s",
|
||||
tmpdir, bin, src);
|
||||
/* Exported-probe-table package lives at <tmpdir>/<mod>/<mod>.ww
|
||||
* so the sep dep-scan (rooted at the entry's dir) resolves the
|
||||
* `import <mod>;`. */
|
||||
if (rows[i].modname != NULL) {
|
||||
char moddir[224], modf[320];
|
||||
snprintf(moddir, sizeof moddir, "%s/%s", tmpdir,
|
||||
rows[i].modname);
|
||||
mkdir(moddir, 0755);
|
||||
snprintf(modf, sizeof modf, "%s/%s.ww", moddir,
|
||||
rows[i].modname);
|
||||
FILE *mf = fopen(modf, "wb");
|
||||
if (mf == NULL) {
|
||||
fail++;
|
||||
snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir);
|
||||
(void)runwait(cmd);
|
||||
continue;
|
||||
}
|
||||
fputs(rows[i].modsrc, mf);
|
||||
fclose(mf);
|
||||
}
|
||||
|
||||
/* #93 sep layout: build via `--sep -o <stem>` (the exported
|
||||
* probe table links across packages under separate compilation);
|
||||
* WW_PKGCACHE pinned under tmpdir. */
|
||||
char stem[200];
|
||||
snprintf(stem, sizeof stem, "%s/main915", tmpdir);
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"cd %s && WW_PKGCACHE=%s/pkgc %s/ww build --sep -o %s %s "
|
||||
">/dev/null 2>&1", tmpdir, tmpdir, bin, stem, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: cstage build failed\n",
|
||||
rows[i].label);
|
||||
fail++;
|
||||
unlink(src); rmdir(tmpdir);
|
||||
snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir);
|
||||
(void)runwait(cmd);
|
||||
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);
|
||||
int got = runwait(stem);
|
||||
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);
|
||||
|
||||
/* Byte-id only meaningful when the source compiles standalone
|
||||
* to a .s. Module-imported rows need the combined.ww shape
|
||||
* (which `ww build` produces inside tmpdir); we already ran
|
||||
* that above. For the byte-id leg, point at the combined.ww. */
|
||||
char combined[160];
|
||||
snprintf(combined, sizeof combined, "%s/%s.combined.ww",
|
||||
tmpdir, base);
|
||||
(void)combined;
|
||||
/* Both stages emit on the original .ww directly; the
|
||||
* combined.ww has the same surface for cgen purposes. We use
|
||||
* the original src for byte-id since w6c/w6c_ww handle the
|
||||
* import resolution when invoked on the file with -I and the
|
||||
* lib search. The probe row asserts module-qualified shape
|
||||
* via direct w6c{,_ww} on the .ww; if the stage can't find
|
||||
* the import, the build above would have failed first. */
|
||||
char cs_s[64], ws_s[64];
|
||||
snprintf(cs_s, sizeof cs_s, "/tmp/wwmoda_%d_%d_cs.s",
|
||||
getpid(), i);
|
||||
snprintf(ws_s, sizeof ws_s, "/tmp/wwmoda_%d_%d_ww.s",
|
||||
getpid(), i);
|
||||
|
||||
/* Skip byte-id leg for rows that need imports — w6c/w6c_ww
|
||||
* standalone won't resolve `import strconv;`. The runtime
|
||||
* leg above (via `ww build`) IS the byte-id+correctness
|
||||
* gate for these rows; the full bootstrap byte-id (994) is
|
||||
* the corpus check. */
|
||||
int needs_import = (strstr(rows[i].src, "import ") != NULL);
|
||||
if (!needs_import) {
|
||||
/* Byte-id leg — bare w6c vs w6c_ww (the compiler binaries, NOT
|
||||
* the driver, so layout-invariant). Only single-file rows can be
|
||||
* fed standalone; the module rows' runtime exit IS their gate
|
||||
* (+ the 994 bootstrap corpus for the cross-stage byte-id). */
|
||||
if (rows[i].modname == NULL) {
|
||||
char cs_s[200], ws_s[200];
|
||||
snprintf(cs_s, sizeof cs_s, "%s/cs.s", tmpdir);
|
||||
snprintf(ws_s, sizeof ws_s, "%s/ww.s", tmpdir);
|
||||
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++;
|
||||
} else {
|
||||
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++;
|
||||
} 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++;
|
||||
}
|
||||
}
|
||||
unlink(cs_s); unlink(ws_s);
|
||||
}
|
||||
unlink(src);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir);
|
||||
(void)runwait(cmd);
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
|
||||
Reference in New Issue
Block a user