From 35b517ca3e1feaffa12493202724f91dcb2082fb Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Tue, 2 Jun 2026 11:37:34 +0900 Subject: [PATCH] w6c+wwstage: aggregate let-init copy from a struct-DEF global (#268 reviewer) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fold-1b unified arm (bb2f4e1) added an N_IDENT addressable-rhs source setup, but the two stages gated the GLOBAL case differently: cstage used let_islet || def_isarraydef, wwstage used isletvar || deflookup (ANY def). On a struct-typed `def` used as an aggregate-copy rhs (`let c: T = G`) wwstage copied the whole value (correct) while cstage truncated to the 8B scalar tail — a cs!=ww divergence (rule-10). A struct-LET global already copies on both, so the def gap was also an internal cstage inconsistency. Struct defs are first-class laid-out aggregates (DATA storage + field load, #129 A.2/A.3), so converge on the correct full copy on both: add def_isstructdef to cstage's predicate and replace wwstage's broad deflookup with the def_is{array,struct}def pairing already held identical in defisaddressable. 949 +2 rows (array-def + struct-def global, full readback, byteid=1). --- cmd/w6c/cgen.c | 9 ++++++++- selfhost/cmd/w6c/main.combined.ww | 16 ++++++++++++++-- selfhost/cmd/wcc/cgenstmt.ww | 16 ++++++++++++++-- selfhost/cmd/wwdump/main.combined.ww | 16 ++++++++++++++-- test/wcc/949_dotbase_addr_slice_run.c | 24 ++++++++++++++++++++++++ 5 files changed, 74 insertions(+), 7 deletions(-) diff --git a/cmd/w6c/cgen.c b/cmd/w6c/cgen.c index fc87036f..91c3a35d 100644 --- a/cmd/w6c/cgen.c +++ b/cmd/w6c/cgen.c @@ -8793,8 +8793,15 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame) ins2(c, A_LEAQ, amem(D_BP, soff), areg(D_SI)); havesrc = 1; + /* the laid-out-aggregate globals (#129 + * A.2/A.3): a let, an array def, or a struct + * def. Struct defs copy here exactly as + * struct-let globals do; omitting def_is- + * structdef truncated the def case alone and + * diverged from wwstage (rule-10). */ } else if (let_islet(n->rhs->str) - || def_isarraydef(n->rhs->str)) { + || def_isarraydef(n->rhs->str) + || def_isstructdef(n->rhs->str)) { ins2(c, A_LEAQ, masym(c, n->rhs->str), areg(D_SI)); havesrc = 1; diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index f7b69a7f..dcf8a161 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -28906,8 +28906,20 @@ fn cglet(c: *cgen, n: *node) void = { emitline("(BP), SI\n"); havesrc = true; } else { - if (isletvar(c, rhs.str) - || deflookup(c, rhs.str)) { + // rule-10: the addressable-def set must + // equal cstage's let_islet || + // def_isarraydef || def_isstructdef — + // the laid-out-aggregate globals (#129 + // A.2/A.3). Bare deflookup (any def) + // over-copies struct-defs on wwstage + // only; mirror the defisaddressable + // pairing instead. + let aggdtn: *node = defvartnode(c, rhs.str); + let aggisdef: bool = defvarstructinfo(c, rhs.str) != nil; + if (aggdtn != nil) { + if (aggdtn.kind == nkind.N_TARRAY) { aggisdef = true; }; + }; + if (isletvar(c, rhs.str) || aggisdef) { emitline("\tLEAQ\t"); emitsymname(c, rhs.str); emitline("(SB), SI\n"); diff --git a/selfhost/cmd/wcc/cgenstmt.ww b/selfhost/cmd/wcc/cgenstmt.ww index 2ceb0685..28fbc076 100644 --- a/selfhost/cmd/wcc/cgenstmt.ww +++ b/selfhost/cmd/wcc/cgenstmt.ww @@ -1766,8 +1766,20 @@ fn cglet(c: *cgen, n: *node) void = { emitline("(BP), SI\n"); havesrc = true; } else { - if (isletvar(c, rhs.str) - || deflookup(c, rhs.str)) { + // rule-10: the addressable-def set must + // equal cstage's let_islet || + // def_isarraydef || def_isstructdef — + // the laid-out-aggregate globals (#129 + // A.2/A.3). Bare deflookup (any def) + // over-copies struct-defs on wwstage + // only; mirror the defisaddressable + // pairing instead. + let aggdtn: *node = defvartnode(c, rhs.str); + let aggisdef: bool = defvarstructinfo(c, rhs.str) != nil; + if (aggdtn != nil) { + if (aggdtn.kind == nkind.N_TARRAY) { aggisdef = true; }; + }; + if (isletvar(c, rhs.str) || aggisdef) { emitline("\tLEAQ\t"); emitsymname(c, rhs.str); emitline("(SB), SI\n"); diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 619d5af4..c44f4465 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -28906,8 +28906,20 @@ fn cglet(c: *cgen, n: *node) void = { emitline("(BP), SI\n"); havesrc = true; } else { - if (isletvar(c, rhs.str) - || deflookup(c, rhs.str)) { + // rule-10: the addressable-def set must + // equal cstage's let_islet || + // def_isarraydef || def_isstructdef — + // the laid-out-aggregate globals (#129 + // A.2/A.3). Bare deflookup (any def) + // over-copies struct-defs on wwstage + // only; mirror the defisaddressable + // pairing instead. + let aggdtn: *node = defvartnode(c, rhs.str); + let aggisdef: bool = defvarstructinfo(c, rhs.str) != nil; + if (aggdtn != nil) { + if (aggdtn.kind == nkind.N_TARRAY) { aggisdef = true; }; + }; + if (isletvar(c, rhs.str) || aggisdef) { emitline("\tLEAQ\t"); emitsymname(c, rhs.str); emitline("(SB), SI\n"); diff --git a/test/wcc/949_dotbase_addr_slice_run.c b/test/wcc/949_dotbase_addr_slice_run.c index 518f93a2..a3dd71a0 100644 --- a/test/wcc/949_dotbase_addr_slice_run.c +++ b/test/wcc/949_dotbase_addr_slice_run.c @@ -930,6 +930,30 @@ static const struct row rows[] = { " return (c.m[0]+c.m[1]+c.m[2]+c.m[3]\n" " +c.m[4]+c.m[5]+c.m[6]+c.m[7]): i32;\n" "};\n", 36, 1 }, + /* #268 reviewer: the addressable-rhs N_IDENT axis also covers a + * laid-out-aggregate GLOBAL (#129 A.2/A.3) — an array `def` and a + * struct `def`, both DATA-stored and LEAQ'd by symbol. The struct- + * def case was the one cs!=ww divergence the unified arm shipped: + * wwstage's deflookup (any def) copied it while cstage's def_is- + * arraydef alone truncated, so they diverged (a struct-LET global + * already copied on both, making the def gap an inconsistency). + * Aligned both to copy via the def_is{array,struct}def pairing held + * identical to defisaddressable. Full readback; byteid=1. */ + { "arraydef_global", + "package main;\n" + "def G: [4]u32 = [11u32, 22u32, 33u32, 44u32];\n" + "export fn main() i32 = {\n" + " let c: [4]u32 = G;\n" + " return (c[0]+c[1]+c[2]+c[3]): i32;\n" + "};\n", 110, 1 }, + { "structdef_global", + "package main;\n" + "type T = struct { a: u32, b: u32, c: u32, d: u32 };\n" + "def G: T = T { a = 10u32, b = 20u32, c = 30u32, d = 40u32 };\n" + "export fn main() i32 = {\n" + " let c: T = G;\n" + " return (c.a+c.b+c.c+c.d): i32;\n" + "};\n", 100, 1 }, { NULL, NULL, 0, 0 } };