Files
ww/test/wcc/921_amp_def_global_run.c
Hojun-Cho db7523e0e7 wcc: address-of symbol-bearing def + cross-module module-qual (#149)
Widen cgaddr / N_UN TK_AMP for addressable globals — the address-of
twin of A.2/A.3's value-LOAD widening (which covered cgexpr N_DOT /
cgindex but never the &-path, so &<mod>.<def> emitted uninitialized-AX
garbage). Two shapes, one class:
- Shape 1 (&G, N_IDENT def): LEAQ masym(G) for struct/array/scalar
  defs. Scalar gate = emit_defs/emit_floatlit_data eligibility exactly
  (fold_int_literal OR rhs-peels-to-N_FLOATLIT) so the addressable set
  equals the symbol-bearing set — never LEAQs a missing symbol. Reuses
  DefStruct/DefArray registries (A.2/A.3); new def_isscalardef/DefAny.
- Shape 2 (&mod.G, N_DOT module-qual): LEAQ leaf (TY_FN -> mafn),
  mirroring the value-READ resolution (cgen.c:6043). Kind-agnostic —
  fixes &mod.def, &mod.let, &mod.scalar, &mod.func. This is fold-4's
  &math.f64info pattern.
Non-addressable def (str-def, computed-float-def) -> loud error both
stages; upgrades #147 &NAN(0.0/0.0) from silent wild-deref to loud
compile error. Computed-float-def symbol emission split to #147
(needs float-const-fold; not needed by gamma/fold-4). wwstage Shape-2
gate carries !deflookup so a def base routes to silent-drop matching
cstage's type-gate (rule-10 parity).

Unblocks gamma-cleanup (#40) + strconv fold-4 (&math.f64info).
Bootstrap-NEUTRAL (no &global in lib/selfhost; only &local). Test 921
(12 rows: same-pkg struct/array/scalar-int/scalar-float + cross-pkg
def_struct/let_struct/scalar_let/func + regression &let/&arr[i] +
2 loud-reject). Make test: 184/184 incl 990-997 byte-id +
combined_ww_fresh.

Followups: #147 (computed-float-def symbol), #152/#153 (pre-existing
ptr-param element read divergences, dodged by 921), #154 (address-of
def subparts &def.field/&def_array[i]).
2026-05-27 10:27:55 +09:00

291 lines
9.3 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 921_amp_def_global_run — address-of a module-level global (#149) +
* scalar-def address-of (#147 consolidation). Regression net for the
* cgaddr def-symbol / module-qualified gap: A.2 (0ed0b39) + A.3
* (9e3bc4e) widened the VALUE-LOAD path for struct/array defs but the
* ADDRESS-OF twin (cgexpr N_UN TK_AMP / cgun) never handled def-symbols
* nor module-qualified globals, so `&def` / `&mod.global` emitted an
* uninitialised AX (PUSHQ AX / POPQ DI / CALL — no LEAQ) → garbage
* pointer, despite the DATA symbol existing. cs+ww were BYTE-IDENTICAL
* on the bug (gate-blind), so each row carries BOTH dimensions:
* (a) `ww build` + run, asserting the exit code == the value reached
* THROUGH the pointer (a garbage pointer yields a wrong value, not
* a false exit-0 — the probe lesson).
* (b) w6c vs w6c_ww `.s` cmp — FAILS if the stages diverge (rule-10).
*
* Two operand shapes, one class (cgaddr-doesn't-handle-defs):
* Shape 1 `&G` — N_IDENT, G a top-level def (struct/array/scalar)
* Shape 2 `&mod.G` — N_DOT, mod a module qualifier (kind-agnostic:
* cross-module let / def / scalar / fn)
* The fold-4 / γ-cleanup driver is the cross-module struct-def row
* (`&mod.def_struct`, the `&math::f64info` shape).
*
* Non-addressable defs (str def inlined; computed-rhs float like
* `def NAN = 0.0/0.0` — #147, no DATA symbol) are NOT silently dropped:
* `&them` is a LOUD build error in both stages (rule-7). Those rows set
* want_build_fail.
*
* NOTE: array-element reads THROUGH a `*[N]T` pointer param have a
* pre-existing cs!=ww divergence (narrow MOVSXD/MOVL + esz-stride),
* unrelated to #149 — so the def-array row reads element 0 via a
* `*[N]T -> *T` cast + plain deref, which isolates the `&A` LEAQ.
*
* Single-file multi-package form (like 953_f64crossmod_run): `package
* myf; ... package main; import myf; ...` so w6c/w6c_ww see the cross-
* module reference without -I plumbing.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.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;
int want_exit;
int want_build_fail; /* 1: w6c must reject (loud rule-7 error) */
};
static const struct row rows[] = {
/* Shape 1 — same-pkg `&def`. */
{ "def_struct",
"package main;\n"
"type pt = struct { x: i32, y: i32 };\n"
"def P: pt = pt{x=7, y=11};\n"
"fn rd(p: *pt) i32 = { return p.x + p.y; };\n"
"export fn main() i32 = { return rd(&P); };\n", 18, 0 },
{ "def_array",
"package main;\n"
"def A: [3]i64 = [18i64, 11i64, 0i64];\n"
"fn rd(p: *[3]i64) i64 = { let q: *i64 = p: *i64; return *q; };\n"
"export fn main() i32 = { return rd(&A): i32; };\n", 18, 0 },
{ "def_scalar_int",
"package main;\n"
"def N: i32 = 42;\n"
"fn rd(p: *i32) i32 = { return *p; };\n"
"export fn main() i32 = { return rd(&N); };\n", 42, 0 },
{ "def_scalar_float",
"package main;\n"
"def D: f64 = 0.5;\n"
"fn rd(p: *f64) f64 = { return *p; };\n"
"export fn main() i32 = { return (rd(&D) * 100.0): i32; };\n", 50, 0 },
/* Shape 2 — cross-pkg `&mod.G` (kind-agnostic). */
{ "xmod_def_struct", /* the &math::f64info / fold-4 shape */
"package myf;\n"
"export type pt = struct { x: i32, y: i32 };\n"
"export def P: pt = pt{x=7, y=11};\n"
"package main;\n"
"import myf;\n"
"fn rd(p: *myf.pt) i32 = { return p.x + p.y; };\n"
"export fn main() i32 = { return rd(&myf.P); };\n", 18, 0 },
{ "xmod_let_struct",
"package myf;\n"
"export type pt = struct { x: i32, y: i32 };\n"
"export let L: pt = pt{x=7, y=11};\n"
"package main;\n"
"import myf;\n"
"fn rd(p: *myf.pt) i32 = { return p.x + p.y; };\n"
"export fn main() i32 = { return rd(&myf.L); };\n", 18, 0 },
{ "xmod_scalar_let",
"package myf;\n"
"export let S: i32 = 42;\n"
"package main;\n"
"import myf;\n"
"fn rd(p: *i32) i32 = { return *p; };\n"
"export fn main() i32 = { return rd(&myf.S); };\n", 42, 0 },
{ "xmod_func", /* Shape 2 TY_FN leaf → LEAQ fn(SB) */
"package myf;\n"
"export fn helper() i32 = { return 42; };\n"
"package main;\n"
"import myf;\n"
"fn take(p: *fn() i32) i32 = { return 7; };\n"
"export fn main() i32 = { return take(&myf.helper); };\n", 7, 0 },
/* Regressions — paths the fix must NOT disturb. */
{ "reg_let_struct", /* &let_struct worked same-pkg pre-#149 */
"package main;\n"
"type pt = struct { x: i32, y: i32 };\n"
"let L: pt = pt{x=7, y=11};\n"
"fn rd(p: *pt) i32 = { return p.x + p.y; };\n"
"export fn main() i32 = { return rd(&L); };\n", 18, 0 },
{ "reg_amp_arr_idx", /* &local_arr[i] */
"package main;\n"
"fn rd(p: *i64) i64 = { return *p; };\n"
"export fn main() i32 = {\n"
" let a: [3]i64 = [5i64, 18i64, 9i64];\n"
" let p: *i64 = &a[1];\n"
" return rd(p): i32; };\n", 18, 0 },
/* rule-7 LOUD: `&non-addressable def` (no DATA symbol). */
{ "fail_str_def",
"package main;\n"
"def MSG: str = \"hello\";\n"
"fn rd(p: *str) i32 = { return 7; };\n"
"export fn main() i32 = { return rd(&MSG); };\n", 0, 1 },
{ "fail_computed_float", /* #147 `def NAN = 0.0/0.0` shape */
"package main;\n"
"def D: f64 = 1.0 / 4.0;\n"
"fn rd(p: *f64) f64 = { return *p; };\n"
"export fn main() i32 = { return (rd(&D) * 100.0): i32; };\n", 0, 1 },
{ NULL, NULL, 0, 0 }
};
static int
slurp_eq(const char *a, const char *b)
{
FILE *fa = fopen(a, "rb");
FILE *fb = fopen(b, "rb");
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
int rc = 0;
for (;;) {
int ca = fgetc(fa);
int cb = fgetc(fb);
if (ca != cb) { rc = -1; break; }
if (ca == EOF) break;
}
fclose(fa); fclose(fb);
return rc;
}
int
main(void)
{
const char *bin = getenv("BIN");
if (!bin) bin = "out/bin";
char absbin[1024];
if (bin[0] != '/') {
char cwd[1024];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
char w6c[1100], w6c_ww[1100];
snprintf(w6c, sizeof w6c, "%s/w6c", bin);
snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin);
if (access(w6c_ww, X_OK) != 0) {
fprintf(stderr, "amp_def_global: w6c_ww missing — cannot run the "
"cs==ww byte-id gate (the whole point of this test)\n");
return 1;
}
int n = 0, fail = 0;
for (int i = 0; rows[i].src; i++, n++) {
char src[64];
snprintf(src, sizeof src, "/tmp/wwamp_%d_%d.ww", getpid(), i);
FILE *f = fopen(src, "wb");
if (f == NULL) { fail++; continue; }
fputs(rows[i].src, f);
fclose(f);
if (rows[i].want_build_fail) {
/* rule-7 LOUD: both stages must REJECT (`&` of a non-
* addressable def has no DATA symbol). Build to .s and
* assert non-zero exit on each stage. */
char cmd[2048], dump[64];
snprintf(dump, sizeof dump, "/tmp/wwamp_%d_%d.s",
getpid(), i);
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
w6c, dump, src);
if (runwait(cmd) == 0) {
fprintf(stderr, "row[%s]: w6c ACCEPTED &non-"
"addressable-def (want loud reject)\n",
rows[i].label);
fail++;
}
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
w6c_ww, dump, src);
if (runwait(cmd) == 0) {
fprintf(stderr, "row[%s]: w6c_ww ACCEPTED &non-"
"addressable-def (want loud reject)\n",
rows[i].label);
fail++;
}
unlink(src); unlink(dump);
continue;
}
/* (a) cstage build + run; assert exit == value reached
* through the pointer. */
char tmpdir[64];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwamp_%d_d_%d",
getpid(), i);
mkdir(tmpdir, 0755);
char cmd[2048];
snprintf(cmd, sizeof cmd, "cd %s && %s/ww build %s",
tmpdir, bin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: cstage build failed\n",
rows[i].label);
fail++;
unlink(src); rmdir(tmpdir);
continue;
}
char outbin[128];
const char *base = strrchr(src, '/');
base = base ? base + 1 : src;
snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base);
char *dot = strrchr(outbin, '.');
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
int got = runwait(outbin);
if (got != rows[i].want_exit) {
fprintf(stderr, "row[%s]: cstage exit %d, want %d\n",
rows[i].label, got, rows[i].want_exit);
fail++;
}
unlink(outbin); rmdir(tmpdir);
/* (b) cs==ww byte-id gate. */
char cs_s[64], ws_s[64];
snprintf(cs_s, sizeof cs_s, "/tmp/wwamp_%d_%d_cs.s",
getpid(), i);
snprintf(ws_s, sizeof ws_s, "/tmp/wwamp_%d_%d_ww.s",
getpid(), i);
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
w6c, cs_s, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label);
fail++; unlink(src); continue;
}
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
w6c_ww, ws_s, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww failed\n",
rows[i].label);
fail++; unlink(src); unlink(cs_s); continue;
}
if (slurp_eq(cs_s, ws_s) != 0) {
fprintf(stderr,
"row[%s]: cstage/wwstage .s DIFFER (rule-10 "
"byte-id violation)\n", rows[i].label);
fail++;
}
unlink(src); unlink(cs_s); unlink(ws_s);
}
if (fail) {
fprintf(stderr, "%d/%d amp-def-global tests failed\n", fail, n);
return 1;
}
printf("amp_def_global: %d/%d ok (cstage run + cs==ww byte-id)\n",
n, n);
return 0;
}