wwstage: copy full tagged-element slot for N_DOT/N_INDEX-base index read (#261)
The #259 store fix unmasked a pre-existing latent cs!=ww in the tagged- element READ via an N_DOT base (`x.o[i]`) / chained N_INDEX base (`m[i][j]`): wwstage materialized the element as a SCALAR one-word load + zeroed tag where cstage copies the full tagged slot — silently dropping the tag/payload-high word (wrong variant). Three sites all keyed off the same N_IDENT-only gate; cstage classifies TY_TAGGED for ANY base off the checker-stamped element type. Align wwstage UP: - cgindex (cgenexpr.ww): the N_DOT/N_INDEX-base arm now sets elem_tagged/elem_slot_sz from n.type_ (the stamped element tinfo), mirroring cstage cgen.c:8101 — the full-slot copy arms then fire. - rhstaggedabicall (cgenutil.ww): the N_INDEX branch reads typeistagged(src.type_) for any base instead of an N_IDENT-only structural lookup, mirroring cstage's src->type keying — fixes the let-init / call-arg widen-source spill. - forwardtagged (cgenstmt.ww): the return-path passthrough gate now accepts N_INDEX/N_DOT tagged rhs (which cgexpr materializes into the tagged ABI), not just N_CALL — fixes `return x.o[i]`. read + call-arg + return + chained 2D all close by construction (one materialization path). cstage unchanged (pure wwstage-align-up). 949 gains 9 #261 rows (i32 + explicit-void variant per shape proves the tag survives) and flips the two #259 read-back rows to byteid=1.
This commit is contained in:
@@ -511,15 +511,26 @@ static const struct row rows[] = {
|
||||
" wr(&x);\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 1 },
|
||||
/* #259 STORE-correctness rows (byteid=0, run-only on cstage): store
|
||||
* then read the element back via match to confirm the store wrote the
|
||||
* right slot (66/77, not garbage) and no longer segfaults. byte-id is
|
||||
* BLOCKED here by an ORTHOGONAL newly-surfaced divergence in the
|
||||
* N_DOT-base tagged-element READ materialization (sibling of #255 in
|
||||
* the same N_DOT-base index fallback: wwstage loads ONE word + zeroes
|
||||
* the tag where cstage copies the full 16-byte slot) — the store base
|
||||
* is already byte-id (see the store-only rows above); only the
|
||||
* read-back diverges. Filed separately; do not gate byte-id here. */
|
||||
/* #261 tagged-element READ materialization via an N_DOT / chained
|
||||
* N_INDEX base. The #259 store fix UNMASKED a pre-existing latent
|
||||
* cs!=ww in the read-back: wwstage's cgindex N_DOT/N_INDEX-base arm
|
||||
* never set elem_tagged (the detection block was gated on an N_IDENT
|
||||
* base), so a tagged element fell to the SCALAR loadopsz path — one
|
||||
* word into AX + a zeroed tag — where cstage copies the full 16B slot
|
||||
* (AX=tag, DX=payload). Two more sites keyed off the same N_IDENT-only
|
||||
* gate: rhstaggedabicall (let/call-arg widen source) and the return-
|
||||
* path forwardtagged. All three drop the tag/payload-high word -> the
|
||||
* wrong variant. Fix mirrors cstage (classify TY_TAGGED for ANY base,
|
||||
* read the checker-stamped element type_): cgindex slot-copy +
|
||||
* rhstaggedabicall typeistagged(src.type_) + forwardtagged broadened
|
||||
* to N_INDEX/N_DOT. read + call-arg + return + chained 2D all close by
|
||||
* construction. byteid=1 throughout: post-fix the materialization is
|
||||
* byte-identical.
|
||||
*
|
||||
* Tag-survival proof: each shape is tested with BOTH an i32 variant
|
||||
* AND an explicit `= void` variant. The old one-word load that zeroed
|
||||
* the tag would misread the void slot as the i32 variant (tag 0); the
|
||||
* void rows return 1 only if the tag survived. */
|
||||
{ "tagged_store_own_rd",
|
||||
"package main;\n"
|
||||
"type e = struct { o: [4](i32 | void) };\n"
|
||||
@@ -531,7 +542,7 @@ static const struct row rows[] = {
|
||||
" case let n: i32 => yield n;\n"
|
||||
" case void => yield 0: i32;\n"
|
||||
" };\n"
|
||||
"};\n", 66, 0 },
|
||||
"};\n", 66, 1 },
|
||||
{ "tagged_store_ptr_rd",
|
||||
"package main;\n"
|
||||
"type e = struct { o: [4](i32 | void) };\n"
|
||||
@@ -544,7 +555,108 @@ static const struct row rows[] = {
|
||||
" case let n: i32 => yield n;\n"
|
||||
" case void => yield 0: i32;\n"
|
||||
" };\n"
|
||||
"};\n", 77, 0 },
|
||||
"};\n", 77, 1 },
|
||||
{ "tagged_rd_void",
|
||||
"package main;\n"
|
||||
"type e = struct { o: [4](i32 | void) };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let x: e;\n"
|
||||
" x.o[1] = void;\n"
|
||||
" let v: (i32 | void) = x.o[1];\n"
|
||||
" return match (v) {\n"
|
||||
" case let n: i32 => yield 99: i32;\n"
|
||||
" case void => yield 1: i32;\n"
|
||||
" };\n"
|
||||
"};\n", 1, 1 },
|
||||
{ "tagged_callarg_i32",
|
||||
"package main;\n"
|
||||
"type e = struct { o: [4](i32 | void) };\n"
|
||||
"fn take(v: (i32 | void)) i32 = {\n"
|
||||
" return match (v) {\n"
|
||||
" case let n: i32 => yield n;\n"
|
||||
" case void => yield 1: i32;\n"
|
||||
" };\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let x: e;\n"
|
||||
" x.o[2] = 55;\n"
|
||||
" return take(x.o[2]);\n"
|
||||
"};\n", 55, 1 },
|
||||
{ "tagged_callarg_void",
|
||||
"package main;\n"
|
||||
"type e = struct { o: [4](i32 | void) };\n"
|
||||
"fn take(v: (i32 | void)) i32 = {\n"
|
||||
" return match (v) {\n"
|
||||
" case let n: i32 => yield 99: i32;\n"
|
||||
" case void => yield 1: i32;\n"
|
||||
" };\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let x: e;\n"
|
||||
" x.o[2] = void;\n"
|
||||
" return take(x.o[2]);\n"
|
||||
"};\n", 1, 1 },
|
||||
{ "tagged_return_i32",
|
||||
"package main;\n"
|
||||
"type e = struct { o: [4](i32 | void) };\n"
|
||||
"fn ret(x: *e) (i32 | void) = { return x.o[1]; };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let x: e;\n"
|
||||
" x.o[1] = 88;\n"
|
||||
" let v: (i32 | void) = ret(&x);\n"
|
||||
" return match (v) {\n"
|
||||
" case let n: i32 => yield n;\n"
|
||||
" case void => yield 1: i32;\n"
|
||||
" };\n"
|
||||
"};\n", 88, 1 },
|
||||
{ "tagged_return_void",
|
||||
"package main;\n"
|
||||
"type e = struct { o: [4](i32 | void) };\n"
|
||||
"fn ret(x: *e) (i32 | void) = { return x.o[1]; };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let x: e;\n"
|
||||
" x.o[1] = void;\n"
|
||||
" let v: (i32 | void) = ret(&x);\n"
|
||||
" return match (v) {\n"
|
||||
" case let n: i32 => yield 99: i32;\n"
|
||||
" case void => yield 1: i32;\n"
|
||||
" };\n"
|
||||
"};\n", 1, 1 },
|
||||
{ "tagged_chained_i32",
|
||||
"package main;\n"
|
||||
"type m = struct { g: [2][2](i32 | void) };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let y: m;\n"
|
||||
" y.g[1][1] = 44;\n"
|
||||
" let v: (i32 | void) = y.g[1][1];\n"
|
||||
" return match (v) {\n"
|
||||
" case let n: i32 => yield n;\n"
|
||||
" case void => yield 1: i32;\n"
|
||||
" };\n"
|
||||
"};\n", 44, 1 },
|
||||
{ "tagged_chained_void",
|
||||
"package main;\n"
|
||||
"type m = struct { g: [2][2](i32 | void) };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let y: m;\n"
|
||||
" y.g[1][1] = void;\n"
|
||||
" let v: (i32 | void) = y.g[1][1];\n"
|
||||
" return match (v) {\n"
|
||||
" case let n: i32 => yield 99: i32;\n"
|
||||
" case void => yield 1: i32;\n"
|
||||
" };\n"
|
||||
"};\n", 1, 1 },
|
||||
{ "tagged_ctrl_bare_rd",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [4](i32 | void);\n"
|
||||
" a[1] = 33;\n"
|
||||
" let v: (i32 | void) = a[1];\n"
|
||||
" return match (v) {\n"
|
||||
" case let n: i32 => yield n;\n"
|
||||
" case void => yield 1: i32;\n"
|
||||
" };\n"
|
||||
"};\n", 33, 1 },
|
||||
{ NULL, NULL, 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user