wcc+w6c+w6c_ww: tuple slot layout SSoT — checker size = cgen slot stride (C-t0)

The checker computed TY_TUPLE size as the packed element-size sum
((u32,u32) = 8B) while every cgen cursor-transport site strode 8B
slots (16B). 16B tuples were blind to the split (slot == packed);
packed tuples hit it everywhere: cstage let-receive keyed on sz 16/32
missed sz 8 and dropped word 1, the cgfn param receive spilled
8B/element into a packed-sized local (saved-BP clobber, SIGSEGV), and
mixed (u32,f64)/(u32,str) shapes missed the receive arms entirely.

Slot layout is now the SSoT (user-ratified): the flip lives in the two
checkers' N_TTUPLE size computation only (check.c, check.ww
tupleelemslot + stamp); cgen's packed-keyed walks (t.N read, #235 len
arm, over-cap sret send/receive pair) align onto the slot stride, and
the wwstage t.N read gains the natural-width load (tnodeloadop) to
byte-id with cstage's fldloadop. ttupleelem.offset re-stamped
slot-cumulative (no consumers yet). The #242/#243 eightbyte-share
loud-stop dissolves by construction (no two narrows ever share an
eightbyte) — 940's eightbyte_share row graduates to a runtime
round-trip. Hare-layout divergence documented at both checker sites;
re-alignment is task #60. #32 send skew and #33 wwstage literal-let
receive are separate commits on this base.

941_tuple_slot_layout_run pins the matrix: 4 packed rows fail at the
parent (8/21 checks), 3 neutral anchors prove 16B/32B emission
untouched.
This commit is contained in:
2026-06-04 18:20:07 +09:00
parent 0139652180
commit fdfc2ce318
9 changed files with 500 additions and 106 deletions

View File

@@ -6660,9 +6660,17 @@ cgexpr(Cg *c, Node *n, Local *locals)
idx = idx * 10 + (*q - '0');
Tparam *tp = bu->params;
int foff = 0;
/* C-t0: slot stride, twin of the N_DOT
* TY_TUPLE walk. */
while (idx > 0 && tp) {
if (tp->type)
foff += (int)tp->type->size;
Type *su = (tp->type
&& tp->type->kind == TY_NAMED)
? tp->type->under : tp->type;
if (su && (su->kind == TY_STR
|| su->kind == TY_SLICE))
foff += (int)su->size;
else
foff += 8;
tp = tp->next;
idx--;
}
@@ -9504,14 +9512,25 @@ cgexpr(Cg *c, Node *n, Local *locals)
break;
}
}
/* tuple positional field access: t.0, t.1, ... */
/* tuple positional field access: t.0, t.1, ...
* C-t0: slot stride (a str/slice its header, everything else
* one 8B eightbyte) — the layout every cursor transport site
* writes and the checker's TY_TUPLE size now counts. The load
* below keeps the element's NATURAL width (fldloadop). */
if (u && u->kind == TY_TUPLE && n->lhs->kind == N_IDENT && n->str) {
int idx = 0;
for (const char *q = n->str; *q; q++) idx = idx * 10 + (*q - '0');
Tparam *tp = u->params;
int foff = 0;
while (idx > 0 && tp) {
if (tp->type) foff += (int)tp->type->size;
Type *su = (tp->type
&& tp->type->kind == TY_NAMED)
? tp->type->under : tp->type;
if (su && (su->kind == TY_STR
|| su->kind == TY_SLICE))
foff += (int)su->size;
else
foff += 8;
tp = tp->next;
idx--;
}
@@ -11765,7 +11784,11 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
} else
ins2(c, fldstoreop(e->type, esz),
areg(D_AX), amem(D_DX, foff));
foff += esz;
/* C-t0: the sret buffer is slot-laid
* like every tuple home (checker size,
* t.N reader, mlet receive agree); esz
* keeps the store WIDTH natural. */
foff += wide ? esz : 8;
if (pp) pp = pp->next;
}
ins2(c, A_MOVQ, amem(D_BP, cg_sret_arg_off),
@@ -12132,7 +12155,9 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
ins2(c, fldstoreop(t, esz),
areg(D_AX), amem(D_BP, off));
}
foff += esz;
/* C-t0: slot stride — must mirror the
* N_RETURN over-cap SEND's buffer layout. */
foff += wide ? esz : 8;
}
break;
}
@@ -12239,7 +12264,9 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
areg(D_AX), amem(D_BP, off));
}
}
foff += esz;
/* C-t0: slot stride — must mirror the
* N_RETURN over-cap SEND's buffer layout. */
foff += wide ? esz : 8;
if (tp) tp = tp->next;
}
break;

View File

@@ -629,7 +629,30 @@ resolve_type(Checker *c, Node *n)
if (!require_sized(c, tp->type, e->pos, "a tuple member"))
continue;
if (tp->type && tp->type->align > al) al = tp->type->align;
if (tp->type) sz += tp->type->size;
/* Slot layout is the tuple SSoT (tuple arc C-t0,
* user-ratified): every element occupies the stride
* cgen's cursor transport actually writes — a
* str/slice its header, everything else (narrow
* scalars included) one 8B eightbyte. ww-internal ABI
* only (tuples never cross extern); size((u32,u32))=16
* is observable via size() and diverges from Hare
* (harec type_store.c:533-580 anonymous-struct rule)
* AND from ww's own structs (which pack narrow fields
* post-fldloadop) — that internal inconsistency is
* what task #60 eventually fixes; re-open before any
* serialization/FFI/density use. Pre-C-t0 this summed
* packed element sizes while cgen strode 8B slots —
* the checker-says-8/cgen-does-16 split behind the
* packed-tuple miscompile family (#32/#33/#48). */
if (tp->type) {
Type *eu = tp->type->kind == TY_NAMED
? tp->type->under : tp->type;
if (eu && (eu->kind == TY_STR
|| eu->kind == TY_SLICE))
sz += eu->size;
else if (eu == NULL || eu->kind != TY_VOID)
sz += 8; /* sizelint-ok: the slot IS the 8B eightbyte */
}
if (head == NULL) head = tp;
else tail->next = tp;
tail = tp;