cgen: str chained <expr>.field = v store -> 3-word -- Phase 2 G2 (both stages)

Storing a str into a field reached through a *struct-valued expression (e.g. r.sym.flag = v) wrote only 2 words (ptr,len), dropping cap -- the second STORE-cluster fold. Direct transfer of G1 (c692923): the prior arm spilled only ptr/len across the base eval; now spill the full value (PUSHQ CX/BX/AX) after the rhs eval and before the base-expr eval (the stack slot insulates it, base-formation-agnostic), stage the *struct ptr in DX (off the AX/BX/CX str convention), store ptr/len/cap at foff+{0,8,16}. Mirrors the s.f=v oracle (cgen.c:2603); G2 adds the spill the oracle skips because the oracle's base is a slot read, not a clobbering expr. Kind-gated (TY_STR/typeisstr, never size==24). cstage==wwstage byte-identical at the store site.

test/wcc/938: table-driven write-then-read-cap over depth-2 (r.sym.f=) and depth-3 (r.a.b.f=) chained bases, both asm-confirmed to hit the chained arm. rhs is a cap!=len str; all 3 slot words pre-poisoned via a non-G2 direct store; full {ptr,len,cap} triple asserted. 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 16:18:49 +09:00
parent c6929231dc
commit b51a7daa25
6 changed files with 287 additions and 27 deletions

View File

@@ -3140,21 +3140,30 @@ cgexpr(Cg *c, Node *n, Local *locals)
break;
}
if (fu && fu->kind == TY_STR) {
/* str rhs: (AX=ptr, BX=len). Stash
* both, then load the struct ptr
* into CX and write both halves. */
/* str IS []u8: rhs leaves AX=ptr,
* BX=len, CX=cap (#1/Phase 3). Spill
* all three across the base-expr eval
* (it may clobber any reg), stage the
* *struct ptr 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, areg(D_AX));
cgexpr(c, n->lhs->lhs, locals);
ins2(c, A_MOVQ, areg(D_AX),
areg(D_CX));
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));
} else {
cgexpr(c, n->rhs, locals);
ins1(c, A_PUSHQ, areg(D_AX));