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

@@ -2916,11 +2916,21 @@ cgexpr(Cg *c, Node *n, Local *locals)
}
if (n->op == TK_ASSIGN
&& fu && fu->kind == TY_STR) {
/* str rhs: AX=ptr, BX=len.
* Stash both, compute addr
* in CX so the pop pair
* restores AX/BX cleanly. */
/* 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. */
cgexpr(c, n->rhs, locals);
ins1(c, A_PUSHQ,
areg(D_CX));
ins1(c, A_PUSHQ,
areg(D_BX));
ins1(c, A_PUSHQ,
@@ -2937,28 +2947,33 @@ cgexpr(Cg *c, Node *n, Local *locals)
if (is_arr)
ins2(c, A_LEAQ,
amem(D_BP, off),
areg(D_CX));
areg(D_DX));
else
ins2(c, A_MOVQ,
amem(D_BP, off),
areg(D_CX));
areg(D_DX));
ins2(c, A_ADDQ,
areg(D_AX),
areg(D_CX));
areg(D_DX));
if (viaptr)
ins2(c, A_MOVQ,
amem(D_CX, 0),
areg(D_CX));
amem(D_DX, 0),
areg(D_DX));
ins1(c, A_POPQ,
areg(D_AX));
ins1(c, A_POPQ,
areg(D_BX));
ins1(c, A_POPQ,
areg(D_CX));
ins2(c, A_MOVQ,
areg(D_AX),
amem(D_CX, foff + 0));
amem(D_DX, foff + 0));
ins2(c, A_MOVQ,
areg(D_BX),
amem(D_CX, foff + 8));
amem(D_DX, foff + 8));
ins2(c, A_MOVQ,
areg(D_CX),
amem(D_DX, foff + 16));
break;
}
if (n->op == TK_ASSIGN) {