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:
@@ -3980,29 +3980,76 @@ fn cgcall(c: *cgen, n: *node) void = {
|
||||
p = p.next;
|
||||
};
|
||||
} else {
|
||||
let extra: i32 = 0;
|
||||
// str IS []u8: 3-word arg, same as slice (#1/Phase 3).
|
||||
if (nodeisstr(c, a)) { extra = 2; };
|
||||
if (nodeisslice(c, a)) { extra = 2; };
|
||||
// #21: tagged-CALL arg was pushed AX/DX/CX/R8 high→low
|
||||
// by pushargsrev; size the per-arg pop to match so the
|
||||
// next arg's POPQ doesn't land on residual tag/payload
|
||||
// words and shift intidx out of sync.
|
||||
let tcs: i32 = taggedcallslot(c, a);
|
||||
if (tcs > 0) { extra = tcs / 8 - 1; };
|
||||
let words: i32 = 1 + extra;
|
||||
let w: i32 = 0;
|
||||
for (w < words) {
|
||||
if (intidx < 6) {
|
||||
emitline("\tPOPQ\t");
|
||||
emitline(argregname(intidx));
|
||||
emitline("\n");
|
||||
intidx += 1;
|
||||
} else {
|
||||
stackslots += 1;
|
||||
let stfc: i32 = 0;
|
||||
if (a.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, a.str);
|
||||
if (lc != nil) { stfc = structfloatclass(c, lc.tnode); };
|
||||
};
|
||||
if (stfc != 0) {
|
||||
// #165: float-bearing struct arg — drain by SysV
|
||||
// eightbyte class: a lone-f64 eightbyte MOVSD off
|
||||
// (SP) into the next XMM (X0..X7), a pure-INT
|
||||
// eightbyte POPQ into the next INTEGER arg reg
|
||||
// (DI/SI/..). The struct-ident push staged raw slot
|
||||
// words (class-independent); only the drain differs.
|
||||
// Gated to qualifying floats; all-int + f32-packed
|
||||
// keep the generic pop below. Reg overflow loud-
|
||||
// stops (rule 7), the partial-spill stitch out of
|
||||
// scope (#163 twin).
|
||||
let nb: i32 = stfc & 15;
|
||||
let e: i32 = 0;
|
||||
for (e < nb) {
|
||||
let issse: bool = (stfc & (16 << e)) != 0;
|
||||
if (issse) {
|
||||
if (fpidx >= 8) {
|
||||
let msg: str = "float struct arg 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(SP), ");
|
||||
emitline(fargregname(fpidx));
|
||||
emitline("\n");
|
||||
emitline("\tADDQ\t$8, SP\n");
|
||||
fpidx += 1;
|
||||
} else {
|
||||
if (intidx >= 6) {
|
||||
let msg: str = "float struct arg 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("\tPOPQ\t");
|
||||
emitline(argregname(intidx));
|
||||
emitline("\n");
|
||||
intidx += 1;
|
||||
};
|
||||
popped += 1;
|
||||
e += 1;
|
||||
};
|
||||
} else {
|
||||
let extra: i32 = 0;
|
||||
// str IS []u8: 3-word arg, same as slice (#1/Phase 3).
|
||||
if (nodeisstr(c, a)) { extra = 2; };
|
||||
if (nodeisslice(c, a)) { extra = 2; };
|
||||
// #21: tagged-CALL arg was pushed AX/DX/CX/R8 high→low
|
||||
// by pushargsrev; size the per-arg pop to match so the
|
||||
// next arg's POPQ doesn't land on residual tag/payload
|
||||
// words and shift intidx out of sync.
|
||||
let tcs: i32 = taggedcallslot(c, a);
|
||||
if (tcs > 0) { extra = tcs / 8 - 1; };
|
||||
let words: i32 = 1 + extra;
|
||||
let w: i32 = 0;
|
||||
for (w < words) {
|
||||
if (intidx < 6) {
|
||||
emitline("\tPOPQ\t");
|
||||
emitline(argregname(intidx));
|
||||
emitline("\n");
|
||||
intidx += 1;
|
||||
} else {
|
||||
stackslots += 1;
|
||||
};
|
||||
popped += 1;
|
||||
w += 1;
|
||||
};
|
||||
popped += 1;
|
||||
w += 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user