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:
@@ -3554,9 +3554,22 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
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;
|
||||
case TK_SLASHEQ:
|
||||
case TK_PERCENTEQ: {
|
||||
int unsignd = (vt && type_isunsigned(vt))
|
||||
|| (n->rhs && type_isunsigned(n->rhs->type));
|
||||
if (unsignd)
|
||||
ins2(c, A_MOVQ, aimm(0), areg(D_DX));
|
||||
else
|
||||
ins0(c, A_CQO);
|
||||
ins1(c, unsignd ? A_DIVQ : A_IDIVQ, areg(D_CX));
|
||||
if (n->op == TK_PERCENTEQ)
|
||||
ins2(c, A_MOVQ, areg(D_DX), areg(D_AX));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* TK_SLASHEQ / unknown: store rhs only,
|
||||
* matching the IDENT-compound fallback. */
|
||||
/* unknown compound: legacy fallback —
|
||||
* store rhs only. */
|
||||
ins2(c, A_MOVQ, areg(D_CX), areg(D_AX));
|
||||
break;
|
||||
}
|
||||
@@ -3719,10 +3732,33 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
ins2(c, A_MOVQ, areg(D_AX), areg(D_CX));
|
||||
ins2(c, A_SHRQ, areg(D_CX), areg(D_BX));
|
||||
break;
|
||||
case TK_SLASHEQ:
|
||||
case TK_PERCENTEQ: {
|
||||
/* Sister site of the IDENT-local path
|
||||
* below. Park rhs (AX) in CX, slot value
|
||||
* (BX) into AX, CQO sign-extend (or
|
||||
* MOVQ $0, DX zero-extend), IDIVQ (or
|
||||
* DIVQ) CX, ferry AX (quotient) or DX
|
||||
* (remainder) back to BX for the shared
|
||||
* store-BX tail. */
|
||||
int unsignd = (n->lhs && type_isunsigned(n->lhs->type))
|
||||
|| (n->rhs && type_isunsigned(n->rhs->type));
|
||||
ins2(c, A_MOVQ, areg(D_AX), areg(D_CX));
|
||||
ins2(c, A_MOVQ, areg(D_BX), areg(D_AX));
|
||||
if (unsignd)
|
||||
ins2(c, A_MOVQ, aimm(0), areg(D_DX));
|
||||
else
|
||||
ins0(c, A_CQO);
|
||||
ins1(c, unsignd ? A_DIVQ : A_IDIVQ, areg(D_CX));
|
||||
if (n->op == TK_SLASHEQ)
|
||||
ins2(c, A_MOVQ, areg(D_AX), areg(D_BX));
|
||||
else
|
||||
ins2(c, A_MOVQ, areg(D_DX), areg(D_BX));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* Unsupported compound op: store rhs
|
||||
* directly. Mirrors the local path's
|
||||
* fallback for TK_SLASHEQ etc. */
|
||||
/* unknown compound: legacy fallback —
|
||||
* store rhs only. */
|
||||
did_compound = 0;
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
masym(c, n->lhs->str));
|
||||
@@ -3772,12 +3808,29 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
ins2(c, A_SHRQ, areg(D_CX), areg(D_BX));
|
||||
break;
|
||||
case TK_SLASHEQ:
|
||||
/* BX = BX / AX. IDIV uses DX:AX/RAX. Move
|
||||
* BX→AX first, sign-extend via CQO would be
|
||||
* cleanest; skip for now and fall back to
|
||||
* MOVQ to avoid emitting wrong code. */
|
||||
ins2(c, A_MOVQ, areg(D_BX), amem(D_BP, off));
|
||||
goto skip_assign_store;
|
||||
case TK_PERCENTEQ: {
|
||||
/* IDIV/DIV needs dividend in RDX:RAX, divisor
|
||||
* in a GPR. Park rhs (currently AX) in CX, move
|
||||
* slot value (BX) into AX, sign- or zero-extend
|
||||
* into RDX:RAX, divide, then ferry the quotient
|
||||
* (AX) or remainder (DX) back into BX for the
|
||||
* shared store-BX-to-slot tail below. Post-#16:
|
||||
* CQO is now in the assembler. */
|
||||
int unsignd = (n->lhs && type_isunsigned(n->lhs->type))
|
||||
|| (n->rhs && type_isunsigned(n->rhs->type));
|
||||
ins2(c, A_MOVQ, areg(D_AX), areg(D_CX));
|
||||
ins2(c, A_MOVQ, areg(D_BX), areg(D_AX));
|
||||
if (unsignd)
|
||||
ins2(c, A_MOVQ, aimm(0), areg(D_DX));
|
||||
else
|
||||
ins0(c, A_CQO);
|
||||
ins1(c, unsignd ? A_DIVQ : A_IDIVQ, areg(D_CX));
|
||||
if (n->op == TK_SLASHEQ)
|
||||
ins2(c, A_MOVQ, areg(D_AX), areg(D_BX));
|
||||
else
|
||||
ins2(c, A_MOVQ, areg(D_DX), areg(D_BX));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* unknown: just store rhs (legacy fallback) */
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, off));
|
||||
|
||||
Reference in New Issue
Block a user