w6c: set variadic-call AL to XMM-reg count, not hardcoded 0 (catB-54 C1)

SysV §3.5.7 requires a variadic call to set AL = number of vector (XMM) regs used for the variable float args; the C callee gates its xmm-save-area stores on `test %al,%al`, so the old hardcoded XORQ AX,AX (AL=0) made va_arg(double) read garbage for any float-bearing C variadic call. Emit MOVQ $fi,AX (fi = the in-scope XMM cursor, ≤8); w6a has no MOVL-immediate encoding so MOVQ is the assemblable form and sets AL=fi identically. fi==0 keeps XORQ → byte-identical to pre-fix for no-float variadic calls. Runtime test 989_ffivariadic links a cc-compiled va_arg(double) fixture (zero relocs/undefined, w6l-linkable) and sweeps N=3/5/8 floats (N=2 is vacuous via stale-stack aliasing). C1 of the C-FFI-variadic align-up (USER ruling); C2 wwstage + C3 bodiless gate follow. ref/qbe/amd64/sysv.c:384. 454 green.
This commit is contained in:
2026-06-20 23:21:44 +09:00
parent b9692b14f1
commit 294f4c93fd
4 changed files with 218 additions and 5 deletions

View File

@@ -10215,11 +10215,20 @@ cgexpr(Cg *c, Node *n, Local *locals)
areg(D_DI));
}
}
/* SysV: variadic callees require AL to hold the count of
* XMM regs used in the variable portion. We don't pass
* floats yet, so AL=0 covers every case we emit. */
if (cu && cu->kind == TY_FN && cu->variadic)
ins2(c, A_XORQ, areg(D_AX), areg(D_AX));
/* SysV §3.5.7: a variadic call must set AL to the number of
* vector (XMM) regs used to pass the variable float args — the
* callee gates its xmm-save-area stores on `test %al,%al`, so a
* wrong AL makes va_arg(double) read garbage. fi is the XMM
* cursor (capped at 8 above). cstage formerly hardcoded AL=0,
* correct only for zero-float variadic calls. Ref ref/qbe/
* amd64/sysv.c:384. MOVL-imm has no w6a encoding, so the imm→reg
* MOVQ idiom carries it (AL = low byte, fi ≤ 8). */
if (cu && cu->kind == TY_FN && cu->variadic) {
if (fi > 0)
ins2(c, A_MOVQ, aimm(fi), areg(D_AX));
else
ins2(c, A_XORQ, areg(D_AX), areg(D_AX));
}
if (n->lhs->kind == N_IDENT) {
/* If the callee names a local variable holding a
* function pointer, load it and call indirect. Without