w6c+selfhost: cgen *p OP= v (was silent no-op)
`*p += 1` and the rest of the compound-deref family (-= *= |= &= ^=
<<= >>=) fell through the N_ASSIGN switch in both stages and emitted
nothing. The `*p = v` block was gated on TK_ASSIGN, the IDENT-compound
block required N_IDENT, and there was no N_UN/TK_STAR compound branch
between them. Test 994/995/997 byte-identity hid it: both stages
mis-compiled identically, so the diffs were clean.
Surfaced via fmt.println("hello") segfaulting in wwstage builds.
findvariadicparam in cgenutil.ww does `*nfixed_out += 1`; the drop
left nfixed at 0, so `fprint(fd, args...)` mis-counted variadic args,
gathered fd as a formattable element, and segfaulted on tagged
dispatch.
Adds the missing branch in cmd/w6c/cgen.c N_ASSIGN and
selfhost/cmd/wcc/cgenexpr.ww cgassign: eval rhs → push, eval ptr →
BX, sized+extended load (BX) → AX (MOVZBQ for 1B, MOVSXD/MOVL for 4B
by signedness, MOVQ for 8B), pop rhs → CX, combine via
ADDQ/SUBQ/IMULQ/ANDQ/ORQ/XORQ/SHLQ/SHRQ on CX,AX, sized store back.
TK_SLASHEQ stays the rhs-only fallback, matching the IDENT path.
Float and aggregate deref compounds still fall through — uncommon.
This commit is contained in:
@@ -2144,6 +2144,57 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
/* `*p OP= v` — compound assign through a pointer deref. The
|
||||||
|
* plain-assign branch above only fires for TK_ASSIGN; without
|
||||||
|
* this, compound ops fall through the switch and emit nothing
|
||||||
|
* (silent no-op). Evaluate rhs → save, evaluate ptr → BX, load
|
||||||
|
* *BX (sized + extended), combine with rhs in CX, sized store
|
||||||
|
* back. Scalar deref targets only — float and aggregate deref
|
||||||
|
* compounds (rare) still fall through. */
|
||||||
|
if (n->lhs && n->lhs->kind == N_UN && n->lhs->op == TK_STAR
|
||||||
|
&& n->op != TK_ASSIGN) {
|
||||||
|
Type *pt = n->lhs->lhs ? n->lhs->lhs->type : NULL;
|
||||||
|
Type *pu = (pt && pt->kind == TY_NAMED) ? pt->under : pt;
|
||||||
|
Type *vt = (pu && pu->kind == TY_PTR) ? pu->sub : NULL;
|
||||||
|
if (vt && vt->kind == TY_NAMED) vt = vt->under;
|
||||||
|
int sz = vt ? (int)vt->size : 8;
|
||||||
|
int load_op = A_MOVQ, store_op = A_MOVQ;
|
||||||
|
int handled = 1;
|
||||||
|
if (sz == 8) {
|
||||||
|
load_op = A_MOVQ; store_op = A_MOVQ;
|
||||||
|
} else if (sz == 4) {
|
||||||
|
load_op = A_MOVSXD; store_op = A_MOVL;
|
||||||
|
} else if (sz == 1) {
|
||||||
|
load_op = A_MOVZBQ; store_op = A_MOVB;
|
||||||
|
} else {
|
||||||
|
handled = 0;
|
||||||
|
}
|
||||||
|
if (handled) {
|
||||||
|
cgexpr(c, n->rhs, locals); /* AX = rhs */
|
||||||
|
ins1(c, A_PUSHQ, areg(D_AX));
|
||||||
|
cgexpr(c, n->lhs->lhs, locals); /* AX = ptr */
|
||||||
|
ins2(c, A_MOVQ, areg(D_AX), areg(D_BX));
|
||||||
|
ins2(c, load_op, amem(D_BX, 0), areg(D_AX));
|
||||||
|
ins1(c, A_POPQ, areg(D_CX));
|
||||||
|
switch (n->op) {
|
||||||
|
case TK_PLUSEQ: ins2(c, A_ADDQ, areg(D_CX), areg(D_AX)); break;
|
||||||
|
case TK_MINUSEQ: ins2(c, A_SUBQ, areg(D_CX), areg(D_AX)); break;
|
||||||
|
case TK_STAREQ: ins2(c, A_IMULQ, areg(D_CX), areg(D_AX)); break;
|
||||||
|
case TK_AMPEQ: ins2(c, A_ANDQ, areg(D_CX), areg(D_AX)); break;
|
||||||
|
case TK_PIPEEQ: ins2(c, A_ORQ, areg(D_CX), areg(D_AX)); break;
|
||||||
|
case TK_CARETEQ: ins2(c, A_XORQ, areg(D_CX), areg(D_AX)); break;
|
||||||
|
case TK_LSHIFTEQ: ins2(c, A_SHLQ, areg(D_CX), areg(D_AX)); break;
|
||||||
|
case TK_RSHIFTEQ: ins2(c, A_SHRQ, areg(D_CX), areg(D_AX)); break;
|
||||||
|
default:
|
||||||
|
/* TK_SLASHEQ / unknown: store rhs only,
|
||||||
|
* matching the IDENT-compound fallback. */
|
||||||
|
ins2(c, A_MOVQ, areg(D_CX), areg(D_AX));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ins2(c, store_op, areg(D_AX), amem(D_BX, 0));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
/* Plain `name = strexpr;` for a str-typed local. cgexpr leaves
|
/* Plain `name = strexpr;` for a str-typed local. cgexpr leaves
|
||||||
* (AX=ptr, BX=len); store both halves at off+0 and off+8.
|
* (AX=ptr, BX=len); store both halves at off+0 and off+8.
|
||||||
* Mirrors the let-init shape so reassignment doesn't truncate.
|
* Mirrors the let-init shape so reassignment doesn't truncate.
|
||||||
|
|||||||
@@ -10268,6 +10268,76 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
// `*p OP= v` — compound assign through a pointer deref. The
|
||||||
|
// plain-assign branch above only fires for TK_ASSIGN; without
|
||||||
|
// this, compound ops fall through and emit nothing (silent
|
||||||
|
// no-op — exactly the trap that broke fmt.println). Mirror of
|
||||||
|
// cmd/w6c/cgen.c's N_UN/TK_STAR compound branch.
|
||||||
|
if (lhs != nil) {
|
||||||
|
if (lhs.kind == nkind.N_UN) {
|
||||||
|
if (lhs.op == tkind.TK_STAR) {
|
||||||
|
if (n.op != tkind.TK_ASSIGN) {
|
||||||
|
let inner: *node = lhs.lhs;
|
||||||
|
let loadop: str = "MOVQ";
|
||||||
|
let storeop: str = "MOVQ";
|
||||||
|
if (inner != nil) {
|
||||||
|
if (inner.kind == nkind.N_IDENT) {
|
||||||
|
let lc: *local = localfindnode(c, inner.str);
|
||||||
|
if (lc != nil) {
|
||||||
|
let tn: *node = lc.tnode;
|
||||||
|
if (tn != nil) {
|
||||||
|
if (tn.kind == nkind.N_TPTR) {
|
||||||
|
let pe: *node = tn.lhs;
|
||||||
|
if (pe != nil) {
|
||||||
|
if (pe.kind == nkind.N_TNAME) {
|
||||||
|
let ps: i32 = primsize(pe.str);
|
||||||
|
if (ps == 1) {
|
||||||
|
loadop = "MOVZBQ";
|
||||||
|
storeop = "MOVB";
|
||||||
|
} else { if (ps == 4) {
|
||||||
|
if (typenameissigned(pe.str)) {
|
||||||
|
loadop = "MOVSXD";
|
||||||
|
} else {
|
||||||
|
loadop = "MOVL";
|
||||||
|
};
|
||||||
|
storeop = "MOVL";
|
||||||
|
}; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
cgexpr(c, n.rhs);
|
||||||
|
emitline("\tPUSHQ\tAX\n");
|
||||||
|
cgexpr(c, inner);
|
||||||
|
emitline("\tMOVQ\tAX, BX\n");
|
||||||
|
emitline("\t");
|
||||||
|
emitline(loadop);
|
||||||
|
emitline("\t(BX), AX\n");
|
||||||
|
emitline("\tPOPQ\tCX\n");
|
||||||
|
let combineop: str = "MOVQ";
|
||||||
|
if (n.op == tkind.TK_PLUSEQ) { combineop = "ADDQ"; }
|
||||||
|
else { if (n.op == tkind.TK_MINUSEQ) { combineop = "SUBQ"; }
|
||||||
|
else { if (n.op == tkind.TK_STAREQ) { combineop = "IMULQ"; }
|
||||||
|
else { if (n.op == tkind.TK_AMPEQ) { combineop = "ANDQ"; }
|
||||||
|
else { if (n.op == tkind.TK_PIPEEQ) { combineop = "ORQ"; }
|
||||||
|
else { if (n.op == tkind.TK_CARETEQ) { combineop = "XORQ"; }
|
||||||
|
else { if (n.op == tkind.TK_LSHIFTEQ) { combineop = "SHLQ"; }
|
||||||
|
else { if (n.op == tkind.TK_RSHIFTEQ) { combineop = "SHRQ"; };
|
||||||
|
}; }; }; }; }; }; };
|
||||||
|
emitline("\t");
|
||||||
|
emitline(combineop);
|
||||||
|
emitline("\tCX, AX\n");
|
||||||
|
emitline("\t");
|
||||||
|
emitline(storeop);
|
||||||
|
emitline("\tAX, (BX)\n");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
// Array/slice/ptr index store: `arr[i] = v;`. Element size
|
// Array/slice/ptr index store: `arr[i] = v;`. Element size
|
||||||
// from base.tnode picks MOVB vs MOVQ.
|
// from base.tnode picks MOVB vs MOVQ.
|
||||||
if (lhs != nil) {
|
if (lhs != nil) {
|
||||||
|
|||||||
@@ -2574,6 +2574,76 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
// `*p OP= v` — compound assign through a pointer deref. The
|
||||||
|
// plain-assign branch above only fires for TK_ASSIGN; without
|
||||||
|
// this, compound ops fall through and emit nothing (silent
|
||||||
|
// no-op — exactly the trap that broke fmt.println). Mirror of
|
||||||
|
// cmd/w6c/cgen.c's N_UN/TK_STAR compound branch.
|
||||||
|
if (lhs != nil) {
|
||||||
|
if (lhs.kind == nkind.N_UN) {
|
||||||
|
if (lhs.op == tkind.TK_STAR) {
|
||||||
|
if (n.op != tkind.TK_ASSIGN) {
|
||||||
|
let inner: *node = lhs.lhs;
|
||||||
|
let loadop: str = "MOVQ";
|
||||||
|
let storeop: str = "MOVQ";
|
||||||
|
if (inner != nil) {
|
||||||
|
if (inner.kind == nkind.N_IDENT) {
|
||||||
|
let lc: *local = localfindnode(c, inner.str);
|
||||||
|
if (lc != nil) {
|
||||||
|
let tn: *node = lc.tnode;
|
||||||
|
if (tn != nil) {
|
||||||
|
if (tn.kind == nkind.N_TPTR) {
|
||||||
|
let pe: *node = tn.lhs;
|
||||||
|
if (pe != nil) {
|
||||||
|
if (pe.kind == nkind.N_TNAME) {
|
||||||
|
let ps: i32 = primsize(pe.str);
|
||||||
|
if (ps == 1) {
|
||||||
|
loadop = "MOVZBQ";
|
||||||
|
storeop = "MOVB";
|
||||||
|
} else { if (ps == 4) {
|
||||||
|
if (typenameissigned(pe.str)) {
|
||||||
|
loadop = "MOVSXD";
|
||||||
|
} else {
|
||||||
|
loadop = "MOVL";
|
||||||
|
};
|
||||||
|
storeop = "MOVL";
|
||||||
|
}; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
cgexpr(c, n.rhs);
|
||||||
|
emitline("\tPUSHQ\tAX\n");
|
||||||
|
cgexpr(c, inner);
|
||||||
|
emitline("\tMOVQ\tAX, BX\n");
|
||||||
|
emitline("\t");
|
||||||
|
emitline(loadop);
|
||||||
|
emitline("\t(BX), AX\n");
|
||||||
|
emitline("\tPOPQ\tCX\n");
|
||||||
|
let combineop: str = "MOVQ";
|
||||||
|
if (n.op == tkind.TK_PLUSEQ) { combineop = "ADDQ"; }
|
||||||
|
else { if (n.op == tkind.TK_MINUSEQ) { combineop = "SUBQ"; }
|
||||||
|
else { if (n.op == tkind.TK_STAREQ) { combineop = "IMULQ"; }
|
||||||
|
else { if (n.op == tkind.TK_AMPEQ) { combineop = "ANDQ"; }
|
||||||
|
else { if (n.op == tkind.TK_PIPEEQ) { combineop = "ORQ"; }
|
||||||
|
else { if (n.op == tkind.TK_CARETEQ) { combineop = "XORQ"; }
|
||||||
|
else { if (n.op == tkind.TK_LSHIFTEQ) { combineop = "SHLQ"; }
|
||||||
|
else { if (n.op == tkind.TK_RSHIFTEQ) { combineop = "SHRQ"; };
|
||||||
|
}; }; }; }; }; }; };
|
||||||
|
emitline("\t");
|
||||||
|
emitline(combineop);
|
||||||
|
emitline("\tCX, AX\n");
|
||||||
|
emitline("\t");
|
||||||
|
emitline(storeop);
|
||||||
|
emitline("\tAX, (BX)\n");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
// Array/slice/ptr index store: `arr[i] = v;`. Element size
|
// Array/slice/ptr index store: `arr[i] = v;`. Element size
|
||||||
// from base.tnode picks MOVB vs MOVQ.
|
// from base.tnode picks MOVB vs MOVQ.
|
||||||
if (lhs != nil) {
|
if (lhs != nil) {
|
||||||
|
|||||||
@@ -10268,6 +10268,76 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
// `*p OP= v` — compound assign through a pointer deref. The
|
||||||
|
// plain-assign branch above only fires for TK_ASSIGN; without
|
||||||
|
// this, compound ops fall through and emit nothing (silent
|
||||||
|
// no-op — exactly the trap that broke fmt.println). Mirror of
|
||||||
|
// cmd/w6c/cgen.c's N_UN/TK_STAR compound branch.
|
||||||
|
if (lhs != nil) {
|
||||||
|
if (lhs.kind == nkind.N_UN) {
|
||||||
|
if (lhs.op == tkind.TK_STAR) {
|
||||||
|
if (n.op != tkind.TK_ASSIGN) {
|
||||||
|
let inner: *node = lhs.lhs;
|
||||||
|
let loadop: str = "MOVQ";
|
||||||
|
let storeop: str = "MOVQ";
|
||||||
|
if (inner != nil) {
|
||||||
|
if (inner.kind == nkind.N_IDENT) {
|
||||||
|
let lc: *local = localfindnode(c, inner.str);
|
||||||
|
if (lc != nil) {
|
||||||
|
let tn: *node = lc.tnode;
|
||||||
|
if (tn != nil) {
|
||||||
|
if (tn.kind == nkind.N_TPTR) {
|
||||||
|
let pe: *node = tn.lhs;
|
||||||
|
if (pe != nil) {
|
||||||
|
if (pe.kind == nkind.N_TNAME) {
|
||||||
|
let ps: i32 = primsize(pe.str);
|
||||||
|
if (ps == 1) {
|
||||||
|
loadop = "MOVZBQ";
|
||||||
|
storeop = "MOVB";
|
||||||
|
} else { if (ps == 4) {
|
||||||
|
if (typenameissigned(pe.str)) {
|
||||||
|
loadop = "MOVSXD";
|
||||||
|
} else {
|
||||||
|
loadop = "MOVL";
|
||||||
|
};
|
||||||
|
storeop = "MOVL";
|
||||||
|
}; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
cgexpr(c, n.rhs);
|
||||||
|
emitline("\tPUSHQ\tAX\n");
|
||||||
|
cgexpr(c, inner);
|
||||||
|
emitline("\tMOVQ\tAX, BX\n");
|
||||||
|
emitline("\t");
|
||||||
|
emitline(loadop);
|
||||||
|
emitline("\t(BX), AX\n");
|
||||||
|
emitline("\tPOPQ\tCX\n");
|
||||||
|
let combineop: str = "MOVQ";
|
||||||
|
if (n.op == tkind.TK_PLUSEQ) { combineop = "ADDQ"; }
|
||||||
|
else { if (n.op == tkind.TK_MINUSEQ) { combineop = "SUBQ"; }
|
||||||
|
else { if (n.op == tkind.TK_STAREQ) { combineop = "IMULQ"; }
|
||||||
|
else { if (n.op == tkind.TK_AMPEQ) { combineop = "ANDQ"; }
|
||||||
|
else { if (n.op == tkind.TK_PIPEEQ) { combineop = "ORQ"; }
|
||||||
|
else { if (n.op == tkind.TK_CARETEQ) { combineop = "XORQ"; }
|
||||||
|
else { if (n.op == tkind.TK_LSHIFTEQ) { combineop = "SHLQ"; }
|
||||||
|
else { if (n.op == tkind.TK_RSHIFTEQ) { combineop = "SHRQ"; };
|
||||||
|
}; }; }; }; }; }; };
|
||||||
|
emitline("\t");
|
||||||
|
emitline(combineop);
|
||||||
|
emitline("\tCX, AX\n");
|
||||||
|
emitline("\t");
|
||||||
|
emitline(storeop);
|
||||||
|
emitline("\tAX, (BX)\n");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
// Array/slice/ptr index store: `arr[i] = v;`. Element size
|
// Array/slice/ptr index store: `arr[i] = v;`. Element size
|
||||||
// from base.tnode picks MOVB vs MOVQ.
|
// from base.tnode picks MOVB vs MOVQ.
|
||||||
if (lhs != nil) {
|
if (lhs != nil) {
|
||||||
|
|||||||
Reference in New Issue
Block a user