/* * 944_alias_idx_family_run — #5 alias arc F2a batch 1 (task #60 + * the #79 init-store rider): wwstage INDEX / SLICE / FOR-RANGE / * LITERAL-INIT over an alias-NAMED base type. The tnode-keyed cgen * walks saw only the N_TNAME leaf — esz fell to the 1-sentinel (8 on * the init-store side) and the base classified as a POINTER (MOVQ of * array words + no IMULQ): m8b_idx1/range1 SEGV 139, m8b_slice1 * silent-wrong past prefix-luck (m8c_slice1big exit 2), alias-[4]u32 * literal-init MOVQ stride-8 over a stride-4 slot = saved-BP/RIP * smash (#79, masked when esz==8). cstage reads everything off the * chased stamped type (type_chase_named/idx_eff, post-F1), so it is * the runtime-correct reference; the fix re-keys the ww sites off * tichase'd stamped tinfos (cgindex/cgassign-N_INDEX/cgslice/ * cgbasecap in cgenexpr.ww, cgforrange + cgarrlitfillbp + bare-let * classify in cgenstmt.ww, pusharg N_SLICE in cgenutil.ww) and every * row graduates to 0/0 byte-id. * * row | shape | want * ------------------+------------------------------------------+----- * idx0_ctl | named [4]int base read/store (control) | 0 * idx1_alias | m8b_idx1: r/w via 1-level alias arr | 0 * idx2_2level | write+read via arr2 = arr (944 pin) | 0 * idx_fwdref | decl-order permuted chain + compound += | 0 * init_u32_alias | #79: type A=[4]u32 literal-init, LAST | * | elem readback (pre-fix BP/RIP smash) | 0 * initrep_u32_alias | #79: alias `[v...]` repeat fill — bound | * | off the chased alen, LAST elem | 0 * loopfill_1024 | uninit alias [1024]int + loop-fill, LAST | * | elem (pins the bare-let array classify: | * | no composite zero-fill, cs-identical) | 0 * slice0_ctl | named base slice (control) | 0 * slice1_alias | m8b_slice1 + 1-level alias ARRAY base: | * | sl dest + store + readback | 0 * slice2_2level | sl2 = sl, len/read/store + RE-SLICE of | * | the alias slice | 0 * slice1big | m8c: 1000 elems, values > 255, LAST-elem | * | readback + default-hi + .cap + range cnt | 0 * slice_fwdref | decl-after-use chain over 2-level alias | * | array base, s[1] *= 3 compound | 0 * range0_ctl | named [4]int range (control) | 0 * range1_alias | m8b_range1: sum over alias arr | 0 * range2_2level | sum over arr2 = arr | 0 * range_slicealias | for-range over an alias SLICE | 0 * m7c_global2d | global [2]row (row = [3]int): g[1][2] | 0 * gslice_alias | GLOBAL alias-slice: .len + indexed read | 0 * slicearg_alias | slice-of-alias as CALL ARG (pusharg leg) | 0 * * Values exceed 255 so an esz=1 byte-load cannot pass by little-endian * prefix-luck; readbacks assert the LAST element. Every row runs BOTH * drivers and asserts cstage/wwstage asm byte-identity (alias rows are * graduations: ww aligned UP to the cs runtime-correct side). NNN<950, * self-contained /tmp sources, no imports (944 precedent). * * Deliberately NOT pinned (g-fold territory, tasks #77/#78): any * DIRECT alias-typed global ARRAY row. Expected state at this commit, * probe-verified UNCHANGED by the fix: `let g: arr = [...]` — ww * link-ERR (no DATA emitted for the alias-typed global), cs 1-level * runs 0, cs 2-level runs WRONG (silent). */ #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; } 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), cb = fgetc(fb); if (ca != cb) { rc = -1; break; } if (ca == EOF) break; } fclose(fa); fclose(fb); return rc; } struct row { const char *label; const char *src; int want; }; static const struct row rows[] = { { "idx0_ctl", "package main;\n" "export fn main() i32 = {\n" " let a: [4]int = [1000: int, 2000: int, 3000: int, 4000: int];\n" " if (a[3] != 4000) { return 1; };\n" " a[3] = 9999;\n" " if (a[3] != 9999) { return 2; };\n" " return 0;\n" "};\n", 0 }, { "idx1_alias", "package main;\n" "type arr = [4]int;\n" "export fn main() i32 = {\n" " let a: arr = [1000: int, 2000: int, 3000: int, 4000: int];\n" " if (a[2] != 3000) { return 1; };\n" " if (a[3] != 4000) { return 2; };\n" " a[3] = 8888;\n" " if (a[3] != 8888) { return 3; };\n" " return 0;\n" "};\n", 0 }, { "idx2_2level", "package main;\n" "type arr = [4]int;\n" "type arr2 = arr;\n" "export fn main() i32 = {\n" " let a: arr2 = [1000: int, 2000: int, 3000: int, 4000: int];\n" " if (a[2] != 3000) { return 1; };\n" " a[1] = 9999;\n" " if (a[1] != 9999) { return 2; };\n" " if (a[3] != 4000) { return 3; };\n" " return 0;\n" "};\n", 0 }, { "idx_fwdref", "package main;\n" "type arr2 = arr;\n" "type arr = [4]int;\n" "export fn main() i32 = {\n" " let a: arr2 = [1000: int, 2000: int, 3000: int, 4000: int];\n" " if (a[3] != 4000) { return 1; };\n" " a[3] += 500;\n" " if (a[3] != 4500) { return 2; };\n" " return 0;\n" "};\n", 0 }, /* #79: narrow-elem alias literal-init. Pre-fix the per-element * store rode the esz=8 sentinel + MOVQ over a stride-4 slot — * elements 2/3 landed at 0(BP)/+8(BP) = saved-BP/RIP smash * (masked when esz==8). LAST element read back. */ { "init_u32_alias", "package main;\n" "type A = [4]u32;\n" "export fn main() i32 = {\n" " let a: A = [1: u32, 2: u32, 3: u32, 4: u32];\n" " if (a[3] != 4) { return 1; };\n" " if (a[0] != 1) { return 2; };\n" " return 0;\n" "};\n", 0 }, /* #79 repeat leg: the `[v...]` fill bound comes off the chased * tinfo's alen (an alias arrtn has no length tnode). */ { "initrep_u32_alias", "package main;\n" "type A = [6]u32;\n" "export fn main() i32 = {\n" " let a: A = [7: u32...];\n" " if (a[5] != 7) { return 1; };\n" " if (a[0] != 7) { return 2; };\n" " return 0;\n" "};\n", 0 }, /* Uninit alias array + per-index loop fill. Also pins the * bare-let classify: an alias-to-array keeps the per-index-write * contract (no composite zero-fill — cstage-identical asm). */ { "loopfill_1024", "package main;\n" "type arrk = [1024]int;\n" "export fn main() i32 = {\n" " let a: arrk;\n" " let i: int = 0;\n" " for (i < 1024) {\n" " a[i] = i * 3 + 1000;\n" " i += 1;\n" " };\n" " if (a[1023] != 1023 * 3 + 1000) { return 1; };\n" " return 0;\n" "};\n", 0 }, { "slice0_ctl", "package main;\n" "export fn main() i32 = {\n" " let a: [4]int = [1000: int, 2000: int, 3000: int, 4000: int];\n" " let s: []int = a[1:3];\n" " if (s.len != 2) { return 1; };\n" " if (s[1] != 3000) { return 2; };\n" " return 0;\n" "};\n", 0 }, /* alias slice DEST over a 1-level alias ARRAY base (slice-expr * over alias base, 1-level leg). */ { "slice1_alias", "package main;\n" "type arr = [4]int;\n" "type sl = []int;\n" "export fn main() i32 = {\n" " let a: arr = [1000: int, 2000: int, 3000: int, 4000: int];\n" " let s: sl = a[1:3];\n" " if (s.len != 2) { return 1; };\n" " if (s[0] != 2000) { return 2; };\n" " s[1] = 7777;\n" " if (a[2] != 7777) { return 3; };\n" " return 0;\n" "};\n", 0 }, /* 2-level alias slice dest + RE-SLICE of the alias slice (the * cgslice alias-SLICE-base leg). */ { "slice2_2level", "package main;\n" "type sl = []int;\n" "type sl2 = sl;\n" "export fn main() i32 = {\n" " let a: [4]int = [1000: int, 2000: int, 3000: int, 4000: int];\n" " let s: sl2 = a[1:3];\n" " if (s.len != 2) { return 1; };\n" " if (s[0] != 2000) { return 2; };\n" " s[1] = 7777;\n" " if (a[2] != 7777) { return 3; };\n" " if (s[1] != 7777) { return 4; };\n" " let r: sl = s[0:2];\n" " if (r.len != 2) { return 5; };\n" " if (r[1] != 7777) { return 6; };\n" " return 0;\n" "};\n", 0 }, /* 1000 elements, values 300..7293: an esz=1 stride/byte load * cannot land on the right word, and the LAST-element readback * breaks prefix-luck (m8c). Also pins alias default-hi (a[2:]), * .cap via the chased cgbasecap leg, and range over the * 2-level-alias sub-slice. Base array stays plain [1000]int so * this row pins the slice legs in isolation — the alias bare-let * + loop-fill shape is loopfill_1024's pin (the #79-rider * bare-let array classify). */ { "slice1big", "package main;\n" "type slk = []int;\n" "type slk2 = slk;\n" "export fn main() i32 = {\n" " let a: [1000]int;\n" " let i: int = 0;\n" " for (i < 1000) {\n" " a[i] = i * 7 + 300;\n" " i += 1;\n" " };\n" " let s: slk = a[1:1000];\n" " if (s.len != 999) { return 1; };\n" " if (s[998] != 999 * 7 + 300) { return 2; };\n" " s[998] = 123456;\n" " if (a[999] != 123456) { return 3; };\n" " let t: slk2 = a[2:];\n" " if (t.len != 998) { return 4; };\n" " if (t.cap != 998) { return 5; };\n" " if (t[997] != 123456) { return 6; };\n" " let cnt: int = 0;\n" " for (let x .. t) { cnt = cnt + 1; if (x < 0) { return 8; }; };\n" " if (cnt != 998) { return 7; };\n" " return 0;\n" "};\n", 0 }, { "slice_fwdref", "package main;\n" "export fn main() i32 = {\n" " let a: arr2 = [1000: int, 2000: int, 3000: int, 4000: int];\n" " let s: sl2 = a[1:3];\n" " if (s[1] != 3000) { return 1; };\n" " s[1] *= 3;\n" " if (a[2] != 9000) { return 2; };\n" " return 0;\n" "};\n" "type sl2 = sl;\n" "type sl = []int;\n" "type arr2 = arr;\n" "type arr = [4]int;\n", 0 }, { "range0_ctl", "package main;\n" "export fn main() i32 = {\n" " let a: [4]int = [1: int, 2: int, 3: int, 4: int];\n" " let sum: int = 0;\n" " for (let x .. a) { sum = sum + x; };\n" " if (sum != 10) { return 1; };\n" " return 0;\n" "};\n", 0 }, { "range1_alias", "package main;\n" "type arr = [4]int;\n" "export fn main() i32 = {\n" " let a: arr = [1000: int, 2000: int, 3000: int, 4000: int];\n" " let sum: int = 0;\n" " for (let x .. a) { sum = sum + x; };\n" " if (sum != 10000) { return 1; };\n" " return 0;\n" "};\n", 0 }, { "range2_2level", "package main;\n" "type arr = [4]int;\n" "type arr2 = arr;\n" "export fn main() i32 = {\n" " let a: arr2 = [1000: int, 2000: int, 3000: int, 4000: int];\n" " let sum: int = 0;\n" " for (let x .. a) { sum = sum + x; };\n" " if (sum != 10000) { return 1; };\n" " return 0;\n" "};\n", 0 }, { "range_slicealias", "package main;\n" "type sl = []int;\n" "export fn main() i32 = {\n" " let a: [4]int = [1000: int, 2000: int, 3000: int, 4000: int];\n" " let t: sl = a[0:4];\n" " let sum: int = 0;\n" " for (let x .. t) { sum = sum + x; };\n" " if (sum != 10000) { return 1; };\n" " return 0;\n" "};\n", 0 }, /* m7c: the BASE is a plain [2]row global — the alias sits on the * ELEMENT (row = [3]int). elemisarrayc's node walk can't see * through the element's N_TNAME, so g[0] loaded a VALUE where the * sub-array ADDRESS was needed (ww SEGV on the read; DATA emit * was already correct both stages, F0). LAST element checked. */ { "m7c_global2d", "package main;\n" "type row = [3]int;\n" "let g: [2]row = [[10: int, 20: int, 30: int], [40: int, 50: int, 60: int]];\n" "export fn main() i32 = {\n" " if (g[0][0] != 10) { return 1; };\n" " if (g[1][2] != 60) { return 2; };\n" " return 0;\n" "};\n", 0 }, /* GLOBAL alias-typed SLICE: DATA emit + .len + indexed read all * resolve (the global ARRAY twin does NOT — see the header note; * g-fold #77/#78). */ { "gslice_alias", "package main;\n" "type sl = []int;\n" "let g: sl = [1000: int, 2000: int, 3000: int];\n" "export fn main() i32 = {\n" " if (g.len != 3) { return 1; };\n" " if (g[2] != 3000) { return 2; };\n" " return 0;\n" "};\n", 0 }, /* slice-of-alias-array as a CALL ARG exercises the pusharg * N_SLICE leg (cgenutil.ww), a distinct lowering from cgslice; * plus the inferred-let receive of an alias-base slice. */ { "slicearg_alias", "package main;\n" "type arr = [4]int;\n" "type arr2 = arr;\n" "fn sum(s: []int) int = {\n" " let t: int = 0;\n" " for (let x .. s) { t = t + x; };\n" " return t;\n" "};\n" "export fn main() i32 = {\n" " let a: arr2 = [1000: int, 2000: int, 3000: int, 4000: int];\n" " let s = a[1:3];\n" " if (s.len != 2) { return 1; };\n" " if (s[1] != 3000) { return 2; };\n" " if (sum(a[0:4]) != 10000) { 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], errf[96], cmd[1024]; snprintf(src, sizeof src, "/tmp/aif_%d_%d.ww", getpid(), i); snprintf(tmpdir, sizeof tmpdir, "/tmp/aif_%d_d_%d", getpid(), i); snprintf(errf, sizeof errf, "/tmp/aif_%d_e_%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 && timeout 20 %s build %s >/dev/null 2>%s", tmpdir, driver, src, errf); int brc = runwait(cmd); if (brc != 0) { fprintf(stderr, "row[%s]: build via %s failed\n", r->label, driver); unlink(src); unlink(errf); rmdir(tmpdir); return -1; } const char *base = strrchr(src, '/'); base = base ? base + 1 : src; char outbin[256]; 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); unlink(errf); rmdir(tmpdir); if (got != r->want) { fprintf(stderr, "row[%s]: %s exit %d, want %d\n", r->label, driver, got, r->want); return 1; } return 0; } static int asm_byte_identical(const char *bin, const struct row *r, int i) { char src[96], cs[96], ws[96], cmd[1024]; snprintf(src, sizeof src, "/tmp/aif_asm_%d_%d.ww", getpid(), i); snprintf(cs, sizeof cs, "/tmp/aif_asm_%d_%d_c.s", getpid(), i); snprintf(ws, sizeof ws, "/tmp/aif_asm_%d_%d_w.s", getpid(), i); FILE *f = fopen(src, "wb"); if (!f) return -1; fputs(r->src, f); fclose(f); snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c errored\n", r->label); unlink(src); return -1; } snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null", bin, ws, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label); unlink(src); unlink(cs); return -1; } int rc = slurp_eq(cs, ws); if (rc != 0) fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n", r->label); unlink(src); unlink(cs); unlink(ws); return rc; } int main(void) { const char *bin = getenv("BIN"); if (!bin) bin = "out/bin"; char absbin[2080]; 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[2120], wdrv[2120]; snprintf(cdrv, sizeof cdrv, "%s/ww", bin); snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); int n = (int)(sizeof rows / sizeof rows[0]); int total = 0, fail = 0; for (int i = 0; i < n; i++) { total++; if (run_driver(cdrv, &rows[i], i) != 0) fail++; } if (access(wdrv, X_OK) == 0) { for (int i = 0; i < n; i++) { total++; if (run_driver(wdrv, &rows[i], i) != 0) fail++; } for (int i = 0; i < n; i++) { total++; if (asm_byte_identical(bin, &rows[i], i) != 0) fail++; } } if (fail) { fprintf(stderr, "alias_idx_family: %d/%d checks failed\n", fail, total); return 1; } printf("alias_idx_family: %d/%d ok\n", total, total); return 0; }