wcc/ww: cgcall drain pops widened GP words before the float arm

The arg-drain loop checked node_isfloat before popping a widened
arg's GP words, so a float arg adjacent to a widened (tagged) arg
read the wrong stack slot: the f64 took the widened payload (#30), or
the float arm ate the tag word into X0 and the payload landed in DI
as the tag (#48). One missing branch, two manifestations — mirror
cstage's widen-first pop (cgen.c:9650-9665). Review items #30+#48
(fold reviewer-verified one-mechanism against the cstage twin).
This commit is contained in:
2026-06-12 21:11:35 +09:00
parent f00775759d
commit a4a4cd7c16
6 changed files with 474 additions and 0 deletions

View File

@@ -7756,6 +7756,36 @@ fn cgcall(c: *cgen, n: *node) void = {
a = a.next;
continue;
};
// Widen-first pop (#30/#48): a concrete arg widened into a
// tagged-union param was pushed as slotsize/8 GP words (tag +
// payload). Drain those words into the INTEGER arg cursor
// BEFORE the float check below — else a widened f64-source box
// gets misclassified as a float arg (its tag word drained into
// X0, #48), and a float arg FOLLOWING a widened arg reads the
// widened box's leftover payload word (#30). Mirror of cstage's
// precomputed widen[i] branch (cmd/w6c/cgen.c cgcall, popped
// before node_isfloat). argtaggedwidensz is the shared SSoT with
// pushargsrev's push count.
let dwsz: i32 = argtaggedwidensz(c, a, dparam);
if (dwsz >= 16) {
let dwb: i32 = dwsz / 8;
let dwk: i32 = 0;
for (dwk < dwb) {
if (intidx < 6) {
emitline("\tPOPQ\t");
emitline(argregname(intidx));
emitline("\n");
intidx += 1;
} else {
stackslots += 1;
};
popped += 1;
dwk += 1;
};
if (dparam != nil) { dparam = dparam.next; };
a = a.next;
continue;
};
let fk: i32 = 0;
if (a != nil) {
let at: *tinfo = a.type_: *tinfo;