A multi-float tuple return mis-routed: SEND pushed a stale AX leaving the float stranded in X0, while RECV (#105) read every float from X0 — so a (f64,f64) return collided both floats. Add an SSE cursor [X0,X1] parallel to the GP cursor [AX,DX,CX,R8], placing each element by its SysV class + within-class index (ref/qbe/amd64/sysv.c retr), symmetric send/recv across both stages, via a generic tuple_store/tupstore+tupsse helper that #171 will reuse for struct-return convergence. (f64,f64,f64) = 3 SSE eightbytes exceeds the 2-register cap and now fails loud (rule 7) rather than colliding. Unifying the 16B and 32B whole-tuple-single-var branches onto the dual cursor was required for f64+str coexistence; it also fixes a latent str-first single-var bug (the old 32B branch read .ptr from DX while the send placed it in AX). No str-first or 32B tuple exists in-tree, so integer paths stay byte-identical (990-997 green).
This commit is contained in:
358
cmd/w6c/cgen.c
358
cmd/w6c/cgen.c
@@ -39,6 +39,13 @@ static int *cg_frame;
|
||||
* semantics for synthetic scratches). 0 means "not yet allocated";
|
||||
* negative offsets returned by local_alloc are the live value. */
|
||||
static int cg_retscr;
|
||||
/* Per-fn @tupfscr offset (single-slot SSoT). A multi-float tuple return
|
||||
* (#164/#107) spills each float out of X0 to this scratch as the L→R
|
||||
* element walk clobbers X0, then reloads X0/X1 by SSE index after the
|
||||
* integer POPQ dance. Sized to the SSE register cap (X0,X1). Mirrors the
|
||||
* @retscr single-slot convention + wwstage's `@tupfscr` '@'-prefix dedup;
|
||||
* 0 means "not yet allocated". */
|
||||
static int cg_tupfscr;
|
||||
/* Per-fn @-prefix scratch SSoT (task #26, follow-up to #15-cstage's
|
||||
* @retscr). Pre-#26 each site allocated a labelseq-stamped fresh slot
|
||||
* per call (mklabel "tagbase" / "tagscr" / "argscr" / "idxscr"); the
|
||||
@@ -192,6 +199,17 @@ node_isslice(Node *n)
|
||||
* create_unpack_bindings element walk (ref/harec/src/check.c:1354-1416). */
|
||||
static const int tuple_rseq[] = { D_AX, D_DX, D_CX, D_R8 };
|
||||
|
||||
/* #164 (#107): SysV dual register-class return. A tuple (and, per #171,
|
||||
* a struct) return places each element by SysV class — a float rides the
|
||||
* SSE row [X0,X1], everything else the INTEGER row [AX,DX,CX,R8]
|
||||
* (tuple_rseq) — with the two rows advancing on INDEPENDENT counters, so
|
||||
* a float lands in the next XMM regardless of its positional slot
|
||||
* (ref/qbe/amd64/sysv.c retr L95-108, retreg={{RAX,RDX},{XMM0,XMM1}}).
|
||||
* ww extends the INTEGER row to 4 eightbytes; the SSE row keeps SysV's 2.
|
||||
* tuple_store is the shared per-element receive lowering so the struct-
|
||||
* return convergence (#171) is a call-site swap, not a redesign. */
|
||||
static const int tuple_sse_seq[] = { D_X0, D_X1 };
|
||||
|
||||
static int
|
||||
tuple_ebytes(int wide)
|
||||
{
|
||||
@@ -1242,6 +1260,36 @@ ins1(Cg *c, int op, Adr to)
|
||||
emit(c, p);
|
||||
}
|
||||
|
||||
/* tuple_store — store one received tuple element at BP-relative `off`
|
||||
* from its SysV-class register. A slice/str rides its 3-word
|
||||
* {ptr,len,cap} header from the INTEGER cursor tuple_rseq[gp..]; a float
|
||||
* rides tuple_sse_seq[sse] via MOVSD/MOVSS (#105 single-float widened to
|
||||
* the SSE cursor for #164/#107 multi-float); a scalar rides one INTEGER
|
||||
* word from tuple_rseq[gp]. The caller owns the dual cursor (validated +
|
||||
* advanced); this just emits the store. Shared by N_LET/N_MLET/N_MASSIGN
|
||||
* and, per #171, struct unpack — mirrors wwstage cgenstmt.ww tupstore. */
|
||||
static void
|
||||
tuple_store(Cg *c, Type *t, int wide, int gp, int sse, int off)
|
||||
{
|
||||
int f32 = 0;
|
||||
|
||||
if (wide) {
|
||||
ins2(c, A_MOVQ, areg(tuple_rseq[gp + 0]),
|
||||
amem(D_BP, off + 0)); /* .ptr */
|
||||
ins2(c, A_MOVQ, areg(tuple_rseq[gp + 1]),
|
||||
amem(D_BP, off + 8)); /* .len */
|
||||
ins2(c, A_MOVQ, areg(tuple_rseq[gp + 2]),
|
||||
amem(D_BP, off + 16)); /* .cap */
|
||||
return;
|
||||
}
|
||||
if (fld_isfloat(t, &f32)) {
|
||||
ins2(c, f32 ? A_MOVSS : A_MOVSD, areg(tuple_sse_seq[sse]),
|
||||
amem(D_BP, off));
|
||||
return;
|
||||
}
|
||||
ins2(c, A_MOVQ, areg(tuple_rseq[gp]), amem(D_BP, off));
|
||||
}
|
||||
|
||||
static void
|
||||
ins0(Cg *c, int op)
|
||||
{
|
||||
@@ -7326,68 +7374,38 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
ins2(c, A_MOVQ, areg(D_CX), amem(D_BP, off + 16));
|
||||
break;
|
||||
}
|
||||
/* 2-tuple initialiser from a function call. An integer word
|
||||
* rides its tuple_rseq[] reg (AX, DX); a single f64/f32 word
|
||||
* rides X0, the SSE return reg — the RETURN leaves the float
|
||||
* in X0 and pushes garbage through that word's integer slot,
|
||||
* so a blanket MOVQ-from-integer spill stores garbage and the
|
||||
* #103-FACE-Z field read (MOVSD-from-slot) reads it (#105).
|
||||
* Spill each word from its real class. Multi-float tuples
|
||||
* collide on X0 at the RETURN (#107), out of scope here. */
|
||||
if (n->rhs && lu && lu->kind == TY_TUPLE && sz == 16) {
|
||||
Tparam *p0 = lu->params;
|
||||
Tparam *p1 = p0 ? p0->next : NULL;
|
||||
int f0_f32 = 0, f1_f32 = 0;
|
||||
int e0_f = p0 && fld_isfloat(p0->type, &f0_f32);
|
||||
int e1_f = p1 && fld_isfloat(p1->type, &f1_f32);
|
||||
/* Tuple initialiser from a function call (#105 / #164/#107),
|
||||
* 16B (two eightbytes) or 32B (scalar/float + slice/str header).
|
||||
* Each element rides its SysV class: a float its SSE cursor reg
|
||||
* (X0,X1 = tuple_sse_seq), an integer/ptr word its INTEGER cursor
|
||||
* reg (tuple_rseq), a slice/str its 3-word {ptr,len,cap} header
|
||||
* over consecutive INTEGER cursor regs — INDEPENDENT counters,
|
||||
* so the RETURN leaves floats in X0/X1 and integer words in
|
||||
* AX/DX/CX/R8. A blanket MOVQ spill would store garbage where a
|
||||
* float rode and the #103-FACE-Z field read (MOVSD-from-slot)
|
||||
* would see it. tuple_store routes each element from its real
|
||||
* class into its positional slot (eoff steps by the element's
|
||||
* slot size: a slice/str takes its 24B header); the same split
|
||||
* drives the destructure / reassign sites. */
|
||||
if (n->rhs && lu && lu->kind == TY_TUPLE
|
||||
&& (sz == 16 || sz == 32)) {
|
||||
cgexpr(c, n->rhs, *locals);
|
||||
if (e0_f)
|
||||
ins2(c, f0_f32 ? A_MOVSS : A_MOVSD,
|
||||
areg(D_X0), amem(D_BP, off + 0));
|
||||
else
|
||||
ins2(c, A_MOVQ, areg(tuple_rseq[0]),
|
||||
amem(D_BP, off + 0));
|
||||
if (e1_f)
|
||||
ins2(c, f1_f32 ? A_MOVSS : A_MOVSD,
|
||||
areg(D_X0), amem(D_BP, off + 8));
|
||||
else
|
||||
ins2(c, A_MOVQ, areg(tuple_rseq[1]),
|
||||
amem(D_BP, off + 8));
|
||||
break;
|
||||
}
|
||||
/* 32B tuple initialiser for `(scalar, str)` / `(str, scalar)`.
|
||||
* Per the AX:DX:CX:R8 return convention: AX = scalar elem,
|
||||
* DX = str.ptr, CX = str.len, R8 = str.cap. The slot is laid
|
||||
* out positionally (str takes 24B at its position), so we route
|
||||
* each register to the slot dictated by the element's type, not
|
||||
* by AX/DX position. str IS []u8 (24B) → 32B tuple (#1/Phase 3,
|
||||
* task #5). */
|
||||
if (n->rhs && lu && lu->kind == TY_TUPLE && sz == 32) {
|
||||
Tparam *p0 = lu->params;
|
||||
Tparam *p1 = p0 ? p0->next : NULL;
|
||||
Type *t0 = p0 ? p0->type : NULL;
|
||||
Type *t1 = p1 ? p1->type : NULL;
|
||||
Type *u0 = (t0 && t0->kind == TY_NAMED) ? t0->under : t0;
|
||||
Type *u1 = (t1 && t1->kind == TY_NAMED) ? t1->under : t1;
|
||||
int e0_str = u0 && u0->kind == TY_STR;
|
||||
int e1_str = u1 && u1->kind == TY_STR;
|
||||
if (e0_str ^ e1_str) {
|
||||
cgexpr(c, n->rhs, *locals);
|
||||
if (e0_str) {
|
||||
/* layout: str@+0 (24B), scalar@+24. */
|
||||
ins2(c, A_MOVQ, areg(D_DX), amem(D_BP, off + 0));
|
||||
ins2(c, A_MOVQ, areg(D_CX), amem(D_BP, off + 8));
|
||||
ins2(c, A_MOVQ, areg(D_R8), amem(D_BP, off + 16));
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, off + 24));
|
||||
} else {
|
||||
/* layout: scalar@+0 (8B), str@+8 (24B). */
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, off + 0));
|
||||
ins2(c, A_MOVQ, areg(D_DX), amem(D_BP, off + 8));
|
||||
ins2(c, A_MOVQ, areg(D_CX), amem(D_BP, off + 16));
|
||||
ins2(c, A_MOVQ, areg(D_R8), amem(D_BP, off + 24));
|
||||
}
|
||||
break;
|
||||
int gpcur = 0, ssecur = 0, eoff = 0, ef32;
|
||||
for (Tparam *p = lu->params; p; p = p->next) {
|
||||
Type *pu = (p->type && p->type->kind == TY_NAMED)
|
||||
? p->type->under : p->type;
|
||||
int wide = pu && (pu->kind == TY_SLICE
|
||||
|| pu->kind == TY_STR);
|
||||
int isflt = fld_isfloat(p->type, &ef32);
|
||||
tuple_store(c, p->type, wide, gpcur, ssecur,
|
||||
off + eoff);
|
||||
if (isflt)
|
||||
ssecur++;
|
||||
else
|
||||
gpcur += tuple_ebytes(wide);
|
||||
eoff += wide ? (int)pu->size : 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* Tagged-union initialiser. Delegates to cg_widen_tagged_store,
|
||||
* which handles nullable fold, tagged→tagged (with tag remap
|
||||
@@ -8050,37 +8068,79 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
break;
|
||||
}
|
||||
if (n->lhs && n->lhs->kind == N_TUPLE) {
|
||||
/* #83: positional per-element register-return. Walk the
|
||||
* tuple's elements (harec create_unpack_bindings,
|
||||
* ref/harec/src/check.c:1354-1416); each rides consecutive
|
||||
* eightbytes over tuple_rseq[]. A slice/str rides its 3-word
|
||||
* {ptr,len,cap} header (ref/hare/rt/ensure.ha:4-8), cgexpr
|
||||
* leaving it in (AX,BX,CX); a scalar rides 1 word in AX.
|
||||
* Spill each element's word(s) L→R, then pop into the
|
||||
* cursor's registers in reverse so positional slot i lands in
|
||||
* tuple_rseq[i] — (scalar,str) keeps the historical AX +
|
||||
* DX,CX,R8 layout, and the SAME cursor drives the receive
|
||||
* sites. Over-capacity is a loud stop (return-ABI #10), never
|
||||
* a silent drop. */
|
||||
int cap = (int)(sizeof tuple_rseq / sizeof tuple_rseq[0]);
|
||||
int total = 0;
|
||||
for (Node *e = n->lhs->list; e; e = e->next)
|
||||
total += tuple_ebytes(node_isstr(e) || node_isslice(e));
|
||||
if (total > cap)
|
||||
fatal("tuple return exceeds register-return ABI "
|
||||
"capacity (%d eightbytes); see return-ABI #10",
|
||||
cap);
|
||||
/* #83 / #164 (#107): positional register-return over a SysV
|
||||
* dual class cursor. Each element rides its SysV class
|
||||
* (harec create_unpack_bindings, ref/harec/src/check.c:1354-
|
||||
* 1416): a float takes one SSE eightbyte (X0,X1 = tuple_sse_
|
||||
* seq), everything else INTEGER eightbytes over tuple_rseq —
|
||||
* a slice/str its 3-word {ptr,len,cap} header (ref/hare/rt/
|
||||
* ensure.ha:4-8) cgexpr leaves in (AX,BX,CX), a scalar 1 word
|
||||
* in AX. Integer words spill L→R to the stack and pop into the
|
||||
* INTEGER cursor in reverse so positional slot i lands in
|
||||
* tuple_rseq[i] (byte-id with #83 when no float is present).
|
||||
* Each float must spill X0 to @tupfscr as we walk, because a
|
||||
* later element's cgexpr clobbers X0; after the integer pops
|
||||
* the saved floats reload into X0/X1 by SSE index — INDEPENDENT
|
||||
* of the integer cursor (ref/qbe/amd64/sysv.c retr L95-108).
|
||||
* Both rows are loud-stopped at their cap (rule-7, never a
|
||||
* silent collide): INTEGER 4, SSE 2. The SAME class split
|
||||
* drives the receive sites. */
|
||||
int gpcap = (int)(sizeof tuple_rseq / sizeof tuple_rseq[0]);
|
||||
int ssecap = (int)(sizeof tuple_sse_seq
|
||||
/ sizeof tuple_sse_seq[0]);
|
||||
int gptotal = 0, ssecount = 0, f32;
|
||||
for (Node *e = n->lhs->list; e; e = e->next) {
|
||||
int wide = node_isstr(e) || node_isslice(e);
|
||||
cgexpr(c, e, *locals); /* scalar=AX; slice/str=AX,BX,CX */
|
||||
if (fld_isfloat(e->type, &f32))
|
||||
ssecount++;
|
||||
else
|
||||
gptotal += tuple_ebytes(node_isstr(e)
|
||||
|| node_isslice(e));
|
||||
}
|
||||
if (gptotal > gpcap)
|
||||
fatal("tuple return exceeds integer register-return "
|
||||
"ABI capacity (%d eightbytes: AX,DX,CX,R8); "
|
||||
"see return-ABI #10", gpcap);
|
||||
if (ssecount > ssecap)
|
||||
fatal("tuple return exceeds SSE register-return ABI "
|
||||
"capacity (%d eightbytes: X0,X1); "
|
||||
"see return-ABI #10", ssecap);
|
||||
int fscr = 0;
|
||||
if (ssecount > 0) {
|
||||
if (cg_tupfscr != 0)
|
||||
fscr = cg_tupfscr;
|
||||
else {
|
||||
fscr = local_alloc(c, locals, "@tupfscr",
|
||||
ssecap * 8, cg_frame);
|
||||
cg_tupfscr = fscr;
|
||||
}
|
||||
}
|
||||
int sseidx = 0;
|
||||
for (Node *e = n->lhs->list; e; e = e->next) {
|
||||
int isflt = fld_isfloat(e->type, &f32);
|
||||
cgexpr(c, e, *locals); /* scalar=AX; slice/str=AX,BX,CX; float=X0 */
|
||||
if (isflt) {
|
||||
ins2(c, f32 ? A_MOVSS : A_MOVSD, areg(D_X0),
|
||||
amem(D_BP, fscr + sseidx * 8));
|
||||
sseidx++;
|
||||
continue;
|
||||
}
|
||||
ins1(c, A_PUSHQ, areg(D_AX)); /* scalar / .ptr */
|
||||
if (wide) {
|
||||
if (node_isstr(e) || node_isslice(e)) {
|
||||
ins1(c, A_PUSHQ, areg(D_BX)); /* .len */
|
||||
ins1(c, A_PUSHQ, areg(D_CX)); /* .cap */
|
||||
}
|
||||
}
|
||||
for (int i = total - 1; i >= 0; i--)
|
||||
for (int i = gptotal - 1; i >= 0; i--)
|
||||
ins1(c, A_POPQ, areg(tuple_rseq[i]));
|
||||
int j = 0;
|
||||
for (Node *e = n->lhs->list; e; e = e->next) {
|
||||
if (!fld_isfloat(e->type, &f32))
|
||||
continue;
|
||||
ins2(c, f32 ? A_MOVSS : A_MOVSD,
|
||||
amem(D_BP, fscr + j * 8),
|
||||
areg(tuple_sse_seq[j]));
|
||||
j++;
|
||||
}
|
||||
} else if (n->lhs) {
|
||||
cgexpr(c, n->lhs, *locals);
|
||||
} else {
|
||||
@@ -8263,55 +8323,51 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
break;
|
||||
}
|
||||
case N_MLET: {
|
||||
/* #83: positional per-element destructure store. The rhs left
|
||||
* each tuple element in the register-return cursor (see N_RETURN
|
||||
* / harec create_unpack_bindings, ref/harec/src/check.c:1354-1416);
|
||||
* walk the bindings over the SAME cursor and store each at its
|
||||
* own width — a slice/str's 3-word {ptr,len,cap} header
|
||||
* (ref/hare/rt/ensure.ha:4-8) into a header-sized slot (sized
|
||||
* from u->size so #1 propagates), a scalar's 1 word into an 8B
|
||||
* slot. Over-capacity is a loud stop, not a silent drop. */
|
||||
/* #83 / #164 (#107): positional per-element destructure store.
|
||||
* The rhs left each tuple element in its SysV-class register
|
||||
* (see N_RETURN / harec create_unpack_bindings, ref/harec/src/
|
||||
* check.c:1354-1416); walk the bindings over the SAME dual
|
||||
* cursor and store each at its own width — a slice/str's 3-word
|
||||
* {ptr,len,cap} header (ref/hare/rt/ensure.ha:4-8) into a
|
||||
* header-sized slot (sized from u->size so #1 propagates), a
|
||||
* float from X0/X1 (SSE cursor), a scalar's 1 word from the
|
||||
* INTEGER cursor into an 8B slot. Both rows loud-stop at their
|
||||
* cap. */
|
||||
cgexpr(c, n->rhs, *locals);
|
||||
int cap = (int)(sizeof tuple_rseq / sizeof tuple_rseq[0]);
|
||||
int total = 0;
|
||||
int gpcap = (int)(sizeof tuple_rseq / sizeof tuple_rseq[0]);
|
||||
int ssecap = (int)(sizeof tuple_sse_seq
|
||||
/ sizeof tuple_sse_seq[0]);
|
||||
int gptotal = 0, ssetotal = 0, lf32;
|
||||
for (Node *l = n->list; l; l = l->next) {
|
||||
Type *t = l->type;
|
||||
Type *u = (t && t->kind == TY_NAMED) ? t->under : t;
|
||||
int wide = u && (u->kind == TY_SLICE || u->kind == TY_STR);
|
||||
total += tuple_ebytes(wide);
|
||||
if (fld_isfloat(t, &lf32))
|
||||
ssetotal++;
|
||||
else
|
||||
gptotal += tuple_ebytes(u && (u->kind == TY_SLICE
|
||||
|| u->kind == TY_STR));
|
||||
}
|
||||
if (total > cap)
|
||||
fatal("tuple destructure exceeds register-return ABI "
|
||||
"capacity (%d eightbytes); see return-ABI #10", cap);
|
||||
int cur = 0;
|
||||
if (gptotal > gpcap)
|
||||
fatal("tuple destructure exceeds integer register-return "
|
||||
"ABI capacity (%d eightbytes: AX,DX,CX,R8); "
|
||||
"see return-ABI #10", gpcap);
|
||||
if (ssetotal > ssecap)
|
||||
fatal("tuple destructure exceeds SSE register-return ABI "
|
||||
"capacity (%d eightbytes: X0,X1); see return-ABI #10",
|
||||
ssecap);
|
||||
int gpcur = 0, ssecur = 0;
|
||||
for (Node *l = n->list; l; l = l->next) {
|
||||
Type *t = l->type;
|
||||
Type *u = (t && t->kind == TY_NAMED) ? t->under : t;
|
||||
int wide = u && (u->kind == TY_SLICE || u->kind == TY_STR);
|
||||
int isflt = fld_isfloat(t, &lf32);
|
||||
int sz = wide ? (int)u->size : 8;
|
||||
int off = localoff(c, locals, l->str, sz, frame);
|
||||
if (wide) {
|
||||
ins2(c, A_MOVQ, areg(tuple_rseq[cur + 0]),
|
||||
amem(D_BP, off + 0)); /* .ptr */
|
||||
ins2(c, A_MOVQ, areg(tuple_rseq[cur + 1]),
|
||||
amem(D_BP, off + 8)); /* .len */
|
||||
ins2(c, A_MOVQ, areg(tuple_rseq[cur + 2]),
|
||||
amem(D_BP, off + 16)); /* .cap */
|
||||
} else {
|
||||
/* #105: an f64/f32 element rides X0, not its
|
||||
* integer cursor reg — MOVSD/MOVSS it, else
|
||||
* the slot gets garbage and the FACE-Z field
|
||||
* read sees it. X0 survives the reg->mem
|
||||
* stores. Single-float scope; #107 is multi. */
|
||||
int e_f32 = 0;
|
||||
if (fld_isfloat(t, &e_f32))
|
||||
ins2(c, e_f32 ? A_MOVSS : A_MOVSD,
|
||||
areg(D_X0), amem(D_BP, off));
|
||||
else
|
||||
ins2(c, A_MOVQ, areg(tuple_rseq[cur]),
|
||||
amem(D_BP, off));
|
||||
}
|
||||
cur += tuple_ebytes(wide);
|
||||
tuple_store(c, t, wide, gpcur, ssecur, off);
|
||||
if (isflt)
|
||||
ssecur++;
|
||||
else
|
||||
gpcur += tuple_ebytes(wide);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -8337,47 +8393,44 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
Type *rt = n->rhs ? n->rhs->type : NULL;
|
||||
Type *ru = (rt && rt->kind == TY_NAMED) ? rt->under : rt;
|
||||
Tparam *tp0 = (ru && ru->kind == TY_TUPLE) ? ru->params : NULL;
|
||||
int cap = (int)(sizeof tuple_rseq / sizeof tuple_rseq[0]);
|
||||
int total = 0;
|
||||
int gpcap = (int)(sizeof tuple_rseq / sizeof tuple_rseq[0]);
|
||||
int ssecap = (int)(sizeof tuple_sse_seq
|
||||
/ sizeof tuple_sse_seq[0]);
|
||||
int gptotal = 0, ssetotal = 0, mf32;
|
||||
for (Tparam *tp = tp0; tp; tp = tp->next) {
|
||||
Type *u = (tp->type && tp->type->kind == TY_NAMED)
|
||||
? tp->type->under : tp->type;
|
||||
total += tuple_ebytes(u && (u->kind == TY_SLICE
|
||||
|| u->kind == TY_STR));
|
||||
if (fld_isfloat(tp->type, &mf32))
|
||||
ssetotal++;
|
||||
else
|
||||
gptotal += tuple_ebytes(u && (u->kind == TY_SLICE
|
||||
|| u->kind == TY_STR));
|
||||
}
|
||||
if (total > cap)
|
||||
fatal("tuple destructure exceeds register-return ABI "
|
||||
"capacity (%d eightbytes); see return-ABI #10", cap);
|
||||
int cur = 0;
|
||||
if (gptotal > gpcap)
|
||||
fatal("tuple destructure exceeds integer register-return "
|
||||
"ABI capacity (%d eightbytes: AX,DX,CX,R8); "
|
||||
"see return-ABI #10", gpcap);
|
||||
if (ssetotal > ssecap)
|
||||
fatal("tuple destructure exceeds SSE register-return ABI "
|
||||
"capacity (%d eightbytes: X0,X1); see return-ABI #10",
|
||||
ssecap);
|
||||
int gpcur = 0, ssecur = 0;
|
||||
Tparam *tp = tp0;
|
||||
for (Node *l = n->list; l; l = l->next) {
|
||||
Type *et = tp ? tp->type : NULL;
|
||||
Type *u = (et && et->kind == TY_NAMED) ? et->under : et;
|
||||
int wide = u && (u->kind == TY_SLICE || u->kind == TY_STR);
|
||||
int isflt = fld_isfloat(et, &mf32);
|
||||
int off = (l->kind == N_IDENT)
|
||||
? localfind(*locals, l->str) : 0;
|
||||
if (off != 0) {
|
||||
if (wide) {
|
||||
ins2(c, A_MOVQ, areg(tuple_rseq[cur + 0]),
|
||||
amem(D_BP, off + 0)); /* .ptr */
|
||||
ins2(c, A_MOVQ, areg(tuple_rseq[cur + 1]),
|
||||
amem(D_BP, off + 8)); /* .len */
|
||||
ins2(c, A_MOVQ, areg(tuple_rseq[cur + 2]),
|
||||
amem(D_BP, off + 16)); /* .cap */
|
||||
} else {
|
||||
/* #105: f64/f32 element rides X0 (SSE),
|
||||
* not its integer cursor reg — see N_MLET. */
|
||||
int e_f32 = 0;
|
||||
if (fld_isfloat(et, &e_f32))
|
||||
ins2(c, e_f32 ? A_MOVSS : A_MOVSD,
|
||||
areg(D_X0), amem(D_BP, off));
|
||||
else
|
||||
ins2(c, A_MOVQ,
|
||||
areg(tuple_rseq[cur]),
|
||||
amem(D_BP, off));
|
||||
}
|
||||
}
|
||||
cur += tuple_ebytes(wide);
|
||||
/* harec `_` (off==0): skip the store but CONSUME the
|
||||
* cursor slot so the next element stays aligned. */
|
||||
if (off != 0)
|
||||
tuple_store(c, et, wide, gpcur, ssecur, off);
|
||||
if (isflt)
|
||||
ssecur++;
|
||||
else
|
||||
gpcur += tuple_ebytes(wide);
|
||||
if (tp) tp = tp->next;
|
||||
}
|
||||
break;
|
||||
@@ -8459,6 +8512,7 @@ cgfn(Cg *c, FILE *out, Node *fn)
|
||||
nloops = 0;
|
||||
cg_ret_type = fn->type ? fn->type->ret : NULL;
|
||||
cg_retscr = 0;
|
||||
cg_tupfscr = 0;
|
||||
cg_tagbase = 0;
|
||||
cg_tagbase_sz = 0;
|
||||
cg_tagscr = 0;
|
||||
|
||||
Reference in New Issue
Block a user