wcc: converge DOT-recv, structlit-fill, and bare-let zero-init onto structabisize

Three wwstage cgen sites still used the unrounded structnaturalsize where cstage
rounds via lu->size (check.c:760), pre-existing gate-blind cs!=ww latents
flagged in #169's reviewer notes: cgenexpr DOT register-RECV for obj.f = mk()
(~5175/5393/5628/6164); cgstructlitfill's TK_ELLIPSIS zero-fill branch
(cgenutil); and cglet bare 'let z: T;' zero-init of a maxalign<8 struct
(cgenstmt). Each produced MOVQ-vs-MOVL or wider-write divergence vs cstage on
the trailing word of a sub-eightbyte tail.

Converge all three onto the maxalign-rounded structabisize the #169 work
established at the register-ABI sites (cite cstage cgen.c:7720 RECV twin +
cgen.c:2085 cg_structlit_fill). cgstructlitfill's signature drops the external
totsize parameter in favor of one internal source; the field-walk path is
untouched, only the ELLIPSIS zero-fill uses the ABI size. cglet's slot
allocation stays on the frame size; only the zero-fill extent uses ABI.

Gate-blind (the bootstrap exercises none of these shapes); covered by 5 new
rows in probe 698 with cs==ww .s byte-cmp and a pre-fix-rebuild proving the
exact MOVQ-vs-MOVL discrimination. 990-997 byte-id hold.
This commit is contained in:
2026-05-28 13:02:47 +09:00
parent d92c199d25
commit 39f9267bc9
6 changed files with 210 additions and 117 deletions

View File

@@ -596,7 +596,7 @@ fn cgreturn(c: *cgen, n: *node) void = {
// the dest pointer IS the struct base.
cgstructlitfill(c, sret_si, rhs,
1, sretargoff, emptys,
0, scs);
0);
};
} else {
let rl: *local = localfindnode(c, rhs.str);
@@ -1469,26 +1469,40 @@ fn cglet(c: *cgen, n: *node) void = {
if (n.lhs != nil) {
if (n.lhs.kind == nkind.N_TARRAY) { isarr = true; };
};
// Zero-fill extent. cstage sizes the run on `lu->size`
// (the maxalign-rounded ABI size, check.c:760); wwstage's
// `sz` from letslotsize is slot-padded (round-to-8), so a
// struct with maxalign<8 and a sub-8 tail would over-zero
// MOVQ where cstage emits MOVL/MOVB. Read structabisize
// for a struct-typed let to converge; slot allocation
// stays on `sz` (frame uses slot-padded slots).
let zsz: i32 = sz;
if (n.lhs != nil) {
if (n.lhs.kind == nkind.N_TNAME) {
let szi: *structinfo = structlookupchain(c, n.lhs);
if (szi != nil) { zsz = structabisize(szi); };
};
};
if (typeis8byteprimitive(c, n.lhs)) {
emitline("\tMOVQ\t$0, ");
emitoff(off: i64);
emitline("(BP)\n");
} else { if (!isarr) { if (sz > 8) {
} else { if (!isarr) { if (zsz > 8) {
emitline("\tXORQ\tAX, AX\n");
let zi: i32 = 0;
for (zi + 8 <= sz) {
for (zi + 8 <= zsz) {
emitline("\tMOVQ\tAX, ");
emitoff((off + zi): i64);
emitline("(BP)\n");
zi += 8;
};
for (zi + 4 <= sz) {
for (zi + 4 <= zsz) {
emitline("\tMOVL\tAX, ");
emitoff((off + zi): i64);
emitline("(BP)\n");
zi += 4;
};
for (zi < sz) {
for (zi < zsz) {
emitline("\tMOVB\tAX, ");
emitoff((off + zi): i64);
emitline("(BP)\n");