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:
@@ -18058,27 +18058,17 @@ fn rhstaggedabicall(c: *cgen, src: *node) bool = {
|
||||
return false;
|
||||
};
|
||||
if (src.kind == nkind.N_INDEX) {
|
||||
let base: *node = src.lhs;
|
||||
if (base != nil) {
|
||||
if (base.kind == nkind.N_IDENT) {
|
||||
let bl: *local = localfindnode(c, base.str);
|
||||
if (bl != nil) {
|
||||
let btn: *node = bl.tnode;
|
||||
if (btn != nil) {
|
||||
let bk: nkind = btn.kind;
|
||||
let elemt: *node = nil;
|
||||
if (bk == nkind.N_TARRAY) { elemt = btn.lhs; };
|
||||
if (bk == nkind.N_TSLICE) { elemt = btn.lhs; };
|
||||
if (bk == nkind.N_TPTR) { elemt = btn.lhs; };
|
||||
if (elemt != nil) {
|
||||
if (istaggedtype(c, elemt)) {
|
||||
return true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
// The checker stamps every N_INDEX node's type_ to the element
|
||||
// tinfo (check.ww:2334-2337 indexresult) for ANY base shape —
|
||||
// N_IDENT, N_DOT (`x.o[i]`), or chained N_INDEX (`m[i][j]`). Read
|
||||
// it directly, mirroring cstage cg_widen_tagged_store keying on
|
||||
// src->type (cmd/w6c/cgen.c:2020-2023). #261: the prior
|
||||
// N_IDENT-base-only structural lookup missed N_DOT/N_INDEX bases,
|
||||
// so a tagged element materialized via `x.o[i]` (correct AX/DX
|
||||
// slot from cgindex) was then spilled by the scalar-widen arm,
|
||||
// dropping the tag/payload-high word — silent cs≠ww.
|
||||
if (typeistagged(src.type_: *tinfo)) { return true; };
|
||||
return false;
|
||||
};
|
||||
// N_DOT of a tagged-typed struct field — cgdot loads
|
||||
// AX=tag, DX=word0, CX=word1[, R8=word2], so downstream
|
||||
@@ -20750,6 +20740,25 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
elemisstr = isstrtype(c, etn);
|
||||
elemisslice = isslicetype(c, etn);
|
||||
};
|
||||
// N_DOT / N_INDEX base (`x.o[i]`, chained `m[i][j]`): the
|
||||
// element tinfo is n.type_ (the checker-stamped indexresult),
|
||||
// same source the esz/str/slice/float flags read above. Mirror
|
||||
// cstage cgen.c:8101 `esubu->kind == TY_TAGGED` — classified for
|
||||
// ANY base, NOT gated on N_IDENT, and NOT excluding the nullable
|
||||
// fold (slot_sz=8 degrades the copy arm to one MOVQ, matching
|
||||
// cstage's fallback `MOVQ AX,BX; MOVQ (BX),AX`). #261: without
|
||||
// this an N_DOT-base tagged element fell to the scalar
|
||||
// loadopsz path and silently dropped the tag/payload-high word.
|
||||
if (base.kind == nkind.N_DOT || base.kind == nkind.N_INDEX) {
|
||||
let dt: *tinfo = n.type_: *tinfo;
|
||||
if (dt != nil) {
|
||||
if (typeistagged(dt)) {
|
||||
elem_tagged = true;
|
||||
elem_slot_sz = dt.size: i32;
|
||||
esz = elem_slot_sz;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
cgexpr(c, idx);
|
||||
if (esz > 1) {
|
||||
@@ -27761,8 +27770,17 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
// chain suffices because variants are primitives (single
|
||||
// tctx tinfo) or NAMED (per-decl identity); a full recursive
|
||||
// tinfo structural-eq helper is gated by #178.
|
||||
// #261: N_INDEX of a tagged element (`return x.o[i]`) and
|
||||
// N_DOT of a tagged field both materialize the full tagged
|
||||
// ABI shape via cgexpr (cgindex slot-copy / cgdot field-load,
|
||||
// AX=tag/DX=v0/...), exactly like an N_CALL of a tagged-
|
||||
// returning fn — so a same-type return forwards them
|
||||
// unchanged. cstage gates passthrough purely on the rhs type
|
||||
// (no kind filter, cgen.c:8845); without these kinds an
|
||||
// N_INDEX tagged-element return fell to the scalar-variant
|
||||
// shuffle (MOVQ AX,DX; MOVQ $0,AX), dropping the payload.
|
||||
let forwardtagged: bool = false;
|
||||
if (rhs.kind == nkind.N_CALL && rhs.type_ != nil && c.fnret != nil && c.fnret.type_ != nil) {
|
||||
if ((rhs.kind == nkind.N_CALL || rhs.kind == nkind.N_INDEX || rhs.kind == nkind.N_DOT) && rhs.type_ != nil && c.fnret != nil && c.fnret.type_ != nil) {
|
||||
let ru: *tinfo = rhs.type_: *tinfo;
|
||||
for (ru != nil && ru.kind == tykind.TY_NAMED) { ru = ru.under; };
|
||||
let fu: *tinfo = c.fnret.type_: *tinfo;
|
||||
|
||||
@@ -1272,6 +1272,25 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
elemisstr = isstrtype(c, etn);
|
||||
elemisslice = isslicetype(c, etn);
|
||||
};
|
||||
// N_DOT / N_INDEX base (`x.o[i]`, chained `m[i][j]`): the
|
||||
// element tinfo is n.type_ (the checker-stamped indexresult),
|
||||
// same source the esz/str/slice/float flags read above. Mirror
|
||||
// cstage cgen.c:8101 `esubu->kind == TY_TAGGED` — classified for
|
||||
// ANY base, NOT gated on N_IDENT, and NOT excluding the nullable
|
||||
// fold (slot_sz=8 degrades the copy arm to one MOVQ, matching
|
||||
// cstage's fallback `MOVQ AX,BX; MOVQ (BX),AX`). #261: without
|
||||
// this an N_DOT-base tagged element fell to the scalar
|
||||
// loadopsz path and silently dropped the tag/payload-high word.
|
||||
if (base.kind == nkind.N_DOT || base.kind == nkind.N_INDEX) {
|
||||
let dt: *tinfo = n.type_: *tinfo;
|
||||
if (dt != nil) {
|
||||
if (typeistagged(dt)) {
|
||||
elem_tagged = true;
|
||||
elem_slot_sz = dt.size: i32;
|
||||
esz = elem_slot_sz;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
cgexpr(c, idx);
|
||||
if (esz > 1) {
|
||||
|
||||
@@ -630,8 +630,17 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
// chain suffices because variants are primitives (single
|
||||
// tctx tinfo) or NAMED (per-decl identity); a full recursive
|
||||
// tinfo structural-eq helper is gated by #178.
|
||||
// #261: N_INDEX of a tagged element (`return x.o[i]`) and
|
||||
// N_DOT of a tagged field both materialize the full tagged
|
||||
// ABI shape via cgexpr (cgindex slot-copy / cgdot field-load,
|
||||
// AX=tag/DX=v0/...), exactly like an N_CALL of a tagged-
|
||||
// returning fn — so a same-type return forwards them
|
||||
// unchanged. cstage gates passthrough purely on the rhs type
|
||||
// (no kind filter, cgen.c:8845); without these kinds an
|
||||
// N_INDEX tagged-element return fell to the scalar-variant
|
||||
// shuffle (MOVQ AX,DX; MOVQ $0,AX), dropping the payload.
|
||||
let forwardtagged: bool = false;
|
||||
if (rhs.kind == nkind.N_CALL && rhs.type_ != nil && c.fnret != nil && c.fnret.type_ != nil) {
|
||||
if ((rhs.kind == nkind.N_CALL || rhs.kind == nkind.N_INDEX || rhs.kind == nkind.N_DOT) && rhs.type_ != nil && c.fnret != nil && c.fnret.type_ != nil) {
|
||||
let ru: *tinfo = rhs.type_: *tinfo;
|
||||
for (ru != nil && ru.kind == tykind.TY_NAMED) { ru = ru.under; };
|
||||
let fu: *tinfo = c.fnret.type_: *tinfo;
|
||||
|
||||
@@ -2548,27 +2548,17 @@ fn rhstaggedabicall(c: *cgen, src: *node) bool = {
|
||||
return false;
|
||||
};
|
||||
if (src.kind == nkind.N_INDEX) {
|
||||
let base: *node = src.lhs;
|
||||
if (base != nil) {
|
||||
if (base.kind == nkind.N_IDENT) {
|
||||
let bl: *local = localfindnode(c, base.str);
|
||||
if (bl != nil) {
|
||||
let btn: *node = bl.tnode;
|
||||
if (btn != nil) {
|
||||
let bk: nkind = btn.kind;
|
||||
let elemt: *node = nil;
|
||||
if (bk == nkind.N_TARRAY) { elemt = btn.lhs; };
|
||||
if (bk == nkind.N_TSLICE) { elemt = btn.lhs; };
|
||||
if (bk == nkind.N_TPTR) { elemt = btn.lhs; };
|
||||
if (elemt != nil) {
|
||||
if (istaggedtype(c, elemt)) {
|
||||
return true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
// The checker stamps every N_INDEX node's type_ to the element
|
||||
// tinfo (check.ww:2334-2337 indexresult) for ANY base shape —
|
||||
// N_IDENT, N_DOT (`x.o[i]`), or chained N_INDEX (`m[i][j]`). Read
|
||||
// it directly, mirroring cstage cg_widen_tagged_store keying on
|
||||
// src->type (cmd/w6c/cgen.c:2020-2023). #261: the prior
|
||||
// N_IDENT-base-only structural lookup missed N_DOT/N_INDEX bases,
|
||||
// so a tagged element materialized via `x.o[i]` (correct AX/DX
|
||||
// slot from cgindex) was then spilled by the scalar-widen arm,
|
||||
// dropping the tag/payload-high word — silent cs≠ww.
|
||||
if (typeistagged(src.type_: *tinfo)) { return true; };
|
||||
return false;
|
||||
};
|
||||
// N_DOT of a tagged-typed struct field — cgdot loads
|
||||
// AX=tag, DX=word0, CX=word1[, R8=word2], so downstream
|
||||
|
||||
@@ -18058,27 +18058,17 @@ fn rhstaggedabicall(c: *cgen, src: *node) bool = {
|
||||
return false;
|
||||
};
|
||||
if (src.kind == nkind.N_INDEX) {
|
||||
let base: *node = src.lhs;
|
||||
if (base != nil) {
|
||||
if (base.kind == nkind.N_IDENT) {
|
||||
let bl: *local = localfindnode(c, base.str);
|
||||
if (bl != nil) {
|
||||
let btn: *node = bl.tnode;
|
||||
if (btn != nil) {
|
||||
let bk: nkind = btn.kind;
|
||||
let elemt: *node = nil;
|
||||
if (bk == nkind.N_TARRAY) { elemt = btn.lhs; };
|
||||
if (bk == nkind.N_TSLICE) { elemt = btn.lhs; };
|
||||
if (bk == nkind.N_TPTR) { elemt = btn.lhs; };
|
||||
if (elemt != nil) {
|
||||
if (istaggedtype(c, elemt)) {
|
||||
return true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
// The checker stamps every N_INDEX node's type_ to the element
|
||||
// tinfo (check.ww:2334-2337 indexresult) for ANY base shape —
|
||||
// N_IDENT, N_DOT (`x.o[i]`), or chained N_INDEX (`m[i][j]`). Read
|
||||
// it directly, mirroring cstage cg_widen_tagged_store keying on
|
||||
// src->type (cmd/w6c/cgen.c:2020-2023). #261: the prior
|
||||
// N_IDENT-base-only structural lookup missed N_DOT/N_INDEX bases,
|
||||
// so a tagged element materialized via `x.o[i]` (correct AX/DX
|
||||
// slot from cgindex) was then spilled by the scalar-widen arm,
|
||||
// dropping the tag/payload-high word — silent cs≠ww.
|
||||
if (typeistagged(src.type_: *tinfo)) { return true; };
|
||||
return false;
|
||||
};
|
||||
// N_DOT of a tagged-typed struct field — cgdot loads
|
||||
// AX=tag, DX=word0, CX=word1[, R8=word2], so downstream
|
||||
@@ -20750,6 +20740,25 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
elemisstr = isstrtype(c, etn);
|
||||
elemisslice = isslicetype(c, etn);
|
||||
};
|
||||
// N_DOT / N_INDEX base (`x.o[i]`, chained `m[i][j]`): the
|
||||
// element tinfo is n.type_ (the checker-stamped indexresult),
|
||||
// same source the esz/str/slice/float flags read above. Mirror
|
||||
// cstage cgen.c:8101 `esubu->kind == TY_TAGGED` — classified for
|
||||
// ANY base, NOT gated on N_IDENT, and NOT excluding the nullable
|
||||
// fold (slot_sz=8 degrades the copy arm to one MOVQ, matching
|
||||
// cstage's fallback `MOVQ AX,BX; MOVQ (BX),AX`). #261: without
|
||||
// this an N_DOT-base tagged element fell to the scalar
|
||||
// loadopsz path and silently dropped the tag/payload-high word.
|
||||
if (base.kind == nkind.N_DOT || base.kind == nkind.N_INDEX) {
|
||||
let dt: *tinfo = n.type_: *tinfo;
|
||||
if (dt != nil) {
|
||||
if (typeistagged(dt)) {
|
||||
elem_tagged = true;
|
||||
elem_slot_sz = dt.size: i32;
|
||||
esz = elem_slot_sz;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
cgexpr(c, idx);
|
||||
if (esz > 1) {
|
||||
@@ -27761,8 +27770,17 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
// chain suffices because variants are primitives (single
|
||||
// tctx tinfo) or NAMED (per-decl identity); a full recursive
|
||||
// tinfo structural-eq helper is gated by #178.
|
||||
// #261: N_INDEX of a tagged element (`return x.o[i]`) and
|
||||
// N_DOT of a tagged field both materialize the full tagged
|
||||
// ABI shape via cgexpr (cgindex slot-copy / cgdot field-load,
|
||||
// AX=tag/DX=v0/...), exactly like an N_CALL of a tagged-
|
||||
// returning fn — so a same-type return forwards them
|
||||
// unchanged. cstage gates passthrough purely on the rhs type
|
||||
// (no kind filter, cgen.c:8845); without these kinds an
|
||||
// N_INDEX tagged-element return fell to the scalar-variant
|
||||
// shuffle (MOVQ AX,DX; MOVQ $0,AX), dropping the payload.
|
||||
let forwardtagged: bool = false;
|
||||
if (rhs.kind == nkind.N_CALL && rhs.type_ != nil && c.fnret != nil && c.fnret.type_ != nil) {
|
||||
if ((rhs.kind == nkind.N_CALL || rhs.kind == nkind.N_INDEX || rhs.kind == nkind.N_DOT) && rhs.type_ != nil && c.fnret != nil && c.fnret.type_ != nil) {
|
||||
let ru: *tinfo = rhs.type_: *tinfo;
|
||||
for (ru != nil && ru.kind == tykind.TY_NAMED) { ru = ru.under; };
|
||||
let fu: *tinfo = c.fnret.type_: *tinfo;
|
||||
|
||||
Reference in New Issue
Block a user