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:
@@ -21049,11 +21049,111 @@ fn cgfor(c: *cgen, n: *node) void = {
|
||||
// cmd/w6c/cgen.c:2424-2440. Lvalues beyond two are dropped (same
|
||||
// as C — no fixture uses >2 today).
|
||||
fn cgmassign(c: *cgen, n: *node) void = {
|
||||
if (n.rhs != nil) { cgexpr(c, n.rhs); };
|
||||
emitline("\tPUSHQ\tDX\n");
|
||||
let l0: *node = n.list;
|
||||
let l1: *node = nil;
|
||||
if (l0 != nil) { l1 = l0.next; };
|
||||
|
||||
// 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 cgmlet's (DX,CX,R8)->(.ptr,.len,.cap)
|
||||
// routing; the bare scalar fallback below would store only DX and
|
||||
// drop len/cap. wwstage has no checker, so str-ness comes from the
|
||||
// called fn's return-type tuple element (as in cgmlet).
|
||||
let p0t: *node = nil;
|
||||
let p1t: *node = nil;
|
||||
let rhs: *node = n.rhs;
|
||||
if (rhs != nil) {
|
||||
if (rhs.kind == nkind.N_CALL) {
|
||||
let callee: *node = rhs.lhs;
|
||||
if (callee != nil) {
|
||||
let cnm: str;
|
||||
cnm.ptr = nil; cnm.len = 0;
|
||||
let cmod: str;
|
||||
cmod.ptr = nil; cmod.len = 0;
|
||||
if (callee.kind == nkind.N_IDENT) {
|
||||
cnm = callee.str;
|
||||
cmod = c.curmod;
|
||||
};
|
||||
if (callee.kind == nkind.N_DOT) {
|
||||
cnm = callee.str;
|
||||
if (callee.lhs != nil) {
|
||||
if (callee.lhs.kind == nkind.N_IDENT) {
|
||||
cmod = callee.lhs.str;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (cnm.len > 0) {
|
||||
let rtyp: *node = fnretlookupmod(c, cnm, cmod);
|
||||
if (rtyp != nil) {
|
||||
if (rtyp.kind == nkind.N_TTUPLE) {
|
||||
let pp: *node = rtyp.list;
|
||||
if (pp != nil) {
|
||||
p0t = pp.lhs;
|
||||
if (pp.next != nil) {
|
||||
p1t = pp.next.lhs;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
let s0_is_str: bool = isstrtype(c, p0t);
|
||||
let s1_is_str: bool = isstrtype(c, p1t);
|
||||
|
||||
if (n.rhs != nil) { cgexpr(c, n.rhs); };
|
||||
|
||||
if (l0 != nil) {
|
||||
if (l1 != nil) {
|
||||
if (l0.kind == nkind.N_IDENT) {
|
||||
if (l1.kind == nkind.N_IDENT) {
|
||||
if (s0_is_str != s1_is_str) {
|
||||
let off0: i32 = localfind(c, l0.str);
|
||||
let off1: i32 = localfind(c, l1.str);
|
||||
if (off0 != 0) {
|
||||
if (off1 != 0) {
|
||||
if (s0_is_str) {
|
||||
// l0 str: ptr=DX, len=CX, cap=R8. l1 scalar = AX.
|
||||
emitline("\tMOVQ\tDX, ");
|
||||
emitoff(off0: i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((off0 + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tR8, ");
|
||||
emitoff((off0 + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(off1: i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
// l0 scalar; l1 str: ptr=DX, len=CX, cap=R8.
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(off0: i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tDX, ");
|
||||
emitoff(off1: i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((off1 + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tR8, ");
|
||||
emitoff((off1 + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
emitline("\tPUSHQ\tDX\n");
|
||||
if (l0 != nil) {
|
||||
if (l0.kind == nkind.N_IDENT) {
|
||||
let off: i32 = localfind(c, l0.str);
|
||||
|
||||
@@ -1197,11 +1197,111 @@ fn cgfor(c: *cgen, n: *node) void = {
|
||||
// cmd/w6c/cgen.c:2424-2440. Lvalues beyond two are dropped (same
|
||||
// as C — no fixture uses >2 today).
|
||||
fn cgmassign(c: *cgen, n: *node) void = {
|
||||
if (n.rhs != nil) { cgexpr(c, n.rhs); };
|
||||
emitline("\tPUSHQ\tDX\n");
|
||||
let l0: *node = n.list;
|
||||
let l1: *node = nil;
|
||||
if (l0 != nil) { l1 = l0.next; };
|
||||
|
||||
// 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 cgmlet's (DX,CX,R8)->(.ptr,.len,.cap)
|
||||
// routing; the bare scalar fallback below would store only DX and
|
||||
// drop len/cap. wwstage has no checker, so str-ness comes from the
|
||||
// called fn's return-type tuple element (as in cgmlet).
|
||||
let p0t: *node = nil;
|
||||
let p1t: *node = nil;
|
||||
let rhs: *node = n.rhs;
|
||||
if (rhs != nil) {
|
||||
if (rhs.kind == nkind.N_CALL) {
|
||||
let callee: *node = rhs.lhs;
|
||||
if (callee != nil) {
|
||||
let cnm: str;
|
||||
cnm.ptr = nil; cnm.len = 0;
|
||||
let cmod: str;
|
||||
cmod.ptr = nil; cmod.len = 0;
|
||||
if (callee.kind == nkind.N_IDENT) {
|
||||
cnm = callee.str;
|
||||
cmod = c.curmod;
|
||||
};
|
||||
if (callee.kind == nkind.N_DOT) {
|
||||
cnm = callee.str;
|
||||
if (callee.lhs != nil) {
|
||||
if (callee.lhs.kind == nkind.N_IDENT) {
|
||||
cmod = callee.lhs.str;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (cnm.len > 0) {
|
||||
let rtyp: *node = fnretlookupmod(c, cnm, cmod);
|
||||
if (rtyp != nil) {
|
||||
if (rtyp.kind == nkind.N_TTUPLE) {
|
||||
let pp: *node = rtyp.list;
|
||||
if (pp != nil) {
|
||||
p0t = pp.lhs;
|
||||
if (pp.next != nil) {
|
||||
p1t = pp.next.lhs;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
let s0_is_str: bool = isstrtype(c, p0t);
|
||||
let s1_is_str: bool = isstrtype(c, p1t);
|
||||
|
||||
if (n.rhs != nil) { cgexpr(c, n.rhs); };
|
||||
|
||||
if (l0 != nil) {
|
||||
if (l1 != nil) {
|
||||
if (l0.kind == nkind.N_IDENT) {
|
||||
if (l1.kind == nkind.N_IDENT) {
|
||||
if (s0_is_str != s1_is_str) {
|
||||
let off0: i32 = localfind(c, l0.str);
|
||||
let off1: i32 = localfind(c, l1.str);
|
||||
if (off0 != 0) {
|
||||
if (off1 != 0) {
|
||||
if (s0_is_str) {
|
||||
// l0 str: ptr=DX, len=CX, cap=R8. l1 scalar = AX.
|
||||
emitline("\tMOVQ\tDX, ");
|
||||
emitoff(off0: i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((off0 + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tR8, ");
|
||||
emitoff((off0 + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(off1: i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
// l0 scalar; l1 str: ptr=DX, len=CX, cap=R8.
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(off0: i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tDX, ");
|
||||
emitoff(off1: i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((off1 + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tR8, ");
|
||||
emitoff((off1 + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
emitline("\tPUSHQ\tDX\n");
|
||||
if (l0 != nil) {
|
||||
if (l0.kind == nkind.N_IDENT) {
|
||||
let off: i32 = localfind(c, l0.str);
|
||||
|
||||
@@ -21049,11 +21049,111 @@ fn cgfor(c: *cgen, n: *node) void = {
|
||||
// cmd/w6c/cgen.c:2424-2440. Lvalues beyond two are dropped (same
|
||||
// as C — no fixture uses >2 today).
|
||||
fn cgmassign(c: *cgen, n: *node) void = {
|
||||
if (n.rhs != nil) { cgexpr(c, n.rhs); };
|
||||
emitline("\tPUSHQ\tDX\n");
|
||||
let l0: *node = n.list;
|
||||
let l1: *node = nil;
|
||||
if (l0 != nil) { l1 = l0.next; };
|
||||
|
||||
// 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 cgmlet's (DX,CX,R8)->(.ptr,.len,.cap)
|
||||
// routing; the bare scalar fallback below would store only DX and
|
||||
// drop len/cap. wwstage has no checker, so str-ness comes from the
|
||||
// called fn's return-type tuple element (as in cgmlet).
|
||||
let p0t: *node = nil;
|
||||
let p1t: *node = nil;
|
||||
let rhs: *node = n.rhs;
|
||||
if (rhs != nil) {
|
||||
if (rhs.kind == nkind.N_CALL) {
|
||||
let callee: *node = rhs.lhs;
|
||||
if (callee != nil) {
|
||||
let cnm: str;
|
||||
cnm.ptr = nil; cnm.len = 0;
|
||||
let cmod: str;
|
||||
cmod.ptr = nil; cmod.len = 0;
|
||||
if (callee.kind == nkind.N_IDENT) {
|
||||
cnm = callee.str;
|
||||
cmod = c.curmod;
|
||||
};
|
||||
if (callee.kind == nkind.N_DOT) {
|
||||
cnm = callee.str;
|
||||
if (callee.lhs != nil) {
|
||||
if (callee.lhs.kind == nkind.N_IDENT) {
|
||||
cmod = callee.lhs.str;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (cnm.len > 0) {
|
||||
let rtyp: *node = fnretlookupmod(c, cnm, cmod);
|
||||
if (rtyp != nil) {
|
||||
if (rtyp.kind == nkind.N_TTUPLE) {
|
||||
let pp: *node = rtyp.list;
|
||||
if (pp != nil) {
|
||||
p0t = pp.lhs;
|
||||
if (pp.next != nil) {
|
||||
p1t = pp.next.lhs;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
let s0_is_str: bool = isstrtype(c, p0t);
|
||||
let s1_is_str: bool = isstrtype(c, p1t);
|
||||
|
||||
if (n.rhs != nil) { cgexpr(c, n.rhs); };
|
||||
|
||||
if (l0 != nil) {
|
||||
if (l1 != nil) {
|
||||
if (l0.kind == nkind.N_IDENT) {
|
||||
if (l1.kind == nkind.N_IDENT) {
|
||||
if (s0_is_str != s1_is_str) {
|
||||
let off0: i32 = localfind(c, l0.str);
|
||||
let off1: i32 = localfind(c, l1.str);
|
||||
if (off0 != 0) {
|
||||
if (off1 != 0) {
|
||||
if (s0_is_str) {
|
||||
// l0 str: ptr=DX, len=CX, cap=R8. l1 scalar = AX.
|
||||
emitline("\tMOVQ\tDX, ");
|
||||
emitoff(off0: i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((off0 + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tR8, ");
|
||||
emitoff((off0 + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(off1: i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
// l0 scalar; l1 str: ptr=DX, len=CX, cap=R8.
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(off0: i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tDX, ");
|
||||
emitoff(off1: i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((off1 + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tR8, ");
|
||||
emitoff((off1 + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
emitline("\tPUSHQ\tDX\n");
|
||||
if (l0 != nil) {
|
||||
if (l0.kind == nkind.N_IDENT) {
|
||||
let off: i32 = localfind(c, l0.str);
|
||||
|
||||
Reference in New Issue
Block a user