diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index ec5841fe..d3d4055d 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -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- diff --git a/selfhost/cmd/wcc/cgenexpr.ww b/selfhost/cmd/wcc/cgenexpr.ww index bbc6655b..ee06fcf1 100644 --- a/selfhost/cmd/wcc/cgenexpr.ww +++ b/selfhost/cmd/wcc/cgenexpr.ww @@ -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- diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 2a60f8d9..bc1837bc 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -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- diff --git a/test/wcc/949_dotbase_addr_slice_run.c b/test/wcc/949_dotbase_addr_slice_run.c index a5edb34b..b6d19918 100644 --- a/test/wcc/949_dotbase_addr_slice_run.c +++ b/test/wcc/949_dotbase_addr_slice_run.c @@ -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