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:
@@ -1926,6 +1926,83 @@ fn structparamsize(c: *cgen, t: *node) i32 = {
|
||||
return si.totsize;
|
||||
};
|
||||
|
||||
// structfloatclass — SysV per-eightbyte classification for the #165
|
||||
// float-bearing-struct param case (param twin of #171's struct return;
|
||||
// classifies per-eightbyte, not #163's per-element). Returns 0 when the
|
||||
// struct does NOT qualify — the caller keeps the all-GP transport, which
|
||||
// is correct + byte-identical there — for: not a <=16B struct; an all-
|
||||
// integer layout (no float to route); an f32 field; >1 float packed in
|
||||
// one eightbyte; a float straddling the 8-byte SysV eightbyte boundary;
|
||||
// or an aggregate field (SysV would recurse, out of scope). Otherwise a
|
||||
// packed result whose low bits hold the eightbyte count nb (1|2) and bit
|
||||
// (4+e) marks eightbyte e SSE-class (a lone f64). Qualifies iff every
|
||||
// eightbyte is pure-INT or a lone f64 AND at least one is f64. f32 /
|
||||
// sub-eightbyte packing deferred (#165b). Mirrors cstage
|
||||
// struct_float_class (cmd/w6c/cgen.c).
|
||||
fn structfloatclass(c: *cgen, t: *node) i32 = {
|
||||
if (c == nil) { return 0; };
|
||||
let r: *node = resolvetype(c, t);
|
||||
if (r == nil) { return 0; };
|
||||
if (r.kind != nkind.N_TNAME) { return 0; };
|
||||
let nm: str = r.str;
|
||||
if (streq(nm, "str")) { return 0; };
|
||||
if (primsize(nm) > 0) { return 0; };
|
||||
let si: *structinfo = structlookup(c, nm);
|
||||
if (si == nil) { return 0; };
|
||||
if (si.totsize <= 0) { return 0; };
|
||||
if (si.totsize > 16) { return 0; };
|
||||
// SysV classifies aggregates in 8-byte eightbytes; 8 is the
|
||||
// eightbyte stride, not a type footprint.
|
||||
let nb: i32 = 1;
|
||||
if (si.totsize > 8) { nb = 2; };
|
||||
let nflt0: i32 = 0; let nflt1: i32 = 0;
|
||||
let nint0: i32 = 0; let nint1: i32 = 0;
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
let foff: i32 = fi.foff;
|
||||
let fsz: i32 = fi.fsz;
|
||||
let e: i32 = foff / 8;
|
||||
if (e < 0) { return 0; };
|
||||
if (e >= nb) { return 0; };
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
if (isf32type(c, fi.tnode)) { return 0; };
|
||||
if ((foff & 7) != 0) { return 0; };
|
||||
if (fsz != 8) { return 0; };
|
||||
if (e == 0) { nflt0 += 1; } else { nflt1 += 1; };
|
||||
} else {
|
||||
if (isslicetype(c, fi.tnode)) { return 0; };
|
||||
if (isstrtype(c, fi.tnode)) { return 0; };
|
||||
if (istaggedtype(c, fi.tnode)) { return 0; };
|
||||
if (structparamsize(c, fi.tnode) > 0) { return 0; };
|
||||
// Alias-aware array/tuple reject, mirroring cstage's
|
||||
// NAMED-peeled TY_ARRAY/TY_TUPLE (cgen.c struct_float_class).
|
||||
// A direct-AST-kind N_TARRAY test misses an aliased array
|
||||
// and every tuple field; the fsz>8 guard below also lets a
|
||||
// <=8B one slip, so such a struct would wrongly SSE-route on
|
||||
// this stage but stay GP on cstage (a #165b leak).
|
||||
let rf: *node = resolvetype(c, fi.tnode);
|
||||
if (rf != nil) {
|
||||
if (rf.kind == nkind.N_TARRAY) { return 0; };
|
||||
if (rf.kind == nkind.N_TTUPLE) { return 0; };
|
||||
};
|
||||
if (fsz > 8) { return 0; };
|
||||
if ((foff + fsz - 1) / 8 != e) { return 0; };
|
||||
if (e == 0) { nint0 += 1; } else { nint1 += 1; };
|
||||
};
|
||||
fi = fi.finext;
|
||||
};
|
||||
let enc: i32 = nb;
|
||||
let hasfloat: bool = false;
|
||||
if (nflt0 == 1 && nint0 == 0) { enc += 16; hasfloat = true; }
|
||||
else { if (nflt0 != 0) { return 0; }; };
|
||||
if (nb == 2) {
|
||||
if (nflt1 == 1 && nint1 == 0) { enc += 32; hasfloat = true; }
|
||||
else { if (nflt1 != 0) { return 0; }; };
|
||||
};
|
||||
if (!hasfloat) { return 0; };
|
||||
return enc;
|
||||
};
|
||||
|
||||
// istaggedtype — alias-aware. Reads stamped tinfo so `T`,
|
||||
// `type alias = (A|B)`, `type error = !(invalid|overflow)` all
|
||||
// resolve to TY_TAGGED — tinfofornode handles the N_TBANG unwrap
|
||||
|
||||
Reference in New Issue
Block a user