cgen: str a,s=call() N_MASSIGN tuple-elem store -> 3-word -- Phase 2 G3 (both stages)
Reassign-destructuring a (scalar,str) tuple (a, s = call(), N_MASSIGN) stored only the str's ptr (DX->slot+0), dropping len/cap -- the last STORE-cluster gap. Reachable (valid ww; checker accepts str tuple elements) but unexercised in bootstrap (all N_MASSIGN sites returned <=8B tuples). Mirror the N_MLET destructure-store oracle (cgen.c:7475): on the one-str XOR, route the str's 3 words DX/CX/R8 -> slot+0/+8/+16; the slot pre-exists (localfind, not localadd). wwstage has no checker, so it derives str-ness from the callee return-type tuple via fnretlookupmod (structurally identical to cgmlet). Both XOR positions (str at l0 and l1). Kind-gated, never size==24. cstage==wwstage byte-identical. Scope = one-str only, matching N_MLET exactly; str+str-both is unhandled by N_MLET too and is filed as a shared gap (task #22), with WHY-comments at both destructure sites. N_MLET emission unchanged (its edit is comment-only, verified byte-identical). test/wcc/939: table-driven write-then-read-cap over both XOR positions (a,s=mk() and s,a=mk2()); cap!=len via mutation (not a sub-slice, #20); pre-poisoned via a non-G3 let-init; full triple+scalar asserted; fail-before/pass-after on both drivers. Completes the str-cap STORE cluster -- the read/write round-trip is now whole. main.combined.ww regenerated via the canonical make path.
This commit is contained in:
@@ -7479,7 +7479,8 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
* Local sizing comes from each l->type so the str slot gets
|
||||
* the full 24B; without this, only DX would land and the
|
||||
* len/cap halves (CX/R8) would have nowhere to go.
|
||||
* str IS []u8 (24B): the cap rides R8 (#1/Phase 3, task #5). */
|
||||
* str IS []u8 (24B): the cap rides R8 (#1/Phase 3, task #5).
|
||||
* one-str only; two-str destructure is gap (task #22). */
|
||||
cgexpr(c, n->rhs, *locals);
|
||||
Node *l0 = n->list;
|
||||
Node *l1 = l0 ? l0->next : NULL;
|
||||
@@ -7525,9 +7526,41 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
}
|
||||
case N_MASSIGN: {
|
||||
cgexpr(c, n->rhs, *locals);
|
||||
ins1(c, A_PUSHQ, areg(D_DX));
|
||||
Node *l0 = n->list;
|
||||
Node *l1 = l0 ? l0->next : NULL;
|
||||
/* one-str only; two-str destructure is gap (task #22). The str
|
||||
* tuple element's 24B slot already exists (reassignment), so
|
||||
* localfind it and mirror N_MLET's (DX,CX,R8)→(.ptr,.len,.cap)
|
||||
* routing; the bare scalar fallback below would store only DX
|
||||
* and drop len/cap. Scalar side stays AX→slot. */
|
||||
Type *t0 = l0 ? l0->type : NULL;
|
||||
Type *t1 = l1 ? l1->type : NULL;
|
||||
Type *u0 = (t0 && t0->kind == TY_NAMED) ? t0->under : t0;
|
||||
Type *u1 = (t1 && t1->kind == TY_NAMED) ? t1->under : t1;
|
||||
int s0_is_str = u0 && u0->kind == TY_STR;
|
||||
int s1_is_str = u1 && u1->kind == TY_STR;
|
||||
if (l0 && l1 && l0->kind == N_IDENT && l1->kind == N_IDENT
|
||||
&& (s0_is_str ^ s1_is_str)) {
|
||||
int off0 = localfind(*locals, l0->str);
|
||||
int off1 = localfind(*locals, l1->str);
|
||||
if (off0 != 0 && off1 != 0) {
|
||||
if (s0_is_str) {
|
||||
/* l0 is str: ptr=DX, len=CX, cap=R8. l1 scalar = AX. */
|
||||
ins2(c, A_MOVQ, areg(D_DX), amem(D_BP, off0 + 0));
|
||||
ins2(c, A_MOVQ, areg(D_CX), amem(D_BP, off0 + 8));
|
||||
ins2(c, A_MOVQ, areg(D_R8), amem(D_BP, off0 + 16));
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, off1));
|
||||
} else {
|
||||
/* l0 is scalar; l1 is str. */
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, off0));
|
||||
ins2(c, A_MOVQ, areg(D_DX), amem(D_BP, off1 + 0));
|
||||
ins2(c, A_MOVQ, areg(D_CX), amem(D_BP, off1 + 8));
|
||||
ins2(c, A_MOVQ, areg(D_R8), amem(D_BP, off1 + 16));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
ins1(c, A_PUSHQ, areg(D_DX));
|
||||
if (l0 && l0->kind == N_IDENT) {
|
||||
int off = localfind(*locals, l0->str);
|
||||
if (off != 0)
|
||||
|
||||
Reference in New Issue
Block a user