w6c+w6c_ww: tuple-let receive keyed on type classify, not producer shape (C-t1, #33)
wwstage cglet's tuple receive was producer-SHAPE-keyed: the mixed str/scalar arm required s0_is_str != s1_is_str (syntactic) and the rt16 arm required an N_CALL rhs (rettupleof), so a scalar-scalar tuple LITERAL `let t: (u32,u32) = (3,4)` matched neither and fell to the generic single-word store — word 1 silently dropped (#209/#211-class syntactic-vs-type keying). cstage's twin arm was sz==16/32 magic-size keyed, so 24B 3-scalar tuples dropped words 2+ on BOTH sources. Both stages now key the same way: declared-type TY_TUPLE + in-cap register classify (cg_sret_retsize / sretretsize == 0, the shared SSoT), alias-peeled; the two wwstage shape arms collapse into one type-keyed arm walking the declared element list (the #240 lesson — never the producer's). Over-cap falls through to the sret receive exactly as before; unannotated `let t = f()` rides inferletcalltype. 941 grows the t1 rows: lit packed/16B/3-scalar + call 3-scalar fail at the C-t0 parent (10/39 checks — wwstage lit halves AND both-stage 24B halves), mixed-lit + unannotated-call anchor the untouched paths. Filed while probing: cstage silently accepts an over-cap tuple-LITERAL let where wwstage loud-stops (pre-existing at master, task #64).
This commit is contained in:
@@ -10499,21 +10499,28 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
ins2(c, A_MOVQ, areg(D_CX), amem(D_BP, off + 16));
|
||||
break;
|
||||
}
|
||||
/* 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. */
|
||||
/* Tuple initialiser (#105 / #164/#107): every IN-CAP tuple
|
||||
* receive routes here. 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.
|
||||
*
|
||||
* C-t1 (#33 family): keyed on the TYPE's register classify
|
||||
* (cg_sret_retsize == 0, the shared SSoT), not the sz==16/32
|
||||
* magic — that key missed sz==24/40/48 in-cap shapes (3-scalar
|
||||
* tuples dropped words 2+ silently) and pre-C-t0 missed the
|
||||
* packed sz==8 entirely. Over-cap falls through to the sret
|
||||
* receive below, exactly as before. */
|
||||
if (n->rhs && lu && lu->kind == TY_TUPLE
|
||||
&& (sz == 16 || sz == 32)) {
|
||||
&& cg_sret_retsize(lt) == 0) {
|
||||
cgexpr(c, n->rhs, *locals);
|
||||
int gpcur = 0, ssecur = 0, eoff = 0, ef32;
|
||||
for (Tparam *p = lu->params; p; p = p->next) {
|
||||
|
||||
@@ -32169,103 +32169,63 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
};
|
||||
// fall through to the generic sret receive below.
|
||||
};
|
||||
// 32B tuple init for `let t: (scalar, str) = call()` /
|
||||
// `let t: (str, scalar) = call()` (#105 / #164/#107). Each
|
||||
// element rides its SysV class — a float its SSE cursor reg
|
||||
// (X0,X1 = tupsse), an integer/ptr word its INTEGER cursor reg
|
||||
// In-cap tuple initialiser (#105 / #164/#107): every in-cap
|
||||
// tuple receive routes here, keyed on the DECLARED TYPE's
|
||||
// register classify (sretretsize == 0, the shared SSoT) —
|
||||
// mirror of cstage cgen.c N_LET tuple arm. Each element rides
|
||||
// its SysV class — a float its SSE cursor reg (X0,X1 =
|
||||
// tupsse), an integer/ptr word its INTEGER cursor reg
|
||||
// (tupreg), a slice/str its 3-word {ptr,len,cap} header over
|
||||
// consecutive INTEGER cursor regs — on INDEPENDENT counters.
|
||||
// tupstore 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). str IS []u8 (24B) → 32B tuple
|
||||
// (#1/Phase 3, task #5). Mirror of the cstage unified branch.
|
||||
// slice/str takes its 24B header). Over-cap falls through to
|
||||
// the sret receive below (#240 — an over-cap receive via the
|
||||
// register cursor read garbage past R8).
|
||||
//
|
||||
// #240: cap-gate on sz (16/32 = in-cap, AX/DX/CX/R8). Without it
|
||||
// an OVER-cap mixed-scalar tuple (e.g. (int,[]u8,str), 56B) was
|
||||
// received here via the register cursor (4 GP regs + R8 fill)
|
||||
// instead of from the sret dest the callee actually wrote — a
|
||||
// silent cs!=ww divergence (cstage gates the twin branch on
|
||||
// `sz == 16 || sz == 32`, cgen.c N_LET, and falls an over-cap
|
||||
// tuple through to the sret receive below).
|
||||
if (n.lhs != nil) {
|
||||
if (n.lhs.kind == nkind.N_TTUPLE) {
|
||||
let p0: *node = n.lhs.list;
|
||||
let p1: *node = nil;
|
||||
if (p0 != nil) { p1 = p0.next; };
|
||||
let p0t: *node = nil;
|
||||
let p1t: *node = nil;
|
||||
if (p0 != nil) { p0t = p0.lhs; };
|
||||
if (p1 != nil) { p1t = p1.lhs; };
|
||||
let s0_is_str: bool = isstrtype(c, p0t)
|
||||
|| isslicetype(c, p0t);
|
||||
let s1_is_str: bool = isstrtype(c, p1t)
|
||||
|| isslicetype(c, p1t);
|
||||
if (p0 != nil) {
|
||||
if (p1 != nil) {
|
||||
if ((s0_is_str != s1_is_str) && (sz == 16 || sz == 32)) {
|
||||
cgexpr(c, rhs);
|
||||
let gpcur: i32 = 0;
|
||||
let ssecur: i32 = 0;
|
||||
let eoff: i32 = 0;
|
||||
let q: *node = n.lhs.list;
|
||||
for (q != nil) {
|
||||
let qt: *node = q.lhs;
|
||||
let isflt: bool = isfloattype(c, qt);
|
||||
let wide: bool = isstrtype(c, qt)
|
||||
|| isslicetype(c, qt);
|
||||
tupstore(c, gpcur, ssecur,
|
||||
off + eoff, wide, qt);
|
||||
if (isflt) {
|
||||
ssecur = ssecur + 1;
|
||||
} else {
|
||||
gpcur = gpcur + tupebytes(wide);
|
||||
};
|
||||
if (wide) {
|
||||
eoff = eoff + (tyslicesize(): i32);
|
||||
} else {
|
||||
eoff = eoff + 8;
|
||||
};
|
||||
q = q.next;
|
||||
};
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
// C-t1 (#33): the old keys were producer-SHAPE — the mixed
|
||||
// str/scalar arm required s0_is_str != s1_is_str (syntactic)
|
||||
// AND sz==16/32, the rt16 arm required an N_CALL rhs
|
||||
// (rettupleof) — so a scalar-scalar tuple LITERAL `(3, 4)`
|
||||
// matched neither and fell to the generic single-word store,
|
||||
// silently dropping word 1 (#209/#211-class syntactic-vs-type
|
||||
// keying). Alias-peel mirrors cstage's type_chase_named; the
|
||||
// unannotated `let t = f()` shape rides the inferletcalltype
|
||||
// tn above.
|
||||
let ttup: *node = tn;
|
||||
for (ttup != nil && ttup.kind == nkind.N_TNAME) {
|
||||
ttup = aliaslookup(c, ttup.str);
|
||||
};
|
||||
// 16B tuple init from a function call (#105 / #164/#107). Each
|
||||
// eightbyte rides its SysV class: a float its SSE cursor reg
|
||||
// (X0,X1 = tupsse), an integer/ptr word its INTEGER cursor reg
|
||||
// (AX,DX = tupreg), INDEPENDENT counters — the RETURN leaves
|
||||
// floats in X0/X1 and integer words in AX/DX, so a blanket MOVQ
|
||||
// spill would store garbage where a float rode and the #103-
|
||||
// FACE-Z field read (MOVSD-from-slot) would see it. tupstore
|
||||
// routes each word from its real class; the same split drives
|
||||
// the destructure / reassign sites. Without this branch a 16B
|
||||
// tuple receive fell to the generic single-word store below and
|
||||
// dropped word1 — silent loss of t.1 (#102).
|
||||
let rt16: *node = rettupleof(c, rhs);
|
||||
if (rt16 != nil && sz == 16) {
|
||||
cgexpr(c, rhs);
|
||||
let gpcur: i32 = 0;
|
||||
let ssecur: i32 = 0;
|
||||
let eoff: i32 = 0;
|
||||
let q: *node = rt16.list;
|
||||
for (q != nil) {
|
||||
let qt: *node = q.lhs;
|
||||
let isflt: bool = isfloattype(c, qt);
|
||||
tupstore(c, gpcur, ssecur, off + eoff, false, qt);
|
||||
if (isflt) {
|
||||
ssecur = ssecur + 1;
|
||||
} else {
|
||||
gpcur = gpcur + 1;
|
||||
if (ttup != nil) {
|
||||
if (ttup.kind == nkind.N_TTUPLE
|
||||
&& sretretsize(c, ttup) == 0) {
|
||||
cgexpr(c, rhs);
|
||||
let gpcur: i32 = 0;
|
||||
let ssecur: i32 = 0;
|
||||
let eoff: i32 = 0;
|
||||
let q: *node = ttup.list;
|
||||
for (q != nil) {
|
||||
let qt: *node = q.lhs;
|
||||
let isflt: bool = isfloattype(c, qt);
|
||||
let wide: bool = isstrtype(c, qt)
|
||||
|| isslicetype(c, qt);
|
||||
tupstore(c, gpcur, ssecur,
|
||||
off + eoff, wide, qt);
|
||||
if (isflt) {
|
||||
ssecur = ssecur + 1;
|
||||
} else {
|
||||
gpcur = gpcur + tupebytes(wide);
|
||||
};
|
||||
if (wide) {
|
||||
eoff = eoff + (tyslicesize(): i32);
|
||||
} else {
|
||||
eoff = eoff + 8;
|
||||
};
|
||||
q = q.next;
|
||||
};
|
||||
eoff = eoff + 8;
|
||||
q = q.next;
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
// Array literal init: `let xs: [N]T = [a, b, c];` (or [_]T).
|
||||
// Walk elements in declaration order, store each at off + i*esz
|
||||
|
||||
@@ -1975,103 +1975,63 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
};
|
||||
// fall through to the generic sret receive below.
|
||||
};
|
||||
// 32B tuple init for `let t: (scalar, str) = call()` /
|
||||
// `let t: (str, scalar) = call()` (#105 / #164/#107). Each
|
||||
// element rides its SysV class — a float its SSE cursor reg
|
||||
// (X0,X1 = tupsse), an integer/ptr word its INTEGER cursor reg
|
||||
// In-cap tuple initialiser (#105 / #164/#107): every in-cap
|
||||
// tuple receive routes here, keyed on the DECLARED TYPE's
|
||||
// register classify (sretretsize == 0, the shared SSoT) —
|
||||
// mirror of cstage cgen.c N_LET tuple arm. Each element rides
|
||||
// its SysV class — a float its SSE cursor reg (X0,X1 =
|
||||
// tupsse), an integer/ptr word its INTEGER cursor reg
|
||||
// (tupreg), a slice/str its 3-word {ptr,len,cap} header over
|
||||
// consecutive INTEGER cursor regs — on INDEPENDENT counters.
|
||||
// tupstore 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). str IS []u8 (24B) → 32B tuple
|
||||
// (#1/Phase 3, task #5). Mirror of the cstage unified branch.
|
||||
// slice/str takes its 24B header). Over-cap falls through to
|
||||
// the sret receive below (#240 — an over-cap receive via the
|
||||
// register cursor read garbage past R8).
|
||||
//
|
||||
// #240: cap-gate on sz (16/32 = in-cap, AX/DX/CX/R8). Without it
|
||||
// an OVER-cap mixed-scalar tuple (e.g. (int,[]u8,str), 56B) was
|
||||
// received here via the register cursor (4 GP regs + R8 fill)
|
||||
// instead of from the sret dest the callee actually wrote — a
|
||||
// silent cs!=ww divergence (cstage gates the twin branch on
|
||||
// `sz == 16 || sz == 32`, cgen.c N_LET, and falls an over-cap
|
||||
// tuple through to the sret receive below).
|
||||
if (n.lhs != nil) {
|
||||
if (n.lhs.kind == nkind.N_TTUPLE) {
|
||||
let p0: *node = n.lhs.list;
|
||||
let p1: *node = nil;
|
||||
if (p0 != nil) { p1 = p0.next; };
|
||||
let p0t: *node = nil;
|
||||
let p1t: *node = nil;
|
||||
if (p0 != nil) { p0t = p0.lhs; };
|
||||
if (p1 != nil) { p1t = p1.lhs; };
|
||||
let s0_is_str: bool = isstrtype(c, p0t)
|
||||
|| isslicetype(c, p0t);
|
||||
let s1_is_str: bool = isstrtype(c, p1t)
|
||||
|| isslicetype(c, p1t);
|
||||
if (p0 != nil) {
|
||||
if (p1 != nil) {
|
||||
if ((s0_is_str != s1_is_str) && (sz == 16 || sz == 32)) {
|
||||
cgexpr(c, rhs);
|
||||
let gpcur: i32 = 0;
|
||||
let ssecur: i32 = 0;
|
||||
let eoff: i32 = 0;
|
||||
let q: *node = n.lhs.list;
|
||||
for (q != nil) {
|
||||
let qt: *node = q.lhs;
|
||||
let isflt: bool = isfloattype(c, qt);
|
||||
let wide: bool = isstrtype(c, qt)
|
||||
|| isslicetype(c, qt);
|
||||
tupstore(c, gpcur, ssecur,
|
||||
off + eoff, wide, qt);
|
||||
if (isflt) {
|
||||
ssecur = ssecur + 1;
|
||||
} else {
|
||||
gpcur = gpcur + tupebytes(wide);
|
||||
};
|
||||
if (wide) {
|
||||
eoff = eoff + (tyslicesize(): i32);
|
||||
} else {
|
||||
eoff = eoff + 8;
|
||||
};
|
||||
q = q.next;
|
||||
};
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
// C-t1 (#33): the old keys were producer-SHAPE — the mixed
|
||||
// str/scalar arm required s0_is_str != s1_is_str (syntactic)
|
||||
// AND sz==16/32, the rt16 arm required an N_CALL rhs
|
||||
// (rettupleof) — so a scalar-scalar tuple LITERAL `(3, 4)`
|
||||
// matched neither and fell to the generic single-word store,
|
||||
// silently dropping word 1 (#209/#211-class syntactic-vs-type
|
||||
// keying). Alias-peel mirrors cstage's type_chase_named; the
|
||||
// unannotated `let t = f()` shape rides the inferletcalltype
|
||||
// tn above.
|
||||
let ttup: *node = tn;
|
||||
for (ttup != nil && ttup.kind == nkind.N_TNAME) {
|
||||
ttup = aliaslookup(c, ttup.str);
|
||||
};
|
||||
// 16B tuple init from a function call (#105 / #164/#107). Each
|
||||
// eightbyte rides its SysV class: a float its SSE cursor reg
|
||||
// (X0,X1 = tupsse), an integer/ptr word its INTEGER cursor reg
|
||||
// (AX,DX = tupreg), INDEPENDENT counters — the RETURN leaves
|
||||
// floats in X0/X1 and integer words in AX/DX, so a blanket MOVQ
|
||||
// spill would store garbage where a float rode and the #103-
|
||||
// FACE-Z field read (MOVSD-from-slot) would see it. tupstore
|
||||
// routes each word from its real class; the same split drives
|
||||
// the destructure / reassign sites. Without this branch a 16B
|
||||
// tuple receive fell to the generic single-word store below and
|
||||
// dropped word1 — silent loss of t.1 (#102).
|
||||
let rt16: *node = rettupleof(c, rhs);
|
||||
if (rt16 != nil && sz == 16) {
|
||||
cgexpr(c, rhs);
|
||||
let gpcur: i32 = 0;
|
||||
let ssecur: i32 = 0;
|
||||
let eoff: i32 = 0;
|
||||
let q: *node = rt16.list;
|
||||
for (q != nil) {
|
||||
let qt: *node = q.lhs;
|
||||
let isflt: bool = isfloattype(c, qt);
|
||||
tupstore(c, gpcur, ssecur, off + eoff, false, qt);
|
||||
if (isflt) {
|
||||
ssecur = ssecur + 1;
|
||||
} else {
|
||||
gpcur = gpcur + 1;
|
||||
if (ttup != nil) {
|
||||
if (ttup.kind == nkind.N_TTUPLE
|
||||
&& sretretsize(c, ttup) == 0) {
|
||||
cgexpr(c, rhs);
|
||||
let gpcur: i32 = 0;
|
||||
let ssecur: i32 = 0;
|
||||
let eoff: i32 = 0;
|
||||
let q: *node = ttup.list;
|
||||
for (q != nil) {
|
||||
let qt: *node = q.lhs;
|
||||
let isflt: bool = isfloattype(c, qt);
|
||||
let wide: bool = isstrtype(c, qt)
|
||||
|| isslicetype(c, qt);
|
||||
tupstore(c, gpcur, ssecur,
|
||||
off + eoff, wide, qt);
|
||||
if (isflt) {
|
||||
ssecur = ssecur + 1;
|
||||
} else {
|
||||
gpcur = gpcur + tupebytes(wide);
|
||||
};
|
||||
if (wide) {
|
||||
eoff = eoff + (tyslicesize(): i32);
|
||||
} else {
|
||||
eoff = eoff + 8;
|
||||
};
|
||||
q = q.next;
|
||||
};
|
||||
eoff = eoff + 8;
|
||||
q = q.next;
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
// Array literal init: `let xs: [N]T = [a, b, c];` (or [_]T).
|
||||
// Walk elements in declaration order, store each at off + i*esz
|
||||
|
||||
@@ -32169,103 +32169,63 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
};
|
||||
// fall through to the generic sret receive below.
|
||||
};
|
||||
// 32B tuple init for `let t: (scalar, str) = call()` /
|
||||
// `let t: (str, scalar) = call()` (#105 / #164/#107). Each
|
||||
// element rides its SysV class — a float its SSE cursor reg
|
||||
// (X0,X1 = tupsse), an integer/ptr word its INTEGER cursor reg
|
||||
// In-cap tuple initialiser (#105 / #164/#107): every in-cap
|
||||
// tuple receive routes here, keyed on the DECLARED TYPE's
|
||||
// register classify (sretretsize == 0, the shared SSoT) —
|
||||
// mirror of cstage cgen.c N_LET tuple arm. Each element rides
|
||||
// its SysV class — a float its SSE cursor reg (X0,X1 =
|
||||
// tupsse), an integer/ptr word its INTEGER cursor reg
|
||||
// (tupreg), a slice/str its 3-word {ptr,len,cap} header over
|
||||
// consecutive INTEGER cursor regs — on INDEPENDENT counters.
|
||||
// tupstore 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). str IS []u8 (24B) → 32B tuple
|
||||
// (#1/Phase 3, task #5). Mirror of the cstage unified branch.
|
||||
// slice/str takes its 24B header). Over-cap falls through to
|
||||
// the sret receive below (#240 — an over-cap receive via the
|
||||
// register cursor read garbage past R8).
|
||||
//
|
||||
// #240: cap-gate on sz (16/32 = in-cap, AX/DX/CX/R8). Without it
|
||||
// an OVER-cap mixed-scalar tuple (e.g. (int,[]u8,str), 56B) was
|
||||
// received here via the register cursor (4 GP regs + R8 fill)
|
||||
// instead of from the sret dest the callee actually wrote — a
|
||||
// silent cs!=ww divergence (cstage gates the twin branch on
|
||||
// `sz == 16 || sz == 32`, cgen.c N_LET, and falls an over-cap
|
||||
// tuple through to the sret receive below).
|
||||
if (n.lhs != nil) {
|
||||
if (n.lhs.kind == nkind.N_TTUPLE) {
|
||||
let p0: *node = n.lhs.list;
|
||||
let p1: *node = nil;
|
||||
if (p0 != nil) { p1 = p0.next; };
|
||||
let p0t: *node = nil;
|
||||
let p1t: *node = nil;
|
||||
if (p0 != nil) { p0t = p0.lhs; };
|
||||
if (p1 != nil) { p1t = p1.lhs; };
|
||||
let s0_is_str: bool = isstrtype(c, p0t)
|
||||
|| isslicetype(c, p0t);
|
||||
let s1_is_str: bool = isstrtype(c, p1t)
|
||||
|| isslicetype(c, p1t);
|
||||
if (p0 != nil) {
|
||||
if (p1 != nil) {
|
||||
if ((s0_is_str != s1_is_str) && (sz == 16 || sz == 32)) {
|
||||
cgexpr(c, rhs);
|
||||
let gpcur: i32 = 0;
|
||||
let ssecur: i32 = 0;
|
||||
let eoff: i32 = 0;
|
||||
let q: *node = n.lhs.list;
|
||||
for (q != nil) {
|
||||
let qt: *node = q.lhs;
|
||||
let isflt: bool = isfloattype(c, qt);
|
||||
let wide: bool = isstrtype(c, qt)
|
||||
|| isslicetype(c, qt);
|
||||
tupstore(c, gpcur, ssecur,
|
||||
off + eoff, wide, qt);
|
||||
if (isflt) {
|
||||
ssecur = ssecur + 1;
|
||||
} else {
|
||||
gpcur = gpcur + tupebytes(wide);
|
||||
};
|
||||
if (wide) {
|
||||
eoff = eoff + (tyslicesize(): i32);
|
||||
} else {
|
||||
eoff = eoff + 8;
|
||||
};
|
||||
q = q.next;
|
||||
};
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
// C-t1 (#33): the old keys were producer-SHAPE — the mixed
|
||||
// str/scalar arm required s0_is_str != s1_is_str (syntactic)
|
||||
// AND sz==16/32, the rt16 arm required an N_CALL rhs
|
||||
// (rettupleof) — so a scalar-scalar tuple LITERAL `(3, 4)`
|
||||
// matched neither and fell to the generic single-word store,
|
||||
// silently dropping word 1 (#209/#211-class syntactic-vs-type
|
||||
// keying). Alias-peel mirrors cstage's type_chase_named; the
|
||||
// unannotated `let t = f()` shape rides the inferletcalltype
|
||||
// tn above.
|
||||
let ttup: *node = tn;
|
||||
for (ttup != nil && ttup.kind == nkind.N_TNAME) {
|
||||
ttup = aliaslookup(c, ttup.str);
|
||||
};
|
||||
// 16B tuple init from a function call (#105 / #164/#107). Each
|
||||
// eightbyte rides its SysV class: a float its SSE cursor reg
|
||||
// (X0,X1 = tupsse), an integer/ptr word its INTEGER cursor reg
|
||||
// (AX,DX = tupreg), INDEPENDENT counters — the RETURN leaves
|
||||
// floats in X0/X1 and integer words in AX/DX, so a blanket MOVQ
|
||||
// spill would store garbage where a float rode and the #103-
|
||||
// FACE-Z field read (MOVSD-from-slot) would see it. tupstore
|
||||
// routes each word from its real class; the same split drives
|
||||
// the destructure / reassign sites. Without this branch a 16B
|
||||
// tuple receive fell to the generic single-word store below and
|
||||
// dropped word1 — silent loss of t.1 (#102).
|
||||
let rt16: *node = rettupleof(c, rhs);
|
||||
if (rt16 != nil && sz == 16) {
|
||||
cgexpr(c, rhs);
|
||||
let gpcur: i32 = 0;
|
||||
let ssecur: i32 = 0;
|
||||
let eoff: i32 = 0;
|
||||
let q: *node = rt16.list;
|
||||
for (q != nil) {
|
||||
let qt: *node = q.lhs;
|
||||
let isflt: bool = isfloattype(c, qt);
|
||||
tupstore(c, gpcur, ssecur, off + eoff, false, qt);
|
||||
if (isflt) {
|
||||
ssecur = ssecur + 1;
|
||||
} else {
|
||||
gpcur = gpcur + 1;
|
||||
if (ttup != nil) {
|
||||
if (ttup.kind == nkind.N_TTUPLE
|
||||
&& sretretsize(c, ttup) == 0) {
|
||||
cgexpr(c, rhs);
|
||||
let gpcur: i32 = 0;
|
||||
let ssecur: i32 = 0;
|
||||
let eoff: i32 = 0;
|
||||
let q: *node = ttup.list;
|
||||
for (q != nil) {
|
||||
let qt: *node = q.lhs;
|
||||
let isflt: bool = isfloattype(c, qt);
|
||||
let wide: bool = isstrtype(c, qt)
|
||||
|| isslicetype(c, qt);
|
||||
tupstore(c, gpcur, ssecur,
|
||||
off + eoff, wide, qt);
|
||||
if (isflt) {
|
||||
ssecur = ssecur + 1;
|
||||
} else {
|
||||
gpcur = gpcur + tupebytes(wide);
|
||||
};
|
||||
if (wide) {
|
||||
eoff = eoff + (tyslicesize(): i32);
|
||||
} else {
|
||||
eoff = eoff + 8;
|
||||
};
|
||||
q = q.next;
|
||||
};
|
||||
eoff = eoff + 8;
|
||||
q = q.next;
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
// Array literal init: `let xs: [N]T = [a, b, c];` (or [_]T).
|
||||
// Walk elements in declaration order, store each at off + i*esz
|
||||
|
||||
@@ -163,6 +163,75 @@ static const struct row rows[] = {
|
||||
" if (b != 4) { return 2; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0 },
|
||||
|
||||
/* ---- C-t1 (#33): the let RECEIVE re-keyed onto the declared
|
||||
* type's register classify. Pre-C-t1 wwstage keyed on producer
|
||||
* SHAPE (mixed-str syntactic / rettupleof N_CALL) so a
|
||||
* scalar-scalar LITERAL matched neither and dropped word 1;
|
||||
* cstage keyed on sz==16/32 so 24B 3-scalar shapes dropped words
|
||||
* on BOTH sources. ---- */
|
||||
{ "t1_lit_packed",
|
||||
"package main;\n"
|
||||
"fn second() u32 = {\n"
|
||||
" let t: (u32, u32) = (3, 4);\n"
|
||||
" if (t.0 != 3) { return 99; };\n"
|
||||
" return t.1;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" if (second() != 4) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0 },
|
||||
{ "t1_lit_16",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let t: (i64, i64) = (3, 4);\n"
|
||||
" if (t.0 != 3) { return 1; };\n"
|
||||
" if (t.1 != 4) { return 2; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0 },
|
||||
{ "t1_lit_mixed",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let t: (i64, str) = (12, \"wxyz\");\n"
|
||||
" if (t.0 != 12) { return 1; };\n"
|
||||
" if (len(t.1) != 4) { return 2; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0 },
|
||||
{ "t1_lit_3scalar",
|
||||
"package main;\n"
|
||||
"fn second() i64 = {\n"
|
||||
" let t: (i64, i64, i64) = (3, 4, 5);\n"
|
||||
" if (t.0 != 3) { return -1; };\n"
|
||||
" if (t.1 != 4) { return -2; };\n"
|
||||
" return t.2;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" if (second() != 5) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0 },
|
||||
{ "t1_call_3scalar",
|
||||
"package main;\n"
|
||||
"fn f() (i64, i64, i64) = {\n"
|
||||
" return (7, 8, 9);\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let t: (i64, i64, i64) = f();\n"
|
||||
" if (t.0 != 7) { return 1; };\n"
|
||||
" if (t.1 != 8) { return 2; };\n"
|
||||
" if (t.2 != 9) { return 3; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0 },
|
||||
{ "t1_call_noannot_neutral",
|
||||
"package main;\n"
|
||||
"fn f() (i64, i64) = {\n"
|
||||
" return (3, 4);\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let t = f();\n"
|
||||
" if (t.0 != 3) { return 1; };\n"
|
||||
" if (t.1 != 4) { return 2; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0 },
|
||||
};
|
||||
|
||||
/* build+run via a driver (ww / ww_ww); returns 0 pass, nonzero fail. */
|
||||
|
||||
Reference in New Issue
Block a user