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) for (int k = 0; k < sz; k += 8)
ins2(c, A_MOVQ, areg(D_AX), ins2(c, A_MOVQ, areg(D_AX),
amem(D_BP, write_off + k)); 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); int soff = localfind(*locals_p, src->str);
for (int k = 0; k < ssz; k += 8) { for (int k = 0; k < ssz; k += 8) {
ins2(c, A_MOVQ, amem(D_BP, soff + k), 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), ins2(c, A_MOVQ, areg(D_AX),
amem(D_BP, write_off + 8 + k)); 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 { } else {
/* #38b: an sret-classified call result is in /* #38b: an sret-classified call result is in
* memory (AX = dest pointer), not the cursor — * 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 * must copy gi's whole box as the nested payload, not store its tag as the
* payload. * payload.
* *
* THE BUG (cat-A silent miscompile, #263 BOTH-WRONG): cgwidentaggedstorebp's * THE BUG (cat-A silent miscompile, #263 BOTH-WRONG, now CLOSED both stages):
* #218 nested-tagged-source arm is gated on rhstaggedident, which returns nil * the #218 nested-tagged-source arm was gated local-only, so a module-global
* for a module-global tagged ident (local-only). So srctagged was false and a * tagged source fell to the scalar word0 widen arm — gi's TAG word landed as
* global tagged source fell to the scalar word0 widen arm — gi's TAG word * the box payload (wwstage), or frame garbage was copied, never gi(SB)
* landed as the box payload, the real value lost. cstage is ALSO wrong: it * (cstage). The ww half landed in F8-c8; the cstage half (the nested arm's
* copies frame garbage (saved-BP / return-address), never gi(SB). Both wrong, * N_IDENT copy grows a global branch — LEAQ gi(SB),SI via aggarg_srcaddr, copy
* divergent (#263); the cstage half is filed as task #44. * 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 * Rows assert the exact runtime-correct value on both stages (cs==ww).
* tagged ident, and the nested arm's N_IDENT copy grows a global branch — * row | shape | exit
* 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 * nest_47 | gi:inner=47; (inner|str)=gi; match→v | 47
* (byte-id). cstage stays wrong → cs≠ww residual. * nest_99 | gi:inner=99; (inner|str)=gi; match→v | 99
*
* 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)
*/ */
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -154,28 +145,16 @@ main(void)
drivers[d].name, drivers[d].drv); drivers[d].name, drivers[d].drv);
continue; continue;
} }
int is_ww = (d == 1);
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
total++; total++;
int got = run_build(drivers[d].drv, &rows[i], i); int got = run_build(drivers[d].drv, &rows[i], i);
if (is_ww) { /* #44 CLOSED: cs==ww at the exact runtime-correct value
/* align-to-runtime-correct: exact value. */ * on both drivers (the .s is byte-identical). */
if (got != rows[i].want_ww) { if (got != rows[i].want_ww) {
fprintf(stderr, "globtagwiden_run[wwstage][%s]:" fprintf(stderr, "globtagwiden_run[%s][%s]:"
" exit=%d want=%d\n", rows[i].label, " exit=%d want=%d\n", drivers[d].name,
got, rows[i].want_ww); rows[i].label, got, rows[i].want_ww);
fail++; 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++;
}
} }
} }
} }