wcc: struct ABI size maxalign-rounded via structabisize (#169)
wwstage struct-return RECV and RETURN used unrounded / round-to-8 sizes where
cstage uses the maxalign-rounded lu->size / rt->size, so a struct with maxalign
8 and a sub-8 tail (e.g. struct{i64,i32}) — or a maxalign<8 struct on the
return path — unpacked with a different trailing-word width (MOVL vs MOVQ)
between stages. Value-correct either way, but a cs!=ww asm divergence.
Add a dedicated structabisize = round(natural, maxalign) used only at the two
register-ABI sites. structnaturalsize stays unrounded: cstage's >24B sret and
memory-move path (cgen.c:8150, Task #33) genuinely uses the unrounded natural
size, so the two are different sizes — rounding the shared metric breaks 995.
maxalign derives from each field's tinfo.align (mirrors cstage check.c:708),
not an fsz ladder (a ladder over-rounds composite [N]u8 fields).
Gate-blind (no bootstrap struct hits the maxalign-8+tail shape) — the
discriminator is the cs==ww .s byte-cmp; covered by probe 698.
This commit is contained in:
@@ -1168,18 +1168,19 @@ fn nodeprimwidth(c: *cgen, n: *node) i32 = {
|
||||
// ---- type-driven slot sizing ----------------------------------------
|
||||
|
||||
// structnaturalsize — type-natural size of `si`, i.e. max(foff +
|
||||
// fsz) across declared fields. Mirrors cstage's `lu->size` for a
|
||||
// TY_STRUCT (rounded only to the struct's maxalign).
|
||||
// fsz) across declared fields, UNROUNDED. This is the memory-copy
|
||||
// extent: cstage copies exactly these bytes for the >24B sret
|
||||
// write-through (cgen.c:8150 `int sz; for fields end=foff+fsz`) and
|
||||
// struct-to-struct moves, so a trailing narrow field (bool@32 in a
|
||||
// 33B struct padded to 40) keeps its MOVB tail rather than widening
|
||||
// to a slot-overrunning MOVQ. #33 fixed cstage to use this; ww
|
||||
// mirrors it. The ≤24B register RECV/RETURN ABI wants a DIFFERENT
|
||||
// number — see structabisize.
|
||||
//
|
||||
// NOTE: si.totsize is mis-named — it's actually the *slot-padded*
|
||||
// size (rounded up to 8 for stack-slot use; see registerstruct's
|
||||
// tail `if ((off & 7) != 0) ...`). Frame allocation, [N]foo stride,
|
||||
// and similar consumers want that slot-padded number. The
|
||||
// receive-side ABI (#5) and any future "TYPE size, not slot size"
|
||||
// query wants the natural size. Until si.totsize is split into
|
||||
// si.naturalsize + si.slotsize (tracked as the wwstage-sizing
|
||||
// follow-up task), recover the type-natural size from the field
|
||||
// chain here.
|
||||
// NOTE: si.totsize is yet a THIRD metric — the slot-padded size
|
||||
// (rounded up to 8 for stack-slot use; see registerstruct's tail
|
||||
// `if ((off & 7) != 0) ...`). Frame allocation and [N]foo stride
|
||||
// want that slot number.
|
||||
fn structnaturalsize(si: *structinfo) i32 = {
|
||||
if (si == nil) { return 0; };
|
||||
let n: i32 = 0;
|
||||
@@ -1192,6 +1193,45 @@ fn structnaturalsize(si: *structinfo) i32 = {
|
||||
return n;
|
||||
};
|
||||
|
||||
// structabisize — the ≤24B register-return ABI size of `si`: the
|
||||
// natural extent rounded up to the struct's maxalign. SSoT-equal to
|
||||
// cstage's `lu->size` (check.c:760 `(off+maxalign-1)&~(maxalign-1)`).
|
||||
// Distinct from structnaturalsize because the register RECV/RETURN
|
||||
// ABI packs the value into AX/DX/CX at 8-byte granularity: cstage
|
||||
// writes/reads the tail at maxalign width (cgen.c:7720 `sz=lu->size`,
|
||||
// :8230 `sz=rt->size`), so a maxalign==8 struct with a sub-8 tail
|
||||
// (struct{i64,i32}, natural 12) round-trips as MOVQ+MOVQ (16), not
|
||||
// MOVQ+MOVL (12). Used ONLY at those register-ABI sites; memory
|
||||
// copies (sret >24B, struct ident-copy) and field-offset math stay
|
||||
// on structnaturalsize. #169.
|
||||
//
|
||||
// maxalign comes from each field's TRUE alignment (tinfo.align), not
|
||||
// the slot-padded fsz: a [N]u8 / sub-struct field has slot ≥8 but
|
||||
// align 1, so an fsz ladder would over-round. Mirrors cstage's
|
||||
// maxalign = max(ft->align) (check.c:708).
|
||||
fn structabisize(si: *structinfo) i32 = {
|
||||
if (si == nil) { return 0; };
|
||||
let n: i32 = 0;
|
||||
let maxaln: i32 = 1;
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
let end: i32 = fi.foff + fi.fsz;
|
||||
if (end > n) { n = end; };
|
||||
if (fi.tnode != nil) {
|
||||
let ti: *tinfo = fi.tnode.type_: *tinfo;
|
||||
for (ti != nil && ti.kind == tykind.TY_NAMED) {
|
||||
ti = ti.under;
|
||||
};
|
||||
if (ti != nil) {
|
||||
let aln: i32 = ti.align: i32;
|
||||
if (aln > maxaln) { maxaln = aln; };
|
||||
};
|
||||
};
|
||||
fi = fi.finext;
|
||||
};
|
||||
return (n + maxaln - 1) & ~(maxaln - 1);
|
||||
};
|
||||
|
||||
// sretretsize — if `t` ultimately denotes a plain TY_STRUCT > 24B,
|
||||
// return its natural size; else 0. Tagged unions, tuples, str,
|
||||
// slices, scalars route through their existing register-return ABIs
|
||||
|
||||
Reference in New Issue
Block a user