selfhost+test: route chained N_INDEX outer element size through indexvaluetnode on write path (#27)

Wwstage cgassign's N_INDEX-lhs base-inspection (cgenexpr.ww) only
computed esz/elemtn when base.kind == N_IDENT or N_DOT. For a
chained `names[i][k] = v` (names: **u8) the outer N_INDEX has
base.kind == N_INDEX; esz fell through to the default 8 so the
outer store emitted `MOVQ AX, (BX)` into a 1-byte u8 slot (8 bytes
written — adjacent memory corrupted) plus a stray
`MOVQ $8, CX; IMULQ CX, AX` scaling on the outer index that cstage
doesn't emit. Wrong-width-store: the byte slot was written as 8
bytes and the outer offset multiplied by sizeof *u8 instead of
sizeof u8.

Cstage walks `n->lhs->type` directly via the typed AST at the
N_ASSIGN N_INDEX-lhs branch (cmd/w6c/cgen.c eff->sub->size = 1).
Wwstage now mirrors via indexvaluetnode (already graduated for
cgindex in #24, commit aa8ca47) — the cgassign N_INDEX-lhs branch
gains the parallel base-N_INDEX arm: call indexvaluetnode, then
elemsizeofc for esz and one-layer-strip for elemtn (so the tagged-
element gate keys honestly on the element type, matching the
N_IDENT branch's pattern).

Class A wwstage cgen UNDER. Sister latent of #24's surfaced read-
path bug; filed during the #24 graduation with selfhost + lib grep
empty for chained-write. No in-tree consumer surfaced this before
the fix, so test 740_chained_write is the sole exerciser — pins
cstage-byte-identical asm for **u8 (MOVB store, 1 inner-stride-8
IMULQ pair, no outer scale) + **i32 (MOVL store, inner $8 + outer
$4 IMULQ pairs). Anti-check on the u8 row guards against the pre-
fix stray `MOVQ AX, (BX)` regression.

Sister latents filed (no in-tree consumer):
  cgassign N_DOT-base elemtn drop (sister of cgindex N_DOT-base
  in #24 review): tagged-element store via obj.arr[i] over a
  struct-field array falls through to scalar store.

114/114 ok. ww2 == ww3 == ww4 byte-id.
This commit is contained in:
2026-05-18 19:55:30 +09:00
parent aa8ca47943
commit 3ba19227ba
5 changed files with 339 additions and 3 deletions

View File

@@ -14242,7 +14242,22 @@ fn cgassign(c: *cgen, n: *node) void = {
};
} else { if (base.kind == nkind.N_DOT) {
esz = indexbaseesz(c, base);
};};
} else { if (base.kind == nkind.N_INDEX) {
// Chained-write write-side parallel of the
// cgindex N_INDEX-base arm graduated in #24:
// `names[i][k] = v` (names: **u8) — outer
// base is the inner N_INDEX whose value-type
// is *u8, so the outer element is u8 and
// the store is MOVB, not MOVQ.
let bt: *node = indexvaluetnode(c, base);
if (bt != nil) {
esz = elemsizeofc(c, bt);
let bk2: nkind = bt.kind;
if (bk2 == nkind.N_TPTR) { elemtn = bt.lhs; };
if (bk2 == nkind.N_TSLICE) { elemtn = bt.lhs; };
if (bk2 == nkind.N_TARRAY) { elemtn = bt.lhs; };
};
};};};
};
// Tagged-union element: materialize source in a shared
// scratch slot via cgwidentaggedstore (handles struct /