wcc: struct-return float fields via SSE return regs (#171a)
The RETURN twin of #165: a qualifying float-struct was returned GP-only (struct{f64,f64} in AX/DX instead of X0/X1) — value-correct via GP transport but not SysV register-class conformant. Route each float eightbyte through the SSE return cursor (X0/X1) and each integer eightbyte through GP (AX/DX) via independent cursors, at the struct-return SEND and RECV, both stages, reusing struct_float_class verbatim. Closes the temporary tuple-SSE/struct-GP divergence opened across #164/#165. A qualifying struct has >=1 lone f64 so maxalign is 8 and the ABI slot is an 8-multiple — no sub-8 tail — so #169's sized tail is unreachable here and the integer eightbyte uses a full MOVQ (cstage agrees, proven by the f64i32 cs==ww byte-id). f32 / multi-float-per-eightbyte stays GP (deferred #171b); >16B stays sret. Gate-blind and value-correct, so the discriminator is the SEND/RECV register class (MOVSD X0/X1 vs MOVQ AX/DX) — covered by probe 946_structret_run.
This commit is contained in:
@@ -25061,15 +25061,57 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(scroff: i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + 8): i64);
|
||||
emitline("(BP), DX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + 16): i64);
|
||||
emitline("(BP), CX\n");
|
||||
// #171a: float-bearing struct RETURN (return
|
||||
// twin of #165's param recv). A qualifying
|
||||
// struct's float eightbytes ride the SSE return
|
||||
// row (X0,X1 = tupsse), its INT eightbytes the
|
||||
// INTEGER return row (AX,DX = tupreg), on
|
||||
// INDEPENDENT cursors per SysV (ref/qbe/amd64/
|
||||
// sysv.c retr) — so a float lands in the next
|
||||
// XMM regardless of its positional eightbyte
|
||||
// (struct{f64,i32}: e0→X0, e1→AX, NOT DX). The
|
||||
// scratch is zero-padded to 24B so a full MOVQ
|
||||
// on a trailing INT eightbyte reads no garbage
|
||||
// (the #169 sized tail is a RECV concern).
|
||||
// structfloatclass gates to qualifying structs;
|
||||
// all-int + f32 keep the AX/DX/CX transport
|
||||
// (byte-id / #171b).
|
||||
let sfc: i32 = structfloatclass(c, c.fnret);
|
||||
if (sfc != 0) {
|
||||
let nb: i32 = sfc & 15;
|
||||
let gpcur: i32 = 0;
|
||||
let ssecur: i32 = 0;
|
||||
let e: i32 = 0;
|
||||
for (e < nb) {
|
||||
let issse: bool = (sfc & (16 << e)) != 0;
|
||||
if (issse) {
|
||||
emitline("\tMOVSD\t");
|
||||
emitoff((scroff + e*8): i64);
|
||||
emitline("(BP), ");
|
||||
emitline(tupsse(ssecur));
|
||||
emitline("\n");
|
||||
ssecur += 1;
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + e*8): i64);
|
||||
emitline("(BP), ");
|
||||
emitline(tupreg(gpcur));
|
||||
emitline("\n");
|
||||
gpcur += 1;
|
||||
};
|
||||
e += 1;
|
||||
};
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(scroff: i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + 8): i64);
|
||||
emitline("(BP), DX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + 16): i64);
|
||||
emitline("(BP), CX\n");
|
||||
};
|
||||
emitline("\tMOVQ\tBP, SP\n");
|
||||
emitline("\tPOPQ\tBP\n");
|
||||
emitline("\tRET\n");
|
||||
@@ -25539,6 +25581,49 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
// stomping MOVQ tail. Sizes >24B also fall through (sret
|
||||
// deferred, same constraint as #4). Mirrors the cstage
|
||||
// cgen.c N_LET receive branch.
|
||||
// #171a: float-bearing struct RECEIVE (return twin of #165's
|
||||
// param recv). cgexpr leaves each float eightbyte in its SSE
|
||||
// return reg (X0,X1 = tupsse) and each INT eightbyte in its
|
||||
// INTEGER return reg (AX,DX = tupreg), on INDEPENDENT cursors
|
||||
// per SysV (ref/qbe/amd64/sysv.c retr) — so a float is read
|
||||
// from the next XMM regardless of its positional eightbyte
|
||||
// (struct{f64,i32}: e0←X0, e1←AX). A qualifying struct's
|
||||
// abisize is maxalign-rounded to a multiple of 8 (an f64
|
||||
// forces align 8), so every eightbyte is a full word — the
|
||||
// #169 sized tail is unreachable here. structfloatclass gates
|
||||
// to qualifying structs; all-int + f32 fall to the GP recv
|
||||
// below (byte-id / #171b).
|
||||
if (rhs.kind == nkind.N_CALL && tn != nil) {
|
||||
let sfc: i32 = structfloatclass(c, tn);
|
||||
if (sfc != 0) {
|
||||
cgexpr(c, rhs);
|
||||
let nb: i32 = sfc & 15;
|
||||
let gpcur: i32 = 0;
|
||||
let ssecur: i32 = 0;
|
||||
let e: i32 = 0;
|
||||
for (e < nb) {
|
||||
let issse: bool = (sfc & (16 << e)) != 0;
|
||||
if (issse) {
|
||||
emitline("\tMOVSD\t");
|
||||
emitline(tupsse(ssecur));
|
||||
emitline(", ");
|
||||
emitoff((off + e*8): i64);
|
||||
emitline("(BP)\n");
|
||||
ssecur += 1;
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(tupreg(gpcur));
|
||||
emitline(", ");
|
||||
emitoff((off + e*8): i64);
|
||||
emitline("(BP)\n");
|
||||
gpcur += 1;
|
||||
};
|
||||
e += 1;
|
||||
};
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
};
|
||||
if (rhs.kind == nkind.N_CALL) {
|
||||
let sname: str;
|
||||
sname.ptr = nil; sname.len = 0;
|
||||
|
||||
@@ -728,15 +728,57 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(scroff: i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + 8): i64);
|
||||
emitline("(BP), DX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + 16): i64);
|
||||
emitline("(BP), CX\n");
|
||||
// #171a: float-bearing struct RETURN (return
|
||||
// twin of #165's param recv). A qualifying
|
||||
// struct's float eightbytes ride the SSE return
|
||||
// row (X0,X1 = tupsse), its INT eightbytes the
|
||||
// INTEGER return row (AX,DX = tupreg), on
|
||||
// INDEPENDENT cursors per SysV (ref/qbe/amd64/
|
||||
// sysv.c retr) — so a float lands in the next
|
||||
// XMM regardless of its positional eightbyte
|
||||
// (struct{f64,i32}: e0→X0, e1→AX, NOT DX). The
|
||||
// scratch is zero-padded to 24B so a full MOVQ
|
||||
// on a trailing INT eightbyte reads no garbage
|
||||
// (the #169 sized tail is a RECV concern).
|
||||
// structfloatclass gates to qualifying structs;
|
||||
// all-int + f32 keep the AX/DX/CX transport
|
||||
// (byte-id / #171b).
|
||||
let sfc: i32 = structfloatclass(c, c.fnret);
|
||||
if (sfc != 0) {
|
||||
let nb: i32 = sfc & 15;
|
||||
let gpcur: i32 = 0;
|
||||
let ssecur: i32 = 0;
|
||||
let e: i32 = 0;
|
||||
for (e < nb) {
|
||||
let issse: bool = (sfc & (16 << e)) != 0;
|
||||
if (issse) {
|
||||
emitline("\tMOVSD\t");
|
||||
emitoff((scroff + e*8): i64);
|
||||
emitline("(BP), ");
|
||||
emitline(tupsse(ssecur));
|
||||
emitline("\n");
|
||||
ssecur += 1;
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + e*8): i64);
|
||||
emitline("(BP), ");
|
||||
emitline(tupreg(gpcur));
|
||||
emitline("\n");
|
||||
gpcur += 1;
|
||||
};
|
||||
e += 1;
|
||||
};
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(scroff: i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + 8): i64);
|
||||
emitline("(BP), DX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + 16): i64);
|
||||
emitline("(BP), CX\n");
|
||||
};
|
||||
emitline("\tMOVQ\tBP, SP\n");
|
||||
emitline("\tPOPQ\tBP\n");
|
||||
emitline("\tRET\n");
|
||||
@@ -1206,6 +1248,49 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
// stomping MOVQ tail. Sizes >24B also fall through (sret
|
||||
// deferred, same constraint as #4). Mirrors the cstage
|
||||
// cgen.c N_LET receive branch.
|
||||
// #171a: float-bearing struct RECEIVE (return twin of #165's
|
||||
// param recv). cgexpr leaves each float eightbyte in its SSE
|
||||
// return reg (X0,X1 = tupsse) and each INT eightbyte in its
|
||||
// INTEGER return reg (AX,DX = tupreg), on INDEPENDENT cursors
|
||||
// per SysV (ref/qbe/amd64/sysv.c retr) — so a float is read
|
||||
// from the next XMM regardless of its positional eightbyte
|
||||
// (struct{f64,i32}: e0←X0, e1←AX). A qualifying struct's
|
||||
// abisize is maxalign-rounded to a multiple of 8 (an f64
|
||||
// forces align 8), so every eightbyte is a full word — the
|
||||
// #169 sized tail is unreachable here. structfloatclass gates
|
||||
// to qualifying structs; all-int + f32 fall to the GP recv
|
||||
// below (byte-id / #171b).
|
||||
if (rhs.kind == nkind.N_CALL && tn != nil) {
|
||||
let sfc: i32 = structfloatclass(c, tn);
|
||||
if (sfc != 0) {
|
||||
cgexpr(c, rhs);
|
||||
let nb: i32 = sfc & 15;
|
||||
let gpcur: i32 = 0;
|
||||
let ssecur: i32 = 0;
|
||||
let e: i32 = 0;
|
||||
for (e < nb) {
|
||||
let issse: bool = (sfc & (16 << e)) != 0;
|
||||
if (issse) {
|
||||
emitline("\tMOVSD\t");
|
||||
emitline(tupsse(ssecur));
|
||||
emitline(", ");
|
||||
emitoff((off + e*8): i64);
|
||||
emitline("(BP)\n");
|
||||
ssecur += 1;
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(tupreg(gpcur));
|
||||
emitline(", ");
|
||||
emitoff((off + e*8): i64);
|
||||
emitline("(BP)\n");
|
||||
gpcur += 1;
|
||||
};
|
||||
e += 1;
|
||||
};
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
};
|
||||
if (rhs.kind == nkind.N_CALL) {
|
||||
let sname: str;
|
||||
sname.ptr = nil; sname.len = 0;
|
||||
|
||||
@@ -25061,15 +25061,57 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(scroff: i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + 8): i64);
|
||||
emitline("(BP), DX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + 16): i64);
|
||||
emitline("(BP), CX\n");
|
||||
// #171a: float-bearing struct RETURN (return
|
||||
// twin of #165's param recv). A qualifying
|
||||
// struct's float eightbytes ride the SSE return
|
||||
// row (X0,X1 = tupsse), its INT eightbytes the
|
||||
// INTEGER return row (AX,DX = tupreg), on
|
||||
// INDEPENDENT cursors per SysV (ref/qbe/amd64/
|
||||
// sysv.c retr) — so a float lands in the next
|
||||
// XMM regardless of its positional eightbyte
|
||||
// (struct{f64,i32}: e0→X0, e1→AX, NOT DX). The
|
||||
// scratch is zero-padded to 24B so a full MOVQ
|
||||
// on a trailing INT eightbyte reads no garbage
|
||||
// (the #169 sized tail is a RECV concern).
|
||||
// structfloatclass gates to qualifying structs;
|
||||
// all-int + f32 keep the AX/DX/CX transport
|
||||
// (byte-id / #171b).
|
||||
let sfc: i32 = structfloatclass(c, c.fnret);
|
||||
if (sfc != 0) {
|
||||
let nb: i32 = sfc & 15;
|
||||
let gpcur: i32 = 0;
|
||||
let ssecur: i32 = 0;
|
||||
let e: i32 = 0;
|
||||
for (e < nb) {
|
||||
let issse: bool = (sfc & (16 << e)) != 0;
|
||||
if (issse) {
|
||||
emitline("\tMOVSD\t");
|
||||
emitoff((scroff + e*8): i64);
|
||||
emitline("(BP), ");
|
||||
emitline(tupsse(ssecur));
|
||||
emitline("\n");
|
||||
ssecur += 1;
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + e*8): i64);
|
||||
emitline("(BP), ");
|
||||
emitline(tupreg(gpcur));
|
||||
emitline("\n");
|
||||
gpcur += 1;
|
||||
};
|
||||
e += 1;
|
||||
};
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(scroff: i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + 8): i64);
|
||||
emitline("(BP), DX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + 16): i64);
|
||||
emitline("(BP), CX\n");
|
||||
};
|
||||
emitline("\tMOVQ\tBP, SP\n");
|
||||
emitline("\tPOPQ\tBP\n");
|
||||
emitline("\tRET\n");
|
||||
@@ -25539,6 +25581,49 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
// stomping MOVQ tail. Sizes >24B also fall through (sret
|
||||
// deferred, same constraint as #4). Mirrors the cstage
|
||||
// cgen.c N_LET receive branch.
|
||||
// #171a: float-bearing struct RECEIVE (return twin of #165's
|
||||
// param recv). cgexpr leaves each float eightbyte in its SSE
|
||||
// return reg (X0,X1 = tupsse) and each INT eightbyte in its
|
||||
// INTEGER return reg (AX,DX = tupreg), on INDEPENDENT cursors
|
||||
// per SysV (ref/qbe/amd64/sysv.c retr) — so a float is read
|
||||
// from the next XMM regardless of its positional eightbyte
|
||||
// (struct{f64,i32}: e0←X0, e1←AX). A qualifying struct's
|
||||
// abisize is maxalign-rounded to a multiple of 8 (an f64
|
||||
// forces align 8), so every eightbyte is a full word — the
|
||||
// #169 sized tail is unreachable here. structfloatclass gates
|
||||
// to qualifying structs; all-int + f32 fall to the GP recv
|
||||
// below (byte-id / #171b).
|
||||
if (rhs.kind == nkind.N_CALL && tn != nil) {
|
||||
let sfc: i32 = structfloatclass(c, tn);
|
||||
if (sfc != 0) {
|
||||
cgexpr(c, rhs);
|
||||
let nb: i32 = sfc & 15;
|
||||
let gpcur: i32 = 0;
|
||||
let ssecur: i32 = 0;
|
||||
let e: i32 = 0;
|
||||
for (e < nb) {
|
||||
let issse: bool = (sfc & (16 << e)) != 0;
|
||||
if (issse) {
|
||||
emitline("\tMOVSD\t");
|
||||
emitline(tupsse(ssecur));
|
||||
emitline(", ");
|
||||
emitoff((off + e*8): i64);
|
||||
emitline("(BP)\n");
|
||||
ssecur += 1;
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(tupreg(gpcur));
|
||||
emitline(", ");
|
||||
emitoff((off + e*8): i64);
|
||||
emitline("(BP)\n");
|
||||
gpcur += 1;
|
||||
};
|
||||
e += 1;
|
||||
};
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
};
|
||||
if (rhs.kind == nkind.N_CALL) {
|
||||
let sname: str;
|
||||
sname.ptr = nil; sname.len = 0;
|
||||
|
||||
Reference in New Issue
Block a user