w6c: widen of a module-global tagged ident copies the whole box

cstage spilled frame garbage as the box; mirror the landed wwstage
emission (copy from gi(SB)). The F8-era divergence-only rows gain
pinned cs==ww values. Task #44.
This commit is contained in:
2026-06-12 22:41:45 +09:00
parent ca4ff4b882
commit fc47c3d0f2
2 changed files with 37 additions and 42 deletions

View File

@@ -2611,7 +2611,8 @@ cg_widen_tagged_store(Cg *c, Local **locals_p, Type *dst, Node *src,
for (int k = 0; k < sz; k += 8)
ins2(c, A_MOVQ, areg(D_AX),
amem(D_BP, write_off + k));
if (src->kind == N_IDENT) {
if (src->kind == N_IDENT
&& localfind(*locals_p, src->str) != 0) {
int soff = localfind(*locals_p, src->str);
for (int k = 0; k < ssz; k += 8) {
ins2(c, A_MOVQ, amem(D_BP, soff + k),
@@ -2619,6 +2620,21 @@ cg_widen_tagged_store(Cg *c, Local **locals_p, Type *dst, Node *src,
ins2(c, A_MOVQ, areg(D_AX),
amem(D_BP, write_off + 8 + k));
}
} else if (src->kind == N_IDENT) {
/* #44 (#263): a module-global tagged ident
* source has no BP slot — land gi(SB) in SI
* (aggarg_srcaddr) and copy the inner box into
* slot+8. Pre-fix the BP copy read frame garbage
* off 0(BP). ww half landed F8-c8. */
if (aggarg_srcaddr(c, src, D_SI, *locals_p)) {
for (int k = 0; k < ssz; k += 8) {
ins2(c, A_MOVQ,
amem(D_SI, k), areg(D_AX));
ins2(c, A_MOVQ, areg(D_AX),
amem(D_BP,
write_off + 8 + k));
}
}
} else {
/* #38b: an sret-classified call result is in
* memory (AX = dest pointer), not the cursor —

View File

@@ -4,29 +4,20 @@
* must copy gi's whole box as the nested payload, not store its tag as the
* payload.
*
* THE BUG (cat-A silent miscompile, #263 BOTH-WRONG): cgwidentaggedstorebp's
* #218 nested-tagged-source arm is gated on rhstaggedident, which returns nil
* for a module-global tagged ident (local-only). So srctagged was false and a
* global tagged source fell to the scalar word0 widen arm — gi's TAG word
* landed as the box payload, the real value lost. cstage is ALSO wrong: it
* copies frame garbage (saved-BP / return-address), never gi(SB). Both wrong,
* divergent (#263); the cstage half is filed as task #44.
* THE BUG (cat-A silent miscompile, #263 BOTH-WRONG, now CLOSED both stages):
* the #218 nested-tagged-source arm was gated local-only, so a module-global
* tagged source fell to the scalar word0 widen arm — gi's TAG word landed as
* the box payload (wwstage), or frame garbage was copied, never gi(SB)
* (cstage). The ww half landed in F8-c8; the cstage half (the nested arm's
* N_IDENT copy grows a global branch — LEAQ gi(SB),SI via aggarg_srcaddr, copy
* the inner box into slot+8) landed in ww-core TASK #44. Both stages now copy
* the whole box and the .s is byte-identical.
*
* THE WWSTAGE FIX (ww-runtime-correct): srctagged also recognises a global
* tagged ident, and the nested arm's N_IDENT copy grows a global branch —
* LEAQ gi(SB),SI via aggargsrcaddr, then copy the inner box (ssz bytes) into
* slot+8 with the outer tag at slot+0. Local sources keep the BP copy
* (byte-id). cstage stays wrong → cs≠ww residual.
*
* cstage's residual is FRAME GARBAGE (non-deterministic across stack
* layouts), so — unlike the deterministic #263 members — the cstage rows
* assert only that cstage DIVERGES (got != the runtime-correct value and the
* build succeeded), not a pinned value. The wwstage rows assert the exact
* runtime-correct value. (#44 closes the cs side → cstage will then match.)
* row | shape | ww | cs
* ----------+-----------------------------------------+----+--------
* nest_47 | gi:inner=47; (inner|str)=gi; match→v | 47 | != 47 (garbage)
* nest_99 | gi:inner=99; (inner|str)=gi; match→v | 99 | != 99 (garbage)
* Rows assert the exact runtime-correct value on both stages (cs==ww).
* row | shape | exit
* ----------+-----------------------------------------+-----
* nest_47 | gi:inner=47; (inner|str)=gi; match→v | 47
* nest_99 | gi:inner=99; (inner|str)=gi; match→v | 99
*/
#include <stdio.h>
#include <stdlib.h>
@@ -154,28 +145,16 @@ main(void)
drivers[d].name, drivers[d].drv);
continue;
}
int is_ww = (d == 1);
for (int i = 0; i < n; i++) {
total++;
int got = run_build(drivers[d].drv, &rows[i], i);
if (is_ww) {
/* align-to-runtime-correct: exact value. */
if (got != rows[i].want_ww) {
fprintf(stderr, "globtagwiden_run[wwstage][%s]:"
" exit=%d want=%d\n", rows[i].label,
got, rows[i].want_ww);
fail++;
}
} else {
/* #263 residual: cstage built but diverges from the
* runtime-correct value (frame garbage, #44). */
if (got == -1 || got == rows[i].want_ww) {
fprintf(stderr, "globtagwiden_run[cstage][%s]:"
" exit=%d expected build-ok and !=%d "
"(#263 residual, task #44)\n",
rows[i].label, got, rows[i].want_ww);
fail++;
}
/* #44 CLOSED: cs==ww at the exact runtime-correct value
* on both drivers (the .s is byte-identical). */
if (got != rows[i].want_ww) {
fprintf(stderr, "globtagwiden_run[%s][%s]:"
" exit=%d want=%d\n", drivers[d].name,
rows[i].label, got, rows[i].want_ww);
fail++;
}
}
}