node_tuplearg was N_CALL-scoped and its comment claimed non-call forms "loud-stop" — they did NOT: a tuple ident/literal/unwrap arg fell to the scalar single-PUSHQ default, skewing every later arg register so the callee read garbage word 2 (byte-id both stages, the gate-blind both-wrong class; packed shapes SIGSEGV'd pre-C-t0). The receive side (cgfn #163 walk) was already correct. cgexpr already fills the return-ABI cursor for every supported producer (#241: ident via slot-to-cursor, literal via lit-to-cursor, unwrap via payload shift; call via the return ABI) — the send now admits exactly those into the existing @tupargscr restage + per-class drain (node_tuplearg widened; wwstage gains nodetuplearg, mirroring it over the local tnode / inferletcalltype; rettupleof stays N_CALL-scoped for the destructure receives). Any OTHER tuple-typed source shape loud-stops at the push site — the false comment's claim, now true (rule 7). Literal tuple elements are stamped expr types, so the restage/drain wide test goes type_isstr/type_isslice (TY_UNTYPED_STR- aware) with the ty_str->size header stride; the wwstage twin walks a literal's VALUE exprs the way cgtuplelittocursor classifies them. Ken review demands folded in: (1) a NESTED composite element (tuple/struct/array/tagged inside the tuple) occupies more than the one GP word the restage walk counts — the checker accepted it and it ran WRONG (inner words skewed, wwstage SIGSEGV); both stages' restage walks now loud-stop the element kind (wiring is the filed follow-up, task #65). (2) the variadic interaction probed: a tuple arg ahead of a variadic tail rides the restage correctly (positive row); variadic-of-tuples stays bounded-loud via the tuple-in-slice read surface. 941 grows the t2 matrix: packed/16B params with branched callees, mixed arg orders both ways, literal arg, (f64,i64) param, unwrap arg, ken's >6-GP-pressure stress (4 leading scalars + tuple + a 7th stack-class word), variadic-after-tuple, plus rule-7 reject rows (chain-source arg, nested-element arg, variadic-of-tuples, over-cap ident arg) and the fold-4 charset substrate pin ([](u32,u32) append stays LOUD). At the C-t1 parent 18/73 checks fail: every runtime arg row except the (f64,i64) anchor on BOTH stages (byte-identically — the gate-blind both-wrong class) and the chain/nested args silently accepted.
This commit is contained in:
@@ -238,16 +238,22 @@ node_isslice(Node *n)
|
||||
}
|
||||
|
||||
/* node_tuplearg — the underlying TY_TUPLE Type of a tuple-typed argument
|
||||
* VALUE, else NULL. #163: scoped to an N_CALL producer — the only form
|
||||
* that leaves a tuple in the return-ABI cursor (AX/DX/CX/R8 + X0/X1, per
|
||||
* #164). A tuple ident / literal as a first-class value is a separate
|
||||
* unimplemented gap (`let t = (1,2)` does not materialise a slot today),
|
||||
* so the SEND restricts to the call form and loud-stops the rest rather
|
||||
* than push stale registers (rule 7, never a silent drop). */
|
||||
* VALUE, else NULL. #163/#32 (C-t2): admits every producer whose cgexpr
|
||||
* leaves the tuple in the return-ABI cursor (AX/DX/CX/R8 + X0/X1, per
|
||||
* #164) — a CALL (return ABI), an IDENT (cg_tuple_slot_to_cursor, #241),
|
||||
* a LITERAL (cg_tuple_lit_to_cursor, #241), a `?`/`!` unwrap
|
||||
* (cg_tagged_tuple_payload_shift, #241). Pre-C-t2 this was N_CALL-scoped
|
||||
* and the comment claimed the rest "loud-stop" — they did NOT: a tuple
|
||||
* ident arg fell to the scalar single-PUSHQ default, skewing every later
|
||||
* arg register (callee read garbage word 2). The cgcall push site now
|
||||
* loud-stops any OTHER tuple-typed source shape (rule 7). */
|
||||
static Type *
|
||||
node_tuplearg(Node *n)
|
||||
{
|
||||
if (n == NULL || n->kind != N_CALL) return NULL;
|
||||
if (n == NULL) return NULL;
|
||||
if (n->kind != N_CALL && n->kind != N_IDENT && n->kind != N_TUPLE
|
||||
&& n->kind != N_TRYUNW && n->kind != N_TRYPROP)
|
||||
return NULL;
|
||||
Type *t = n->type;
|
||||
Type *u = (t && t->kind == TY_NAMED) ? t->under : t;
|
||||
return (u && u->kind == TY_TUPLE) ? u : NULL;
|
||||
@@ -8087,6 +8093,21 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
}
|
||||
cgexpr(c, args[i], locals);
|
||||
Type *tuparg_push = node_tuplearg(args[i]);
|
||||
/* #32 (C-t2, rule 7): a tuple-typed arg from a source
|
||||
* shape whose cgexpr does NOT fill the return cursor
|
||||
* (chain reads, match exprs, ...) must die loud here —
|
||||
* pre-fix it fell to the scalar single-PUSHQ default
|
||||
* and silently skewed every later arg register. */
|
||||
if (tuparg_push == NULL) {
|
||||
Type *targ = args[i]->type;
|
||||
if (targ && targ->kind == TY_NAMED)
|
||||
targ = targ->under;
|
||||
if (targ && targ->kind == TY_TUPLE)
|
||||
fatal("#32: tuple arg from unsupported "
|
||||
"source shape %d (call/ident/"
|
||||
"literal/unwrap only; rule 7)",
|
||||
args[i]->kind);
|
||||
}
|
||||
if (node_isfloat(args[i])) {
|
||||
/* f32 spills 4B (MOVSS), f64 8B (MOVSD): the SysV
|
||||
* float class drives the width per ref/qbe
|
||||
@@ -8146,11 +8167,36 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
int gpcur = 0, ssecur = 0, eoff = 0, ef32;
|
||||
int gptot = 0, sstot = 0, tsz = 0;
|
||||
for (Tparam *p = tuparg_push->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);
|
||||
/* C-t2 (ken demand 1, rule 7): a
|
||||
* COMPOSITE element (nested tuple /
|
||||
* struct / array / tagged) occupies
|
||||
* more than the one GP word this walk
|
||||
* counts — the checker accepts the
|
||||
* shape but the cursor transport
|
||||
* cannot carry it; pre-guard it ran
|
||||
* WRONG (inner words skewed). Loud
|
||||
* until a consumer motivates wiring. */
|
||||
Type *cu = type_chase_named(p->type);
|
||||
if (cu && (cu->kind == TY_TUPLE
|
||||
|| cu->kind == TY_STRUCT
|
||||
|| cu->kind == TY_ARRAY
|
||||
|| cu->kind == TY_TAGGED))
|
||||
fatal("#32: tuple arg element "
|
||||
"kind unsupported (nested "
|
||||
"tuple/struct/array/tagged; "
|
||||
"rule 7)");
|
||||
/* C-t2: type_isstr/type_isslice, not a
|
||||
* raw kind test — a LITERAL tuple's
|
||||
* element types are the stamped expr
|
||||
* types, so a strlit element is
|
||||
* TY_UNTYPED_STR (size 0); the raw test
|
||||
* under-classified it as 1 GP word
|
||||
* against lit-to-cursor's node_isstr
|
||||
* 3-word push. The wide stride reads
|
||||
* ty_str->size (the header SSoT) for
|
||||
* the same reason. */
|
||||
int wide = type_isstr(p->type)
|
||||
|| type_isslice(p->type);
|
||||
if (fld_isfloat(p->type, &ef32))
|
||||
sstot++;
|
||||
else
|
||||
@@ -8159,7 +8205,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* size); matches the wwstage slotsize() walk so
|
||||
* the @tupargscr width + reverse-push count agree
|
||||
* byte-for-byte. */
|
||||
tsz += wide ? (int)pu->size : 8;
|
||||
tsz += wide ? (int)ty_str->size : 8;
|
||||
}
|
||||
/* The producing call already satisfied #164's
|
||||
* return caps; guard anyway (tuple_store indexes
|
||||
@@ -8177,11 +8223,8 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
"#163)", cg_tupargscr_sz, tsz);
|
||||
}
|
||||
for (Tparam *p = tuparg_push->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 wide = type_isstr(p->type)
|
||||
|| type_isslice(p->type);
|
||||
int isflt = fld_isfloat(p->type, &ef32);
|
||||
tuple_store(c, p->type, wide, gpcur, ssecur,
|
||||
cg_tupargscr + eoff);
|
||||
@@ -8189,7 +8232,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
ssecur++;
|
||||
else
|
||||
gpcur += tuple_ebytes(wide);
|
||||
eoff += wide ? (int)pu->size : 8;
|
||||
eoff += wide ? (int)ty_str->size : 8;
|
||||
}
|
||||
for (int w = tsz - 8; w >= 0; w -= 8) {
|
||||
ins2(c, A_MOVQ,
|
||||
@@ -8400,11 +8443,10 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* scope (twin of #164's cap). */
|
||||
int ef32;
|
||||
for (Tparam *p = tu->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);
|
||||
/* C-t2: untyped-str-aware wide test —
|
||||
* twin of the @tupargscr restage walk. */
|
||||
int wide = type_isstr(p->type)
|
||||
|| type_isslice(p->type);
|
||||
if (fld_isfloat(p->type, &ef32)) {
|
||||
if (fi >= 8)
|
||||
fatal("tuple arg float "
|
||||
|
||||
Reference in New Issue
Block a user