w6c+wwstage: full-size aggregate copy for deref-rhs let-init (#265 fold-1)
A `let c: T = *p` (T a struct or array, >8B) copied no full aggregate: cstage dropped the init entirely (c read garbage); wwstage emitted only the scalar `MOVQ AX,off(BP)` tail (first 8 bytes). Both wrong, differently — converge BOTH stages on a size-driven slot-to-slot memcpy: cgexpr the deref operand to the source address in AX, MOVQ AX,SI, then a MOVQ run plus a sized MOVL/MOVW/MOVB tail over the #254 non-slot-padded ABI extent (lu->size / structabisize for a struct, tinfo.size for an array). Mirror arms in cgen.c N_LET and cgenstmt.ww cglet, byte-identical (rule-10). Unblocks sha256's faithful `let copy = *h`. The by-value aggregate RETURN ABI (array/struct return truncates to AX) is fold-2 (#267, deferred). 949 gains 6 full-readback rows (every member written distinct + summed, so a truncated copy fails): struct{[4]u32} 16B, struct{[8]u32} 32B via both *(&s) and *p (sha256 shape), bare [4]u32, and non-8-mult tails ([3]u32 12B → MOVL, [11]u8 11B → MOVW+MOVB). w6c+wwdump combined.ww regen (#110). 61/61 949, test-unit 240, sizelint, smoke green.
This commit is contained in:
@@ -8763,6 +8763,49 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* #265 fold-1: aggregate deref-rhs let-init
|
||||||
|
* `let c: T = *p` (T a struct or array, >8B). Neither
|
||||||
|
* the scalar tail below (one 8B word) nor a missing arm
|
||||||
|
* (cstage dropped the copy entirely) materialised the
|
||||||
|
* whole aggregate. cgexpr(rhs->lhs) leaves the SOURCE
|
||||||
|
* ADDRESS in AX (a `*p` ident loads the pointer value;
|
||||||
|
* `*(&s)` LEAQs the slot); memcpy sz bytes slot→slot via
|
||||||
|
* SI — a MOVQ run plus a sized MOVL/MOVW/MOVB tail. N is
|
||||||
|
* lu->size (sz), the #254 non-slot-padded ABI extent. Both
|
||||||
|
* stages emit this identical sequence (rule-10); the by-
|
||||||
|
* value RETURN ABI is fold-2 (#267). */
|
||||||
|
if (n->rhs && n->rhs->kind == N_UN
|
||||||
|
&& n->rhs->op == TK_STAR && lu
|
||||||
|
&& (lu->kind == TY_STRUCT || lu->kind == TY_ARRAY)
|
||||||
|
&& sz > 8) {
|
||||||
|
cgexpr(c, n->rhs->lhs, *locals);
|
||||||
|
ins2(c, A_MOVQ, areg(D_AX), areg(D_SI));
|
||||||
|
int k = 0;
|
||||||
|
for (; k + 8 <= sz; k += 8) {
|
||||||
|
ins2(c, A_MOVQ, amem(D_SI, k), areg(D_AX));
|
||||||
|
ins2(c, A_MOVQ, areg(D_AX),
|
||||||
|
amem(D_BP, off + k));
|
||||||
|
}
|
||||||
|
if (k + 4 <= sz) {
|
||||||
|
ins2(c, A_MOVL, amem(D_SI, k), areg(D_AX));
|
||||||
|
ins2(c, A_MOVL, areg(D_AX),
|
||||||
|
amem(D_BP, off + k));
|
||||||
|
k += 4;
|
||||||
|
}
|
||||||
|
if (k + 2 <= sz) {
|
||||||
|
ins2(c, A_MOVW, amem(D_SI, k), areg(D_AX));
|
||||||
|
ins2(c, A_MOVW, areg(D_AX),
|
||||||
|
amem(D_BP, off + k));
|
||||||
|
k += 2;
|
||||||
|
}
|
||||||
|
if (k + 1 <= sz) {
|
||||||
|
ins2(c, A_MOVB, amem(D_SI, k), areg(D_AX));
|
||||||
|
ins2(c, A_MOVB, areg(D_AX),
|
||||||
|
amem(D_BP, off + k));
|
||||||
|
k += 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (n->rhs && sz == 8) {
|
if (n->rhs && sz == 8) {
|
||||||
cgexpr(c, n->rhs, *locals);
|
cgexpr(c, n->rhs, *locals);
|
||||||
if (isf) {
|
if (isf) {
|
||||||
|
|||||||
@@ -28858,6 +28858,78 @@ fn cglet(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
// #265 fold-1: aggregate deref-rhs let-init `let c: T = *p`
|
||||||
|
// (T a struct or array, >8B). cgexpr(rhs.lhs) leaves the
|
||||||
|
// SOURCE ADDRESS in AX (a `*p` ident loads the pointer value;
|
||||||
|
// `*(&s)` LEAQs the slot); memcpy N bytes slot→slot via SI — a
|
||||||
|
// MOVQ run plus a sized MOVL/MOVW/MOVB tail. N is the #254
|
||||||
|
// non-slot-padded ABI extent: structabisize for a struct (=
|
||||||
|
// cstage lu->size), tinfo.size for an array. Pre-fix wwstage
|
||||||
|
// copied only the first 8B (scalar tail below) and cstage
|
||||||
|
// dropped the copy entirely — both wrong; converge on the full
|
||||||
|
// copy (rule-10). Mirror of cstage cgen.c N_LET deref arm; the
|
||||||
|
// by-value RETURN ABI is fold-2 (#267).
|
||||||
|
if (rhs.kind == nkind.N_UN) { if (rhs.op == tkind.TK_STAR) {
|
||||||
|
let ncopy: i32 = 0;
|
||||||
|
let dsi: *structinfo = structlookupchain(c, tn);
|
||||||
|
if (dsi != nil) {
|
||||||
|
ncopy = structabisize(dsi);
|
||||||
|
} else {
|
||||||
|
let dti: *tinfo = nil;
|
||||||
|
if (tn != nil) { dti = tn.type_: *tinfo; };
|
||||||
|
for (dti != nil && dti.kind == tykind.TY_NAMED) {
|
||||||
|
dti = dti.under;
|
||||||
|
};
|
||||||
|
if (dti != nil) {
|
||||||
|
if (dti.kind == tykind.TY_ARRAY) {
|
||||||
|
ncopy = dti.size: i32;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (ncopy > 8) {
|
||||||
|
cgexpr(c, rhs.lhs);
|
||||||
|
emitline("\tMOVQ\tAX, SI\n");
|
||||||
|
let k: i32 = 0;
|
||||||
|
for (k + 8 <= ncopy) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(k: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitoff((off + k): i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
k += 8;
|
||||||
|
};
|
||||||
|
if (k + 4 <= ncopy) {
|
||||||
|
emitline("\tMOVL\t");
|
||||||
|
emitoff(k: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVL\tAX, ");
|
||||||
|
emitoff((off + k): i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
k += 4;
|
||||||
|
};
|
||||||
|
if (k + 2 <= ncopy) {
|
||||||
|
emitline("\tMOVW\t");
|
||||||
|
emitoff(k: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVW\tAX, ");
|
||||||
|
emitoff((off + k): i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
k += 2;
|
||||||
|
};
|
||||||
|
if (k + 1 <= ncopy) {
|
||||||
|
emitline("\tMOVB\t");
|
||||||
|
emitoff(k: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVB\tAX, ");
|
||||||
|
emitoff((off + k): i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
k += 1;
|
||||||
|
};
|
||||||
|
c.lastwasreturn = 0;
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}; };
|
||||||
cgexpr(c, rhs);
|
cgexpr(c, rhs);
|
||||||
// Float local: cgexpr leaves the value in X0. Spill via
|
// Float local: cgexpr leaves the value in X0. Spill via
|
||||||
// MOVSS (f32, 4B) or MOVSD (f64, 8B).
|
// MOVSS (f32, 4B) or MOVSD (f64, 8B).
|
||||||
|
|||||||
@@ -1718,6 +1718,78 @@ fn cglet(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
// #265 fold-1: aggregate deref-rhs let-init `let c: T = *p`
|
||||||
|
// (T a struct or array, >8B). cgexpr(rhs.lhs) leaves the
|
||||||
|
// SOURCE ADDRESS in AX (a `*p` ident loads the pointer value;
|
||||||
|
// `*(&s)` LEAQs the slot); memcpy N bytes slot→slot via SI — a
|
||||||
|
// MOVQ run plus a sized MOVL/MOVW/MOVB tail. N is the #254
|
||||||
|
// non-slot-padded ABI extent: structabisize for a struct (=
|
||||||
|
// cstage lu->size), tinfo.size for an array. Pre-fix wwstage
|
||||||
|
// copied only the first 8B (scalar tail below) and cstage
|
||||||
|
// dropped the copy entirely — both wrong; converge on the full
|
||||||
|
// copy (rule-10). Mirror of cstage cgen.c N_LET deref arm; the
|
||||||
|
// by-value RETURN ABI is fold-2 (#267).
|
||||||
|
if (rhs.kind == nkind.N_UN) { if (rhs.op == tkind.TK_STAR) {
|
||||||
|
let ncopy: i32 = 0;
|
||||||
|
let dsi: *structinfo = structlookupchain(c, tn);
|
||||||
|
if (dsi != nil) {
|
||||||
|
ncopy = structabisize(dsi);
|
||||||
|
} else {
|
||||||
|
let dti: *tinfo = nil;
|
||||||
|
if (tn != nil) { dti = tn.type_: *tinfo; };
|
||||||
|
for (dti != nil && dti.kind == tykind.TY_NAMED) {
|
||||||
|
dti = dti.under;
|
||||||
|
};
|
||||||
|
if (dti != nil) {
|
||||||
|
if (dti.kind == tykind.TY_ARRAY) {
|
||||||
|
ncopy = dti.size: i32;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (ncopy > 8) {
|
||||||
|
cgexpr(c, rhs.lhs);
|
||||||
|
emitline("\tMOVQ\tAX, SI\n");
|
||||||
|
let k: i32 = 0;
|
||||||
|
for (k + 8 <= ncopy) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(k: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitoff((off + k): i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
k += 8;
|
||||||
|
};
|
||||||
|
if (k + 4 <= ncopy) {
|
||||||
|
emitline("\tMOVL\t");
|
||||||
|
emitoff(k: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVL\tAX, ");
|
||||||
|
emitoff((off + k): i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
k += 4;
|
||||||
|
};
|
||||||
|
if (k + 2 <= ncopy) {
|
||||||
|
emitline("\tMOVW\t");
|
||||||
|
emitoff(k: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVW\tAX, ");
|
||||||
|
emitoff((off + k): i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
k += 2;
|
||||||
|
};
|
||||||
|
if (k + 1 <= ncopy) {
|
||||||
|
emitline("\tMOVB\t");
|
||||||
|
emitoff(k: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVB\tAX, ");
|
||||||
|
emitoff((off + k): i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
k += 1;
|
||||||
|
};
|
||||||
|
c.lastwasreturn = 0;
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}; };
|
||||||
cgexpr(c, rhs);
|
cgexpr(c, rhs);
|
||||||
// Float local: cgexpr leaves the value in X0. Spill via
|
// Float local: cgexpr leaves the value in X0. Spill via
|
||||||
// MOVSS (f32, 4B) or MOVSD (f64, 8B).
|
// MOVSS (f32, 4B) or MOVSD (f64, 8B).
|
||||||
|
|||||||
@@ -28858,6 +28858,78 @@ fn cglet(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
// #265 fold-1: aggregate deref-rhs let-init `let c: T = *p`
|
||||||
|
// (T a struct or array, >8B). cgexpr(rhs.lhs) leaves the
|
||||||
|
// SOURCE ADDRESS in AX (a `*p` ident loads the pointer value;
|
||||||
|
// `*(&s)` LEAQs the slot); memcpy N bytes slot→slot via SI — a
|
||||||
|
// MOVQ run plus a sized MOVL/MOVW/MOVB tail. N is the #254
|
||||||
|
// non-slot-padded ABI extent: structabisize for a struct (=
|
||||||
|
// cstage lu->size), tinfo.size for an array. Pre-fix wwstage
|
||||||
|
// copied only the first 8B (scalar tail below) and cstage
|
||||||
|
// dropped the copy entirely — both wrong; converge on the full
|
||||||
|
// copy (rule-10). Mirror of cstage cgen.c N_LET deref arm; the
|
||||||
|
// by-value RETURN ABI is fold-2 (#267).
|
||||||
|
if (rhs.kind == nkind.N_UN) { if (rhs.op == tkind.TK_STAR) {
|
||||||
|
let ncopy: i32 = 0;
|
||||||
|
let dsi: *structinfo = structlookupchain(c, tn);
|
||||||
|
if (dsi != nil) {
|
||||||
|
ncopy = structabisize(dsi);
|
||||||
|
} else {
|
||||||
|
let dti: *tinfo = nil;
|
||||||
|
if (tn != nil) { dti = tn.type_: *tinfo; };
|
||||||
|
for (dti != nil && dti.kind == tykind.TY_NAMED) {
|
||||||
|
dti = dti.under;
|
||||||
|
};
|
||||||
|
if (dti != nil) {
|
||||||
|
if (dti.kind == tykind.TY_ARRAY) {
|
||||||
|
ncopy = dti.size: i32;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (ncopy > 8) {
|
||||||
|
cgexpr(c, rhs.lhs);
|
||||||
|
emitline("\tMOVQ\tAX, SI\n");
|
||||||
|
let k: i32 = 0;
|
||||||
|
for (k + 8 <= ncopy) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(k: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitoff((off + k): i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
k += 8;
|
||||||
|
};
|
||||||
|
if (k + 4 <= ncopy) {
|
||||||
|
emitline("\tMOVL\t");
|
||||||
|
emitoff(k: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVL\tAX, ");
|
||||||
|
emitoff((off + k): i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
k += 4;
|
||||||
|
};
|
||||||
|
if (k + 2 <= ncopy) {
|
||||||
|
emitline("\tMOVW\t");
|
||||||
|
emitoff(k: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVW\tAX, ");
|
||||||
|
emitoff((off + k): i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
k += 2;
|
||||||
|
};
|
||||||
|
if (k + 1 <= ncopy) {
|
||||||
|
emitline("\tMOVB\t");
|
||||||
|
emitoff(k: i64);
|
||||||
|
emitline("(SI), AX\n");
|
||||||
|
emitline("\tMOVB\tAX, ");
|
||||||
|
emitoff((off + k): i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
k += 1;
|
||||||
|
};
|
||||||
|
c.lastwasreturn = 0;
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}; };
|
||||||
cgexpr(c, rhs);
|
cgexpr(c, rhs);
|
||||||
// Float local: cgexpr leaves the value in X0. Spill via
|
// Float local: cgexpr leaves the value in X0. Spill via
|
||||||
// MOVSS (f32, 4B) or MOVSD (f64, 8B).
|
// MOVSS (f32, 4B) or MOVSD (f64, 8B).
|
||||||
|
|||||||
@@ -750,6 +750,78 @@ static const struct row rows[] = {
|
|||||||
" case void => yield 1: i32;\n"
|
" case void => yield 1: i32;\n"
|
||||||
" };\n"
|
" };\n"
|
||||||
"};\n", 63, 1 },
|
"};\n", 63, 1 },
|
||||||
|
/* #265 fold-1 aggregate deref-rhs let-init `let c: T = *p` (T a
|
||||||
|
* struct or array, >8B). Pre-fix NEITHER stage copied the whole
|
||||||
|
* aggregate: cstage DROPPED the copy entirely (c read garbage);
|
||||||
|
* wwstage copied only the FIRST 8 bytes (the scalar `MOVQ AX,off`
|
||||||
|
* tail). Fix: both stages memcpy the ABI-size aggregate slot→slot
|
||||||
|
* via SI — a MOVQ run plus a sized MOVL/MOVW/MOVB tail. Converged
|
||||||
|
* (rule-10, byteid=1). Each row writes DISTINCT values to ALL
|
||||||
|
* members and reads back EVERY member (sum), so a partial/zero copy
|
||||||
|
* fails — a c[0]-only readback would pass a truncated copy. Covers:
|
||||||
|
* struct{[4]u32} 16B + struct{[8]u32} 32B (sha256 `*h` shape, both
|
||||||
|
* `*(&s)` and `*p` pointer-ident) + a bare [4]u32 array + non-8-mult
|
||||||
|
* tails ([3]u32 12B → MOVL tail; [11]u8 11B → MOVW+MOVB tail). The
|
||||||
|
* by-value aggregate RETURN ABI is fold-2 (#267, deferred). */
|
||||||
|
{ "deref_struct16",
|
||||||
|
"package main;\n"
|
||||||
|
"type t = struct { h: [4]u32 };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
" let s: t;\n"
|
||||||
|
" s.h[0]=10u32; s.h[1]=20u32; s.h[2]=30u32; s.h[3]=40u32;\n"
|
||||||
|
" let c: t = *(&s);\n"
|
||||||
|
" return (c.h[0]+c.h[1]+c.h[2]+c.h[3]): i32;\n"
|
||||||
|
"};\n", 100, 1 },
|
||||||
|
{ "deref_struct32",
|
||||||
|
"package main;\n"
|
||||||
|
"type t = struct { h: [8]u32 };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
" let s: t;\n"
|
||||||
|
" s.h[0]=1u32; s.h[1]=2u32; s.h[2]=3u32; s.h[3]=4u32;\n"
|
||||||
|
" s.h[4]=5u32; s.h[5]=6u32; s.h[6]=7u32; s.h[7]=8u32;\n"
|
||||||
|
" let c: t = *(&s);\n"
|
||||||
|
" return (c.h[0]+c.h[1]+c.h[2]+c.h[3]\n"
|
||||||
|
" +c.h[4]+c.h[5]+c.h[6]+c.h[7]): i32;\n"
|
||||||
|
"};\n", 36, 1 },
|
||||||
|
{ "deref_ptr32",
|
||||||
|
"package main;\n"
|
||||||
|
"type t = struct { h: [8]u32 };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
" let s: t;\n"
|
||||||
|
" s.h[0]=1u32; s.h[1]=2u32; s.h[2]=3u32; s.h[3]=4u32;\n"
|
||||||
|
" s.h[4]=5u32; s.h[5]=6u32; s.h[6]=7u32; s.h[7]=8u32;\n"
|
||||||
|
" let p: *t = &s;\n"
|
||||||
|
" let c: t = *p;\n"
|
||||||
|
" return (c.h[0]+c.h[1]+c.h[2]+c.h[3]\n"
|
||||||
|
" +c.h[4]+c.h[5]+c.h[6]+c.h[7]): i32;\n"
|
||||||
|
"};\n", 36, 1 },
|
||||||
|
{ "deref_array16",
|
||||||
|
"package main;\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
" let s: [4]u32;\n"
|
||||||
|
" s[0]=5u32; s[1]=6u32; s[2]=7u32; s[3]=8u32;\n"
|
||||||
|
" let c: [4]u32 = *(&s);\n"
|
||||||
|
" return (c[0]+c[1]+c[2]+c[3]): i32;\n"
|
||||||
|
"};\n", 26, 1 },
|
||||||
|
{ "deref_tail12",
|
||||||
|
"package main;\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
" let s: [3]u32;\n"
|
||||||
|
" s[0]=7u32; s[1]=8u32; s[2]=9u32;\n"
|
||||||
|
" let c: [3]u32 = *(&s);\n"
|
||||||
|
" return (c[0]+c[1]+c[2]): i32;\n"
|
||||||
|
"};\n", 24, 1 },
|
||||||
|
{ "deref_tail11",
|
||||||
|
"package main;\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
" let s: [11]u8;\n"
|
||||||
|
" s[0]=1u8; s[1]=2u8; s[2]=3u8; s[3]=4u8; s[4]=5u8;\n"
|
||||||
|
" s[5]=6u8; s[6]=7u8; s[7]=8u8; s[8]=9u8; s[9]=10u8;\n"
|
||||||
|
" s[10]=11u8;\n"
|
||||||
|
" let c: [11]u8 = *(&s);\n"
|
||||||
|
" return (c[0]+c[1]+c[2]+c[3]+c[4]+c[5]\n"
|
||||||
|
" +c[6]+c[7]+c[8]+c[9]+c[10]): i32;\n"
|
||||||
|
"};\n", 66, 1 },
|
||||||
{ NULL, NULL, 0, 0 }
|
{ NULL, NULL, 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user