w6c: chained-DOT struct field-copy tail uses 4/2/1 ladder, not over-MOVQ (#9)

The depth->=2 chained `t.m.l = s` struct-ident field copy selected its tail with (tail==4)?MOVL:(tail==1?MOVB:MOVQ), so every tail in {2,3,5,6,7} fell to an 8-byte MOVQ that over-wrote past the destination field — clobbering a @packed neighbour (t.m.l=s wrote s's slop over t.m.z: cstage exit 0 vs wwstage's correct 222) and diverging cs!=ww gate-blind. Replace with a descending 4/2/1 MOVL/MOVW/MOVB ladder comparing to ssz (the field's natural type-table size), aligning cstage UP to wwstage's sized ladder + cg_aggcopy. New table-driven gate 949_chained_dot_struct_copy_run (7 sizes x runtime-exit oracle on both drivers + cs==ww byte-id; negative-control proven).
This commit is contained in:
2026-06-19 13:41:44 +09:00
parent 1466f50a09
commit 801d105c0f
3 changed files with 386 additions and 7 deletions

View File

@@ -6365,19 +6365,57 @@ cgexpr(Cg *c, Node *n, Local *locals)
amem(D_BP, base_disp + total_off + k));
k += 8;
}
if (k < ssz) {
int tail = ssz - k;
int lop = (tail == 4) ? A_MOVL
: (tail == 1 ? A_MOVB : A_MOVQ);
ins2(c, lop,
/* Descending 4/2/1 sized tail, not a
* 4/1-then-MOVQ: ssz is the field's
* natural (non-slot-padded) struct size,
* so a tail of {2,3,5,6,7} packs at its
* own stride (struct{u16,[8]u8}=10 has
* tail 2). The old `tail==1?MOVB:MOVQ`
* fell every other tail to an 8B MOVQ that
* over-writes past the destination FIELD —
* a @packed neighbour gets clobbered (the
* depth-≥2 chained `t.m.l = s` wrote s's
* slop over t.m.z, exit 0 not 222) and
* cstage diverged from wwstage's sized
* MOVW (gate-blind cs≠ww). Aligns cstage
* UP to the wwstage 8/4/2/1 ladder
* (cgenexpr.ww:10083) and cg_aggcopy.
* #107 class. */
if (k + 4 <= ssz) {
ins2(c, A_MOVL,
amem(D_BP, soff + k),
areg(D_AX));
if (via_cx)
ins2(c, lop, areg(D_AX),
ins2(c, A_MOVL, areg(D_AX),
amem(D_CX, total_off + k));
else
ins2(c, lop, areg(D_AX),
ins2(c, A_MOVL, areg(D_AX),
amem(D_BP, base_disp + total_off + k));
k += 4;
}
if (k + 2 <= ssz) {
ins2(c, A_MOVW,
amem(D_BP, soff + k),
areg(D_AX));
if (via_cx)
ins2(c, A_MOVW, areg(D_AX),
amem(D_CX, total_off + k));
else
ins2(c, A_MOVW, areg(D_AX),
amem(D_BP, base_disp + total_off + k));
k += 2;
}
if (k + 1 <= ssz) {
ins2(c, A_MOVB,
amem(D_BP, soff + k),
areg(D_AX));
if (via_cx)
ins2(c, A_MOVB, areg(D_AX),
amem(D_CX, total_off + k));
else
ins2(c, A_MOVB, areg(D_AX),
amem(D_BP, base_disp + total_off + k));
k += 1;
}
break;
}