wcc: indexed-field compound assignment wires all ten ops, both stages

arr[i].field /= %= <<= >>= silently dropped the op (load-combine-store
emitted plain assignment) in BOTH stages — gate-blind, the #133 class.
Route every compound op through the combine dispatch at the indexed-
field arm and hard-error the unhandled operand kinds (float/str/slice/
tagged), per the #133 template (3986818). The runtime-correct target is
the op's own algebra (a OP= b == a = a OP b). Review item #33.

Both stages move in one commit: the fix is a single emission contract —
splitting cstage cgen.c from selfhost cgenexpr.ww would leave the
byte-id gates red between the halves.
This commit is contained in:
2026-06-12 19:26:24 +09:00
parent 02eb867036
commit 1be0e6b5db
6 changed files with 613 additions and 25 deletions

View File

@@ -5555,6 +5555,19 @@ cgexpr(Cg *c, Node *n, Local *locals)
* pop addr→BX, rhs→CX; combine;
* store. Float/str compound
* not wired. */
Type *fchk33 = type_chase_named(ft);
if (fchk33 && fchk33->kind == TY_TAGGED)
fatal("arr[i].field compound on "
"tagged field not wired (#33/rule-7)");
if (fchk33 && fchk33->kind == TY_STR)
fatal("arr[i].field compound on "
"str field not wired (#33/rule-7)");
if (fchk33 && fchk33->kind == TY_SLICE)
fatal("arr[i].field compound on "
"slice field not wired (#33/rule-7)");
if (ft && type_isfloat(ft))
fatal("arr[i].field compound on "
"float field not wired (#33/rule-7)");
cgexpr(c, n->rhs, locals);
ins1(c, A_PUSHQ, areg(D_AX));
cgexpr(c, idx, locals);
@@ -5587,6 +5600,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
areg(D_AX));
ins1(c, A_POPQ, areg(D_BX));
ins1(c, A_POPQ, areg(D_CX));
int unsignd33 = type_isunsigned(ft);
switch (n->op) {
case TK_PLUSEQ:
ins2(c, A_ADDQ,
@@ -5618,7 +5632,39 @@ cgexpr(Cg *c, Node *n, Local *locals)
areg(D_CX),
areg(D_AX));
break;
default: break;
case TK_SLASHEQ:
if (unsignd33)
ins2(c, A_MOVQ, aimm(0),
areg(D_DX));
else
ins0(c, A_CQO);
ins1(c, unsignd33 ? A_DIVQ : A_IDIVQ,
areg(D_CX));
break;
case TK_PERCENTEQ:
if (unsignd33)
ins2(c, A_MOVQ, aimm(0),
areg(D_DX));
else
ins0(c, A_CQO);
ins1(c, unsignd33 ? A_DIVQ : A_IDIVQ,
areg(D_CX));
ins2(c, A_MOVQ, areg(D_DX),
areg(D_AX));
break;
case TK_LSHIFTEQ:
ins2(c, A_SHLQ, areg(D_CX),
areg(D_AX));
break;
case TK_RSHIFTEQ:
ins2(c, unsignd33 ? A_SHRQ : A_SARQ,
areg(D_CX),
areg(D_AX));
break;
default:
fatal("arr[i].field compound: "
"unknown op tk=%d (#33/rule-7)",
n->op);
}
ins2(c, store_op, areg(D_AX),
amem(D_BX, foff));