cgen: #57 in-cap tuple cursor fill keys on the DECLARED element type — tagged elems from concrete rvalues widen, both stages
The N_TUPLE literal's stamped type is CONSTRUCTED from its elements
(check.c N_TUPLE keeps untyped/concrete element types; assignability
is consumer-side), so the in-cap cursor fill — count
(tuple_lit_gpwords/tuplitgpwords) + push (tuple_lit_push_elem/
tuplitpushelem) — never saw the DECLARED tuple type. A declared-TAGGED
element whose expr is a concrete rvalue (`return (5: size, 9)` into
(un16, size)) counted ONE word and skipped the widen entirely: 2 words
sent against the receiver's declared 3-word walk, every later element
read garbage. Both stages, byte-identical, gate-blind (ken /tmp/ken57
p8/p9: t.1 read entry-junk). The let-literal twin
(`let t: (un16, size) = (5: size, 9)`) and the tagged-SECOND-elem
shift broke identically (probes q1/q2). The over-cap (sret) arm
already walks declared params (#240/#22b) — only the in-cap path was
declared-blind.
Fix threads the declared tuple type into the ONE shared helper pair
and its two loop sites:
- tuple_lit_gpwords/tuplitpushelem take the declared elem type;
declared-TAGGED + concrete rvalue widens into the shared tagged
scratch (cg_tagscr_slot/tagscradd + cg_widen_tagged_store/
cgwidentaggedstore, the cgreturn tagged-@retscr shape) and pushes
the box words; declared-TAGGED gates the SSE row off (a (void|f64)
box rides INTEGER eightbytes). Tagged->tagged subset (eslot
mismatch) louds — the #23/#40 widening-remap family.
- cg_tuple_lit_to_cursor/cgtuplelittocursor grow a decl param;
cgreturn's in-cap N_TUPLE loops thread cg_ret_type/c.fnret.list
(the same pp/pt walk its over-cap arm does); the N_LET in-cap
tuple arm passes the declared type for an N_TUPLE rhs; the bare
cgexpr route passes NULL/nil (emission unchanged).
Ident-elem sources keep the existing slot-load push byte-identically
(t57_ident_no_regress); the CALL-elem tripwire stays loud (#41,
t57_loud_call_elem). RESIDUAL FILED, not folded (rule 11): the
N_MASSIGN destructure-reassign literal rhs routes through the bare
cgexpr path (decl=NULL) and stays silent-wrong — probe q5_massign,
task #64, cited at the massign arm both stages. The annotated
multi-let spelling (`let (a, b): (un, size) = lit`) does not parse
(both stages), so N_MLET has no declared-literal route.
941 rows t57_*: return (named + inline union), let-literal, tagged
second elem, float payload, bare-untyped payload (rides the #33
chooser through the new wire), ident anchor, loud CALL tripwire;
ken's adversarial shapes (tagged-MID elem, two tagged rvalue elems
incl. void, plain-f64 SSE coexisting with a declared-tagged box), the
in-cap/over-cap boundary loud (k57d), and the NEW #57 tag-remap loud
pinned. Pre-fix at e8977a4: p8/p9 rows exit 1, q1_let exit 1,
q2_mixed exit 2.
Task #57.
This commit is contained in:
184
cmd/w6c/cgen.c
184
cmd/w6c/cgen.c
@@ -3289,10 +3289,20 @@ cg_structlit_fill_bp(Cg *c, Local **locals_p, Type *lu, Node *lit, int bp_off)
|
||||
* float rides the SSE row (0 GP words); str/slice push their 3-word
|
||||
* header; a tagged element its tuple_eslot/8 box words; a void element
|
||||
* pushes nothing (the checker's 0-slot — pre-#22 the push/receive
|
||||
* disagreed with the checker here, latent, no consumer); a scalar 1. */
|
||||
* disagreed with the checker here, latent, no consumer); a scalar 1.
|
||||
*
|
||||
* #57: `dt` is the DECLARED tuple element type (NULL when the consumer
|
||||
* has none). The N_TUPLE literal's stamped type is CONSTRUCTED from
|
||||
* its elements (check.c N_TUPLE), so a concrete rvalue under a
|
||||
* declared-TAGGED slot counted ONE word here while the receive walks
|
||||
* the declared eslot — the cursor shifted and every later element
|
||||
* read garbage. Declared-tagged keys the count on the DECLARED box. */
|
||||
static int
|
||||
tuple_lit_gpwords(Node *e)
|
||||
tuple_lit_gpwords(Node *e, Type *dt)
|
||||
{
|
||||
Type *du = dt ? type_chase_named(dt) : NULL;
|
||||
if (du && du->kind == TY_TAGGED)
|
||||
return tuple_eslot(dt) / 8;
|
||||
int f32;
|
||||
if (fld_isfloat(e->type, &f32)) return 0;
|
||||
if (node_isstr(e) || node_isslice(e)) return (int)(ty_str->size / 8);
|
||||
@@ -3306,20 +3316,49 @@ tuple_lit_gpwords(Node *e)
|
||||
* its INTEGER cursor words L→R (the pop side fills tuple_rseq in
|
||||
* reverse). A tagged element loads its box words straight from its
|
||||
* local slot — cgexpr's ident load is word0-only for tagged (every
|
||||
* tagged consumer reads memory), so the cursor fill must too; any
|
||||
* other tagged source shape is loud (rule 7; the cursor-receive arm
|
||||
* for call results rides the #35 non-ident-source family, widening
|
||||
* literals #23). Shared by cg_tuple_lit_to_cursor and the cgreturn
|
||||
* N_TUPLE arm — count (tuple_lit_gpwords) and push live or die
|
||||
* together. */
|
||||
* tagged consumer reads memory), so the cursor fill must too. Shared
|
||||
* by cg_tuple_lit_to_cursor and the cgreturn N_TUPLE arm — count
|
||||
* (tuple_lit_gpwords) and push live or die together.
|
||||
*
|
||||
* #57: a DECLARED-tagged element whose expr is a concrete rvalue
|
||||
* (`return (5: size, 9)` — cast, literal, call) skipped the widen
|
||||
* entirely: the stamped-keyed arm below saw a scalar and pushed ONE
|
||||
* word, the receiver read the declared box words — silent shift, both
|
||||
* stages, gate-blind (ken /tmp/ken57). Such an element now widens
|
||||
* into the shared tagged scratch (cg_widen_tagged_store, the cgreturn
|
||||
* tagged-@retscr shape) and pushes the box words. A tagged→tagged
|
||||
* SUBSET element (eslot mismatch) needs a tag remap on the way into
|
||||
* the slot — loud (rule 7, the #23/#40 widening family). */
|
||||
static void
|
||||
tuple_lit_push_elem(Cg *c, Local *locals, Node *e)
|
||||
tuple_lit_push_elem(Cg *c, Local **locals_p, Node *e, Type *dt)
|
||||
{
|
||||
Type *du = dt ? type_chase_named(dt) : NULL;
|
||||
Type *eu = type_chase_named(e->type);
|
||||
if (du && du->kind == TY_TAGGED
|
||||
&& !(eu && eu->kind == TY_TAGGED)) {
|
||||
int eslot = tuple_eslot(dt);
|
||||
int scr = cg_tagscr_slot(c, locals_p, eslot);
|
||||
ins2(c, A_XORQ, areg(D_AX), areg(D_AX));
|
||||
for (int k = 0; k < eslot; k += 8)
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, scr + k));
|
||||
cg_widen_tagged_store(c, locals_p, du, e, D_BP, scr,
|
||||
eslot);
|
||||
for (int k = 0; k < eslot / 8; k++) {
|
||||
ins2(c, A_MOVQ, amem(D_BP, scr + k * 8),
|
||||
areg(D_AX));
|
||||
ins1(c, A_PUSHQ, areg(D_AX));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (du && du->kind == TY_TAGGED && eu && eu->kind == TY_TAGGED
|
||||
&& tuple_eslot(dt) != tuple_eslot(e->type))
|
||||
fatal("#57: tagged tuple element widening into a wider "
|
||||
"declared union slot needs a tag remap (rule 7; "
|
||||
"the #23/#40 widening family)");
|
||||
if (eu && eu->kind == TY_TAGGED) {
|
||||
int eslot = tuple_eslot(e->type);
|
||||
int eoff = (e->kind == N_IDENT && e->str)
|
||||
? localfind(locals, e->str) : 0;
|
||||
? localfind(*locals_p, e->str) : 0;
|
||||
if (eoff == 0)
|
||||
fatal("#22a: tagged tuple element from a non-local "
|
||||
"source shape unwired (ident locals only; "
|
||||
@@ -3332,7 +3371,7 @@ tuple_lit_push_elem(Cg *c, Local *locals, Node *e)
|
||||
}
|
||||
return;
|
||||
}
|
||||
cgexpr(c, e, locals);
|
||||
cgexpr(c, e, *locals_p);
|
||||
if (eu && eu->kind == TY_VOID)
|
||||
return;
|
||||
ins1(c, A_PUSHQ, areg(D_AX));
|
||||
@@ -3355,17 +3394,30 @@ tuple_lit_push_elem(Cg *c, Local *locals, Node *e)
|
||||
* amd64/sysv.c retr). Byte-identical extraction of cgreturn's N_TUPLE arm,
|
||||
* now shared with cgexpr. Over-cap loud-stops (rule 7); a bare expression
|
||||
* value can't sret, so the >cap rvalue-tuple materialisation is the #10
|
||||
* follow-up. */
|
||||
* follow-up.
|
||||
*
|
||||
* #57: `decl` is the consumer's DECLARED tuple type (NULL when it has
|
||||
* none — the bare cgexpr route). A declared-TAGGED element gates the
|
||||
* SSE row off (its payload may be float-stamped but the BOX rides
|
||||
* INTEGER eightbytes) and keys count + push on the declared eslot —
|
||||
* see tuple_lit_gpwords / tuple_lit_push_elem. */
|
||||
static void
|
||||
cg_tuple_lit_to_cursor(Cg *c, Local **locals, Node *tuple)
|
||||
cg_tuple_lit_to_cursor(Cg *c, Local **locals, Node *tuple, Type *decl)
|
||||
{
|
||||
Type *du = decl ? type_chase_named(decl) : NULL;
|
||||
Tparam *dp0 = (du && du->kind == TY_TUPLE) ? du->params : NULL;
|
||||
int f32;
|
||||
int gptotal = 0, ssecount = 0;
|
||||
Tparam *dp = dp0;
|
||||
for (Node *e = tuple->list; e; e = e->next) {
|
||||
if (fld_isfloat(e->type, &f32))
|
||||
Type *dtu = dp ? type_chase_named(dp->type) : NULL;
|
||||
int dtagged = dtu && dtu->kind == TY_TAGGED;
|
||||
if (!dtagged && fld_isfloat(e->type, &f32))
|
||||
ssecount++;
|
||||
else
|
||||
gptotal += tuple_lit_gpwords(e);
|
||||
gptotal += tuple_lit_gpwords(e,
|
||||
dp ? dp->type : NULL);
|
||||
if (dp) dp = dp->next;
|
||||
}
|
||||
if (gptotal > TUPLE_GPCAP || ssecount > TUPLE_SSECAP)
|
||||
fatal("tuple literal exceeds register-return ABI capacity "
|
||||
@@ -3383,27 +3435,36 @@ cg_tuple_lit_to_cursor(Cg *c, Local **locals, Node *tuple)
|
||||
}
|
||||
}
|
||||
int sseidx = 0;
|
||||
dp = dp0;
|
||||
for (Node *e = tuple->list; e; e = e->next) {
|
||||
int isflt = fld_isfloat(e->type, &f32);
|
||||
Type *dtu = dp ? type_chase_named(dp->type) : NULL;
|
||||
int dtagged = dtu && dtu->kind == TY_TAGGED;
|
||||
int isflt = !dtagged && fld_isfloat(e->type, &f32);
|
||||
if (isflt) {
|
||||
cgexpr(c, e, *locals);
|
||||
ins2(c, f32 ? A_MOVSS : A_MOVSD, areg(D_X0),
|
||||
amem(D_BP, fscr + sseidx * 8));
|
||||
sseidx++;
|
||||
continue;
|
||||
} else {
|
||||
tuple_lit_push_elem(c, locals, e,
|
||||
dp ? dp->type : NULL);
|
||||
}
|
||||
tuple_lit_push_elem(c, *locals, e);
|
||||
if (dp) dp = dp->next;
|
||||
}
|
||||
for (int i = gptotal - 1; i >= 0; i--)
|
||||
ins1(c, A_POPQ, areg(tuple_rseq[i]));
|
||||
int j = 0;
|
||||
dp = dp0;
|
||||
for (Node *e = tuple->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++;
|
||||
Type *dtu = dp ? type_chase_named(dp->type) : NULL;
|
||||
int dtagged = dtu && dtu->kind == TY_TAGGED;
|
||||
if (!dtagged && fld_isfloat(e->type, &f32)) {
|
||||
ins2(c, f32 ? A_MOVSS : A_MOVSD,
|
||||
amem(D_BP, fscr + j * 8),
|
||||
areg(tuple_sse_seq[j]));
|
||||
j++;
|
||||
}
|
||||
if (dp) dp = dp->next;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11474,7 +11535,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* elements into the register cursor (mirror cgreturn's N_TUPLE
|
||||
* arm) so a let-bind / destructure consumer reads every element,
|
||||
* not just AX = 0 from the default arm below. */
|
||||
cg_tuple_lit_to_cursor(c, &locals, n);
|
||||
cg_tuple_lit_to_cursor(c, &locals, n, NULL);
|
||||
break;
|
||||
default:
|
||||
cgexpr_int(c, 0);
|
||||
@@ -11636,7 +11697,17 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
* receive below, exactly as before. */
|
||||
if (n->rhs && lu && lu->kind == TY_TUPLE
|
||||
&& cg_sret_retsize(lt) == 0) {
|
||||
cgexpr(c, n->rhs, *locals);
|
||||
/* #57: a tuple LITERAL rhs carries the DECLARED type
|
||||
* into the cursor fill — its stamped type is element-
|
||||
* constructed, so a declared-tagged element's concrete
|
||||
* rvalue skipped the widen and the fill/receive cursor
|
||||
* walks skewed (let-twin of the return-position bug;
|
||||
* probe /tmp/p57/q1_let). Same emission as the cgexpr
|
||||
* route for every declared-tagged-free literal. */
|
||||
if (n->rhs->kind == N_TUPLE)
|
||||
cg_tuple_lit_to_cursor(c, locals, n->rhs, lu);
|
||||
else
|
||||
cgexpr(c, n->rhs, *locals);
|
||||
int gpcur = 0, ssecur = 0, eoff = 0, ef32;
|
||||
for (Tparam *p = lu->params; p; p = p->next) {
|
||||
int isflt = fld_isfloat(p->type, &ef32);
|
||||
@@ -12857,11 +12928,29 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
* drives the receive sites. */
|
||||
int ssecap = TUPLE_SSECAP;
|
||||
int gptotal = 0, ssecount = 0, f32;
|
||||
/* #57: count + push key on the DECLARED return-type
|
||||
* element (cg_ret_type tuple params) — the literal's
|
||||
* stamped type is element-constructed, so a declared-
|
||||
* TAGGED element's concrete rvalue counted 1 word and
|
||||
* skipped the widen while the caller's receive walks
|
||||
* the declared eslot (2 words sent for a 3-word shape;
|
||||
* ken /tmp/ken57 p8/p9). Same pp walk the over-cap arm
|
||||
* already does (#240/#22b). */
|
||||
Type *rttc = cg_ret_type
|
||||
? type_chase_named(cg_ret_type) : NULL;
|
||||
Tparam *rp0 = (rttc && rttc->kind == TY_TUPLE)
|
||||
? rttc->params : NULL;
|
||||
Tparam *rp = rp0;
|
||||
for (Node *e = n->lhs->list; e; e = e->next) {
|
||||
if (fld_isfloat(e->type, &f32))
|
||||
Type *rdu = rp
|
||||
? type_chase_named(rp->type) : NULL;
|
||||
int rdtag = rdu && rdu->kind == TY_TAGGED;
|
||||
if (!rdtag && fld_isfloat(e->type, &f32))
|
||||
ssecount++;
|
||||
else
|
||||
gptotal += tuple_lit_gpwords(e);
|
||||
gptotal += tuple_lit_gpwords(e,
|
||||
rp ? rp->type : NULL);
|
||||
if (rp) rp = rp->next;
|
||||
}
|
||||
/* #22b: classify and emit MUST agree (the #10 SSoT
|
||||
* note at TUPLE_GPCAP). The over-cap DECISION rides
|
||||
@@ -13014,29 +13103,42 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
}
|
||||
}
|
||||
int sseidx = 0;
|
||||
rp = rp0;
|
||||
for (Node *e = n->lhs->list; e; e = e->next) {
|
||||
int isflt = fld_isfloat(e->type, &f32);
|
||||
Type *rdu = rp
|
||||
? type_chase_named(rp->type) : NULL;
|
||||
int rdtag = rdu && rdu->kind == TY_TAGGED;
|
||||
int isflt = !rdtag
|
||||
&& fld_isfloat(e->type, &f32);
|
||||
if (isflt) {
|
||||
cgexpr(c, e, *locals); /* float=X0 */
|
||||
ins2(c, f32 ? A_MOVSS : A_MOVSD, areg(D_X0),
|
||||
amem(D_BP, fscr + sseidx * 8));
|
||||
sseidx++;
|
||||
continue;
|
||||
} else {
|
||||
/* scalar=AX; slice/str=AX,BX,CX; tagged
|
||||
* box from its slot or widened scratch
|
||||
* (tuple_lit_push_elem) */
|
||||
tuple_lit_push_elem(c, locals, e,
|
||||
rp ? rp->type : NULL);
|
||||
}
|
||||
/* scalar=AX; slice/str=AX,BX,CX; tagged box
|
||||
* from its slot (tuple_lit_push_elem) */
|
||||
tuple_lit_push_elem(c, *locals, e);
|
||||
if (rp) rp = rp->next;
|
||||
}
|
||||
for (int i = gptotal - 1; i >= 0; i--)
|
||||
ins1(c, A_POPQ, areg(tuple_rseq[i]));
|
||||
int j = 0;
|
||||
rp = rp0;
|
||||
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++;
|
||||
Type *rdu = rp
|
||||
? type_chase_named(rp->type) : NULL;
|
||||
int rdtag = rdu && rdu->kind == TY_TAGGED;
|
||||
if (!rdtag && fld_isfloat(e->type, &f32)) {
|
||||
ins2(c, f32 ? A_MOVSS : A_MOVSD,
|
||||
amem(D_BP, fscr + j * 8),
|
||||
areg(tuple_sse_seq[j]));
|
||||
j++;
|
||||
}
|
||||
if (rp) rp = rp->next;
|
||||
}
|
||||
} else if (n->lhs) {
|
||||
cgexpr(c, n->lhs, *locals);
|
||||
@@ -13461,6 +13563,10 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
* SAME producer source the SEND walks. */
|
||||
int sret_recv = (n->rhs && n->rhs->kind == N_CALL)
|
||||
? cg_sret_retsize(n->rhs->type) : 0;
|
||||
/* #64 (filed, rule 7): an N_TUPLE literal rhs rides this
|
||||
* decl-less cgexpr route, so a declared-TAGGED element's
|
||||
* concrete rvalue still fills the cursor stamped-keyed
|
||||
* (silent skew) — the #57 decl wire stops at return/let. */
|
||||
cgexpr(c, n->rhs, *locals);
|
||||
Type *rt = n->rhs ? n->rhs->type : NULL;
|
||||
Type *ru = (rt && rt->kind == TY_NAMED) ? rt->under : rt;
|
||||
|
||||
@@ -20959,7 +20959,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
||||
// elements into the register cursor (mirror cgreturn's N_TUPLE
|
||||
// arm) so a let-bind / destructure consumer reads every element,
|
||||
// not just AX = 0 from the default arm below.
|
||||
cgtuplelittocursor(c, n);
|
||||
cgtuplelittocursor(c, n, nil);
|
||||
return;
|
||||
case:
|
||||
// Default fallback: produce a deterministic AX = 0. Mirrors
|
||||
@@ -32211,7 +32211,17 @@ fn tupstore(c: *cgen, gpcur: i32, ssecur: i32, off: i32, eslot: i32, tn: *node)
|
||||
// (0 GP words); str/slice push their 3-word header; a tagged element
|
||||
// its tupeslot/8 box words; a void element pushes nothing (the
|
||||
// checker's 0-slot); a scalar 1. Mirror of cstage tuple_lit_gpwords.
|
||||
fn tuplitgpwords(c: *cgen, e: *node) i32 = {
|
||||
//
|
||||
// #57: `dtn` is the DECLARED tuple element TYPE node (nil when the
|
||||
// consumer has none). The N_TUPLE literal's stamped type is
|
||||
// CONSTRUCTED from its elements, so a concrete rvalue under a
|
||||
// declared-TAGGED slot counted ONE word here while the receive walks
|
||||
// the declared eslot — the cursor shifted and every later element
|
||||
// read garbage. Declared-tagged keys the count on the DECLARED box.
|
||||
fn tuplitgpwords(c: *cgen, e: *node, dtn: *node) i32 = {
|
||||
if (dtn != nil) {
|
||||
if (istaggedtype(c, dtn)) { return tupeslotn(dtn) / 8; };
|
||||
};
|
||||
if (isfloattype(c, e)) { return 0; };
|
||||
if (nodeisstr(c, e) || nodeisslice(c, e)) {
|
||||
return (tyslicesize() / 8i64): i32;
|
||||
@@ -32229,15 +32239,56 @@ fn tuplitgpwords(c: *cgen, e: *node) i32 = {
|
||||
// INTEGER cursor words L->R (the pop side fills tupreg in reverse). A
|
||||
// tagged element loads its box words straight from its local slot —
|
||||
// cgexpr's ident load is word0-only for tagged (every tagged consumer
|
||||
// reads memory), so the cursor fill must too; any other tagged source
|
||||
// shape is loud (rule 7; the cursor-receive arm for call results
|
||||
// rides the #35 non-ident-source family, widening literals #23). Mirror of
|
||||
// cstage tuple_lit_push_elem — count (tuplitgpwords) and push live or
|
||||
// die together.
|
||||
fn tuplitpushelem(c: *cgen, e: *node) void = {
|
||||
// reads memory), so the cursor fill must too. Mirror of cstage
|
||||
// tuple_lit_push_elem — count (tuplitgpwords) and push live or die
|
||||
// together.
|
||||
//
|
||||
// #57: a DECLARED-tagged element whose expr is a concrete rvalue
|
||||
// (`return (5: size, 9)` — cast, literal, call) skipped the widen
|
||||
// entirely: the stamped-keyed arm below saw a scalar and pushed ONE
|
||||
// word, the receiver read the declared box words — silent shift, both
|
||||
// stages, gate-blind (ken /tmp/ken57). Such an element now widens
|
||||
// into the shared tagged scratch (cgwidentaggedstore, the cgreturn
|
||||
// tagged-@retscr shape) and pushes the box words. A tagged->tagged
|
||||
// SUBSET element (eslot mismatch) needs a tag remap on the way into
|
||||
// the slot — loud (rule 7, the #23/#40 widening family).
|
||||
fn tuplitpushelem(c: *cgen, e: *node, dtn: *node) void = {
|
||||
let t: *tinfo = e.type_: *tinfo;
|
||||
for (t != nil && t.kind == tykind.TY_NAMED) { t = t.under; };
|
||||
if (t != nil && t.kind == tykind.TY_TAGGED) {
|
||||
let etagged: bool = false;
|
||||
if (t != nil) { if (t.kind == tykind.TY_TAGGED) { etagged = true; }; };
|
||||
if (dtn != nil) {
|
||||
if (istaggedtype(c, dtn) && !etagged) {
|
||||
let eslot: i32 = tupeslotn(dtn);
|
||||
let scr: i32 = tagscradd(c, eslot);
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
let z: i32 = 0;
|
||||
for (z < eslot) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((scr + z): i64);
|
||||
emitline("(BP)\n");
|
||||
z += 8;
|
||||
};
|
||||
cgwidentaggedstore(c, dtn.type_: *tinfo, e,
|
||||
"BP", scr, eslot);
|
||||
let pk: i32 = 0;
|
||||
for (pk < eslot / 8) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scr + pk * 8): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
pk += 1;
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (istaggedtype(c, dtn) && etagged
|
||||
&& tupeslotn(dtn) != tupeslotn(e)) {
|
||||
let m57: str = "#57: tagged tuple element widening into a wider declared union slot needs a tag remap (rule 7; the #23/#40 widening family)\n";
|
||||
os.write(2, m57.ptr, m57.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
};
|
||||
if (etagged) {
|
||||
let eslot: i32 = tupeslotn(e);
|
||||
let eoff: i32 = 0;
|
||||
if (e.kind == nkind.N_IDENT) { eoff = localfind(c, e.str); };
|
||||
@@ -32275,17 +32326,35 @@ fn tuplitpushelem(c: *cgen, e: *node) void = {
|
||||
// extraction of cgreturn's in-register N_TUPLE arm (cgenstmt.ww), now shared
|
||||
// with cgexpr. Over-cap loud-stops (rule 7); a bare expression value can't
|
||||
// sret, so the >cap rvalue-tuple materialisation is the #10 follow-up.
|
||||
fn cgtuplelittocursor(c: *cgen, tuple: *node) void = {
|
||||
//
|
||||
// #57: `decl` is the consumer's DECLARED tuple TYPE node (N_TTUPLE,
|
||||
// nil when it has none — the bare cgexpr route). A declared-TAGGED
|
||||
// element gates the SSE row off (its payload may be float-stamped but
|
||||
// the BOX rides INTEGER eightbytes) and keys count + push on the
|
||||
// declared eslot — see tuplitgpwords / tuplitpushelem. Mirrors cstage
|
||||
// cg_tuple_lit_to_cursor's decl walk; decl.list nodes wrap the elem
|
||||
// type in .lhs (the c.fnret.list shape the over-cap arm walks).
|
||||
fn cgtuplelittocursor(c: *cgen, tuple: *node, decl: *node) void = {
|
||||
let dp0: *node = nil;
|
||||
if (decl != nil) {
|
||||
if (decl.kind == nkind.N_TTUPLE) { dp0 = decl.list; };
|
||||
};
|
||||
let ssecap: i32 = TUPLE_SSECAP;
|
||||
let gptotal: i32 = 0;
|
||||
let ssecount: i32 = 0;
|
||||
let dp: *node = dp0;
|
||||
let e: *node = tuple.list;
|
||||
for (e != nil) {
|
||||
if (isfloattype(c, e)) {
|
||||
let dtn: *node = nil;
|
||||
if (dp != nil) { dtn = dp.lhs; };
|
||||
let dtagged: bool = false;
|
||||
if (dtn != nil) { dtagged = istaggedtype(c, dtn); };
|
||||
if (!dtagged && isfloattype(c, e)) {
|
||||
ssecount = ssecount + 1;
|
||||
} else {
|
||||
gptotal = gptotal + tuplitgpwords(c, e);
|
||||
gptotal = gptotal + tuplitgpwords(c, e, dtn);
|
||||
};
|
||||
if (dp != nil) { dp = dp.next; };
|
||||
e = e.next;
|
||||
};
|
||||
if (gptotal > TUPLE_GPCAP || ssecount > ssecap) {
|
||||
@@ -32298,9 +32367,14 @@ fn cgtuplelittocursor(c: *cgen, tuple: *node) void = {
|
||||
fscr = localadd(c, "@tupfscr", ssecap * 8, nil);
|
||||
};
|
||||
let sseidx: i32 = 0;
|
||||
dp = dp0;
|
||||
e = tuple.list;
|
||||
for (e != nil) {
|
||||
let isflt: bool = isfloattype(c, e);
|
||||
let dtn: *node = nil;
|
||||
if (dp != nil) { dtn = dp.lhs; };
|
||||
let dtagged: bool = false;
|
||||
if (dtn != nil) { dtagged = istaggedtype(c, dtn); };
|
||||
let isflt: bool = !dtagged && isfloattype(c, e);
|
||||
if (isflt) {
|
||||
cgexpr(c, e);
|
||||
let mov: str = "MOVSD";
|
||||
@@ -32310,8 +32384,9 @@ fn cgtuplelittocursor(c: *cgen, tuple: *node) void = {
|
||||
emitline("(BP)\n");
|
||||
sseidx = sseidx + 1;
|
||||
} else {
|
||||
tuplitpushelem(c, e);
|
||||
tuplitpushelem(c, e, dtn);
|
||||
};
|
||||
if (dp != nil) { dp = dp.next; };
|
||||
e = e.next;
|
||||
};
|
||||
let i: i32 = gptotal - 1;
|
||||
@@ -32322,9 +32397,14 @@ fn cgtuplelittocursor(c: *cgen, tuple: *node) void = {
|
||||
i = i - 1;
|
||||
};
|
||||
let j: i32 = 0;
|
||||
dp = dp0;
|
||||
e = tuple.list;
|
||||
for (e != nil) {
|
||||
if (isfloattype(c, e)) {
|
||||
let dtn: *node = nil;
|
||||
if (dp != nil) { dtn = dp.lhs; };
|
||||
let dtagged: bool = false;
|
||||
if (dtn != nil) { dtagged = istaggedtype(c, dtn); };
|
||||
if (!dtagged && isfloattype(c, e)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, e)) { mov = "MOVSS"; };
|
||||
emitline("\t"); emitline(mov); emitline("\t");
|
||||
@@ -32334,6 +32414,7 @@ fn cgtuplelittocursor(c: *cgen, tuple: *node) void = {
|
||||
emitline("\n");
|
||||
j = j + 1;
|
||||
};
|
||||
if (dp != nil) { dp = dp.next; };
|
||||
e = e.next;
|
||||
};
|
||||
};
|
||||
@@ -32470,13 +32551,33 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
let ssecap: i32 = TUPLE_SSECAP; // X0,X1 per SysV
|
||||
let gptotal: i32 = 0;
|
||||
let ssecount: i32 = 0;
|
||||
// #57: count + push key on the DECLARED return-type
|
||||
// element (c.fnret.list) — the literal's stamped type
|
||||
// is element-constructed, so a declared-TAGGED
|
||||
// element's concrete rvalue counted 1 word and skipped
|
||||
// the widen while the caller's receive walks the
|
||||
// declared eslot (2 words sent for a 3-word shape;
|
||||
// ken /tmp/ken57 p8/p9). Same pt walk the over-cap arm
|
||||
// already does (#240/#22b). Mirrors cstage cgreturn.
|
||||
let rp0: *node = nil;
|
||||
if (c.fnret != nil) {
|
||||
if (c.fnret.kind == nkind.N_TTUPLE) {
|
||||
rp0 = c.fnret.list;
|
||||
};
|
||||
};
|
||||
let rp: *node = rp0;
|
||||
let e: *node = rhs.list;
|
||||
for (e != nil) {
|
||||
if (isfloattype(c, e)) {
|
||||
let rdtn: *node = nil;
|
||||
if (rp != nil) { rdtn = rp.lhs; };
|
||||
let rdtag: bool = false;
|
||||
if (rdtn != nil) { rdtag = istaggedtype(c, rdtn); };
|
||||
if (!rdtag && isfloattype(c, e)) {
|
||||
ssecount = ssecount + 1;
|
||||
} else {
|
||||
gptotal = gptotal + tuplitgpwords(c, e);
|
||||
gptotal = gptotal + tuplitgpwords(c, e, rdtn);
|
||||
};
|
||||
if (rp != nil) { rp = rp.next; };
|
||||
e = e.next;
|
||||
};
|
||||
// #22b: classify and emit MUST agree (the #10 SSoT note
|
||||
@@ -32640,9 +32741,14 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
fscr = localadd(c, "@tupfscr", ssecap * 8, nil);
|
||||
};
|
||||
let sseidx: i32 = 0;
|
||||
rp = rp0;
|
||||
e = rhs.list;
|
||||
for (e != nil) {
|
||||
let isflt: bool = isfloattype(c, e);
|
||||
let rdtn: *node = nil;
|
||||
if (rp != nil) { rdtn = rp.lhs; };
|
||||
let rdtag: bool = false;
|
||||
if (rdtn != nil) { rdtag = istaggedtype(c, rdtn); };
|
||||
let isflt: bool = !rdtag && isfloattype(c, e);
|
||||
if (isflt) {
|
||||
cgexpr(c, e);
|
||||
let mov: str = "MOVSD";
|
||||
@@ -32655,9 +32761,11 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
sseidx = sseidx + 1;
|
||||
} else {
|
||||
// scalar=AX; slice/str=AX,BX,CX; tagged
|
||||
// box from its slot (tuplitpushelem)
|
||||
tuplitpushelem(c, e);
|
||||
// box from its slot or widened scratch
|
||||
// (tuplitpushelem)
|
||||
tuplitpushelem(c, e, rdtn);
|
||||
};
|
||||
if (rp != nil) { rp = rp.next; };
|
||||
e = e.next;
|
||||
};
|
||||
let i: i32 = gptotal - 1;
|
||||
@@ -32668,9 +32776,14 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
i = i - 1;
|
||||
};
|
||||
let j: i32 = 0;
|
||||
rp = rp0;
|
||||
e = rhs.list;
|
||||
for (e != nil) {
|
||||
if (isfloattype(c, e)) {
|
||||
let rdtn: *node = nil;
|
||||
if (rp != nil) { rdtn = rp.lhs; };
|
||||
let rdtag: bool = false;
|
||||
if (rdtn != nil) { rdtag = istaggedtype(c, rdtn); };
|
||||
if (!rdtag && isfloattype(c, e)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, e)) { mov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
@@ -32682,6 +32795,7 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
emitline("\n");
|
||||
j = j + 1;
|
||||
};
|
||||
if (rp != nil) { rp = rp.next; };
|
||||
e = e.next;
|
||||
};
|
||||
emitline("\tMOVQ\tBP, SP\n");
|
||||
@@ -34098,7 +34212,19 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
if (ttup != nil) {
|
||||
if (ttup.kind == nkind.N_TTUPLE
|
||||
&& sretretsize(c, ttup) == 0) {
|
||||
cgexpr(c, rhs);
|
||||
// #57: a tuple LITERAL rhs carries the DECLARED
|
||||
// type into the cursor fill — its stamped type
|
||||
// is element-constructed, so a declared-tagged
|
||||
// element's concrete rvalue skipped the widen
|
||||
// and the fill/receive cursor walks skewed
|
||||
// (let-twin of the return-position bug; probe
|
||||
// /tmp/p57/q1_let). Same emission as the cgexpr
|
||||
// route for every declared-tagged-free literal.
|
||||
if (rhs.kind == nkind.N_TUPLE) {
|
||||
cgtuplelittocursor(c, rhs, ttup);
|
||||
} else {
|
||||
cgexpr(c, rhs);
|
||||
};
|
||||
let gpcur: i32 = 0;
|
||||
let ssecur: i32 = 0;
|
||||
let eoff: i32 = 0;
|
||||
@@ -34918,6 +35044,10 @@ fn cgmassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
|
||||
// #64 (filed, rule 7): an N_TUPLE literal rhs rides this decl-less
|
||||
// cgexpr route, so a declared-TAGGED element's concrete rvalue
|
||||
// still fills the cursor stamped-keyed (silent skew) — the #57
|
||||
// decl wire stops at return/let.
|
||||
if (n.rhs != nil) { cgexpr(c, n.rhs); };
|
||||
|
||||
if (sretrecv > 0) {
|
||||
|
||||
@@ -115,7 +115,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
||||
// elements into the register cursor (mirror cgreturn's N_TUPLE
|
||||
// arm) so a let-bind / destructure consumer reads every element,
|
||||
// not just AX = 0 from the default arm below.
|
||||
cgtuplelittocursor(c, n);
|
||||
cgtuplelittocursor(c, n, nil);
|
||||
return;
|
||||
case:
|
||||
// Default fallback: produce a deterministic AX = 0. Mirrors
|
||||
|
||||
@@ -306,7 +306,17 @@ fn tupstore(c: *cgen, gpcur: i32, ssecur: i32, off: i32, eslot: i32, tn: *node)
|
||||
// (0 GP words); str/slice push their 3-word header; a tagged element
|
||||
// its tupeslot/8 box words; a void element pushes nothing (the
|
||||
// checker's 0-slot); a scalar 1. Mirror of cstage tuple_lit_gpwords.
|
||||
fn tuplitgpwords(c: *cgen, e: *node) i32 = {
|
||||
//
|
||||
// #57: `dtn` is the DECLARED tuple element TYPE node (nil when the
|
||||
// consumer has none). The N_TUPLE literal's stamped type is
|
||||
// CONSTRUCTED from its elements, so a concrete rvalue under a
|
||||
// declared-TAGGED slot counted ONE word here while the receive walks
|
||||
// the declared eslot — the cursor shifted and every later element
|
||||
// read garbage. Declared-tagged keys the count on the DECLARED box.
|
||||
fn tuplitgpwords(c: *cgen, e: *node, dtn: *node) i32 = {
|
||||
if (dtn != nil) {
|
||||
if (istaggedtype(c, dtn)) { return tupeslotn(dtn) / 8; };
|
||||
};
|
||||
if (isfloattype(c, e)) { return 0; };
|
||||
if (nodeisstr(c, e) || nodeisslice(c, e)) {
|
||||
return (tyslicesize() / 8i64): i32;
|
||||
@@ -324,15 +334,56 @@ fn tuplitgpwords(c: *cgen, e: *node) i32 = {
|
||||
// INTEGER cursor words L->R (the pop side fills tupreg in reverse). A
|
||||
// tagged element loads its box words straight from its local slot —
|
||||
// cgexpr's ident load is word0-only for tagged (every tagged consumer
|
||||
// reads memory), so the cursor fill must too; any other tagged source
|
||||
// shape is loud (rule 7; the cursor-receive arm for call results
|
||||
// rides the #35 non-ident-source family, widening literals #23). Mirror of
|
||||
// cstage tuple_lit_push_elem — count (tuplitgpwords) and push live or
|
||||
// die together.
|
||||
fn tuplitpushelem(c: *cgen, e: *node) void = {
|
||||
// reads memory), so the cursor fill must too. Mirror of cstage
|
||||
// tuple_lit_push_elem — count (tuplitgpwords) and push live or die
|
||||
// together.
|
||||
//
|
||||
// #57: a DECLARED-tagged element whose expr is a concrete rvalue
|
||||
// (`return (5: size, 9)` — cast, literal, call) skipped the widen
|
||||
// entirely: the stamped-keyed arm below saw a scalar and pushed ONE
|
||||
// word, the receiver read the declared box words — silent shift, both
|
||||
// stages, gate-blind (ken /tmp/ken57). Such an element now widens
|
||||
// into the shared tagged scratch (cgwidentaggedstore, the cgreturn
|
||||
// tagged-@retscr shape) and pushes the box words. A tagged->tagged
|
||||
// SUBSET element (eslot mismatch) needs a tag remap on the way into
|
||||
// the slot — loud (rule 7, the #23/#40 widening family).
|
||||
fn tuplitpushelem(c: *cgen, e: *node, dtn: *node) void = {
|
||||
let t: *tinfo = e.type_: *tinfo;
|
||||
for (t != nil && t.kind == tykind.TY_NAMED) { t = t.under; };
|
||||
if (t != nil && t.kind == tykind.TY_TAGGED) {
|
||||
let etagged: bool = false;
|
||||
if (t != nil) { if (t.kind == tykind.TY_TAGGED) { etagged = true; }; };
|
||||
if (dtn != nil) {
|
||||
if (istaggedtype(c, dtn) && !etagged) {
|
||||
let eslot: i32 = tupeslotn(dtn);
|
||||
let scr: i32 = tagscradd(c, eslot);
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
let z: i32 = 0;
|
||||
for (z < eslot) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((scr + z): i64);
|
||||
emitline("(BP)\n");
|
||||
z += 8;
|
||||
};
|
||||
cgwidentaggedstore(c, dtn.type_: *tinfo, e,
|
||||
"BP", scr, eslot);
|
||||
let pk: i32 = 0;
|
||||
for (pk < eslot / 8) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scr + pk * 8): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
pk += 1;
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (istaggedtype(c, dtn) && etagged
|
||||
&& tupeslotn(dtn) != tupeslotn(e)) {
|
||||
let m57: str = "#57: tagged tuple element widening into a wider declared union slot needs a tag remap (rule 7; the #23/#40 widening family)\n";
|
||||
os.write(2, m57.ptr, m57.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
};
|
||||
if (etagged) {
|
||||
let eslot: i32 = tupeslotn(e);
|
||||
let eoff: i32 = 0;
|
||||
if (e.kind == nkind.N_IDENT) { eoff = localfind(c, e.str); };
|
||||
@@ -370,17 +421,35 @@ fn tuplitpushelem(c: *cgen, e: *node) void = {
|
||||
// extraction of cgreturn's in-register N_TUPLE arm (cgenstmt.ww), now shared
|
||||
// with cgexpr. Over-cap loud-stops (rule 7); a bare expression value can't
|
||||
// sret, so the >cap rvalue-tuple materialisation is the #10 follow-up.
|
||||
fn cgtuplelittocursor(c: *cgen, tuple: *node) void = {
|
||||
//
|
||||
// #57: `decl` is the consumer's DECLARED tuple TYPE node (N_TTUPLE,
|
||||
// nil when it has none — the bare cgexpr route). A declared-TAGGED
|
||||
// element gates the SSE row off (its payload may be float-stamped but
|
||||
// the BOX rides INTEGER eightbytes) and keys count + push on the
|
||||
// declared eslot — see tuplitgpwords / tuplitpushelem. Mirrors cstage
|
||||
// cg_tuple_lit_to_cursor's decl walk; decl.list nodes wrap the elem
|
||||
// type in .lhs (the c.fnret.list shape the over-cap arm walks).
|
||||
fn cgtuplelittocursor(c: *cgen, tuple: *node, decl: *node) void = {
|
||||
let dp0: *node = nil;
|
||||
if (decl != nil) {
|
||||
if (decl.kind == nkind.N_TTUPLE) { dp0 = decl.list; };
|
||||
};
|
||||
let ssecap: i32 = TUPLE_SSECAP;
|
||||
let gptotal: i32 = 0;
|
||||
let ssecount: i32 = 0;
|
||||
let dp: *node = dp0;
|
||||
let e: *node = tuple.list;
|
||||
for (e != nil) {
|
||||
if (isfloattype(c, e)) {
|
||||
let dtn: *node = nil;
|
||||
if (dp != nil) { dtn = dp.lhs; };
|
||||
let dtagged: bool = false;
|
||||
if (dtn != nil) { dtagged = istaggedtype(c, dtn); };
|
||||
if (!dtagged && isfloattype(c, e)) {
|
||||
ssecount = ssecount + 1;
|
||||
} else {
|
||||
gptotal = gptotal + tuplitgpwords(c, e);
|
||||
gptotal = gptotal + tuplitgpwords(c, e, dtn);
|
||||
};
|
||||
if (dp != nil) { dp = dp.next; };
|
||||
e = e.next;
|
||||
};
|
||||
if (gptotal > TUPLE_GPCAP || ssecount > ssecap) {
|
||||
@@ -393,9 +462,14 @@ fn cgtuplelittocursor(c: *cgen, tuple: *node) void = {
|
||||
fscr = localadd(c, "@tupfscr", ssecap * 8, nil);
|
||||
};
|
||||
let sseidx: i32 = 0;
|
||||
dp = dp0;
|
||||
e = tuple.list;
|
||||
for (e != nil) {
|
||||
let isflt: bool = isfloattype(c, e);
|
||||
let dtn: *node = nil;
|
||||
if (dp != nil) { dtn = dp.lhs; };
|
||||
let dtagged: bool = false;
|
||||
if (dtn != nil) { dtagged = istaggedtype(c, dtn); };
|
||||
let isflt: bool = !dtagged && isfloattype(c, e);
|
||||
if (isflt) {
|
||||
cgexpr(c, e);
|
||||
let mov: str = "MOVSD";
|
||||
@@ -405,8 +479,9 @@ fn cgtuplelittocursor(c: *cgen, tuple: *node) void = {
|
||||
emitline("(BP)\n");
|
||||
sseidx = sseidx + 1;
|
||||
} else {
|
||||
tuplitpushelem(c, e);
|
||||
tuplitpushelem(c, e, dtn);
|
||||
};
|
||||
if (dp != nil) { dp = dp.next; };
|
||||
e = e.next;
|
||||
};
|
||||
let i: i32 = gptotal - 1;
|
||||
@@ -417,9 +492,14 @@ fn cgtuplelittocursor(c: *cgen, tuple: *node) void = {
|
||||
i = i - 1;
|
||||
};
|
||||
let j: i32 = 0;
|
||||
dp = dp0;
|
||||
e = tuple.list;
|
||||
for (e != nil) {
|
||||
if (isfloattype(c, e)) {
|
||||
let dtn: *node = nil;
|
||||
if (dp != nil) { dtn = dp.lhs; };
|
||||
let dtagged: bool = false;
|
||||
if (dtn != nil) { dtagged = istaggedtype(c, dtn); };
|
||||
if (!dtagged && isfloattype(c, e)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, e)) { mov = "MOVSS"; };
|
||||
emitline("\t"); emitline(mov); emitline("\t");
|
||||
@@ -429,6 +509,7 @@ fn cgtuplelittocursor(c: *cgen, tuple: *node) void = {
|
||||
emitline("\n");
|
||||
j = j + 1;
|
||||
};
|
||||
if (dp != nil) { dp = dp.next; };
|
||||
e = e.next;
|
||||
};
|
||||
};
|
||||
@@ -565,13 +646,33 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
let ssecap: i32 = TUPLE_SSECAP; // X0,X1 per SysV
|
||||
let gptotal: i32 = 0;
|
||||
let ssecount: i32 = 0;
|
||||
// #57: count + push key on the DECLARED return-type
|
||||
// element (c.fnret.list) — the literal's stamped type
|
||||
// is element-constructed, so a declared-TAGGED
|
||||
// element's concrete rvalue counted 1 word and skipped
|
||||
// the widen while the caller's receive walks the
|
||||
// declared eslot (2 words sent for a 3-word shape;
|
||||
// ken /tmp/ken57 p8/p9). Same pt walk the over-cap arm
|
||||
// already does (#240/#22b). Mirrors cstage cgreturn.
|
||||
let rp0: *node = nil;
|
||||
if (c.fnret != nil) {
|
||||
if (c.fnret.kind == nkind.N_TTUPLE) {
|
||||
rp0 = c.fnret.list;
|
||||
};
|
||||
};
|
||||
let rp: *node = rp0;
|
||||
let e: *node = rhs.list;
|
||||
for (e != nil) {
|
||||
if (isfloattype(c, e)) {
|
||||
let rdtn: *node = nil;
|
||||
if (rp != nil) { rdtn = rp.lhs; };
|
||||
let rdtag: bool = false;
|
||||
if (rdtn != nil) { rdtag = istaggedtype(c, rdtn); };
|
||||
if (!rdtag && isfloattype(c, e)) {
|
||||
ssecount = ssecount + 1;
|
||||
} else {
|
||||
gptotal = gptotal + tuplitgpwords(c, e);
|
||||
gptotal = gptotal + tuplitgpwords(c, e, rdtn);
|
||||
};
|
||||
if (rp != nil) { rp = rp.next; };
|
||||
e = e.next;
|
||||
};
|
||||
// #22b: classify and emit MUST agree (the #10 SSoT note
|
||||
@@ -735,9 +836,14 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
fscr = localadd(c, "@tupfscr", ssecap * 8, nil);
|
||||
};
|
||||
let sseidx: i32 = 0;
|
||||
rp = rp0;
|
||||
e = rhs.list;
|
||||
for (e != nil) {
|
||||
let isflt: bool = isfloattype(c, e);
|
||||
let rdtn: *node = nil;
|
||||
if (rp != nil) { rdtn = rp.lhs; };
|
||||
let rdtag: bool = false;
|
||||
if (rdtn != nil) { rdtag = istaggedtype(c, rdtn); };
|
||||
let isflt: bool = !rdtag && isfloattype(c, e);
|
||||
if (isflt) {
|
||||
cgexpr(c, e);
|
||||
let mov: str = "MOVSD";
|
||||
@@ -750,9 +856,11 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
sseidx = sseidx + 1;
|
||||
} else {
|
||||
// scalar=AX; slice/str=AX,BX,CX; tagged
|
||||
// box from its slot (tuplitpushelem)
|
||||
tuplitpushelem(c, e);
|
||||
// box from its slot or widened scratch
|
||||
// (tuplitpushelem)
|
||||
tuplitpushelem(c, e, rdtn);
|
||||
};
|
||||
if (rp != nil) { rp = rp.next; };
|
||||
e = e.next;
|
||||
};
|
||||
let i: i32 = gptotal - 1;
|
||||
@@ -763,9 +871,14 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
i = i - 1;
|
||||
};
|
||||
let j: i32 = 0;
|
||||
rp = rp0;
|
||||
e = rhs.list;
|
||||
for (e != nil) {
|
||||
if (isfloattype(c, e)) {
|
||||
let rdtn: *node = nil;
|
||||
if (rp != nil) { rdtn = rp.lhs; };
|
||||
let rdtag: bool = false;
|
||||
if (rdtn != nil) { rdtag = istaggedtype(c, rdtn); };
|
||||
if (!rdtag && isfloattype(c, e)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, e)) { mov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
@@ -777,6 +890,7 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
emitline("\n");
|
||||
j = j + 1;
|
||||
};
|
||||
if (rp != nil) { rp = rp.next; };
|
||||
e = e.next;
|
||||
};
|
||||
emitline("\tMOVQ\tBP, SP\n");
|
||||
@@ -2193,7 +2307,19 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
if (ttup != nil) {
|
||||
if (ttup.kind == nkind.N_TTUPLE
|
||||
&& sretretsize(c, ttup) == 0) {
|
||||
cgexpr(c, rhs);
|
||||
// #57: a tuple LITERAL rhs carries the DECLARED
|
||||
// type into the cursor fill — its stamped type
|
||||
// is element-constructed, so a declared-tagged
|
||||
// element's concrete rvalue skipped the widen
|
||||
// and the fill/receive cursor walks skewed
|
||||
// (let-twin of the return-position bug; probe
|
||||
// /tmp/p57/q1_let). Same emission as the cgexpr
|
||||
// route for every declared-tagged-free literal.
|
||||
if (rhs.kind == nkind.N_TUPLE) {
|
||||
cgtuplelittocursor(c, rhs, ttup);
|
||||
} else {
|
||||
cgexpr(c, rhs);
|
||||
};
|
||||
let gpcur: i32 = 0;
|
||||
let ssecur: i32 = 0;
|
||||
let eoff: i32 = 0;
|
||||
@@ -3013,6 +3139,10 @@ fn cgmassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
|
||||
// #64 (filed, rule 7): an N_TUPLE literal rhs rides this decl-less
|
||||
// cgexpr route, so a declared-TAGGED element's concrete rvalue
|
||||
// still fills the cursor stamped-keyed (silent skew) — the #57
|
||||
// decl wire stops at return/let.
|
||||
if (n.rhs != nil) { cgexpr(c, n.rhs); };
|
||||
|
||||
if (sretrecv > 0) {
|
||||
|
||||
@@ -20959,7 +20959,7 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
||||
// elements into the register cursor (mirror cgreturn's N_TUPLE
|
||||
// arm) so a let-bind / destructure consumer reads every element,
|
||||
// not just AX = 0 from the default arm below.
|
||||
cgtuplelittocursor(c, n);
|
||||
cgtuplelittocursor(c, n, nil);
|
||||
return;
|
||||
case:
|
||||
// Default fallback: produce a deterministic AX = 0. Mirrors
|
||||
@@ -32211,7 +32211,17 @@ fn tupstore(c: *cgen, gpcur: i32, ssecur: i32, off: i32, eslot: i32, tn: *node)
|
||||
// (0 GP words); str/slice push their 3-word header; a tagged element
|
||||
// its tupeslot/8 box words; a void element pushes nothing (the
|
||||
// checker's 0-slot); a scalar 1. Mirror of cstage tuple_lit_gpwords.
|
||||
fn tuplitgpwords(c: *cgen, e: *node) i32 = {
|
||||
//
|
||||
// #57: `dtn` is the DECLARED tuple element TYPE node (nil when the
|
||||
// consumer has none). The N_TUPLE literal's stamped type is
|
||||
// CONSTRUCTED from its elements, so a concrete rvalue under a
|
||||
// declared-TAGGED slot counted ONE word here while the receive walks
|
||||
// the declared eslot — the cursor shifted and every later element
|
||||
// read garbage. Declared-tagged keys the count on the DECLARED box.
|
||||
fn tuplitgpwords(c: *cgen, e: *node, dtn: *node) i32 = {
|
||||
if (dtn != nil) {
|
||||
if (istaggedtype(c, dtn)) { return tupeslotn(dtn) / 8; };
|
||||
};
|
||||
if (isfloattype(c, e)) { return 0; };
|
||||
if (nodeisstr(c, e) || nodeisslice(c, e)) {
|
||||
return (tyslicesize() / 8i64): i32;
|
||||
@@ -32229,15 +32239,56 @@ fn tuplitgpwords(c: *cgen, e: *node) i32 = {
|
||||
// INTEGER cursor words L->R (the pop side fills tupreg in reverse). A
|
||||
// tagged element loads its box words straight from its local slot —
|
||||
// cgexpr's ident load is word0-only for tagged (every tagged consumer
|
||||
// reads memory), so the cursor fill must too; any other tagged source
|
||||
// shape is loud (rule 7; the cursor-receive arm for call results
|
||||
// rides the #35 non-ident-source family, widening literals #23). Mirror of
|
||||
// cstage tuple_lit_push_elem — count (tuplitgpwords) and push live or
|
||||
// die together.
|
||||
fn tuplitpushelem(c: *cgen, e: *node) void = {
|
||||
// reads memory), so the cursor fill must too. Mirror of cstage
|
||||
// tuple_lit_push_elem — count (tuplitgpwords) and push live or die
|
||||
// together.
|
||||
//
|
||||
// #57: a DECLARED-tagged element whose expr is a concrete rvalue
|
||||
// (`return (5: size, 9)` — cast, literal, call) skipped the widen
|
||||
// entirely: the stamped-keyed arm below saw a scalar and pushed ONE
|
||||
// word, the receiver read the declared box words — silent shift, both
|
||||
// stages, gate-blind (ken /tmp/ken57). Such an element now widens
|
||||
// into the shared tagged scratch (cgwidentaggedstore, the cgreturn
|
||||
// tagged-@retscr shape) and pushes the box words. A tagged->tagged
|
||||
// SUBSET element (eslot mismatch) needs a tag remap on the way into
|
||||
// the slot — loud (rule 7, the #23/#40 widening family).
|
||||
fn tuplitpushelem(c: *cgen, e: *node, dtn: *node) void = {
|
||||
let t: *tinfo = e.type_: *tinfo;
|
||||
for (t != nil && t.kind == tykind.TY_NAMED) { t = t.under; };
|
||||
if (t != nil && t.kind == tykind.TY_TAGGED) {
|
||||
let etagged: bool = false;
|
||||
if (t != nil) { if (t.kind == tykind.TY_TAGGED) { etagged = true; }; };
|
||||
if (dtn != nil) {
|
||||
if (istaggedtype(c, dtn) && !etagged) {
|
||||
let eslot: i32 = tupeslotn(dtn);
|
||||
let scr: i32 = tagscradd(c, eslot);
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
let z: i32 = 0;
|
||||
for (z < eslot) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((scr + z): i64);
|
||||
emitline("(BP)\n");
|
||||
z += 8;
|
||||
};
|
||||
cgwidentaggedstore(c, dtn.type_: *tinfo, e,
|
||||
"BP", scr, eslot);
|
||||
let pk: i32 = 0;
|
||||
for (pk < eslot / 8) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scr + pk * 8): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
pk += 1;
|
||||
};
|
||||
return;
|
||||
};
|
||||
if (istaggedtype(c, dtn) && etagged
|
||||
&& tupeslotn(dtn) != tupeslotn(e)) {
|
||||
let m57: str = "#57: tagged tuple element widening into a wider declared union slot needs a tag remap (rule 7; the #23/#40 widening family)\n";
|
||||
os.write(2, m57.ptr, m57.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
};
|
||||
if (etagged) {
|
||||
let eslot: i32 = tupeslotn(e);
|
||||
let eoff: i32 = 0;
|
||||
if (e.kind == nkind.N_IDENT) { eoff = localfind(c, e.str); };
|
||||
@@ -32275,17 +32326,35 @@ fn tuplitpushelem(c: *cgen, e: *node) void = {
|
||||
// extraction of cgreturn's in-register N_TUPLE arm (cgenstmt.ww), now shared
|
||||
// with cgexpr. Over-cap loud-stops (rule 7); a bare expression value can't
|
||||
// sret, so the >cap rvalue-tuple materialisation is the #10 follow-up.
|
||||
fn cgtuplelittocursor(c: *cgen, tuple: *node) void = {
|
||||
//
|
||||
// #57: `decl` is the consumer's DECLARED tuple TYPE node (N_TTUPLE,
|
||||
// nil when it has none — the bare cgexpr route). A declared-TAGGED
|
||||
// element gates the SSE row off (its payload may be float-stamped but
|
||||
// the BOX rides INTEGER eightbytes) and keys count + push on the
|
||||
// declared eslot — see tuplitgpwords / tuplitpushelem. Mirrors cstage
|
||||
// cg_tuple_lit_to_cursor's decl walk; decl.list nodes wrap the elem
|
||||
// type in .lhs (the c.fnret.list shape the over-cap arm walks).
|
||||
fn cgtuplelittocursor(c: *cgen, tuple: *node, decl: *node) void = {
|
||||
let dp0: *node = nil;
|
||||
if (decl != nil) {
|
||||
if (decl.kind == nkind.N_TTUPLE) { dp0 = decl.list; };
|
||||
};
|
||||
let ssecap: i32 = TUPLE_SSECAP;
|
||||
let gptotal: i32 = 0;
|
||||
let ssecount: i32 = 0;
|
||||
let dp: *node = dp0;
|
||||
let e: *node = tuple.list;
|
||||
for (e != nil) {
|
||||
if (isfloattype(c, e)) {
|
||||
let dtn: *node = nil;
|
||||
if (dp != nil) { dtn = dp.lhs; };
|
||||
let dtagged: bool = false;
|
||||
if (dtn != nil) { dtagged = istaggedtype(c, dtn); };
|
||||
if (!dtagged && isfloattype(c, e)) {
|
||||
ssecount = ssecount + 1;
|
||||
} else {
|
||||
gptotal = gptotal + tuplitgpwords(c, e);
|
||||
gptotal = gptotal + tuplitgpwords(c, e, dtn);
|
||||
};
|
||||
if (dp != nil) { dp = dp.next; };
|
||||
e = e.next;
|
||||
};
|
||||
if (gptotal > TUPLE_GPCAP || ssecount > ssecap) {
|
||||
@@ -32298,9 +32367,14 @@ fn cgtuplelittocursor(c: *cgen, tuple: *node) void = {
|
||||
fscr = localadd(c, "@tupfscr", ssecap * 8, nil);
|
||||
};
|
||||
let sseidx: i32 = 0;
|
||||
dp = dp0;
|
||||
e = tuple.list;
|
||||
for (e != nil) {
|
||||
let isflt: bool = isfloattype(c, e);
|
||||
let dtn: *node = nil;
|
||||
if (dp != nil) { dtn = dp.lhs; };
|
||||
let dtagged: bool = false;
|
||||
if (dtn != nil) { dtagged = istaggedtype(c, dtn); };
|
||||
let isflt: bool = !dtagged && isfloattype(c, e);
|
||||
if (isflt) {
|
||||
cgexpr(c, e);
|
||||
let mov: str = "MOVSD";
|
||||
@@ -32310,8 +32384,9 @@ fn cgtuplelittocursor(c: *cgen, tuple: *node) void = {
|
||||
emitline("(BP)\n");
|
||||
sseidx = sseidx + 1;
|
||||
} else {
|
||||
tuplitpushelem(c, e);
|
||||
tuplitpushelem(c, e, dtn);
|
||||
};
|
||||
if (dp != nil) { dp = dp.next; };
|
||||
e = e.next;
|
||||
};
|
||||
let i: i32 = gptotal - 1;
|
||||
@@ -32322,9 +32397,14 @@ fn cgtuplelittocursor(c: *cgen, tuple: *node) void = {
|
||||
i = i - 1;
|
||||
};
|
||||
let j: i32 = 0;
|
||||
dp = dp0;
|
||||
e = tuple.list;
|
||||
for (e != nil) {
|
||||
if (isfloattype(c, e)) {
|
||||
let dtn: *node = nil;
|
||||
if (dp != nil) { dtn = dp.lhs; };
|
||||
let dtagged: bool = false;
|
||||
if (dtn != nil) { dtagged = istaggedtype(c, dtn); };
|
||||
if (!dtagged && isfloattype(c, e)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, e)) { mov = "MOVSS"; };
|
||||
emitline("\t"); emitline(mov); emitline("\t");
|
||||
@@ -32334,6 +32414,7 @@ fn cgtuplelittocursor(c: *cgen, tuple: *node) void = {
|
||||
emitline("\n");
|
||||
j = j + 1;
|
||||
};
|
||||
if (dp != nil) { dp = dp.next; };
|
||||
e = e.next;
|
||||
};
|
||||
};
|
||||
@@ -32470,13 +32551,33 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
let ssecap: i32 = TUPLE_SSECAP; // X0,X1 per SysV
|
||||
let gptotal: i32 = 0;
|
||||
let ssecount: i32 = 0;
|
||||
// #57: count + push key on the DECLARED return-type
|
||||
// element (c.fnret.list) — the literal's stamped type
|
||||
// is element-constructed, so a declared-TAGGED
|
||||
// element's concrete rvalue counted 1 word and skipped
|
||||
// the widen while the caller's receive walks the
|
||||
// declared eslot (2 words sent for a 3-word shape;
|
||||
// ken /tmp/ken57 p8/p9). Same pt walk the over-cap arm
|
||||
// already does (#240/#22b). Mirrors cstage cgreturn.
|
||||
let rp0: *node = nil;
|
||||
if (c.fnret != nil) {
|
||||
if (c.fnret.kind == nkind.N_TTUPLE) {
|
||||
rp0 = c.fnret.list;
|
||||
};
|
||||
};
|
||||
let rp: *node = rp0;
|
||||
let e: *node = rhs.list;
|
||||
for (e != nil) {
|
||||
if (isfloattype(c, e)) {
|
||||
let rdtn: *node = nil;
|
||||
if (rp != nil) { rdtn = rp.lhs; };
|
||||
let rdtag: bool = false;
|
||||
if (rdtn != nil) { rdtag = istaggedtype(c, rdtn); };
|
||||
if (!rdtag && isfloattype(c, e)) {
|
||||
ssecount = ssecount + 1;
|
||||
} else {
|
||||
gptotal = gptotal + tuplitgpwords(c, e);
|
||||
gptotal = gptotal + tuplitgpwords(c, e, rdtn);
|
||||
};
|
||||
if (rp != nil) { rp = rp.next; };
|
||||
e = e.next;
|
||||
};
|
||||
// #22b: classify and emit MUST agree (the #10 SSoT note
|
||||
@@ -32640,9 +32741,14 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
fscr = localadd(c, "@tupfscr", ssecap * 8, nil);
|
||||
};
|
||||
let sseidx: i32 = 0;
|
||||
rp = rp0;
|
||||
e = rhs.list;
|
||||
for (e != nil) {
|
||||
let isflt: bool = isfloattype(c, e);
|
||||
let rdtn: *node = nil;
|
||||
if (rp != nil) { rdtn = rp.lhs; };
|
||||
let rdtag: bool = false;
|
||||
if (rdtn != nil) { rdtag = istaggedtype(c, rdtn); };
|
||||
let isflt: bool = !rdtag && isfloattype(c, e);
|
||||
if (isflt) {
|
||||
cgexpr(c, e);
|
||||
let mov: str = "MOVSD";
|
||||
@@ -32655,9 +32761,11 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
sseidx = sseidx + 1;
|
||||
} else {
|
||||
// scalar=AX; slice/str=AX,BX,CX; tagged
|
||||
// box from its slot (tuplitpushelem)
|
||||
tuplitpushelem(c, e);
|
||||
// box from its slot or widened scratch
|
||||
// (tuplitpushelem)
|
||||
tuplitpushelem(c, e, rdtn);
|
||||
};
|
||||
if (rp != nil) { rp = rp.next; };
|
||||
e = e.next;
|
||||
};
|
||||
let i: i32 = gptotal - 1;
|
||||
@@ -32668,9 +32776,14 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
i = i - 1;
|
||||
};
|
||||
let j: i32 = 0;
|
||||
rp = rp0;
|
||||
e = rhs.list;
|
||||
for (e != nil) {
|
||||
if (isfloattype(c, e)) {
|
||||
let rdtn: *node = nil;
|
||||
if (rp != nil) { rdtn = rp.lhs; };
|
||||
let rdtag: bool = false;
|
||||
if (rdtn != nil) { rdtag = istaggedtype(c, rdtn); };
|
||||
if (!rdtag && isfloattype(c, e)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, e)) { mov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
@@ -32682,6 +32795,7 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
emitline("\n");
|
||||
j = j + 1;
|
||||
};
|
||||
if (rp != nil) { rp = rp.next; };
|
||||
e = e.next;
|
||||
};
|
||||
emitline("\tMOVQ\tBP, SP\n");
|
||||
@@ -34098,7 +34212,19 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
if (ttup != nil) {
|
||||
if (ttup.kind == nkind.N_TTUPLE
|
||||
&& sretretsize(c, ttup) == 0) {
|
||||
cgexpr(c, rhs);
|
||||
// #57: a tuple LITERAL rhs carries the DECLARED
|
||||
// type into the cursor fill — its stamped type
|
||||
// is element-constructed, so a declared-tagged
|
||||
// element's concrete rvalue skipped the widen
|
||||
// and the fill/receive cursor walks skewed
|
||||
// (let-twin of the return-position bug; probe
|
||||
// /tmp/p57/q1_let). Same emission as the cgexpr
|
||||
// route for every declared-tagged-free literal.
|
||||
if (rhs.kind == nkind.N_TUPLE) {
|
||||
cgtuplelittocursor(c, rhs, ttup);
|
||||
} else {
|
||||
cgexpr(c, rhs);
|
||||
};
|
||||
let gpcur: i32 = 0;
|
||||
let ssecur: i32 = 0;
|
||||
let eoff: i32 = 0;
|
||||
@@ -34918,6 +35044,10 @@ fn cgmassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
|
||||
// #64 (filed, rule 7): an N_TUPLE literal rhs rides this decl-less
|
||||
// cgexpr route, so a declared-TAGGED element's concrete rvalue
|
||||
// still fills the cursor stamped-keyed (silent skew) — the #57
|
||||
// decl wire stops at return/let.
|
||||
if (n.rhs != nil) { cgexpr(c, n.rhs); };
|
||||
|
||||
if (sretrecv > 0) {
|
||||
|
||||
@@ -1925,6 +1925,201 @@ static const struct row rows[] = {
|
||||
" if (t.1 != 36) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
/* #57: an IN-CAP tuple literal's cursor fill keyed each element on
|
||||
* its STAMPED type (element-constructed, check.c N_TUPLE), so a
|
||||
* declared-TAGGED element from a concrete rvalue (`(5: size, 9)`)
|
||||
* counted ONE word and skipped the widen — 2 words sent against the
|
||||
* receiver's declared 3-word walk; every later element read garbage
|
||||
* (both stages, byte-id, gate-blind; ken /tmp/ken57). Rows: the
|
||||
* return position (named + inline union spelling, t.N + destructure
|
||||
* readback), the let-literal twin, tagged SECOND element (shift
|
||||
* direction), float payload (the SSE-row gate: a (void|f64) box
|
||||
* rides INTEGER eightbytes), bare-untyped payload (rides the #33
|
||||
* chooser through the new widen wire), the ident no-regress anchor,
|
||||
* and the still-loud CALL-elem tripwire (#41). */
|
||||
{ "t57_ret_cast_elem",
|
||||
"package main;\n"
|
||||
"type un16 = (void | size);\n"
|
||||
"fn mk() (un16, size) = {\n"
|
||||
" return (5: size, 9);\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let t = mk();\n"
|
||||
" if (t.1 != 9) { return 1; };\n"
|
||||
" let (a, b) = mk();\n"
|
||||
" if (!(a is size)) { return 2; };\n"
|
||||
" if (a as size != 5) { return 3; };\n"
|
||||
" if (b != 9) { return 4; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
{ "t57_ret_inline_union",
|
||||
"package main;\n"
|
||||
"fn mk() ((void | size), size) = {\n"
|
||||
" return (5: size, 9);\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let (a, b) = mk();\n"
|
||||
" if (!(a is size)) { return 1; };\n"
|
||||
" if (b != 9) { return 2; };\n"
|
||||
" if (a as size != 5) { return 3; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
{ "t57_let_literal",
|
||||
"package main;\n"
|
||||
"type un16 = (void | size);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let t: (un16, size) = (5: size, 9);\n"
|
||||
" if (t.1 != 9) { return 1; };\n"
|
||||
" if (!(t.0 is size)) { return 2; };\n"
|
||||
" if (t.0 as size != 5) { return 3; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
{ "t57_tagged_second_elem",
|
||||
"package main;\n"
|
||||
"type un16 = (void | size);\n"
|
||||
"fn mk2() (size, un16) = {\n"
|
||||
" return (3, 7: size);\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let (a, b) = mk2();\n"
|
||||
" if (a != 3) { return 1; };\n"
|
||||
" if (!(b is size)) { return 2; };\n"
|
||||
" if (b as size != 7) { return 3; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
{ "t57_float_payload",
|
||||
"package main;\n"
|
||||
"type unf = (void | f64);\n"
|
||||
"fn mkf() (unf, size) = {\n"
|
||||
" return (1.5, 9);\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let (a, b) = mkf();\n"
|
||||
" if (b != 9) { return 1; };\n"
|
||||
" if (!(a is f64)) { return 2; };\n"
|
||||
" if (a as f64 != 1.5) { return 3; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
/* ken's adversarial shapes (k57a/b/c, /tmp/ken57) as rows: the
|
||||
* tagged-MID elem shifts the cursor on BOTH sides of the box; two
|
||||
* tagged rvalue elems (incl. a VOID rvalue) reuse the shared scratch
|
||||
* sequentially; a true SSE elem (plain f64) coexisting with a
|
||||
* declared-tagged box pins the gate splitting the rows. */
|
||||
{ "t57_tagged_mid_elem",
|
||||
"package main;\n"
|
||||
"type un = (void | size);\n"
|
||||
"fn mk3() (size, un, size) = {\n"
|
||||
" return (1, 5: size, 9);\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let (a, b, c) = mk3();\n"
|
||||
" if (a != 1) { return 1; };\n"
|
||||
" if (!(b is size)) { return 2; };\n"
|
||||
" if (b as size != 5) { return 3; };\n"
|
||||
" if (c != 9) { return 4; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
{ "t57_two_tagged_void_elem",
|
||||
"package main;\n"
|
||||
"type un = (void | size);\n"
|
||||
"fn mk() (un, un) = {\n"
|
||||
" return (5: size, void);\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let t = mk();\n"
|
||||
" if (!(t.0 is size)) { return 1; };\n"
|
||||
" if (t.0 as size != 5) { return 2; };\n"
|
||||
" if (!(t.1 is void)) { return 3; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
{ "t57_sse_coexist",
|
||||
"package main;\n"
|
||||
"type un = (void | size);\n"
|
||||
"fn mk() (f64, un) = {\n"
|
||||
" return (0.5, 7: size);\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let (x, e) = mk();\n"
|
||||
" if (x * 4.0 != 2.0) { return 1; };\n"
|
||||
" if (!(e is size)) { return 2; };\n"
|
||||
" if (e as size != 7) { return 3; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
{ "t57_bare_untyped_payload",
|
||||
"package main;\n"
|
||||
"type un16 = (void | size);\n"
|
||||
"fn mkb() (un16, size) = {\n"
|
||||
" return (5, 9);\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let (a, b) = mkb();\n"
|
||||
" if (!(a is size)) { return 1; };\n"
|
||||
" if (a as size != 5) { return 2; };\n"
|
||||
" if (b != 9) { return 3; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
{ "t57_ident_no_regress",
|
||||
"package main;\n"
|
||||
"type un16 = (void | size);\n"
|
||||
"fn mk() (un16, size) = {\n"
|
||||
" let e: un16 = 5: size;\n"
|
||||
" return (e, 9);\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let t = mk();\n"
|
||||
" if (t.1 != 9) { return 1; };\n"
|
||||
" if (!(t.0 is size)) { return 2; };\n"
|
||||
" if (t.0 as size != 5) { return 3; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
{ "t57_loud_call_elem",
|
||||
"package main;\n"
|
||||
"type un16 = (void | size);\n"
|
||||
"fn mke() un16 = {\n"
|
||||
" return 5: size;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let t: (un16, size) = (mke(), 9);\n"
|
||||
" if (t.1 != 9) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0,
|
||||
K_BUILDERR, "tagged tuple element from a non-local source shape unwired" },
|
||||
/* the NEW #57 loud: an in-cap tagged->tagged SUBSET elem (stamped
|
||||
* eslot 16 into declared eslot 24) needs a tag remap on the way into
|
||||
* the slot — pinned loud both stages until the #23/#40 widening
|
||||
* family wires it. (big,size) = 3+1 GP words, in-cap by GPCAP. */
|
||||
{ "t57_loud_remap",
|
||||
"package main;\n"
|
||||
"type st16 = struct { a: size, b: size };\n"
|
||||
"type small = (void | u8);\n"
|
||||
"type big = (void | u8 | st16);\n"
|
||||
"fn mk() (big, size) = {\n"
|
||||
" let s: small = 5u8;\n"
|
||||
" return (s, 9);\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let t = mk();\n"
|
||||
" if (t.1 != 9) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0,
|
||||
K_BUILDERR, "widening into a wider declared union slot needs a tag remap" },
|
||||
/* ken k57d: the in-cap/over-cap BOUNDARY — an over-cap
|
||||
* (un,un,size,size) return with concrete rvalue tagged elems must
|
||||
* route to the sret arm's #22b ident-only loud-stop, not slip into
|
||||
* the (now declared-keyed) in-cap path: pins that the declared
|
||||
* count and the over-cap classify (cg_sret_retsize) agree. */
|
||||
{ "t57_loud_overcap_boundary",
|
||||
"package main;\n"
|
||||
"type un = (void | size);\n"
|
||||
"fn mk() (un, un, size, size) = {\n"
|
||||
" return (5: size, void, 8, 9);\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let t = mk();\n"
|
||||
" if (t.2 != 8) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0,
|
||||
K_BUILDERR, "tagged element in an over-cap (sret) tuple return" },
|
||||
};
|
||||
|
||||
/* build+run via a driver (ww / ww_ww); returns 0 pass, nonzero fail. */
|
||||
|
||||
Reference in New Issue
Block a user