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:
@@ -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-
|
||||
|
||||
@@ -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-
|
||||
|
||||
@@ -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-
|
||||
|
||||
@@ -56,14 +56,14 @@
|
||||
*
|
||||
* Run-only rows (byteid=0): the chained VALUE-container arm (`o.i.m`,
|
||||
* inner is a value nested struct). These exercise the same fixed helper
|
||||
* and run correctly, but a value nested-struct instance trips THREE
|
||||
* and run correctly, but a value nested-struct instance trips two
|
||||
* orthogonal pre-existing cs!=ww divergences unrelated to #253 — bare-
|
||||
* let zero-init policy (wwstage emits an extra `MOVQ $0,off(BP)`),
|
||||
* global DATAW byte count (wwstage over-emits), and the i32 element-
|
||||
* LOAD opcode in the index fallback (cstage MOVSXD vs wwstage MOVL) —
|
||||
* so the rule-10 byte-id gate can't apply here until those are fixed
|
||||
* (filed: wwstage value-nested-struct emission divergence). Run
|
||||
* correctness alone proves the #253 segfault is gone for this cell.
|
||||
* let zero-init policy (wwstage emits an extra `MOVQ $0,off(BP)`) and
|
||||
* global DATAW byte count (wwstage over-emits) — so the rule-10 byte-id
|
||||
* gate can't apply here until those are fixed (#254). (A third, the
|
||||
* signed-narrow index-fallback element-LOAD opcode, was #255 — now
|
||||
* fixed; these u8 rows never hit it anyway.) Run correctness alone
|
||||
* proves the #253 segfault is gone for this cell.
|
||||
* - chain_val_rd o.i.m[1] read (i value nested) → 66
|
||||
* - chain_val_addr &o.i.m[2] then *q read → 55
|
||||
* - chain_val_slice o.i.m[1:4], s[0] → 66
|
||||
@@ -304,6 +304,40 @@ static const struct row rows[] = {
|
||||
" let x: e; x.m[1] = 66u8;\n"
|
||||
" return x.m[1]: i32;\n"
|
||||
"};\n", 66, 1 },
|
||||
/* #255 signed-narrow N_DOT-base index read. Reading x.o[k] of a
|
||||
* [N]i32/i16/i8 struct field via the N_DOT-base index fallback must
|
||||
* sign-extend the narrow element (loadopsz keys on (signed,sz) →
|
||||
* MOVSXD/MOVSWQ/MOVSBQ); pre-fix wwstage left signedness unset and
|
||||
* emitted MOVL/MOVZ* (zero-extend) where cstage emits MOVS* — a
|
||||
* byte-id divergence bootstrap never indexes, so these rows ARE the
|
||||
* net. Negative round-trip (-5 → exit 251 = 256-5). */
|
||||
{ "nload_i32",
|
||||
"package main;\n"
|
||||
"type e = struct { o: [4]i32 };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let x: e;\n"
|
||||
" x.o[2] = -5;\n"
|
||||
" let v: i32 = x.o[2];\n"
|
||||
" return v;\n"
|
||||
"};\n", 251, 1 },
|
||||
{ "nload_i16",
|
||||
"package main;\n"
|
||||
"type e = struct { o: [4]i16 };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let x: e;\n"
|
||||
" x.o[2] = -5i16;\n"
|
||||
" let v: i16 = x.o[2];\n"
|
||||
" return v: i32;\n"
|
||||
"};\n", 251, 1 },
|
||||
{ "nload_i8",
|
||||
"package main;\n"
|
||||
"type e = struct { o: [4]i8 };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let x: e;\n"
|
||||
" x.o[2] = -5i8;\n"
|
||||
" let v: i8 = x.o[2];\n"
|
||||
" return v: i32;\n"
|
||||
"};\n", 251, 1 },
|
||||
/* #253 chained VALUE-container arm (o.i.m). Run-only (byteid=0):
|
||||
* a value nested-struct instance trips orthogonal pre-existing
|
||||
* cs!=ww emission divergences (see header). The fixed helper runs
|
||||
|
||||
Reference in New Issue
Block a user