w6c/cgen: #71 chained-dot walks chase alias-typed hops — direct offsets, byte-id graduation

rob probe-ruled F1 enrollment (fold-or-file decided by the dispatch
test): the chained-dot STORE walk, its READ twin, and the addr-of
sibling each single-peeled every hop's type, so an alias-typed field
(type fa = inner; outer{x: fa}) aborted the offset-folding fast arm and
fell to the generic address spine — store via cgplaceaddr
(PUSHQ/LEAQ/ADDQ/POPQ), read via ADDQ-per-hop. Runtime-correct BOTH
stages; byte-id NO vs wwstage's folded direct MOVQ offsets
(reviewer-62r diamond find). Chasing the walk hops (+ the read arm's
leaf gate and the two ptr-root sub peels) flips cs onto the fast arm =
wwstage's asm exactly.

Blast radius measured per rob's caution: bootstrap asm cmp-identical
vs the pristine 738d7f4 scratch on all five main.combined.ww, 989
lib_byteid pins unchanged (31/9/3), every control row byte-id — the
flip moves ONLY the #71 shapes.

test: 944_alias_accept_run +2 rows, both decl orders: nested store +
last-field readback; the fwd row adds the &v.y.b addr-of + deref-write
leg. Mutation-checked at the 738d7f4 scratch: both rows byte-id-diff
there; 70/70 green here.

Also graduates the reviewer-F1 slicefield rows: their `.len` readbacks
ride this walk, so the 7 K_RUN_CS rows from commit 1 flip to K_RUN
(byte-id) here.
This commit is contained in:
2026-06-05 11:43:35 +09:00
parent c138605563
commit ac63951d54
2 changed files with 66 additions and 22 deletions

View File

@@ -4053,8 +4053,9 @@ cgexpr(Cg *c, Node *n, Local *locals)
int abort = 0;
while (cur && cur->kind == N_DOT && cur->lhs) {
Type *pt = cur->lhs->type;
Type *pu = (pt && pt->kind == TY_NAMED)
? pt->under : pt;
/* Transitive chase (#71) — addr-of sibling of the
* chained-dot walks. */
Type *pu = type_chase_named(pt);
if (!pu) { abort = 1; break; }
if (cur == opnd && (pu->kind == TY_SLICE
|| pu->kind == TY_STR)) {
@@ -5517,8 +5518,12 @@ cgexpr(Cg *c, Node *n, Local *locals)
int abort = 0;
while (cur && cur->kind == N_DOT && cur->lhs) {
Type *pt = cur->lhs->type;
Type *pu = (pt && pt->kind == TY_NAMED)
? pt->under : pt;
/* Transitive chase (#71): an alias-typed hop left pu
* TY_NAMED -> abort -> the generic cgplaceaddr spine
* (PUSHQ/LEAQ/ADDQ/POPQ) while wwstage emits the direct
* offset store. Runtime-correct both; chasing converges
* cs onto the fast direct arm = wwstage's asm. */
Type *pu = type_chase_named(pt);
if (!pu) { abort = 1; break; }
if (cur == n->lhs && (pu->kind == TY_SLICE
|| pu->kind == TY_STR)) {
@@ -5530,8 +5535,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
/* `*T` root: dereference at emit time;
* walk through pointee struct fields.
* Last-hop only (root is a bare ident). */
Type *sub = (pu->sub->kind == TY_NAMED)
? pu->sub->under : pu->sub;
Type *sub = type_chase_named(pu->sub);
if (sub && sub->kind == TY_STRUCT) {
pu = sub;
ptr_root = 1;
@@ -10298,7 +10302,10 @@ cgexpr(Cg *c, Node *n, Local *locals)
* stale. Sibling of the pointer-chain branch further down. */
if (n->lhs && n->lhs->kind == N_DOT) {
Type *lt0 = n->lhs->type;
Type *lu0 = (lt0 && lt0->kind == TY_NAMED) ? lt0->under : lt0;
/* Transitive chase (#71) — the READ twin of the chained-dot
* store walk; an alias-typed hop skipped this arm onto the
* ADDQ-per-hop address spine while wwstage folds the offsets. */
Type *lu0 = type_chase_named(lt0);
int leaf_is_pseudo = lu0 && n->str
&& (lu0->kind == TY_SLICE || lu0->kind == TY_STR)
&& (strcmp(n->str, "ptr") == 0
@@ -10313,8 +10320,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
int abort = 0;
while (cur && cur->kind == N_DOT && cur->lhs) {
Type *pt = cur->lhs->type;
Type *pu = (pt && pt->kind == TY_NAMED)
? pt->under : pt;
Type *pu = type_chase_named(pt);
if (!pu) { abort = 1; break; }
if (cur == n && (pu->kind == TY_SLICE
|| pu->kind == TY_STR)) {
@@ -10329,8 +10335,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
* is a bare ident) — `*T`-field mid-
* chain keeps its cgexpr-based pointer-
* field branch further down. */
Type *sub = (pu->sub->kind == TY_NAMED)
? pu->sub->under : pu->sub;
Type *sub = type_chase_named(pu->sub);
if (sub && sub->kind == TY_STRUCT) {
pu = sub;
ptr_root = 1;

View File

@@ -50,6 +50,10 @@
* | store/push word0-only; CS-ONLY until |
* | F2 lands the wwstage half; base ctl |
* | + the #54-bound `as` row pinned loud | 0(cs)
* nested_alias_field_* | task #71: alias-typed nested-field |
* | store/read/addr-of walks fold to |
* | direct offsets (byte-id graduation, |
* | both decl orders) | 0
*
* K_RUN rows also assert cstage/wwstage asm byte-id (acceptance
* graduations land byte-identical — F0's accept side was wwstage).
@@ -275,10 +279,9 @@ static const struct row rows[] = {
* noise() call clobbers BX/CX so stale-register luck can't pass a
* word0-only header. Unprobed sibling gates are LOUD-tripwired,
* pinned by the #73 row at the end of this block. Rows whose
* `.len` readback rides the chained-dot READ walk are K_RUN_CS
* here (runtime-correct both stages; cs takes the generic spine
* pre-#71) and flip to K_RUN at the #71 walk-chase commit, which
* graduates their byte-id. */
* `.len` readback rides the chained-dot READ walk entered as
* K_RUN_CS (runtime-correct both stages; cs took the generic
* spine) and graduated to K_RUN with the #71 walk chase. */
{ "slicefield_store_2lvl",
"package main;\n"
"type sl = []int;\n"
@@ -294,7 +297,7 @@ static const struct row rows[] = {
" if (b.s[2] != 900) { return 2; };\n"
" if (b.s.len != 3) { return 3; };\n"
" return 0;\n"
"};\n", 0, K_RUN_CS, NULL }, /* ->K_RUN at the #71 walk-chase commit */
"};\n", 0, K_RUN, NULL },
{ "slicefield_range_2lvl",
"package main;\n"
"type sl = []int;\n"
@@ -311,7 +314,7 @@ static const struct row rows[] = {
" if (x < 700) { return 3; };\n"
" };\n"
" return 0;\n"
"};\n", 0, K_RUN_CS, NULL }, /* ->K_RUN at the #71 walk-chase commit */
"};\n", 0, K_RUN, NULL },
{ "strfield_store_2lvl",
"package main;\n"
"type s1t = str;\n"
@@ -323,7 +326,7 @@ static const struct row rows[] = {
" b.n = 5;\n"
" if (b.s.len != 5) { return 1; };\n"
" return 0;\n"
"};\n", 0, K_RUN_CS, NULL }, /* ->K_RUN at the #71 walk-chase commit */
"};\n", 0, K_RUN, NULL },
{ "slicefield_structlit_2lvl",
"package main;\n"
"type sl = []int;\n"
@@ -336,7 +339,7 @@ static const struct row rows[] = {
" if (b.s.len != 3) { return 2; };\n"
" if (b.s[2] != 900) { return 3; };\n"
" return 0;\n"
"};\n", 0, K_RUN_CS, NULL }, /* ->K_RUN at the #71 walk-chase commit */
"};\n", 0, K_RUN, NULL },
{ "slicefield_viaptr_2lvl",
"package main;\n"
"type sl = []int;\n"
@@ -350,7 +353,7 @@ static const struct row rows[] = {
" if (p.s.len != 3) { return 1; };\n"
" if (p.s[2] != 900) { return 2; };\n"
" return 0;\n"
"};\n", 0, K_RUN_CS, NULL }, /* ->K_RUN at the #71 walk-chase commit */
"};\n", 0, K_RUN, NULL },
{ "slicefield_chainstore_2lvl",
"package main;\n"
"type sl = []int;\n"
@@ -364,7 +367,7 @@ static const struct row rows[] = {
" if (v.i.s.len != 3) { return 1; };\n"
" if (v.i.s[2] != 900) { return 2; };\n"
" return 0;\n"
"};\n", 0, K_RUN_CS, NULL }, /* ->K_RUN at the #71 walk-chase commit */
"};\n", 0, K_RUN, NULL },
{ "slicefield_ptrchain_2lvl",
"package main;\n"
"type sl = []int;\n"
@@ -381,7 +384,7 @@ static const struct row rows[] = {
" if (i.s.len != 3) { return 1; };\n"
" if (i.s[2] != 900) { return 2; };\n"
" return 0;\n"
"};\n", 0, K_RUN_CS, NULL }, /* ->K_RUN at the #71 walk-chase commit */
"};\n", 0, K_RUN, NULL },
{ "slicefield_chainread_2lvl",
"package main;\n"
"type sl = []int;\n"
@@ -749,6 +752,42 @@ static const struct row rows[] = {
" };\n"
" return 0;\n"
"};\n", 0, K_RUN, NULL },
/* ---- task #71 (rob probe-ruled F1 enrollment): nested fields
* whose TYPES are aliases. The chained-dot store/read/addr-of
* walks single-peeled each hop, aborted on the alias, and fell
* to the generic PUSHQ/LEAQ/ADDQ/POPQ address spine — runtime-
* correct but byte-id NO vs wwstage's folded direct offsets.
* Chasing the walks converges cs onto the direct arm = ww's asm.
* Distinct values; the LAST field of the SECOND member checked. */
{ "nested_alias_field_norm",
"package main;\n"
"type fa = inner;\n"
"type inner = struct { a: size, b: size };\n"
"type outer = struct { x: fa, y: fa };\n"
"export fn main() i32 = {\n"
" let v: outer;\n"
" v.x.a = 4; v.x.b = 9;\n"
" v.y.a = 7; v.y.b = 3;\n"
" if (v.x.b != 9) { return 1; };\n"
" if (v.y.b != 3) { return 2; };\n"
" return 0;\n"
"};\n", 0, K_RUN, NULL },
{ "nested_alias_field_fwd",
"package main;\n"
"type outer = struct { x: fa, y: fa };\n"
"type fa = inner;\n"
"type inner = struct { a: size, b: size };\n"
"export fn main() i32 = {\n"
" let v: outer;\n"
" v.x.a = 4; v.x.b = 9;\n"
" v.y.a = 7; v.y.b = 3;\n"
" if (v.x.b != 9) { return 1; };\n"
" if (v.y.b != 3) { return 2; };\n"
" let pa = &v.y.b;\n"
" *pa = 11;\n"
" if (v.y.b != 11) { return 3; };\n"
" return 0;\n"
"};\n", 0, K_RUN, NULL },
/* banked row L2-3 (`v as ali` readback) trips task #54's as-binding
* aggregate-init bound — LOUD both stages today; fold the row into
* #54's pin set on its fold (do not drop). Pinned here as the