wwstage: sign-extend signed-narrow struct-array-field element load via N_DOT base (#255)

The cgindex N_DOT-base arm set esz from the checker-stamped element
tinfo but skipped signedness, so loadopsz saw signed_elem=false and
emitted MOVL/MOVZ* (zero-extend) where cstage's fldloadop reads
signedness from the element type and emits MOVSXD/MOVSWQ/MOVSBQ. A
negative i8/i16/i32 read of `x.o[k]` (struct `[N]T` field) round-tripped
with the wrong upper bits — silent cs!=ww, byte-id-blind since bootstrap
never indexes signed-narrow struct array-fields.

Mirror the sibling N_INDEX-base arm: signed_elem = typeissigned(dt).
loadopsz already keys on (signed,sz), so this closes all three narrow
widths at once. Pure wwstage-up; cstage unchanged.

949 gains nload_i32/i16/i8 negative-read rows (run + cs==ww byte-id).
combined.ww regen'd for w6c + wwdump (the cgen embedders).
This commit is contained in:
2026-06-02 04:07:42 +09:00
parent 585ec50676
commit d8aaa54b41
4 changed files with 56 additions and 16 deletions

View File

@@ -20499,10 +20499,12 @@ fn cgindex(c: *cgen, n: *node) void = {
// 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). esz-only — N_DOT-base signedness
// stays unset, as before.
// 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).
let dt: *tinfo = n.type_: *tinfo;
if (dt != nil) { esz = dt.size: i32; elemisstr = typeisstr(dt); elemisslice = typeisslice(dt); float_elem = typeisfloat(dt); f32_elem = typeisf32(dt); };
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-

View File

@@ -1201,10 +1201,12 @@ fn cgindex(c: *cgen, n: *node) void = {
// 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). esz-only — N_DOT-base signedness
// stays unset, as before.
// 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).
let dt: *tinfo = n.type_: *tinfo;
if (dt != nil) { esz = dt.size: i32; elemisstr = typeisstr(dt); elemisslice = typeisslice(dt); float_elem = typeisfloat(dt); f32_elem = typeisf32(dt); };
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-

View File

@@ -20499,10 +20499,12 @@ fn cgindex(c: *cgen, n: *node) void = {
// 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). esz-only — N_DOT-base signedness
// stays unset, as before.
// 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).
let dt: *tinfo = n.type_: *tinfo;
if (dt != nil) { esz = dt.size: i32; elemisstr = typeisstr(dt); elemisslice = typeisslice(dt); float_elem = typeisfloat(dt); f32_elem = typeisf32(dt); };
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-