cstage+selfhost+test: revert compound-assign div/mod workarounds (post-#16)
B1 (63332fe) landed CQO in both stages' assemblers and switched the
binary `/` and `%` paths to it. The compound-assign sisters (`/=`,
`%=`) were six explicit workarounds across both stages, all calling
out either "fallback for TK_SLASHEQ" or just falling through with no
case at all. With CQO available, every site mechanically ports to the
same "park rhs in CX, slot value into AX, CQO/IDIVQ CX, ferry result
back" sequence.
wwstage cgenexpr.ww:5381-5403 silently no-op'd IDENT-local signed
compound div/mod — `x /= y` and `x %= y` produced no IDIV emit at
all, just a load-bearing `MOVQ BX, off(BP)` that wrote the freshly
loaded slot value back unchanged. Bootstrap byte-id passed because
no selfhost-corpus path exercises signed compound. Latent miscompile
retired alongside the workaround revert.
cstage cgen.c:3735 (top-level-let global compound) was NOT in the
initial five-site bundle and surfaced via worker probing the
wwstage:5147 fix — `let gs: i32 = 100; gs /= 7;` returned 7 (divisor)
on cstage but 14 (correct quotient) on wwstage. Rule 10 caught the
would-be Class A divergence; the sixth site bundles in.
Six sites, one family:
cmd/w6c/cgen.c:3549 deref-compound `*p OP= v`
cmd/w6c/cgen.c:3735 top-level-let `gs OP= v`
cmd/w6c/cgen.c:3765 IDENT-local `x OP= v`
selfhost/cmd/wcc/cgenexpr.ww:3338 deref-compound
selfhost/cmd/wcc/cgenexpr.ww:5147 top-level-let
selfhost/cmd/wcc/cgenexpr.ww:5381 IDENT-local (silent-no-op)
test/wcc/978_intdiv_signed.c adds 7 compound rows × 2 drivers = 14
fixtures (now 68/68): IDENT-local /= /=- /=u, deref *p /= *p %= *p
/=u, with negative-dividend, negative-divisor, and unsigned-high-
bit-set coverage. Top-level-let compound coverage is deferred per
task #18 — single-file inline drivers hit a pre-existing linker
`undefined reference to '<file>.gs'` for LEAQ name(SB) targets.
cstage:3735 and wwstage:5147 are code-review-verified for rule-10
symmetry until #18 lands.
Grep-sweep (`if (n < 0) { neg = true; n = -n; }`) returned two sites
in lib/fmt/fmt.ww i64dec and lib/strconv/strconv.ww — both mirror
ref/hare/strconv/itos.ha's pre-negate idiom for INT64_MIN safety.
Per the Hare-faithful filter, both stay.
This commit is contained in:
@@ -13124,6 +13124,10 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
let inner: *node = lhs.lhs;
|
||||
let loadop: str = "MOVQ";
|
||||
let storeop: str = "MOVQ";
|
||||
// Pointee node for the lhs-sign side of the /=
|
||||
// and %= dispatch. Mirror of cstage's `vt` at
|
||||
// cmd/w6c/cgen.c's TK_STAR-compound branch.
|
||||
let pe: *node = nil;
|
||||
if (inner != nil) {
|
||||
if (inner.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, inner.str);
|
||||
@@ -13131,7 +13135,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
let tn: *node = lc.tnode;
|
||||
if (tn != nil) {
|
||||
if (tn.kind == nkind.N_TPTR) {
|
||||
let pe: *node = tn.lhs;
|
||||
pe = tn.lhs;
|
||||
if (pe != nil) {
|
||||
let ps: i32 = fieldsize(c, pe);
|
||||
if (ps == 1 || ps == 2 || ps == 4) {
|
||||
@@ -13152,6 +13156,30 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline(loadop);
|
||||
emitline("\t(BX), AX\n");
|
||||
emitline("\tPOPQ\tCX\n");
|
||||
// Post-63332fe: /= and %= via CQO/IDIVQ on the
|
||||
// signed arm and MOVQ-zero/DIVQ on the unsigned
|
||||
// arm. Pre-fix the default branch silently stored
|
||||
// rhs into *p (combineop = MOVQ shape).
|
||||
if (n.op == tkind.TK_SLASHEQ || n.op == tkind.TK_PERCENTEQ) {
|
||||
let unsignd: bool = typenodeisunsignedc(c, pe);
|
||||
if (!unsignd) {
|
||||
unsignd = nodeisunsigned(c, n.rhs);
|
||||
};
|
||||
if (unsignd) {
|
||||
emitline("\tMOVQ\t$0, DX\n");
|
||||
emitline("\tDIVQ\tCX\n");
|
||||
} else {
|
||||
emitline("\tCQO\n");
|
||||
emitline("\tIDIVQ\tCX\n");
|
||||
};
|
||||
if (n.op == tkind.TK_PERCENTEQ) {
|
||||
emitline("\tMOVQ\tDX, AX\n");
|
||||
};
|
||||
emitline("\t");
|
||||
emitline(storeop);
|
||||
emitline("\tAX, (BX)\n");
|
||||
return;
|
||||
};
|
||||
let combineop: str = "MOVQ";
|
||||
if (n.op == tkind.TK_PLUSEQ) { combineop = "ADDQ"; }
|
||||
else { if (n.op == tkind.TK_MINUSEQ) { combineop = "SUBQ"; }
|
||||
@@ -14976,15 +15004,41 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tAX, CX\n");
|
||||
emitline("\tSHRQ\tCX, BX\n");
|
||||
}
|
||||
// Post-63332fe: /= and %= for a top-level
|
||||
// let. Same shape as the IDENT-local path:
|
||||
// park rhs in CX, slot value (BX) into AX,
|
||||
// CQO (or zero DX), IDIVQ (or DIVQ) CX,
|
||||
// ferry AX or DX back to BX for the shared
|
||||
// store-BX tail below.
|
||||
else { if (n.op == tkind.TK_SLASHEQ || n.op == tkind.TK_PERCENTEQ) {
|
||||
let unsignd: bool = typenodeisunsignedc(c, lvftn);
|
||||
if (!unsignd) {
|
||||
unsignd = nodeisunsigned(c, n.rhs);
|
||||
};
|
||||
emitline("\tMOVQ\tAX, CX\n");
|
||||
emitline("\tMOVQ\tBX, AX\n");
|
||||
if (unsignd) {
|
||||
emitline("\tMOVQ\t$0, DX\n");
|
||||
emitline("\tDIVQ\tCX\n");
|
||||
} else {
|
||||
emitline("\tCQO\n");
|
||||
emitline("\tIDIVQ\tCX\n");
|
||||
};
|
||||
if (n.op == tkind.TK_SLASHEQ) {
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\tDX, BX\n");
|
||||
};
|
||||
}
|
||||
else {
|
||||
// Unsupported compound: store rhs
|
||||
// directly. Mirrors the local path's
|
||||
// fallback for TK_SLASHEQ etc.
|
||||
// legacy fallback for unknown ops.
|
||||
didcompound = false;
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB)\n");
|
||||
};};};};};};};};
|
||||
};};};};};};};};};
|
||||
if (didcompound) {
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitsymname(c, nm);
|
||||
@@ -15215,6 +15269,36 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tAX, CX\n");
|
||||
emitline("\tSHRQ\tCX, BX\n");
|
||||
};
|
||||
// Post-63332fe: /= and %= for an IDENT local. Pre-fix
|
||||
// fell through with no case, so BX (still holding the
|
||||
// freshly loaded slot value) was stored back unchanged
|
||||
// — a silent no-op rather than the natural rhs-only
|
||||
// shape the global/deref siblings took. Park rhs in
|
||||
// CX, slot value (BX) into AX, CQO/IDIVQ, ferry AX
|
||||
// (quotient) or DX (remainder) back to BX.
|
||||
if (n.op == tkind.TK_SLASHEQ || n.op == tkind.TK_PERCENTEQ) {
|
||||
let unsignd: bool = false;
|
||||
if (lcn != nil) {
|
||||
unsignd = typenodeisunsignedc(c, lcn.tnode);
|
||||
};
|
||||
if (!unsignd) {
|
||||
unsignd = nodeisunsigned(c, n.rhs);
|
||||
};
|
||||
emitline("\tMOVQ\tAX, CX\n");
|
||||
emitline("\tMOVQ\tBX, AX\n");
|
||||
if (unsignd) {
|
||||
emitline("\tMOVQ\t$0, DX\n");
|
||||
emitline("\tDIVQ\tCX\n");
|
||||
} else {
|
||||
emitline("\tCQO\n");
|
||||
emitline("\tIDIVQ\tCX\n");
|
||||
};
|
||||
if (n.op == tkind.TK_SLASHEQ) {
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\tDX, BX\n");
|
||||
};
|
||||
};
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
|
||||
@@ -3307,6 +3307,10 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
let inner: *node = lhs.lhs;
|
||||
let loadop: str = "MOVQ";
|
||||
let storeop: str = "MOVQ";
|
||||
// Pointee node for the lhs-sign side of the /=
|
||||
// and %= dispatch. Mirror of cstage's `vt` at
|
||||
// cmd/w6c/cgen.c's TK_STAR-compound branch.
|
||||
let pe: *node = nil;
|
||||
if (inner != nil) {
|
||||
if (inner.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, inner.str);
|
||||
@@ -3314,7 +3318,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
let tn: *node = lc.tnode;
|
||||
if (tn != nil) {
|
||||
if (tn.kind == nkind.N_TPTR) {
|
||||
let pe: *node = tn.lhs;
|
||||
pe = tn.lhs;
|
||||
if (pe != nil) {
|
||||
let ps: i32 = fieldsize(c, pe);
|
||||
if (ps == 1 || ps == 2 || ps == 4) {
|
||||
@@ -3335,6 +3339,30 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline(loadop);
|
||||
emitline("\t(BX), AX\n");
|
||||
emitline("\tPOPQ\tCX\n");
|
||||
// Post-63332fe: /= and %= via CQO/IDIVQ on the
|
||||
// signed arm and MOVQ-zero/DIVQ on the unsigned
|
||||
// arm. Pre-fix the default branch silently stored
|
||||
// rhs into *p (combineop = MOVQ shape).
|
||||
if (n.op == tkind.TK_SLASHEQ || n.op == tkind.TK_PERCENTEQ) {
|
||||
let unsignd: bool = typenodeisunsignedc(c, pe);
|
||||
if (!unsignd) {
|
||||
unsignd = nodeisunsigned(c, n.rhs);
|
||||
};
|
||||
if (unsignd) {
|
||||
emitline("\tMOVQ\t$0, DX\n");
|
||||
emitline("\tDIVQ\tCX\n");
|
||||
} else {
|
||||
emitline("\tCQO\n");
|
||||
emitline("\tIDIVQ\tCX\n");
|
||||
};
|
||||
if (n.op == tkind.TK_PERCENTEQ) {
|
||||
emitline("\tMOVQ\tDX, AX\n");
|
||||
};
|
||||
emitline("\t");
|
||||
emitline(storeop);
|
||||
emitline("\tAX, (BX)\n");
|
||||
return;
|
||||
};
|
||||
let combineop: str = "MOVQ";
|
||||
if (n.op == tkind.TK_PLUSEQ) { combineop = "ADDQ"; }
|
||||
else { if (n.op == tkind.TK_MINUSEQ) { combineop = "SUBQ"; }
|
||||
@@ -5159,15 +5187,41 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tAX, CX\n");
|
||||
emitline("\tSHRQ\tCX, BX\n");
|
||||
}
|
||||
// Post-63332fe: /= and %= for a top-level
|
||||
// let. Same shape as the IDENT-local path:
|
||||
// park rhs in CX, slot value (BX) into AX,
|
||||
// CQO (or zero DX), IDIVQ (or DIVQ) CX,
|
||||
// ferry AX or DX back to BX for the shared
|
||||
// store-BX tail below.
|
||||
else { if (n.op == tkind.TK_SLASHEQ || n.op == tkind.TK_PERCENTEQ) {
|
||||
let unsignd: bool = typenodeisunsignedc(c, lvftn);
|
||||
if (!unsignd) {
|
||||
unsignd = nodeisunsigned(c, n.rhs);
|
||||
};
|
||||
emitline("\tMOVQ\tAX, CX\n");
|
||||
emitline("\tMOVQ\tBX, AX\n");
|
||||
if (unsignd) {
|
||||
emitline("\tMOVQ\t$0, DX\n");
|
||||
emitline("\tDIVQ\tCX\n");
|
||||
} else {
|
||||
emitline("\tCQO\n");
|
||||
emitline("\tIDIVQ\tCX\n");
|
||||
};
|
||||
if (n.op == tkind.TK_SLASHEQ) {
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\tDX, BX\n");
|
||||
};
|
||||
}
|
||||
else {
|
||||
// Unsupported compound: store rhs
|
||||
// directly. Mirrors the local path's
|
||||
// fallback for TK_SLASHEQ etc.
|
||||
// legacy fallback for unknown ops.
|
||||
didcompound = false;
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB)\n");
|
||||
};};};};};};};};
|
||||
};};};};};};};};};
|
||||
if (didcompound) {
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitsymname(c, nm);
|
||||
@@ -5398,6 +5452,36 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tAX, CX\n");
|
||||
emitline("\tSHRQ\tCX, BX\n");
|
||||
};
|
||||
// Post-63332fe: /= and %= for an IDENT local. Pre-fix
|
||||
// fell through with no case, so BX (still holding the
|
||||
// freshly loaded slot value) was stored back unchanged
|
||||
// — a silent no-op rather than the natural rhs-only
|
||||
// shape the global/deref siblings took. Park rhs in
|
||||
// CX, slot value (BX) into AX, CQO/IDIVQ, ferry AX
|
||||
// (quotient) or DX (remainder) back to BX.
|
||||
if (n.op == tkind.TK_SLASHEQ || n.op == tkind.TK_PERCENTEQ) {
|
||||
let unsignd: bool = false;
|
||||
if (lcn != nil) {
|
||||
unsignd = typenodeisunsignedc(c, lcn.tnode);
|
||||
};
|
||||
if (!unsignd) {
|
||||
unsignd = nodeisunsigned(c, n.rhs);
|
||||
};
|
||||
emitline("\tMOVQ\tAX, CX\n");
|
||||
emitline("\tMOVQ\tBX, AX\n");
|
||||
if (unsignd) {
|
||||
emitline("\tMOVQ\t$0, DX\n");
|
||||
emitline("\tDIVQ\tCX\n");
|
||||
} else {
|
||||
emitline("\tCQO\n");
|
||||
emitline("\tIDIVQ\tCX\n");
|
||||
};
|
||||
if (n.op == tkind.TK_SLASHEQ) {
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\tDX, BX\n");
|
||||
};
|
||||
};
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
|
||||
@@ -13124,6 +13124,10 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
let inner: *node = lhs.lhs;
|
||||
let loadop: str = "MOVQ";
|
||||
let storeop: str = "MOVQ";
|
||||
// Pointee node for the lhs-sign side of the /=
|
||||
// and %= dispatch. Mirror of cstage's `vt` at
|
||||
// cmd/w6c/cgen.c's TK_STAR-compound branch.
|
||||
let pe: *node = nil;
|
||||
if (inner != nil) {
|
||||
if (inner.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, inner.str);
|
||||
@@ -13131,7 +13135,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
let tn: *node = lc.tnode;
|
||||
if (tn != nil) {
|
||||
if (tn.kind == nkind.N_TPTR) {
|
||||
let pe: *node = tn.lhs;
|
||||
pe = tn.lhs;
|
||||
if (pe != nil) {
|
||||
let ps: i32 = fieldsize(c, pe);
|
||||
if (ps == 1 || ps == 2 || ps == 4) {
|
||||
@@ -13152,6 +13156,30 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline(loadop);
|
||||
emitline("\t(BX), AX\n");
|
||||
emitline("\tPOPQ\tCX\n");
|
||||
// Post-63332fe: /= and %= via CQO/IDIVQ on the
|
||||
// signed arm and MOVQ-zero/DIVQ on the unsigned
|
||||
// arm. Pre-fix the default branch silently stored
|
||||
// rhs into *p (combineop = MOVQ shape).
|
||||
if (n.op == tkind.TK_SLASHEQ || n.op == tkind.TK_PERCENTEQ) {
|
||||
let unsignd: bool = typenodeisunsignedc(c, pe);
|
||||
if (!unsignd) {
|
||||
unsignd = nodeisunsigned(c, n.rhs);
|
||||
};
|
||||
if (unsignd) {
|
||||
emitline("\tMOVQ\t$0, DX\n");
|
||||
emitline("\tDIVQ\tCX\n");
|
||||
} else {
|
||||
emitline("\tCQO\n");
|
||||
emitline("\tIDIVQ\tCX\n");
|
||||
};
|
||||
if (n.op == tkind.TK_PERCENTEQ) {
|
||||
emitline("\tMOVQ\tDX, AX\n");
|
||||
};
|
||||
emitline("\t");
|
||||
emitline(storeop);
|
||||
emitline("\tAX, (BX)\n");
|
||||
return;
|
||||
};
|
||||
let combineop: str = "MOVQ";
|
||||
if (n.op == tkind.TK_PLUSEQ) { combineop = "ADDQ"; }
|
||||
else { if (n.op == tkind.TK_MINUSEQ) { combineop = "SUBQ"; }
|
||||
@@ -14976,15 +15004,41 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tAX, CX\n");
|
||||
emitline("\tSHRQ\tCX, BX\n");
|
||||
}
|
||||
// Post-63332fe: /= and %= for a top-level
|
||||
// let. Same shape as the IDENT-local path:
|
||||
// park rhs in CX, slot value (BX) into AX,
|
||||
// CQO (or zero DX), IDIVQ (or DIVQ) CX,
|
||||
// ferry AX or DX back to BX for the shared
|
||||
// store-BX tail below.
|
||||
else { if (n.op == tkind.TK_SLASHEQ || n.op == tkind.TK_PERCENTEQ) {
|
||||
let unsignd: bool = typenodeisunsignedc(c, lvftn);
|
||||
if (!unsignd) {
|
||||
unsignd = nodeisunsigned(c, n.rhs);
|
||||
};
|
||||
emitline("\tMOVQ\tAX, CX\n");
|
||||
emitline("\tMOVQ\tBX, AX\n");
|
||||
if (unsignd) {
|
||||
emitline("\tMOVQ\t$0, DX\n");
|
||||
emitline("\tDIVQ\tCX\n");
|
||||
} else {
|
||||
emitline("\tCQO\n");
|
||||
emitline("\tIDIVQ\tCX\n");
|
||||
};
|
||||
if (n.op == tkind.TK_SLASHEQ) {
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\tDX, BX\n");
|
||||
};
|
||||
}
|
||||
else {
|
||||
// Unsupported compound: store rhs
|
||||
// directly. Mirrors the local path's
|
||||
// fallback for TK_SLASHEQ etc.
|
||||
// legacy fallback for unknown ops.
|
||||
didcompound = false;
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB)\n");
|
||||
};};};};};};};};
|
||||
};};};};};};};};};
|
||||
if (didcompound) {
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitsymname(c, nm);
|
||||
@@ -15215,6 +15269,36 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tAX, CX\n");
|
||||
emitline("\tSHRQ\tCX, BX\n");
|
||||
};
|
||||
// Post-63332fe: /= and %= for an IDENT local. Pre-fix
|
||||
// fell through with no case, so BX (still holding the
|
||||
// freshly loaded slot value) was stored back unchanged
|
||||
// — a silent no-op rather than the natural rhs-only
|
||||
// shape the global/deref siblings took. Park rhs in
|
||||
// CX, slot value (BX) into AX, CQO/IDIVQ, ferry AX
|
||||
// (quotient) or DX (remainder) back to BX.
|
||||
if (n.op == tkind.TK_SLASHEQ || n.op == tkind.TK_PERCENTEQ) {
|
||||
let unsignd: bool = false;
|
||||
if (lcn != nil) {
|
||||
unsignd = typenodeisunsignedc(c, lcn.tnode);
|
||||
};
|
||||
if (!unsignd) {
|
||||
unsignd = nodeisunsigned(c, n.rhs);
|
||||
};
|
||||
emitline("\tMOVQ\tAX, CX\n");
|
||||
emitline("\tMOVQ\tBX, AX\n");
|
||||
if (unsignd) {
|
||||
emitline("\tMOVQ\t$0, DX\n");
|
||||
emitline("\tDIVQ\tCX\n");
|
||||
} else {
|
||||
emitline("\tCQO\n");
|
||||
emitline("\tIDIVQ\tCX\n");
|
||||
};
|
||||
if (n.op == tkind.TK_SLASHEQ) {
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\tDX, BX\n");
|
||||
};
|
||||
};
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
|
||||
Reference in New Issue
Block a user