w6c: promote f32 arg to double in C-variadic tail (#14)
C99 6.5.2.2p6 requires float->double promotion for floats in the variadic region of a call; w6c emitted MOVSS (4B), so a C variadic's va_arg(double) read 8B of garbage. Promote at push (CVTSS2SD) so the 8B slot holds a real double, covering the XMM-reg and >8-float spill cases uniformly; fi/AL XMM-count is unchanged. cstage only; the wwstage twin rides C2 (#10), which adds wwstage's C-variadic-call codegen.
This commit is contained in:
@@ -9395,6 +9395,23 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
memarg[i] =
|
||||
tagged_memarg_size(args[i]->type);
|
||||
}
|
||||
/* C default argument promotion (#14): an f32 passed in the
|
||||
* variadic region of a C-variadic call must widen to f64 — the
|
||||
* callee reads it with va_arg(double) (8B). The region is every
|
||||
* arg past the fixed params (the C `...` adds no Tparam). The
|
||||
* push promotes via CVTSS2SD and stores a full 8B double; the
|
||||
* pop then reloads MOVSD and counts it as exactly one SSE reg.
|
||||
* Fixed params keep their declared width. Ref SysV §3.5.7;
|
||||
* C99 §6.5.2.2p6. */
|
||||
int promote_f32[64] = {0};
|
||||
if (cu && cu->kind == TY_FN && cu->variadic) {
|
||||
int nfixed = 0;
|
||||
for (Tparam *p = callee_params; p; p = p->next)
|
||||
nfixed++;
|
||||
for (int i = nfixed; i < argcount; i++)
|
||||
if (args[i] && node_isf32(args[i]))
|
||||
promote_f32[i] = 1;
|
||||
}
|
||||
/* #38b MEMORY-class pre-pass: stage every >48B tagged arg on
|
||||
* the stack BELOW all register-class words (rightmost-first,
|
||||
* so the leftmost mem arg lands at the lowest address = the
|
||||
@@ -9791,8 +9808,16 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* float class drives the width per ref/qbe
|
||||
* amd64/emit.c:524 (slot-copy single→movss). The
|
||||
* slot is 8B either way; the pop reads the same
|
||||
* width back. #143. */
|
||||
int fmov = op_for(args[i], A_MOVSD, A_MOVSS);
|
||||
* width back. #143.
|
||||
*
|
||||
* #14: a variadic-region f32 is promoted to f64
|
||||
* here (CVTSS2SD in X0) and spilled MOVSD, so the
|
||||
* slot holds a real 8B double for the pop. */
|
||||
int fmov = promote_f32[i] ? A_MOVSD
|
||||
: op_for(args[i], A_MOVSD, A_MOVSS);
|
||||
if (promote_f32[i])
|
||||
ins2(c, A_CVTSS2SD, areg(D_X0),
|
||||
areg(D_X0));
|
||||
ins2(c, A_SUBQ, aimm(8), areg(D_SP));
|
||||
ins2(c, fmov, areg(D_X0), amem(D_SP, 0));
|
||||
} else if (node_isstr(args[i])) {
|
||||
@@ -10017,9 +10042,11 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
if (fi < 8) {
|
||||
/* Reload the spilled f32/f64 at its class
|
||||
* width — MOVSS for f32, MOVSD for f64 —
|
||||
* matching the push above (#143). */
|
||||
int fmov = op_for(args[i], A_MOVSD,
|
||||
A_MOVSS);
|
||||
* matching the push above (#143). A #14
|
||||
* variadic-region f32 was promoted to f64
|
||||
* at push, so its slot reloads MOVSD. */
|
||||
int fmov = promote_f32[i] ? A_MOVSD
|
||||
: op_for(args[i], A_MOVSD, A_MOVSS);
|
||||
ins2(c, fmov, amem(D_SP, 0),
|
||||
areg(sysv_fargregs[fi]));
|
||||
ins2(c, A_ADDQ, aimm(8), areg(D_SP));
|
||||
|
||||
Reference in New Issue
Block a user