cgen: store the full register into padded scratch for a 3/5/6/7-byte aggregate tail (#10)
The in-cap aggregate-receive materialise emitted a single narrow tail MOV that fell to MOVB for a 3/5/6/7-byte sub-8 tail, storing one byte while the scratch->dest copy read the full tail from uninitialised scratch — silently dropping members at the C2c whole-element arm (arr[i]=mk()) and loud-stopping at the #11 field arm. The scratch slot is ceil-8 padded (local_alloc/localadd round to 8) and the copy reads only tsz bytes, so flipping the tail default MOVB->MOVQ stores the full register harmlessly into the slot's own pad (in-bounds for in-cap <=24B); 1/2/4-byte tails stay byte-identical. Both stages symmetric. Removes the now-redundant #11 sub-8-tail loud-stop (keeps the float #165 and over-cap #234 loud-stops). The same narrow-tail materialise recurs at 6 other cstage sites (task #14). Retires the obsolete idx_dot_aggret_subtail_loud //ww:error fixture (both stages now compile the case) and converts it to a positive cstage run-test; the struct-field shape is byte-id-divergent only via the pre-existing #9 frame-size bug, so the value pin uses array-field shapes. Value-asserting, reddens under each stage's independent revert.
This commit is contained in:
@@ -9127,9 +9127,10 @@ fn cgassign(c: *cgen, n: *syntax.node) void = {
|
||||
wi += 1;
|
||||
};
|
||||
if (tail > 0) {
|
||||
let top: str = "MOVB";
|
||||
if (tail == 4) { top = "MOVL"; }
|
||||
else { if (tail == 2) { top = "MOVW"; }; };
|
||||
let top: str = "MOVQ";
|
||||
if (tail == 1) { top = "MOVB"; }
|
||||
else { if (tail == 2) { top = "MOVW"; }
|
||||
else { if (tail == 4) { top = "MOVL"; }; }; };
|
||||
let treg: str = "AX";
|
||||
if (full == 1) { treg = "DX"; }
|
||||
else { if (full == 2) { treg = "CX"; }; };
|
||||
@@ -10054,27 +10055,15 @@ fn cgassign(c: *cgen, n: *syntax.node) void = {
|
||||
// would smash the next field (g at +12).
|
||||
let tsz: i32 = fi.fsz;
|
||||
if (tsz > 8) {
|
||||
// A 3/5/6/7-byte sub-8 tail cannot
|
||||
// be materialised by the single narrow
|
||||
// MOV below — it stores ONE byte while
|
||||
// the copy reads the full tail, dropping
|
||||
// the rest from uninitialised scratch (a
|
||||
// silent both-stage drop; empirically a
|
||||
// 14B 7xi16 field loses f/g). The whole-
|
||||
// element C2c sibling (:9100) shares this
|
||||
// single-tail gap; until a general
|
||||
// register->scratch tail (shift cascade)
|
||||
// lands across BOTH sites, LOUD-STOP
|
||||
// rather than silently drop — rule 7, the
|
||||
// #11 sibling of the over-cap/float stops
|
||||
// above. Tails 0/1/2/4 are exact and flow
|
||||
// through. Mirrors cstage cgen.c.
|
||||
let tl11: i32 = tsz % 8;
|
||||
if (tl11 == 3 || tl11 == 5 || tl11 == 6 || tl11 == 7) {
|
||||
let m11t: str = "#11: aggregate field receive arr[i].f=mk() with a 3/5/6/7-byte sub-8 tail unwired (materialise single-MOV under-stores; C2c-shared)\n";
|
||||
os.write(2, m11t.ptr, m11t.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
// The sub-8 tail materialise stores the FULL
|
||||
// 8-byte register (MOVQ) into a ceil-8-padded
|
||||
// scratch (tagscradd -> localadd rounds to 8):
|
||||
// the over-stored high bytes land in the pad and
|
||||
// the scratch->dest copy reads only tsz bytes, so
|
||||
// every in-cap tail (incl. 3/5/6/7) is exact
|
||||
// without an immediate-shift cascade (w6a has no
|
||||
// SHRQ $imm). Shares the C2c whole-element
|
||||
// materialise (:9100). #10; mirrors cstage cgen.c.
|
||||
let scr11: i32 = tagscradd(c, tsz);
|
||||
cgexpr(c, n.rhs);
|
||||
// AX/DX/CX -> scratch (C2c materialise)
|
||||
@@ -10093,9 +10082,10 @@ fn cgassign(c: *cgen, n: *syntax.node) void = {
|
||||
wi11 += 1;
|
||||
};
|
||||
if (tail11 > 0) {
|
||||
let top11: str = "MOVB";
|
||||
if (tail11 == 4) { top11 = "MOVL"; }
|
||||
else { if (tail11 == 2) { top11 = "MOVW"; }; };
|
||||
let top11: str = "MOVQ";
|
||||
if (tail11 == 1) { top11 = "MOVB"; }
|
||||
else { if (tail11 == 2) { top11 = "MOVW"; }
|
||||
else { if (tail11 == 4) { top11 = "MOVL"; }; }; };
|
||||
let treg11: str = "AX";
|
||||
if (full11 == 1) { treg11 = "DX"; }
|
||||
else { if (full11 == 2) { treg11 = "CX"; }; };
|
||||
|
||||
Reference in New Issue
Block a user