wcc+w6c_ww: >48B tagged by-value args — MEMORY-class two-phase push (#38b)
Task #19 (the #38b residual surfaced by FC2 evidence): a tagged arg whose slot exceeds the 6-reg convention (>48B) is MEMORY-class per ref/qbe/amd64/sysv.c:80-85 (inmem) / :411-426 (stack blit). Caller stages the whole slot below every register-class word (two-phase push, rightmost-first, leftmost mem arg at 16(BP)); callee registers the param in place at positive BP offsets with zero prologue bytes; the merged slot count feeds the existing caller-cleanup ADDQ. Argument-side mirror of the #38 tagged-sret fix, same classify machinery (tagged_memarg_size / taggedmemargsize beside their register-class siblings). Pre-fix, the exact-typed arg loud-stopped on both stages, but WIDENING a concrete variant into a >48B param slipped the old guard silently — cstage pushed one scalar word while wwstage emitted an uncapped greedy stitch (wrong on both AND cs≠ww, gate-blind). Widen sources now route through the @tagscr scratch for mem slots. Loud boundaries kept (rule 7), each with its own diagnostic: sret-class tagged CALL result as mem-arg source (#40-family follow-up), global tagged let (task #25, broken at any size pre-existing), >48B variadic element, and mem-arg + register- overflow mixing (caller check + callee prologue mirror). Single commit: caller staging, callee receive, and both stages are one inseparable ABI class — landing any half alone breaks byte-id or runtime correctness (the #38 flip precedent); test/929 (15 table-driven rows: 56B/64B slots, widen-slip pin, source shapes, mixed orders both ways, two-mem call, 200k-call loop, 48B-boundary absence pin byte-id'd vs master, 5 reject rows pinning the exact per-guard diagnostic on both stages) rides with it.
This commit is contained in:
@@ -35,6 +35,9 @@ fn cgfnparams(c: *cgen, params: *node) void = {
|
||||
// is registered with a *positive* offset pointing into the
|
||||
// caller's frame. Mirrors C cgen's cg_stack_arg_cursor.
|
||||
let stkcursor: i32 = 0;
|
||||
// #38b: words consumed by MEMORY-class (>48B tagged) params —
|
||||
// post-walk consistency check against stkcursor.
|
||||
let memwords: i32 = 0;
|
||||
for (p != nil) {
|
||||
if (p.kind == nkind.N_PARAM) {
|
||||
let nm: str = p.str;
|
||||
@@ -229,7 +232,19 @@ fn cgfnparams(c: *cgen, params: *node) void = {
|
||||
if (istaggedtype(c, p.lhs)) {
|
||||
let slot: i32 = slotsize(c, p.lhs);
|
||||
let nw: i32 = slot / 8;
|
||||
if (idx + nw <= 6) {
|
||||
// #38b: MEMORY-class (>48B tagged) param — the
|
||||
// caller staged the whole slot below the return
|
||||
// address; read it in place at positive BP
|
||||
// offsets. No spill, no frame growth, zero
|
||||
// prologue bytes. Pre-fix this fell into the
|
||||
// greedy stitch arm below while cstage received
|
||||
// one scalar word (cs≠ww, silent).
|
||||
// ref/qbe/amd64/sysv.c:80-85 / :411-426.
|
||||
if (taggedmemargsize(p.lhs.type_: *tinfo) > 0) {
|
||||
localaddstack(c, nm, p.lhs, 16 + stkcursor*8);
|
||||
stkcursor += nw;
|
||||
memwords += nw;
|
||||
} else { if (idx + nw <= 6) {
|
||||
let off: i32 = localadd(c, nm, slot, p.lhs);
|
||||
let w: i32 = 0;
|
||||
for (w < nw) {
|
||||
@@ -270,7 +285,7 @@ fn cgfnparams(c: *cgen, params: *node) void = {
|
||||
} else {
|
||||
localaddstack(c, nm, p.lhs, 16 + stkcursor*8);
|
||||
stkcursor += nw;
|
||||
};};
|
||||
};};};
|
||||
} else { if (isslicetype(c, p.lhs)) {
|
||||
if (idx + 3 <= 6) {
|
||||
let off: i32 = localadd(c, nm, tyslicesize(): i32, p.lhs);
|
||||
@@ -490,6 +505,17 @@ fn cgfnparams(c: *cgen, params: *node) void = {
|
||||
};
|
||||
p = p.next;
|
||||
};
|
||||
// #38b: a MEMORY-class tagged param cannot coexist with stack-
|
||||
// spilled register-class params — both walk the same positive-BP
|
||||
// cursor in declaration order while the caller's residual region
|
||||
// puts spill words below every mem copy. Any non-mem cursor use
|
||||
// leaves stkcursor past the mem words. Mirror of the cgcall
|
||||
// caller-side check; loud-stop (rule 7).
|
||||
if (memwords > 0 && stkcursor != memwords) {
|
||||
let mp: str = "#38b: >48B tagged param mixed with stack-spilled params unwired\n";
|
||||
os.write(2, mp.ptr, mp.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
};
|
||||
|
||||
fn cgfn(c: *cgen, fn_: *node) void = {
|
||||
|
||||
Reference in New Issue
Block a user