w6c/cgen: #61 alias-NAMED struct param classify — transitive chase at the ABI choke

A 2-level alias param (`type row = st; type st = struct{a,b,c}`) fell
through the single NAMED peel at every classify site, so BOTH ends of
the call moved one eightbyte of a multi-word struct: the caller's
node_isstructarg/node_isaggarg said scalar, the callee prologue spilled
ONLY DI, and s.b/s.c read 8(BP)/(BP) — saved-BP/return-address garbage.
SILENT runtime-wrong (F0 m5_arg/m8_arg1/m8b_arg1lit: cs exit 1, ww
correct, byte-id NO).

Route the four classify chokes through type_chase_named: struct_arg_size
+ aggarg_size (shared by caller push AND the size axes), struct_float_
class (the #165 SSE eightbyte leg), and the fn-prologue param classify
pu. Caller and callee key off the same helpers, so the pair cannot
half-land. cs converges to wwstage's already-correct asm — all probe
rows graduate to byte-id YES; bootstrap asm cmp-identical vs master
(2-level alias params unused in selfhost).

test: 944_alias_accept_run +5 rows — fwd-ref / lit-init / 40B
5-eightbyte aggarg leg / f64 struct_float_class leg, every row checking
the LAST field with distinct values, + base-named control. Mutation-
checked at 738d7f4: the four alias rows exit 1 (the silent-wrong
signature) and byte-id-diff there; 55/55 green here.
This commit is contained in:
2026-06-05 11:26:22 +09:00
parent 9bd0d8bc81
commit b9dd29706b
2 changed files with 91 additions and 7 deletions

View File

@@ -558,8 +558,11 @@ localloadop(Type *t)
static int
struct_arg_size(Type *t)
{
if (t == NULL) return 0;
if (t->kind == TY_NAMED) t = t->under;
/* Transitive chase (#61): `type row = st; type st = struct` is two
* NAMED layers — the single peel classified the param SCALAR, so
* caller and callee both moved one eightbyte of a 3-word struct
* (silent caller-frame garbage reads in the callee). */
t = type_chase_named(t);
if (t == NULL || t->kind != TY_STRUCT) return 0;
return (int)t->size;
}
@@ -580,8 +583,8 @@ struct_arg_size(Type *t)
static int
struct_float_class(Type *t, int *cls)
{
if (t == NULL) return 0;
if (t->kind == TY_NAMED) t = t->under;
/* Transitive chase (#61) — same classify choke as struct_arg_size. */
t = type_chase_named(t);
if (t == NULL || t->kind != TY_STRUCT) return 0;
int sz = (int)t->size;
if (sz <= 0 || sz > 16) return 0;
@@ -777,8 +780,8 @@ node_isstructarg(Node *n)
static int
aggarg_size(Type *t)
{
if (t == NULL) return 0;
if (t->kind == TY_NAMED) t = t->under;
/* Transitive chase (#61) — same classify choke as struct_arg_size. */
t = type_chase_named(t);
if (t == NULL) return 0;
if (t->kind == TY_STRUCT || t->kind == TY_ARRAY)
return (int)t->size;
@@ -13837,7 +13840,10 @@ cgfn(Cg *c, FILE *out, Node *fn)
continue;
}
Type *pt = tp ? tp->type : NULL;
Type *pu = (pt && pt->kind == TY_NAMED) ? pt->under : pt;
/* Transitive chase (#61): the callee-side classify twin of
* struct_arg_size/aggarg_size — a 2-level alias param fell to
* the scalar arm and spilled ONLY DI. */
Type *pu = type_chase_named(pt);
int slice = (pu && pu->kind == TY_SLICE);
int is_str = type_isstr(pt);
int is_struct = pu && pu->kind == TY_STRUCT && pu->size <= 16;