wcc/cgen: #19+#22 uniform index element-size for non-ident bases (wwstage)

Indexing a non-ident pointer-yielding base -- a direct cast
((&a):*[4]u32)[i], a call result mk(&a)[i], a slice, a type-assertion --
used wwstage's default 8-byte element stride/load instead of the real
element type's, reading garbage (cast-base i32 index: cs=30, ww=0). The
cgindex esz derivation gated on a whitelist of base node-kinds (DOT /
UN-deref / INDEX); an N_CAST/N_CALL/N_SLICE/N_TYPEASSERT base matched none.

Rather than extend the whitelist (whack-a-mole), this mirrors cstage's
uniform idx_eff read: N_INDEX keeps its own arm (chained-index byte-id
preserved), and every other non-ident base now derives esz/stride/load-
width/signedness from the stamped n.type_ -- closing the class by
construction (base set ident/dot/un/index/cast/call/slice/typeassert).
cstage was already correct (uniform); w6c md5 unchanged. byte-id 990-997
8/8, no lib pin flips. test/wcc/830 (9 base shapes, byte-id per width,
signed + unsigned). Folds the N_CALL sibling #22.
This commit is contained in:
2026-06-09 01:18:32 +09:00
parent ee8082a43b
commit 606c16a28f
5 changed files with 369 additions and 57 deletions

View File

@@ -23562,29 +23562,14 @@ fn cgindex(c: *cgen, n: *node) void = {
if (!elem_isarray) {
elem_isarray = tinfoisarray(n.type_: *tinfo);
};
} else { if (base.kind == nkind.N_DOT
|| (base.kind == nkind.N_UN && base.op == tkind.TK_STAR)) {
// `s.ptr[i]` / struct-field index: stride is the
// checker-stamped element tinfo's natural size, the
// same idiom as the N_INDEX-base arm below (#60/#72).
// cstage idx_eff(base->type)->sub->size (cmd/w6c/
// cgen.c:3517-18). Signedness from the same element tinfo
// so a signed-narrow field element sign-extends on load
// (loadopsz keys on (signed,sz)); cstage's fldloadop reads
// it from the element type — align ww up (#255).
// N_UN deref base (`(*p)[i]`, #61 C): same stamped-tinfo
// source — cstage reads base->type uniformly; without
// this arm esz fell to the 8B default (wrong stride for
// narrow elements once the deref base materializes).
let dt: *tinfo = n.type_: *tinfo;
if (dt != nil) { esz = dt.size: i32; signed_elem = typeissigned(dt); elemisstr = typeisstr(dt); elemisslice = typeisslice(dt); float_elem = typeisfloat(dt); f32_elem = typeisf32(dt); };
elem_isarray = tinfoisarray(dt);
} else { if (base.kind == nkind.N_INDEX) {
// #60: chained `names[i][k]` — n.type_ is the checker-
// stamped outer element tinfo (indexresult over the inner
// index's value type). cstage reads base->type->sub->size
// for esz (cmd/w6c/cgen.c:2070-2071). Drops the
// indexvaluetnode walk.
// indexvaluetnode walk. Kept as its OWN arm (it does not set
// elemisstr/elemisslice) so the chained-index byte-id is
// preserved verbatim; the catch-all below would re-stamp it.
let et: *tinfo = n.type_: *tinfo;
if (et != nil) {
esz = et.size: i32;
@@ -23593,7 +23578,29 @@ fn cgindex(c: *cgen, n: *node) void = {
f32_elem = typeisf32(et);
elem_isarray = tinfoisarray(et);
};
};};};
} else {
// Every OTHER non-ident base — N_DOT (`s.arr[i]`), N_UN-deref
// (`(*p)[i]`, #61), N_CAST (`(e:*[N]T)[i]`, #19old), N_CALL
// (`f()[i]`), N_SLICE (`s[a:b][i]`), N_TYPEASSERT
// (`(v as *[N]T)[i]`), … — derives esz/stride/load-width/
// signedness from the checker-stamped index-RESULT tinfo
// n.type_ (the element T: u32->4). cstage reads it UNIFORMLY
// via idx_eff(base->type)->sub->size with NO node-kind gate
// (cmd/w6c/cgen.c:3517-18); wwstage's prior node-kind whitelist
// (DOT/UN/CAST only) silently left N_CALL/N_SLICE/N_TYPEASSERT
// (and any future base kind) at the 8B default — wrong stride
// AND full-word MOVQ load for narrow elements. One stamped-tinfo
// read mirrors cstage and closes the class by construction
// (#19old + its CALL/SLICE/TYPEASSERT residuals). esz via
// dt.size (type table, rule-13). Signedness from the same tinfo
// so a signed-narrow element sign-extends on load (loadopsz keys
// on (signed,sz); cstage's fldloadop reads it from the element
// type — align ww up, #255). The base ADDRESS materialization
// below is unchanged; only the stride/load-width was wrong.
let dt: *tinfo = n.type_: *tinfo;
if (dt != nil) { esz = dt.size: i32; signed_elem = typeissigned(dt); elemisstr = typeisstr(dt); elemisslice = typeisslice(dt); float_elem = typeisfloat(dt); f32_elem = typeisf32(dt); };
elem_isarray = tinfoisarray(dt);
};};
};
// Tagged-union element: load slot words into (AX=tag, DX=val0,
// CX=val1) matching the tagged-return ABI so call-arg / let /

View File

@@ -1900,29 +1900,14 @@ fn cgindex(c: *cgen, n: *node) void = {
if (!elem_isarray) {
elem_isarray = tinfoisarray(n.type_: *tinfo);
};
} else { if (base.kind == nkind.N_DOT
|| (base.kind == nkind.N_UN && base.op == tkind.TK_STAR)) {
// `s.ptr[i]` / struct-field index: stride is the
// checker-stamped element tinfo's natural size, the
// same idiom as the N_INDEX-base arm below (#60/#72).
// cstage idx_eff(base->type)->sub->size (cmd/w6c/
// cgen.c:3517-18). Signedness from the same element tinfo
// so a signed-narrow field element sign-extends on load
// (loadopsz keys on (signed,sz)); cstage's fldloadop reads
// it from the element type — align ww up (#255).
// N_UN deref base (`(*p)[i]`, #61 C): same stamped-tinfo
// source — cstage reads base->type uniformly; without
// this arm esz fell to the 8B default (wrong stride for
// narrow elements once the deref base materializes).
let dt: *tinfo = n.type_: *tinfo;
if (dt != nil) { esz = dt.size: i32; signed_elem = typeissigned(dt); elemisstr = typeisstr(dt); elemisslice = typeisslice(dt); float_elem = typeisfloat(dt); f32_elem = typeisf32(dt); };
elem_isarray = tinfoisarray(dt);
} else { if (base.kind == nkind.N_INDEX) {
// #60: chained `names[i][k]` — n.type_ is the checker-
// stamped outer element tinfo (indexresult over the inner
// index's value type). cstage reads base->type->sub->size
// for esz (cmd/w6c/cgen.c:2070-2071). Drops the
// indexvaluetnode walk.
// indexvaluetnode walk. Kept as its OWN arm (it does not set
// elemisstr/elemisslice) so the chained-index byte-id is
// preserved verbatim; the catch-all below would re-stamp it.
let et: *tinfo = n.type_: *tinfo;
if (et != nil) {
esz = et.size: i32;
@@ -1931,7 +1916,29 @@ fn cgindex(c: *cgen, n: *node) void = {
f32_elem = typeisf32(et);
elem_isarray = tinfoisarray(et);
};
};};};
} else {
// Every OTHER non-ident base — N_DOT (`s.arr[i]`), N_UN-deref
// (`(*p)[i]`, #61), N_CAST (`(e:*[N]T)[i]`, #19old), N_CALL
// (`f()[i]`), N_SLICE (`s[a:b][i]`), N_TYPEASSERT
// (`(v as *[N]T)[i]`), … — derives esz/stride/load-width/
// signedness from the checker-stamped index-RESULT tinfo
// n.type_ (the element T: u32->4). cstage reads it UNIFORMLY
// via idx_eff(base->type)->sub->size with NO node-kind gate
// (cmd/w6c/cgen.c:3517-18); wwstage's prior node-kind whitelist
// (DOT/UN/CAST only) silently left N_CALL/N_SLICE/N_TYPEASSERT
// (and any future base kind) at the 8B default — wrong stride
// AND full-word MOVQ load for narrow elements. One stamped-tinfo
// read mirrors cstage and closes the class by construction
// (#19old + its CALL/SLICE/TYPEASSERT residuals). esz via
// dt.size (type table, rule-13). Signedness from the same tinfo
// so a signed-narrow element sign-extends on load (loadopsz keys
// on (signed,sz); cstage's fldloadop reads it from the element
// type — align ww up, #255). The base ADDRESS materialization
// below is unchanged; only the stride/load-width was wrong.
let dt: *tinfo = n.type_: *tinfo;
if (dt != nil) { esz = dt.size: i32; signed_elem = typeissigned(dt); elemisstr = typeisstr(dt); elemisslice = typeisslice(dt); float_elem = typeisfloat(dt); f32_elem = typeisf32(dt); };
elem_isarray = tinfoisarray(dt);
};};
};
// Tagged-union element: load slot words into (AX=tag, DX=val0,
// CX=val1) matching the tagged-return ABI so call-arg / let /

View File

@@ -23562,29 +23562,14 @@ fn cgindex(c: *cgen, n: *node) void = {
if (!elem_isarray) {
elem_isarray = tinfoisarray(n.type_: *tinfo);
};
} else { if (base.kind == nkind.N_DOT
|| (base.kind == nkind.N_UN && base.op == tkind.TK_STAR)) {
// `s.ptr[i]` / struct-field index: stride is the
// checker-stamped element tinfo's natural size, the
// same idiom as the N_INDEX-base arm below (#60/#72).
// cstage idx_eff(base->type)->sub->size (cmd/w6c/
// cgen.c:3517-18). Signedness from the same element tinfo
// so a signed-narrow field element sign-extends on load
// (loadopsz keys on (signed,sz)); cstage's fldloadop reads
// it from the element type — align ww up (#255).
// N_UN deref base (`(*p)[i]`, #61 C): same stamped-tinfo
// source — cstage reads base->type uniformly; without
// this arm esz fell to the 8B default (wrong stride for
// narrow elements once the deref base materializes).
let dt: *tinfo = n.type_: *tinfo;
if (dt != nil) { esz = dt.size: i32; signed_elem = typeissigned(dt); elemisstr = typeisstr(dt); elemisslice = typeisslice(dt); float_elem = typeisfloat(dt); f32_elem = typeisf32(dt); };
elem_isarray = tinfoisarray(dt);
} else { if (base.kind == nkind.N_INDEX) {
// #60: chained `names[i][k]` — n.type_ is the checker-
// stamped outer element tinfo (indexresult over the inner
// index's value type). cstage reads base->type->sub->size
// for esz (cmd/w6c/cgen.c:2070-2071). Drops the
// indexvaluetnode walk.
// indexvaluetnode walk. Kept as its OWN arm (it does not set
// elemisstr/elemisslice) so the chained-index byte-id is
// preserved verbatim; the catch-all below would re-stamp it.
let et: *tinfo = n.type_: *tinfo;
if (et != nil) {
esz = et.size: i32;
@@ -23593,7 +23578,29 @@ fn cgindex(c: *cgen, n: *node) void = {
f32_elem = typeisf32(et);
elem_isarray = tinfoisarray(et);
};
};};};
} else {
// Every OTHER non-ident base — N_DOT (`s.arr[i]`), N_UN-deref
// (`(*p)[i]`, #61), N_CAST (`(e:*[N]T)[i]`, #19old), N_CALL
// (`f()[i]`), N_SLICE (`s[a:b][i]`), N_TYPEASSERT
// (`(v as *[N]T)[i]`), … — derives esz/stride/load-width/
// signedness from the checker-stamped index-RESULT tinfo
// n.type_ (the element T: u32->4). cstage reads it UNIFORMLY
// via idx_eff(base->type)->sub->size with NO node-kind gate
// (cmd/w6c/cgen.c:3517-18); wwstage's prior node-kind whitelist
// (DOT/UN/CAST only) silently left N_CALL/N_SLICE/N_TYPEASSERT
// (and any future base kind) at the 8B default — wrong stride
// AND full-word MOVQ load for narrow elements. One stamped-tinfo
// read mirrors cstage and closes the class by construction
// (#19old + its CALL/SLICE/TYPEASSERT residuals). esz via
// dt.size (type table, rule-13). Signedness from the same tinfo
// so a signed-narrow element sign-extends on load (loadopsz keys
// on (signed,sz); cstage's fldloadop reads it from the element
// type — align ww up, #255). The base ADDRESS materialization
// below is unchanged; only the stride/load-width was wrong.
let dt: *tinfo = n.type_: *tinfo;
if (dt != nil) { esz = dt.size: i32; signed_elem = typeissigned(dt); elemisstr = typeisstr(dt); elemisslice = typeisslice(dt); float_elem = typeisfloat(dt); f32_elem = typeisf32(dt); };
elem_isarray = tinfoisarray(dt);
};};
};
// Tagged-union element: load slot words into (AX=tag, DX=val0,
// CX=val1) matching the tagged-return ABI so call-arg / let /