selfhost: cgmatch bsz for TY_STRUCT variant bind (closes #31)
Match-arm bind size in wwstage hardcoded str=16, []T=24, else=8 in both cgmatch (emit) and scanlocals (frame pre-scan). A TY_STRUCT variant fell into the 8B fallback: only the first quadword reached the bind, and the prologue SUBQ underbooked the frame so the emit-time localalloc(bsz=24+) wrote past SP. Replace the hand-rolled table with slotsize(c, pat) at both sites. slotsize already covers N_TNAME named structs (returns si.totsize), str (16), []T (24), tuples, aliases, and primitives (8). Mirrors cstage cgen.c cgmatch which falls through to bu->size for TY_STRUCT. Cstage is correct; no mirror needed. Test 695 covers seven shapes: the 3xi64 headline repro, a 4xi64 struct via let-init scrut (exercises >24B bind), a mixed-quadword struct (i32+i32+i64+i64), str/slice/i32 negative controls, and a direct let-init scrutinee variant. The wider 4xi64 row uses the let-init shape because the N_DOT spill path in cgmatch tops out at AX/DX/CX/R8 — a separate, unrelated gap from the bind size.
This commit is contained in:
@@ -9429,23 +9429,37 @@ fn cgmatch(c: *cgen, n: *node) void = {
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
} else {
|
||||
let bsz: i32 = 8;
|
||||
if (isstrtype(c, pat)) { bsz = 16; }
|
||||
else { if (isslicetype(c, pat)) { bsz = 24; }; };
|
||||
// Size the bind from the variant's declared
|
||||
// layout. slotsize covers str (16), []T (24),
|
||||
// N_TNAME named struct (si.totsize), aliases,
|
||||
// tuples, primitives (8). Hardcoding str/slice
|
||||
// + fall-through-8 dropped the high words of a
|
||||
// TY_STRUCT variant (e.g. only v.x reached the
|
||||
// bind for `case let v: pair`, project #31);
|
||||
// mirrors cstage's `bu->size` fallback in
|
||||
// cgen.c cgmatch.
|
||||
let bsz: i32 = slotsize(c, pat);
|
||||
if (bsz <= 0) { bsz = 8; };
|
||||
// localalloc (not localadd): match-arm
|
||||
// binds don't dedup with same-named binds
|
||||
// in *other* matches, since C's cgexpr
|
||||
// allocates a fresh slot per match expr.
|
||||
let voff: i32 = localalloc(c, bn, bsz, pat);
|
||||
// Word-by-word copy. Round bsz up to 8 in case
|
||||
// a non-multiple-of-8 struct size leaked through
|
||||
// (registerstruct already pads totsize, but be
|
||||
// defensive — same shape as cstage's nwords =
|
||||
// (bsz + 7) / 8).
|
||||
let nwords: i32 = (bsz + 7) / 8;
|
||||
let bw: i32 = 0;
|
||||
for (bw < bsz) {
|
||||
for (bw < nwords) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scrutoff + 8 + bw): i64);
|
||||
emitoff((scrutoff + 8 + 8 * bw): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((voff + bw): i64);
|
||||
emitoff((voff + 8 * bw): i64);
|
||||
emitline("(BP)\n");
|
||||
bw += 8;
|
||||
bw += 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -14505,9 +14519,17 @@ fn scanlocals(c: *cgen, n: *node) i32 = {
|
||||
if (bn.len > 0) {
|
||||
let pat: *node = n.lhs;
|
||||
if (pat != nil) {
|
||||
if (isstrtype(c, pat)) { total += 16; }
|
||||
else { if (isslicetype(c, pat)) { total += 24; }
|
||||
else { total += 8; }; };
|
||||
// Must mirror cgmatch's bind-slot sizing in
|
||||
// cgenexpr.ww (`bsz = slotsize(c, pat)`):
|
||||
// hardcoding str/slice/8 here underbooked the
|
||||
// frame for TY_STRUCT variants — the emit-time
|
||||
// localalloc(bsz=24) then wrote past the SUBQ'd
|
||||
// SP, smashing whatever the OS put under it
|
||||
// (project #31).
|
||||
let psz: i32 = slotsize(c, pat);
|
||||
if (psz <= 0) { psz = 8; };
|
||||
if ((psz & 7) != 0) { psz = (psz + 7) & ~7; };
|
||||
total += psz;
|
||||
};
|
||||
};
|
||||
// Match arms get a fresh local scope at emission time
|
||||
|
||||
@@ -150,9 +150,17 @@ fn scanlocals(c: *cgen, n: *node) i32 = {
|
||||
if (bn.len > 0) {
|
||||
let pat: *node = n.lhs;
|
||||
if (pat != nil) {
|
||||
if (isstrtype(c, pat)) { total += 16; }
|
||||
else { if (isslicetype(c, pat)) { total += 24; }
|
||||
else { total += 8; }; };
|
||||
// Must mirror cgmatch's bind-slot sizing in
|
||||
// cgenexpr.ww (`bsz = slotsize(c, pat)`):
|
||||
// hardcoding str/slice/8 here underbooked the
|
||||
// frame for TY_STRUCT variants — the emit-time
|
||||
// localalloc(bsz=24) then wrote past the SUBQ'd
|
||||
// SP, smashing whatever the OS put under it
|
||||
// (project #31).
|
||||
let psz: i32 = slotsize(c, pat);
|
||||
if (psz <= 0) { psz = 8; };
|
||||
if ((psz & 7) != 0) { psz = (psz + 7) & ~7; };
|
||||
total += psz;
|
||||
};
|
||||
};
|
||||
// Match arms get a fresh local scope at emission time
|
||||
|
||||
@@ -1111,23 +1111,37 @@ fn cgmatch(c: *cgen, n: *node) void = {
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
} else {
|
||||
let bsz: i32 = 8;
|
||||
if (isstrtype(c, pat)) { bsz = 16; }
|
||||
else { if (isslicetype(c, pat)) { bsz = 24; }; };
|
||||
// Size the bind from the variant's declared
|
||||
// layout. slotsize covers str (16), []T (24),
|
||||
// N_TNAME named struct (si.totsize), aliases,
|
||||
// tuples, primitives (8). Hardcoding str/slice
|
||||
// + fall-through-8 dropped the high words of a
|
||||
// TY_STRUCT variant (e.g. only v.x reached the
|
||||
// bind for `case let v: pair`, project #31);
|
||||
// mirrors cstage's `bu->size` fallback in
|
||||
// cgen.c cgmatch.
|
||||
let bsz: i32 = slotsize(c, pat);
|
||||
if (bsz <= 0) { bsz = 8; };
|
||||
// localalloc (not localadd): match-arm
|
||||
// binds don't dedup with same-named binds
|
||||
// in *other* matches, since C's cgexpr
|
||||
// allocates a fresh slot per match expr.
|
||||
let voff: i32 = localalloc(c, bn, bsz, pat);
|
||||
// Word-by-word copy. Round bsz up to 8 in case
|
||||
// a non-multiple-of-8 struct size leaked through
|
||||
// (registerstruct already pads totsize, but be
|
||||
// defensive — same shape as cstage's nwords =
|
||||
// (bsz + 7) / 8).
|
||||
let nwords: i32 = (bsz + 7) / 8;
|
||||
let bw: i32 = 0;
|
||||
for (bw < bsz) {
|
||||
for (bw < nwords) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scrutoff + 8 + bw): i64);
|
||||
emitoff((scrutoff + 8 + 8 * bw): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((voff + bw): i64);
|
||||
emitoff((voff + 8 * bw): i64);
|
||||
emitline("(BP)\n");
|
||||
bw += 8;
|
||||
bw += 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -9429,23 +9429,37 @@ fn cgmatch(c: *cgen, n: *node) void = {
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
} else {
|
||||
let bsz: i32 = 8;
|
||||
if (isstrtype(c, pat)) { bsz = 16; }
|
||||
else { if (isslicetype(c, pat)) { bsz = 24; }; };
|
||||
// Size the bind from the variant's declared
|
||||
// layout. slotsize covers str (16), []T (24),
|
||||
// N_TNAME named struct (si.totsize), aliases,
|
||||
// tuples, primitives (8). Hardcoding str/slice
|
||||
// + fall-through-8 dropped the high words of a
|
||||
// TY_STRUCT variant (e.g. only v.x reached the
|
||||
// bind for `case let v: pair`, project #31);
|
||||
// mirrors cstage's `bu->size` fallback in
|
||||
// cgen.c cgmatch.
|
||||
let bsz: i32 = slotsize(c, pat);
|
||||
if (bsz <= 0) { bsz = 8; };
|
||||
// localalloc (not localadd): match-arm
|
||||
// binds don't dedup with same-named binds
|
||||
// in *other* matches, since C's cgexpr
|
||||
// allocates a fresh slot per match expr.
|
||||
let voff: i32 = localalloc(c, bn, bsz, pat);
|
||||
// Word-by-word copy. Round bsz up to 8 in case
|
||||
// a non-multiple-of-8 struct size leaked through
|
||||
// (registerstruct already pads totsize, but be
|
||||
// defensive — same shape as cstage's nwords =
|
||||
// (bsz + 7) / 8).
|
||||
let nwords: i32 = (bsz + 7) / 8;
|
||||
let bw: i32 = 0;
|
||||
for (bw < bsz) {
|
||||
for (bw < nwords) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scrutoff + 8 + bw): i64);
|
||||
emitoff((scrutoff + 8 + 8 * bw): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((voff + bw): i64);
|
||||
emitoff((voff + 8 * bw): i64);
|
||||
emitline("(BP)\n");
|
||||
bw += 8;
|
||||
bw += 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -14505,9 +14519,17 @@ fn scanlocals(c: *cgen, n: *node) i32 = {
|
||||
if (bn.len > 0) {
|
||||
let pat: *node = n.lhs;
|
||||
if (pat != nil) {
|
||||
if (isstrtype(c, pat)) { total += 16; }
|
||||
else { if (isslicetype(c, pat)) { total += 24; }
|
||||
else { total += 8; }; };
|
||||
// Must mirror cgmatch's bind-slot sizing in
|
||||
// cgenexpr.ww (`bsz = slotsize(c, pat)`):
|
||||
// hardcoding str/slice/8 here underbooked the
|
||||
// frame for TY_STRUCT variants — the emit-time
|
||||
// localalloc(bsz=24) then wrote past the SUBQ'd
|
||||
// SP, smashing whatever the OS put under it
|
||||
// (project #31).
|
||||
let psz: i32 = slotsize(c, pat);
|
||||
if (psz <= 0) { psz = 8; };
|
||||
if ((psz & 7) != 0) { psz = (psz + 7) & ~7; };
|
||||
total += psz;
|
||||
};
|
||||
};
|
||||
// Match arms get a fresh local scope at emission time
|
||||
|
||||
Reference in New Issue
Block a user