cgen: *p=sliceval deref store -> 3-word (both stages, #79)

A slice VALUE stored through a whole-deref lhs `*p = v` dropped len+cap:
the `*p = v` arm kind-gated its 3-word {ptr,len,cap} stash+store on str
ONLY, so a slice fell to the 1-word fldstoreop default (ptr only). The
deref READ is 3-word, so the reader got garbage len/cap -- correctness,
not perf. str IS []u8 since #1, so the str machinery applies verbatim;
widen the gate str -> str||slice (kind-OR, not a sz==24 test). This is
the project #75 str-only-gate one level down (deref-store).

cstage cmd/w6c/cgen.c:3792/3800 (two gates); wwstage cgenexpr.ww `*p=v`
twin detects N_TSLICE syntactically (mirror str). Both stages dropped
identically, so cs==ww + 990-997 + byte-id are all gate-blind here --
only a store->read roundtrip catches it. New 944_deref_slice_store_run
asserts the {ptr,len,cap} survives a poisoned dst, via the direct local
and the field-deref read; str-deref + (*p).field controls guard the
untouched arms. Verified fail-before (1-word ptr store) / pass-after
(8/8), byte-identical asm both stages.

Out-of-gate, deferred to #80: the wwstage syntactic detection is
alias-BLIND -- a slice-alias `*Foo` (Foo=[]T) or non-ident deref-store
stays 1-word, the SAME retained divergence str already carries (cstage's
resolved-type vt fires in both). #80 unifies detection by aligning the
wwstage UP, not gating cstage down. Separately surfaced (filed apart,
not touched here): the whole-deref READ-into-let `let v = *p` drops
len+cap for a slice while the str form is 3-word -- the read-side twin
of this store hole.
This commit is contained in:
2026-05-25 02:59:40 +09:00
parent ab9de65b8e
commit 1304db8871
6 changed files with 259 additions and 5 deletions

View File

@@ -3787,17 +3787,19 @@ cgexpr(Cg *c, Node *n, Local *locals)
ins2(c, mov, areg(D_X0), amem(D_BX, 0));
break;
}
cgexpr(c, n->rhs, locals); /* AX=ptr (BX=len, CX=cap if str) */
cgexpr(c, n->rhs, locals); /* AX=ptr (BX=len, CX=cap if str/slice) */
ins1(c, A_PUSHQ, areg(D_AX));
if (vt && vt->kind == TY_STR) {
/* str IS []u8: also stash len + cap across the
* pointer eval, which clobbers BX/CX (#1/Phase 3). */
if (vt && (vt->kind == TY_STR || vt->kind == TY_SLICE)) {
/* str IS []u8 and a slice is the same 3-word
* {ptr,len,cap} header (ref/hare/rt/ensure.ha:4-8):
* stash len + cap across the pointer eval, which
* clobbers BX/CX (#1/Phase 3; slice arm #79). */
ins1(c, A_PUSHQ, areg(D_BX)); /* len */
ins1(c, A_PUSHQ, areg(D_CX)); /* cap */
}
cgexpr(c, n->lhs->lhs, locals); /* AX = pointer */
ins2(c, A_MOVQ, areg(D_AX), areg(D_BX));
if (vt && vt->kind == TY_STR) {
if (vt && (vt->kind == TY_STR || vt->kind == TY_SLICE)) {
ins1(c, A_POPQ, areg(D_CX)); /* cap */
ins2(c, A_MOVQ, areg(D_CX), amem(D_BX, 16));
ins1(c, A_POPQ, areg(D_CX)); /* len */