w6c: widen of a module-global struct ident copies the full payload

cstage zero-filled the payload; mirror the landed wwstage emission.
Flips the residual row to cs==ww==correct. Task #43.
This commit is contained in:
2026-06-12 22:37:56 +09:00
parent 960b1e796d
commit ca4ff4b882
2 changed files with 29 additions and 24 deletions

View File

@@ -2925,9 +2925,21 @@ cg_widen_tagged_store(Cg *c, Local **locals_p, Type *dst, Node *src,
if (src->kind == N_IDENT) {
int soff = localfind(*locals_p, src->str);
int ssz = (int)su->size;
int srcreg = D_BP, srcbase = soff;
if (soff == 0) {
/* #43 (#263): a module-global struct ident has
* no BP slot — land g(SB) in SI (aggarg_srcaddr)
* and copy the struct words into slot+8. Pre-fix
* the BP copy read frame garbage off 0(BP). ww
* half landed F8-c7. */
if (aggarg_srcaddr(c, src, D_SI, *locals_p)) {
srcreg = D_SI;
srcbase = 0;
}
}
int k = 0;
while (k + 8 <= ssz) {
ins2(c, A_MOVQ, amem(D_BP, soff + k),
ins2(c, A_MOVQ, amem(srcreg, srcbase + k),
areg(D_AX));
ins2(c, A_MOVQ, areg(D_AX),
amem(D_BP, write_off + 8 + k));
@@ -2942,7 +2954,7 @@ cg_widen_tagged_store(Cg *c, Local **locals_p, Type *dst, Node *src,
int lop = (tail == 4) ? A_MOVL :
(tail == 1 ? A_MOVB : A_MOVQ);
ins2(c, lop,
amem(D_BP, soff + k), areg(D_AX));
amem(srcreg, srcbase + k), areg(D_AX));
ins2(c, lop, areg(D_AX),
amem(D_BP, write_off + 8 + k));
}

View File

@@ -3,27 +3,20 @@
* GLOBAL struct ident into a tagged union (`let x: (S | str) = gs;`) must
* copy gs's full payload into the box, not just word0.
*
* THE BUG (cat-A silent miscompile, #263 BOTH-WRONG): cgwidentaggedstorebp's
* struct-payload arm keys off rhsstructpayload, which returns a struct name
* only for an N_STRUCTLIT or a LOCAL ident. A module-global struct ident
* returned empty, so the value fell to the scalar word0 widen arm — only the
* first 8 bytes reached the box, the rest stale/zero. cstage is ALSO wrong:
* it zero-fills the payload (gs is never read). Both wrong (#263); the cstage
* half is filed as task #43.
* THE BUG (cat-A silent miscompile, #263 BOTH-WRONG, now CLOSED both stages):
* the struct-payload widen arm keyed off a local/literal-only struct-name
* lookup, so a module-global struct ident fell to the scalar word0 arm — only
* the first 8 bytes reached the box (wwstage), or the payload zero-filled
* (cstage, gs never read). The ww half landed in F8-c7; the cstage half (a
* global-struct-source branch — LEAQ g(SB),SI via aggarg_srcaddr, byte-copy
* into slot+8) landed in ww-core TASK #43. Both stages now copy the full
* payload and the .s is byte-identical.
*
* THE WWSTAGE FIX (ww-runtime-correct): a dedicated global-struct-source arm
* (gated on structlookupchain so str/slice/tagged globals don't trip it)
* zero-fills the slot, lands g(SB) in SI via aggargsrcaddr, byte-copies the
* struct words into slot+8, and writes the variant tag. Local/literal
* sources keep their existing arms (byte-id). cstage stays wrong → cs≠ww.
*
* Rows assert ww runtime-correct (field-equality check → 0) AND pin cstage's
* deterministic residual: cstage zero-fills the payload → the fields read 0,
* never match → the match arm returns 2. (#43 closes cs → cs==ww.)
* row | shape | cs | ww
* -----------+---------------------------------------------+----+----
* widen_16 | gp:{a=5,b=6}; (pt|str)=gp; match pt fields | 2 | 0
* widen_24 | gp:{a=5,b=6,c=7}; (tri|str)=gp; match fields | 2 | 0
* Rows assert the field-equality match returns 0 on both stages (cs==ww).
* row | shape | exit
* -----------+---------------------------------------------+-----
* widen_16 | gp:{a=5,b=6}; (pt|str)=gp; match pt fields | 0
* widen_24 | gp:{a=5,b=6,c=7}; (tri|str)=gp; match fields | 0
*/
#include <stdio.h>
#include <stdlib.h>
@@ -60,7 +53,7 @@ static const struct row rows[] = {
" case str => return 3;\n"
" };\n"
"};\n",
2, 0 },
0, 0 },
{ "widen_24",
"package main;\n"
@@ -73,7 +66,7 @@ static const struct row rows[] = {
" case str => return 3;\n"
" };\n"
"};\n",
2, 0 },
0, 0 },
};
/* run_build — build+run `src` via `driver`; returns the binary's exit