wcc: struct-param float fields via SSE arg regs (#165)

struct params were passed GP-only, so a struct{f64,f64} argument landed in
DI/SI instead of X0/X1 — value-correct for internal ww calls (the bits
round-trip) but not SysV register-class conformant. Add a per-eightbyte
classifier (struct_float_class) routing a qualifying struct's float eightbytes
through the SSE arg cursor, reusing #163's dual-cursor plumbing and #164's
field classification. A struct qualifies only when every eightbyte is
pure-integer or a lone f64 exactly filling it (and >=1 f64); anything else —
any f32, multiple floats per eightbyte, a straddling or aggregate field —
falls back to the unchanged GP path (f32 sub-eightbyte packing deferred #165b).
Both stages' predicates are alias-aware and identical in coverage.

Gate-blind and value-correct either way, so the discriminator is the callee's
receive instruction (MOVSD vs MOVQ), scoped per-function — covered by probe
946.
This commit is contained in:
2026-05-27 23:31:19 +09:00
parent 0465c423c1
commit a917533fbf
8 changed files with 1048 additions and 73 deletions

View File

@@ -180,6 +180,52 @@ fn cgfnparams(c: *cgen, params: *node) void = {
p = p.next;
continue;
};
let sfc: i32 = structfloatclass(c, p.lhs);
if (sfc != 0) {
// #165: float-bearing struct PARAM receive (param
// twin of #163's tuple). Classify each SysV
// eightbyte; a lone-f64 eightbyte reads its XMM
// (X0..X7), a pure-INT eightbyte its INTEGER arg reg
// (DI/SI/..), stored into the param slot at the
// 8-byte eightbyte stride. Gated to qualifying
// structs by structfloatclass — all-int + f32-packed
// fall through to the GP struct arm below (byte-id /
// #165b). Reg overflow loud-stops (rule 7).
let off: i32 = localadd(c, nm, structparamsize(c, p.lhs), p.lhs);
let nb: i32 = sfc & 15;
let e: i32 = 0;
for (e < nb) {
let issse: bool = (sfc & (16 << e)) != 0;
if (issse) {
if (fidx >= 8) {
let msg: str = "float struct param eightbyte overflows SSE arg regs (X0..X7); stitch out of scope, see #165\n";
os.write(2, msg.ptr, msg.len: u64);
os.exit(1);
};
emitline("\tMOVSD\t");
emitline(fargregname(fidx));
emitline(", ");
emitoff((off + e*8): i64);
emitline("(BP)\n");
fidx += 1;
} else {
if (idx >= 6) {
let msg: str = "float struct param eightbyte overflows integer arg regs (DI/SI/DX/CX/R8/R9); stitch out of scope, see #165\n";
os.write(2, msg.ptr, msg.len: u64);
os.exit(1);
};
emitline("\tMOVQ\t");
emitline(argregname(idx));
emitline(", ");
emitoff((off + e*8): i64);
emitline("(BP)\n");
idx += 1;
};
e += 1;
};
p = p.next;
continue;
};
if (istaggedtype(c, p.lhs)) {
let slot: i32 = slotsize(c, p.lhs);
let nw: i32 = slot / 8;