cgen: str arr[i].field = v store -> 3-word -- Phase 2 G1 (both stages)

Storing a str into a field of an indexed element (arr[i].f = v) wrote only 2 words (ptr,len), dropping cap -- the write-side mirror of the arrfield read (c3bbe17), and the first STORE-cluster fold. The trap: the index scale (IMULQ via CX) clobbers CX=cap and the index-expr eval clobbers AX=ptr before the store. Fix composes two proven oracles -- arr[i]=v (cgen.c:3650) spills the value (PUSHQ CX/BX/AX) across the index/address computation, then s.f=v (cgen.c:2603) stages the dst address in DX (off the AX/BX/CX str convention) and stores ptr/len/cap at foff+{0,8,16}. Kind-gated (TY_STR/isstrtype, never size==24). cstage==wwstage byte-identical.

test/wcc/937: table-driven write-then-read-cap over [N]S / []S / [N]*S arr[i].f= ; rhs is a runtime cap!=len str (not a literal, which would be cap==len); all 3 slot words pre-poisoned via a DIFFERENT already-3-word store path so a stale 2-word store is detectable; asserts the full {ptr,len,cap} triple. Meaningful only now the reads are 3-word. fail-before/pass-after verified on both drivers.

main.combined.ww regenerated via the canonical make path (md5-stable).
This commit is contained in:
2026-05-24 15:35:37 +09:00
parent c3bbe17163
commit c6929231dc
6 changed files with 315 additions and 38 deletions

View File

@@ -4042,11 +4042,17 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("\n");
return;
};
// str rhs: AX=ptr, BX=len. Stash both,
// compute addr in CX so the pop pair
// restores AX/BX intact.
// str IS []u8: rhs leaves AX=ptr,
// BX=len, CX=cap (#1/Phase 3). Spill
// all three across the index/address
// computation (IMULQ's CX scratch
// clobbers cap), stage &arr[i] in DX
// off the str AX/BX/CX convention
// (mirrors s.f=v), then store the full
// triple at foff+0/+8/+16.
if (isstrtype(c, fi.tnode)) {
cgexpr(c, n.rhs);
emitline("\tPUSHQ\tCX\n");
emitline("\tPUSHQ\tBX\n");
emitline("\tPUSHQ\tAX\n");
cgexpr(c, idx);
@@ -4059,21 +4065,25 @@ fn cgassign(c: *cgen, n: *node) void = {
if (baseisarray) {
emitline("\tLEAQ\t");
emitoff(lc.off: i64);
emitline("(BP), CX\n");
emitline("(BP), DX\n");
} else {
emitline("\tMOVQ\t");
emitoff(lc.off: i64);
emitline("(BP), CX\n");
emitline("(BP), DX\n");
};
emitline("\tADDQ\tAX, CX\n");
if (viaptr) { emitline("\tMOVQ\t(CX), CX\n"); };
emitline("\tADDQ\tAX, DX\n");
if (viaptr) { emitline("\tMOVQ\t(DX), DX\n"); };
emitline("\tPOPQ\tAX\n");
emitline("\tPOPQ\tBX\n");
emitline("\tPOPQ\tCX\n");
emitline("\tMOVQ\tAX, ");
emitdispreg(fi.foff: i64, "CX");
emitdispreg(fi.foff: i64, "DX");
emitline("\n");
emitline("\tMOVQ\tBX, ");
emitdispreg((fi.foff + 8): i64, "CX");
emitdispreg((fi.foff + 8): i64, "DX");
emitline("\n");
emitline("\tMOVQ\tCX, ");
emitdispreg((fi.foff + 16): i64, "DX");
emitline("\n");
return;
};