cgen: widen the narrow-tail aggregate-register store to full sz<=24 at every routed site (#14)
Completes the #14 close-by-construction begun by the helper extraction (e7fefa3): every <=24B aggregate register-store now routes through cg_agg_reg_store/cgaggregstore and handles all tail sizes. B/C/D/G fix a silent both-stage drop of a 3/5/6/7-byte tail (byte-id-blind: both stages dropped identically, so the gate could not see it). A/F enable a previously loud-rejected shape (a cgen backend gap, not a type rejection; harec accepts and lowers it). G (global g=f() array) routes symmetrically, dest_padded=false. The #11 arr[i].f scratch loops fold into the helper (dest_padded=true, byte-id zero-change), completing the grep-audit. Pins value-assert each eightbyte (the class is byte-id-blind) and redden under each stage's independent revert; site H's 3/5/6/7 let-receive stays a loud fatal (#22).
This commit is contained in:
@@ -8471,19 +8471,17 @@ fn cgassign(c: *cgen, n: *syntax.node) void = {
|
||||
if (fu != nil) { if (fu.kind == syntax.tykind.TY_STRUCT) {
|
||||
let ssz: i32 = fu.size: i32;
|
||||
if (ssz <= 24) {
|
||||
let tlm: i32 = ssz - (ssz / 8) * 8;
|
||||
if (tlm == 0 || tlm == 1 || tlm == 2 || tlm == 4) {
|
||||
if (!cgplaceaddr(c, lhs, "BX")) {
|
||||
let m16: str = "#16: global/chained aggregate unwrap field dest unresolved (cgplaceaddr)\n";
|
||||
os.write(2, m16.ptr, m16.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
emitline("\tPUSHQ\tBX\n");
|
||||
cgexpr(c, n.rhs);
|
||||
emitline("\tPOPQ\tBX\n");
|
||||
cgaggregstore(c, "BX", 0, ssz, false);
|
||||
return;
|
||||
// #14: choke-point now stores every in-cap tail; 3/5/6/7 no longer dropped to a lone narrow MOV.
|
||||
if (!cgplaceaddr(c, lhs, "BX")) {
|
||||
let m16: str = "#16: global/chained aggregate unwrap field dest unresolved (cgplaceaddr)\n";
|
||||
os.write(2, m16.ptr, m16.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
emitline("\tPUSHQ\tBX\n");
|
||||
cgexpr(c, n.rhs);
|
||||
emitline("\tPOPQ\tBX\n");
|
||||
cgaggregstore(c, "BX", 0, ssz, false);
|
||||
return;
|
||||
};
|
||||
}; };
|
||||
};
|
||||
@@ -10205,37 +10203,11 @@ fn cgassign(c: *cgen, n: *syntax.node) void = {
|
||||
// materialise (:9100). #10; mirrors cstage cgen.c.
|
||||
let scr11: i32 = tagscradd(c, tsz);
|
||||
cgexpr(c, n.rhs);
|
||||
// AX/DX/CX -> scratch (C2c materialise)
|
||||
let full11: i32 = tsz / 8;
|
||||
let tail11: i32 = tsz % 8;
|
||||
let wi11: i32 = 0;
|
||||
for (wi11 < full11) {
|
||||
let rn11: str = "AX";
|
||||
if (wi11 == 1) { rn11 = "DX"; }
|
||||
else { if (wi11 == 2) { rn11 = "CX"; }; };
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(rn11);
|
||||
emitline(", ");
|
||||
emitoff((scr11 + wi11 * 8): i64);
|
||||
emitline("(BP)\n");
|
||||
wi11 += 1;
|
||||
};
|
||||
if (tail11 > 0) {
|
||||
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"; }; };
|
||||
emitline("\t");
|
||||
emitline(top11);
|
||||
emitline("\t");
|
||||
emitline(treg11);
|
||||
emitline(", ");
|
||||
emitoff((scr11 + full11 * 8): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
// AX/DX/CX -> scratch: #14 folds this materialise into the choke-
|
||||
// point (dest_padded=true; the scratch IS a ceil-8 slot so the
|
||||
// tail eightbyte over-store lands in its pad — byte-identical to
|
||||
// the prior inline form).
|
||||
cgaggregstore(c, "BP", scr11, tsz, true);
|
||||
// &arr[i] -> BX (verbatim scalar arm)
|
||||
cgexpr(c, idx);
|
||||
if (esz > 1) {
|
||||
@@ -10620,16 +10592,13 @@ fn cgassign(c: *cgen, n: *syntax.node) void = {
|
||||
if (ssi != nil) {
|
||||
let ssz: i32 = structabisize(ssi);
|
||||
if (ssz <= 24) {
|
||||
let tlm: i32 = ssz - (ssz / 8) * 8;
|
||||
if (tlm == 0 || tlm == 1
|
||||
|| tlm == 2 || tlm == 4) {
|
||||
cgexpr(c, n.rhs);
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
cgaggregstore(c, "BX", fi.foff, ssz, false);
|
||||
return;
|
||||
};
|
||||
// #14: choke-point now stores every in-cap tail; 3/5/6/7 no longer dropped to a lone narrow MOV.
|
||||
cgexpr(c, n.rhs);
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
cgaggregstore(c, "BX", fi.foff, ssz, false);
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -10842,13 +10811,10 @@ fn cgassign(c: *cgen, n: *syntax.node) void = {
|
||||
if (ssi != nil) {
|
||||
let ssz: i32 = structabisize(ssi);
|
||||
if (ssz <= 24) {
|
||||
let tlm: i32 = ssz - (ssz / 8) * 8;
|
||||
if (tlm == 0 || tlm == 1
|
||||
|| tlm == 2 || tlm == 4) {
|
||||
cgexpr(c, n.rhs);
|
||||
cgaggregstore(c, "BP", lc.off + fi.foff, ssz, false);
|
||||
return;
|
||||
};
|
||||
// #14: choke-point now stores every in-cap tail; 3/5/6/7 no longer dropped to a lone narrow MOV.
|
||||
cgexpr(c, n.rhs);
|
||||
cgaggregstore(c, "BP", lc.off + fi.foff, ssz, false);
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -11106,16 +11072,13 @@ fn cgassign(c: *cgen, n: *syntax.node) void = {
|
||||
if (ssi != nil) {
|
||||
let ssz: i32 = structabisize(ssi);
|
||||
if (ssz <= 24) {
|
||||
let tlm: i32 = ssz - (ssz / 8) * 8;
|
||||
if (tlm == 0 || tlm == 1
|
||||
|| tlm == 2 || tlm == 4) {
|
||||
cgexpr(c, n.rhs);
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, bn);
|
||||
emitline("(SB), BX\n");
|
||||
cgaggregstore(c, "BX", fi.foff, ssz, false);
|
||||
return;
|
||||
};
|
||||
// #14: choke-point now stores every in-cap tail; 3/5/6/7 no longer dropped to a lone narrow MOV.
|
||||
cgexpr(c, n.rhs);
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, bn);
|
||||
emitline("(SB), BX\n");
|
||||
cgaggregstore(c, "BX", fi.foff, ssz, false);
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -11778,24 +11741,21 @@ fn cgassign(c: *cgen, n: *syntax.node) void = {
|
||||
// (cstage SSoT lu->size, check.c:760).
|
||||
let lsz: i32 = structabisize(lsi);
|
||||
if (lsz <= 24) {
|
||||
let tlm: i32 = lsz - (lsz / 8) * 8;
|
||||
if (tlm == 0 || tlm == 1
|
||||
|| tlm == 2 || tlm == 4) {
|
||||
cgexpr(c, n.rhs);
|
||||
if (viacx) {
|
||||
if (ptrroot) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(rootoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, rootname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
// #14: choke-point now stores every in-cap tail; 3/5/6/7 no longer dropped to a lone narrow MOV.
|
||||
cgexpr(c, n.rhs);
|
||||
if (viacx) {
|
||||
if (ptrroot) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(rootoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, rootname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
if (viacx) { cgaggregstore(c, "BX", totaloff, lsz, false); } else { cgaggregstore(c, "BP", rootoff + totaloff, lsz, false); };
|
||||
return;
|
||||
};
|
||||
if (viacx) { cgaggregstore(c, "BX", totaloff, lsz, false); } else { cgaggregstore(c, "BP", rootoff + totaloff, lsz, false); };
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -12240,29 +12200,11 @@ fn cgassign(c: *cgen, n: *syntax.node) void = {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), DI\n");
|
||||
let full: i32 = aggsz / 8;
|
||||
let tail: i32 = aggsz - full * 8;
|
||||
let i: i32 = 0;
|
||||
for (i < full) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(tupreg(i));
|
||||
emitline(", ");
|
||||
emitoff((i * 8): i64);
|
||||
emitline("(DI)\n");
|
||||
i += 1;
|
||||
};
|
||||
if (tail > 0) {
|
||||
let top: str = "MOVB";
|
||||
if (tail == 4) { top = "MOVL"; };
|
||||
if (tail == 2) { top = "MOVW"; };
|
||||
emitline("\t");
|
||||
emitline(top);
|
||||
emitline("\t");
|
||||
emitline(tupreg(full));
|
||||
emitline(", ");
|
||||
emitoff((full * 8): i64);
|
||||
emitline("(DI)\n");
|
||||
};
|
||||
// #14: route through the choke-point so a 3/5/6/7 tail is no
|
||||
// longer dropped to a lone MOVB (was silent both-stage). A global
|
||||
// symbol is non-padded => dest_padded=false (its scratch detour
|
||||
// copies exactly `tail` bytes).
|
||||
cgaggregstore(c, "DI", 0, aggsz, false);
|
||||
return;
|
||||
};
|
||||
};
|
||||
@@ -12534,13 +12476,10 @@ fn cgassign(c: *cgen, n: *syntax.node) void = {
|
||||
};
|
||||
let lcsz: i32 = lcnsz;
|
||||
if (lcsz <= 24) {
|
||||
let tlm: i32 = lcsz - (lcsz / 8) * 8;
|
||||
if (tlm == 0 || tlm == 1
|
||||
|| tlm == 2 || tlm == 4) {
|
||||
cgexpr(c, n.rhs);
|
||||
cgaggregstore(c, "BP", off, lcsz, false);
|
||||
return;
|
||||
};
|
||||
// #14: choke-point now stores every in-cap tail; 3/5/6/7 no longer dropped to a lone narrow MOV.
|
||||
cgexpr(c, n.rhs);
|
||||
cgaggregstore(c, "BP", off, lcsz, false);
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -12571,13 +12510,10 @@ fn cgassign(c: *cgen, n: *syntax.node) void = {
|
||||
};
|
||||
};
|
||||
if (lcsz <= 24) {
|
||||
let tlm: i32 = lcsz - (lcsz / 8) * 8;
|
||||
if (tlm == 0 || tlm == 1
|
||||
|| tlm == 2 || tlm == 4) {
|
||||
cgexpr(c, n.rhs);
|
||||
cgaggregstore(c, "BP", off, lcsz, false);
|
||||
return;
|
||||
};
|
||||
// #14: choke-point now stores every in-cap tail; 3/5/6/7 no longer dropped to a lone narrow MOV.
|
||||
cgexpr(c, n.rhs);
|
||||
cgaggregstore(c, "BP", off, lcsz, false);
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -5325,19 +5325,16 @@ fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *syntax.node,
|
||||
// silently dropped past the first qword for any
|
||||
// fsz > 8 (only AX got stored).
|
||||
//
|
||||
// Sized stores: MOVQ for full 8B chunks plus a
|
||||
// sized tail (MOVL/MOVW/MOVB) by `tail = fsz%8`.
|
||||
// Mirror of cstage cg_structlit_fill's #20 branch.
|
||||
// MOVW-for-tail==2 only fires on shapes that
|
||||
// didn't compile before, so no #13 byte-identity
|
||||
// concern.
|
||||
// The choke-point stores MOVQ for full 8B chunks
|
||||
// plus a sized tail (MOVL/MOVW/MOVB) by `tail =
|
||||
// fsz%8`. Mirror of cstage cg_structlit_fill's #20
|
||||
// branch.
|
||||
//
|
||||
// Guard `fsz <= 24 && fsz%8 ∈ {0,1,2,4}` matches
|
||||
// #4's cgreturn ABI: >24B falls through (sret
|
||||
// deferred); fsz%8 ∈ {3,5,6,7} would need shift-
|
||||
// store and is also unsupported by #4 — falls
|
||||
// through to the existing AX-only wrongness
|
||||
// (consistent, tracked as follow-up).
|
||||
// Guard `cfsz <= 24` (#14 widened from {0,1,2,4}):
|
||||
// the choke-point handles every in-cap tail incl.
|
||||
// 3/5/6/7 via its non-padded scratch detour
|
||||
// (dest_padded=false — a struct-lit field is
|
||||
// packed); >24B falls through (sret deferred).
|
||||
//
|
||||
// INVARIANT: between cgexpr(N_CALL) and the
|
||||
// AX/DX/CX stores below, NO instruction may touch
|
||||
@@ -5369,10 +5366,8 @@ fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *syntax.node,
|
||||
// MOVQ+MOVL where cstage
|
||||
// writes MOVQ+MOVQ.
|
||||
let cfsz: i32 = structabisize(csi);
|
||||
let crem: i32 = cfsz - (cfsz / 8) * 8;
|
||||
if (cfsz <= 24) {
|
||||
if (crem == 0 || crem == 1
|
||||
|| crem == 2 || crem == 4) {
|
||||
// #14: choke-point now stores every in-cap tail; 3/5/6/7 no longer dropped to a lone narrow MOV.
|
||||
cgexpr(c, fieldnode.lhs);
|
||||
if (mode == 1) {
|
||||
emitline("\tMOVQ\t");
|
||||
@@ -5390,7 +5385,6 @@ fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *syntax.node,
|
||||
cgaggregstore(c, basereg, disp + fi.foff, cfsz, false);
|
||||
callwhole = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user