w6c: 'as' on a module-global tagged ident loads the full box

cstage's N_TYPEASSERT assumed cgexpr filled the registers and spilled
an uninitialized payload (cs=0 for any stored value); mirror the
landed wwstage emission (tag/payload/cap from g(SB)). Flips the
residual row to cs==ww==correct. Task #46.
This commit is contained in:
2026-06-12 22:45:10 +09:00
parent fc47c3d0f2
commit e09a8c1775
2 changed files with 35 additions and 23 deletions

View File

@@ -14,24 +14,21 @@
* • `is` half = ALIGN-UP. cstage reads the tag correctly (MOVQ g(SB),AX);
* wwstage read 0(BP). FIX routes the global ident to MOVQ g(SB),AX →
* cs==ww + runtime-correct (the is_* rows assert want on BOTH drivers).
* • `as` half = #263 BOTH-WRONG. cstage N_TYPEASSERT on a global ident also
* fails: it spills cgexpr's AX (tag only) plus UNINITIALIZED DX as the
* payload (never loads g(SB)+8) → returns garbage (0). The wwstage FIX
* copies the global box words from g(SB)+0/+8[/+16] into a fresh
* @asrt_spill so the tag-check + payload load index off memory like a
* local → ww runtime-correct, while cstage stays wrong: cs≠ww residual
* BY DESIGN until the cstage half lands (ww-core TASK #46). The as_* rows
* assert ww-correct AND pin cstage's documented-wrong value so the residual
* is never misread as a regression.
* • `as` half = #263 BOTH-WRONG, now CLOSED both stages. cstage N_TYPEASSERT
* on a global ident spilled cgexpr's AX (tag only) plus UNINITIALIZED DX as
* the payload (never loaded g(SB)+8) → garbage (0). The ww half landed in
* F8-c3; the cstage half (copy the box words from g(SB)+0/+8[/+16] into the
* @asrt_spill so the tag-check + payload load index off memory like a local)
* landed in ww-core TASK #46. Both stages now read the real payload and the
* .s is byte-identical.
*
* Rows (built+run on cstage `ww` and wwstage `ww_ww`; each driver checked
* against ITS expected — want_cs / want_ww — to encode the #263 residual):
* row | shape | cs | ww | cat
* ----------+----------------------------------------+----+----+--------
* is_match | g:(i64|bool)=5; if g is i64 →0 else 12 | 0 | 0 | align-UP
* is_nomatch| g:(i64|bool)=5; if g is bool →13 else 0 | 0 | 0 | align-UP
* as_i64 | g:(i64|bool)=42; (g as i64):int | 0 | 42 | #263 (#46)
* as_str | g:(str|i64)="hello"; len(g as str) | 0 | 5 | #263 (#46)
* Rows (built+run on cstage `ww` and wwstage `ww_ww`; cs==ww on every row):
* row | shape | exit | cat
* ----------+----------------------------------------+------+--------
* is_match | g:(i64|bool)=5; if g is i64 →0 else 12 | 0 | align-UP
* is_nomatch| g:(i64|bool)=5; if g is bool →13 else 0 | 0 | align-UP
* as_i64 | g:(i64|bool)=42; (g as i64):int | 42 | #46
* as_str | g:(str|i64)="hello"; len(g as str) | 5 | #46
*/
#include <stdio.h>
#include <stdlib.h>
@@ -71,22 +68,22 @@ static const struct row rows[] = {
"export fn main() int = { return check(); };\n",
0, 0 },
/* #263: cstage spills uninitialized DX as the payload (task #46) →
* returns 0; wwstage copies the box from g(SB) → 42. */
/* #46 CLOSED both stages: cstage now copies the box from g(SB) into the
* @asrt_spill (was spilling uninitialized DX) → 42 == wwstage. */
{ "as_i64",
"package main;\n"
"let g: (i64 | bool) = 42;\n"
"fn check() int = { let x: i64 = g as i64; return x: int; };\n"
"export fn main() int = { return check(); };\n",
0, 42 },
42, 42 },
/* #263: str variant — wwstage copies the +16 cap word too (task #46). */
/* #46 str variant — both stages copy the +16 cap word too. */
{ "as_str",
"package main;\n"
"let g: (str | i64) = \"hello\";\n"
"fn check() int = { let s: str = g as str; return len(s): int; };\n"
"export fn main() int = { return check(); };\n",
0, 5 },
5, 5 },
};
/* run_build — build+run `src` via `driver`; returns the binary's exit