wcc/ww: paramfieldsize sizes slice/tuple fields through the type table
for-range destructure of an element with a slice- or tuple-typed field strode by the default 8 (paramfieldsize had no N_TSLICE/N_TTUPLE arms), silently reading the wrong words (review finding #43; live repro cs=42 vs ww=8). Add the arms routed through tinfo per rule 13. 989_tupfieldsize_run pins cs==ww (red 1/2 pre-fix); rows assert convergence, not absolutes — cstage's own single-word destructure-load bug is filed as task #40. The N_TARRAY arm is deferred (task #39, rule-7 comment at the fall-through).
This commit is contained in:
@@ -38409,13 +38409,36 @@ fn cgmlet(c: *cgen, n: *node) void = {
|
||||
|
||||
// paramfieldsize — raw byte size of a tuple-field type. Mirrors the
|
||||
// `tp->type->size` read in C cgen N_FORRANGE: 1 for i8/u8/bool, 4 for
|
||||
// i32/u32, 8 for i64/u64/*T/fn/slice-elt, 16 for str, default 8.
|
||||
// i32/u32, 8 for i64/u64/*T/fn, 24 for str/slice (str IS []u8, the slice
|
||||
// header SSoT), tuple → sum of its 8B-floored element slots, default 8.
|
||||
fn paramfieldsize(t: *node) i32 = {
|
||||
if (t == nil) { return 8; };
|
||||
let k: nkind = t.kind;
|
||||
if (k == nkind.N_TPTR) { return 8; };
|
||||
if (k == nkind.N_TFN) { return 8; };
|
||||
if (k == nkind.N_TCHAN) { return 8; };
|
||||
// #43 (F7-c4): a slice tuple-field carries the 24B header (ptr+len+
|
||||
// cap), not the 8B scalar default. Without this arm the for-range
|
||||
// destructure over `[N]([]T, U)` strode the tuple at 8 not 24 and
|
||||
// read field-2 at the wrong offset (cs=42/ww=8, the cat-A repro).
|
||||
// tyslicesize() is the slice-header SSoT (rule-13); cstage reads the
|
||||
// same width via tp->type->size (cmd/w6c/cgen.c N_FORRANGE).
|
||||
if (k == nkind.N_TSLICE) { return tyslicesize(): i32; };
|
||||
// #43 (F7-c4): a nested tuple field sizes as the sum of its element
|
||||
// SLOTS — each element floored UP to one 8B eightbyte (str/slice keep
|
||||
// their 24B header), per the tuple-slot ruling and tupeslot's
|
||||
// roundup8. Recurse structurally so a tuple-of-tuple lands the same
|
||||
// stride cstage's tp->type->size computes.
|
||||
if (k == nkind.N_TTUPLE) {
|
||||
let total: i32 = 0;
|
||||
let dp: *node = t.list;
|
||||
for (dp != nil) {
|
||||
let esz: i32 = paramfieldsize(dp.lhs);
|
||||
total = total + (esz + 7) / 8 * 8;
|
||||
dp = dp.next;
|
||||
};
|
||||
return total;
|
||||
};
|
||||
if (k == nkind.N_TNAME) {
|
||||
let nm: str = t.str;
|
||||
if (streq(nm, "str")) { return primtypesize("str"): i32; };
|
||||
@@ -38427,6 +38450,11 @@ fn paramfieldsize(t: *node) i32 = {
|
||||
let ps: i32 = primsize(nm);
|
||||
if (ps > 0) { return ps; };
|
||||
};
|
||||
// rule-7: N_TARRAY (an array-typed tuple/struct field) is intentionally
|
||||
// not sized here — deferred to task #39. No F7 repro or spec-§5-c4
|
||||
// member feeds it, so it stays on the 8B default (mis-sized, latent)
|
||||
// until a consumer surfaces; the divergence is pointed at the task, not
|
||||
// silent.
|
||||
return 8;
|
||||
};
|
||||
|
||||
|
||||
@@ -3646,13 +3646,36 @@ fn cgmlet(c: *cgen, n: *node) void = {
|
||||
|
||||
// paramfieldsize — raw byte size of a tuple-field type. Mirrors the
|
||||
// `tp->type->size` read in C cgen N_FORRANGE: 1 for i8/u8/bool, 4 for
|
||||
// i32/u32, 8 for i64/u64/*T/fn/slice-elt, 16 for str, default 8.
|
||||
// i32/u32, 8 for i64/u64/*T/fn, 24 for str/slice (str IS []u8, the slice
|
||||
// header SSoT), tuple → sum of its 8B-floored element slots, default 8.
|
||||
fn paramfieldsize(t: *node) i32 = {
|
||||
if (t == nil) { return 8; };
|
||||
let k: nkind = t.kind;
|
||||
if (k == nkind.N_TPTR) { return 8; };
|
||||
if (k == nkind.N_TFN) { return 8; };
|
||||
if (k == nkind.N_TCHAN) { return 8; };
|
||||
// #43 (F7-c4): a slice tuple-field carries the 24B header (ptr+len+
|
||||
// cap), not the 8B scalar default. Without this arm the for-range
|
||||
// destructure over `[N]([]T, U)` strode the tuple at 8 not 24 and
|
||||
// read field-2 at the wrong offset (cs=42/ww=8, the cat-A repro).
|
||||
// tyslicesize() is the slice-header SSoT (rule-13); cstage reads the
|
||||
// same width via tp->type->size (cmd/w6c/cgen.c N_FORRANGE).
|
||||
if (k == nkind.N_TSLICE) { return tyslicesize(): i32; };
|
||||
// #43 (F7-c4): a nested tuple field sizes as the sum of its element
|
||||
// SLOTS — each element floored UP to one 8B eightbyte (str/slice keep
|
||||
// their 24B header), per the tuple-slot ruling and tupeslot's
|
||||
// roundup8. Recurse structurally so a tuple-of-tuple lands the same
|
||||
// stride cstage's tp->type->size computes.
|
||||
if (k == nkind.N_TTUPLE) {
|
||||
let total: i32 = 0;
|
||||
let dp: *node = t.list;
|
||||
for (dp != nil) {
|
||||
let esz: i32 = paramfieldsize(dp.lhs);
|
||||
total = total + (esz + 7) / 8 * 8;
|
||||
dp = dp.next;
|
||||
};
|
||||
return total;
|
||||
};
|
||||
if (k == nkind.N_TNAME) {
|
||||
let nm: str = t.str;
|
||||
if (streq(nm, "str")) { return primtypesize("str"): i32; };
|
||||
@@ -3664,6 +3687,11 @@ fn paramfieldsize(t: *node) i32 = {
|
||||
let ps: i32 = primsize(nm);
|
||||
if (ps > 0) { return ps; };
|
||||
};
|
||||
// rule-7: N_TARRAY (an array-typed tuple/struct field) is intentionally
|
||||
// not sized here — deferred to task #39. No F7 repro or spec-§5-c4
|
||||
// member feeds it, so it stays on the 8B default (mis-sized, latent)
|
||||
// until a consumer surfaces; the divergence is pointed at the task, not
|
||||
// silent.
|
||||
return 8;
|
||||
};
|
||||
|
||||
|
||||
@@ -38409,13 +38409,36 @@ fn cgmlet(c: *cgen, n: *node) void = {
|
||||
|
||||
// paramfieldsize — raw byte size of a tuple-field type. Mirrors the
|
||||
// `tp->type->size` read in C cgen N_FORRANGE: 1 for i8/u8/bool, 4 for
|
||||
// i32/u32, 8 for i64/u64/*T/fn/slice-elt, 16 for str, default 8.
|
||||
// i32/u32, 8 for i64/u64/*T/fn, 24 for str/slice (str IS []u8, the slice
|
||||
// header SSoT), tuple → sum of its 8B-floored element slots, default 8.
|
||||
fn paramfieldsize(t: *node) i32 = {
|
||||
if (t == nil) { return 8; };
|
||||
let k: nkind = t.kind;
|
||||
if (k == nkind.N_TPTR) { return 8; };
|
||||
if (k == nkind.N_TFN) { return 8; };
|
||||
if (k == nkind.N_TCHAN) { return 8; };
|
||||
// #43 (F7-c4): a slice tuple-field carries the 24B header (ptr+len+
|
||||
// cap), not the 8B scalar default. Without this arm the for-range
|
||||
// destructure over `[N]([]T, U)` strode the tuple at 8 not 24 and
|
||||
// read field-2 at the wrong offset (cs=42/ww=8, the cat-A repro).
|
||||
// tyslicesize() is the slice-header SSoT (rule-13); cstage reads the
|
||||
// same width via tp->type->size (cmd/w6c/cgen.c N_FORRANGE).
|
||||
if (k == nkind.N_TSLICE) { return tyslicesize(): i32; };
|
||||
// #43 (F7-c4): a nested tuple field sizes as the sum of its element
|
||||
// SLOTS — each element floored UP to one 8B eightbyte (str/slice keep
|
||||
// their 24B header), per the tuple-slot ruling and tupeslot's
|
||||
// roundup8. Recurse structurally so a tuple-of-tuple lands the same
|
||||
// stride cstage's tp->type->size computes.
|
||||
if (k == nkind.N_TTUPLE) {
|
||||
let total: i32 = 0;
|
||||
let dp: *node = t.list;
|
||||
for (dp != nil) {
|
||||
let esz: i32 = paramfieldsize(dp.lhs);
|
||||
total = total + (esz + 7) / 8 * 8;
|
||||
dp = dp.next;
|
||||
};
|
||||
return total;
|
||||
};
|
||||
if (k == nkind.N_TNAME) {
|
||||
let nm: str = t.str;
|
||||
if (streq(nm, "str")) { return primtypesize("str"): i32; };
|
||||
@@ -38427,6 +38450,11 @@ fn paramfieldsize(t: *node) i32 = {
|
||||
let ps: i32 = primsize(nm);
|
||||
if (ps > 0) { return ps; };
|
||||
};
|
||||
// rule-7: N_TARRAY (an array-typed tuple/struct field) is intentionally
|
||||
// not sized here — deferred to task #39. No F7 repro or spec-§5-c4
|
||||
// member feeds it, so it stays on the 8B default (mis-sized, latent)
|
||||
// until a consumer surfaces; the divergence is pointed at the task, not
|
||||
// silent.
|
||||
return 8;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user