wcc: single-dot field compound assignment routes through one combine helper

s.f *= v silently became s.f = v (and the other non-+=/-= ops dropped
likewise) in BOTH stages across five lvalue sub-arms: via-ptr field,
direct local field, str/slice pseudo-field, and the two global-field
forms. Funnel all five through a shared combine dispatch
(cgdotfieldcombine / cg_dotfield_combine) emitting the load-OP-store
sequence at field width, hard-erroring the unhandled kinds — close-by-
construction so no arm stays on the old PLUSEQ-only path (the #133
BUS-routing lesson; #227 sites A/B are the closed siblings). The
refactor routes the corpus's existing +=/-= sites through the same
helper output-identically (byte-id held). Review item #34.

Both stages move in one commit: one emission contract; splitting the
halves would leave the byte-id gates red in between.
This commit is contained in:
2026-06-12 19:29:58 +09:00
parent 1be0e6b5db
commit c405e777d3
6 changed files with 762 additions and 94 deletions

View File

@@ -23409,6 +23409,85 @@ fn cgtrytaggedshift(c: *cgen, n: *node) bool = {
// INDEX/DOT) from the box address cgexpr leaves in AX. Both were
// silent word0 unwraps pre-#35. >32B non-call stays loud (the cursor
// cannot carry it; #40 family). Mirrors cstage N_TRYPROP/N_TRYUNW.
// cgdotfieldcombine — single-dot field compound combine. The old
// field value is in BX, the rhs in AX; the result is left in AX.
// PLUSEQ/MINUSEQ preserve the pre-#34 emission (byte-id); the other 8
// ops were silently DROPPED (the arm fell through to a plain store of
// the rhs → `s.f = rhs`, #34/#263). SLASHEQ/PERCENTEQ/LSHIFTEQ/RSHIFTEQ
// need the lhs in AX and the divisor/count in CX, so swap (rhs AX→CX,
// old BX→AX) first. Signed RSHIFTEQ uses SARQ, unsigned SHRQ (#136).
// Mirrors cstage cg_dotfield_combine — both stages emit identical asm.
fn cgdotfieldcombine(c: *cgen, op: tkind, unsignd: bool) void = {
if (op == tkind.TK_PLUSEQ) { emitline("\tADDQ\tBX, AX\n"); return; };
if (op == tkind.TK_MINUSEQ) {
emitline("\tSUBQ\tAX, BX\n");
emitline("\tMOVQ\tBX, AX\n");
return;
};
if (op == tkind.TK_STAREQ) { emitline("\tIMULQ\tBX, AX\n"); return; };
if (op == tkind.TK_AMPEQ) { emitline("\tANDQ\tBX, AX\n"); return; };
if (op == tkind.TK_PIPEEQ) { emitline("\tORQ\tBX, AX\n"); return; };
if (op == tkind.TK_CARETEQ) { emitline("\tXORQ\tBX, AX\n"); return; };
if (op == tkind.TK_SLASHEQ) {
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"); };
return;
};
if (op == tkind.TK_PERCENTEQ) {
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"); };
emitline("\tMOVQ\tDX, AX\n");
return;
};
if (op == tkind.TK_LSHIFTEQ) {
emitline("\tMOVQ\tAX, CX\n");
emitline("\tMOVQ\tBX, AX\n");
emitline("\tSHLQ\tCX, AX\n");
return;
};
if (op == tkind.TK_RSHIFTEQ) {
emitline("\tMOVQ\tAX, CX\n");
emitline("\tMOVQ\tBX, AX\n");
if (unsignd) { emitline("\tSHRQ\tCX, AX\n"); }
else { emitline("\tSARQ\tCX, AX\n"); };
return;
};
let m: str = "single-dot field compound: unknown op (#34/rule-7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
// cgdotfieldhardstop — loud-stop a single-dot field compound on a
// non-integer field (float/str/slice/tagged): the combine arm only
// speaks integer ABI; pre-#34 these silently became `s.f = rhs`.
// Mirrors the cstage cg_dotfield_hardstop gate. (#34/rule-7)
fn cgdotfieldhardstop(c: *cgen, ftn: *node) void = {
if (istaggedtype(c, ftn)) {
let m: str = "single-dot field compound on tagged field not wired (#34/rule-7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
if (isstrtype(c, ftn)) {
let m: str = "single-dot field compound on str field not wired (#34/rule-7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
if (isslicetype(c, ftn)) {
let m: str = "single-dot field compound on slice field not wired (#34/rule-7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
if (isfloattype(c, ftn)) {
let m: str = "single-dot field compound on float field not wired (#34/rule-7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
};
fn cgtryunwcursor(c: *cgen, n: *node, opname: str) void = {
let u: *tinfo = nil;
if (n.lhs != nil) { u = n.lhs.type_: *tinfo; };
@@ -33041,11 +33120,10 @@ fn cgassign(c: *cgen, n: *node) void = {
// PLUSEQ is commutative; MINUSEQ
// needs lhs - rhs (BX is old lhs,
// AX is rhs).
if (n.op == tkind.TK_PLUSEQ) { emitline("\tADDQ\tBX, AX\n"); };
if (n.op == tkind.TK_MINUSEQ) {
emitline("\tSUBQ\tAX, BX\n");
emitline("\tMOVQ\tBX, AX\n");
};
cgdotfieldhardstop(c, fi.tnode);
let uns34: bool = false;
if (fi.tnode != nil) { if (fi.tnode.type_ != nil) { uns34 = typeisunsigned(fi.tnode.type_: *tinfo); }; };
cgdotfieldcombine(c, n.op, uns34);
};
if (n.op == tkind.TK_ASSIGN) {
// str/slice field via *struct: str IS []u8, so both
@@ -33267,11 +33345,10 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("\tPOPQ\tBX\n");
// PLUSEQ commutes; MINUSEQ needs
// lhs-rhs (BX old lhs, AX rhs).
if (n.op == tkind.TK_PLUSEQ) { emitline("\tADDQ\tBX, AX\n"); };
if (n.op == tkind.TK_MINUSEQ) {
emitline("\tSUBQ\tAX, BX\n");
emitline("\tMOVQ\tBX, AX\n");
};
cgdotfieldhardstop(c, fi.tnode);
let uns34: bool = false;
if (fi.tnode != nil) { if (fi.tnode.type_ != nil) { uns34 = typeisunsigned(fi.tnode.type_: *tinfo); }; };
cgdotfieldcombine(c, n.op, uns34);
};
// str/slice field direct: str IS []u8, so both store the
// full 3-word {ptr,len,cap} from (AX,BX,CX) at +0/+8/+16.
@@ -33342,11 +33419,7 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("\tPOPQ\tBX\n");
// PLUSEQ is commutative; MINUSEQ
// needs lhs - rhs.
if (n.op == tkind.TK_PLUSEQ) { emitline("\tADDQ\tBX, AX\n"); };
if (n.op == tkind.TK_MINUSEQ) {
emitline("\tSUBQ\tAX, BX\n");
emitline("\tMOVQ\tBX, AX\n");
};
cgdotfieldcombine(c, n.op, false);
emitline("\tMOVQ\t");
emitoff(lc.off: i64);
emitline("(BP), BX\n");
@@ -33375,11 +33448,7 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("\tPUSHQ\tBX\n");
cgexpr(c, n.rhs);
emitline("\tPOPQ\tBX\n");
if (n.op == tkind.TK_PLUSEQ) { emitline("\tADDQ\tBX, AX\n"); };
if (n.op == tkind.TK_MINUSEQ) {
emitline("\tSUBQ\tAX, BX\n");
emitline("\tMOVQ\tBX, AX\n");
};
cgdotfieldcombine(c, n.op, false);
emitline("\tMOVQ\tAX, ");
emitoff((lc.off + delta): i64);
emitline("(BP)\n");
@@ -33630,11 +33699,10 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("\tPUSHQ\tBX\n");
cgexpr(c, n.rhs);
emitline("\tPOPQ\tBX\n");
if (n.op == tkind.TK_PLUSEQ) { emitline("\tADDQ\tBX, AX\n"); };
if (n.op == tkind.TK_MINUSEQ) {
emitline("\tSUBQ\tAX, BX\n");
emitline("\tMOVQ\tBX, AX\n");
};
cgdotfieldhardstop(c, fi.tnode);
let uns34: bool = false;
if (fi.tnode != nil) { if (fi.tnode.type_ != nil) { uns34 = typeisunsigned(fi.tnode.type_: *tinfo); }; };
cgdotfieldcombine(c, n.op, uns34);
let sop: str = fieldstoreop(c, fi);
emitline("\tLEAQ\t");
emitsymname(c, bn);

View File

@@ -258,6 +258,85 @@ fn cgtrytaggedshift(c: *cgen, n: *node) bool = {
// INDEX/DOT) from the box address cgexpr leaves in AX. Both were
// silent word0 unwraps pre-#35. >32B non-call stays loud (the cursor
// cannot carry it; #40 family). Mirrors cstage N_TRYPROP/N_TRYUNW.
// cgdotfieldcombine — single-dot field compound combine. The old
// field value is in BX, the rhs in AX; the result is left in AX.
// PLUSEQ/MINUSEQ preserve the pre-#34 emission (byte-id); the other 8
// ops were silently DROPPED (the arm fell through to a plain store of
// the rhs → `s.f = rhs`, #34/#263). SLASHEQ/PERCENTEQ/LSHIFTEQ/RSHIFTEQ
// need the lhs in AX and the divisor/count in CX, so swap (rhs AX→CX,
// old BX→AX) first. Signed RSHIFTEQ uses SARQ, unsigned SHRQ (#136).
// Mirrors cstage cg_dotfield_combine — both stages emit identical asm.
fn cgdotfieldcombine(c: *cgen, op: tkind, unsignd: bool) void = {
if (op == tkind.TK_PLUSEQ) { emitline("\tADDQ\tBX, AX\n"); return; };
if (op == tkind.TK_MINUSEQ) {
emitline("\tSUBQ\tAX, BX\n");
emitline("\tMOVQ\tBX, AX\n");
return;
};
if (op == tkind.TK_STAREQ) { emitline("\tIMULQ\tBX, AX\n"); return; };
if (op == tkind.TK_AMPEQ) { emitline("\tANDQ\tBX, AX\n"); return; };
if (op == tkind.TK_PIPEEQ) { emitline("\tORQ\tBX, AX\n"); return; };
if (op == tkind.TK_CARETEQ) { emitline("\tXORQ\tBX, AX\n"); return; };
if (op == tkind.TK_SLASHEQ) {
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"); };
return;
};
if (op == tkind.TK_PERCENTEQ) {
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"); };
emitline("\tMOVQ\tDX, AX\n");
return;
};
if (op == tkind.TK_LSHIFTEQ) {
emitline("\tMOVQ\tAX, CX\n");
emitline("\tMOVQ\tBX, AX\n");
emitline("\tSHLQ\tCX, AX\n");
return;
};
if (op == tkind.TK_RSHIFTEQ) {
emitline("\tMOVQ\tAX, CX\n");
emitline("\tMOVQ\tBX, AX\n");
if (unsignd) { emitline("\tSHRQ\tCX, AX\n"); }
else { emitline("\tSARQ\tCX, AX\n"); };
return;
};
let m: str = "single-dot field compound: unknown op (#34/rule-7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
// cgdotfieldhardstop — loud-stop a single-dot field compound on a
// non-integer field (float/str/slice/tagged): the combine arm only
// speaks integer ABI; pre-#34 these silently became `s.f = rhs`.
// Mirrors the cstage cg_dotfield_hardstop gate. (#34/rule-7)
fn cgdotfieldhardstop(c: *cgen, ftn: *node) void = {
if (istaggedtype(c, ftn)) {
let m: str = "single-dot field compound on tagged field not wired (#34/rule-7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
if (isstrtype(c, ftn)) {
let m: str = "single-dot field compound on str field not wired (#34/rule-7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
if (isslicetype(c, ftn)) {
let m: str = "single-dot field compound on slice field not wired (#34/rule-7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
if (isfloattype(c, ftn)) {
let m: str = "single-dot field compound on float field not wired (#34/rule-7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
};
fn cgtryunwcursor(c: *cgen, n: *node, opname: str) void = {
let u: *tinfo = nil;
if (n.lhs != nil) { u = n.lhs.type_: *tinfo; };
@@ -9890,11 +9969,10 @@ fn cgassign(c: *cgen, n: *node) void = {
// PLUSEQ is commutative; MINUSEQ
// needs lhs - rhs (BX is old lhs,
// AX is rhs).
if (n.op == tkind.TK_PLUSEQ) { emitline("\tADDQ\tBX, AX\n"); };
if (n.op == tkind.TK_MINUSEQ) {
emitline("\tSUBQ\tAX, BX\n");
emitline("\tMOVQ\tBX, AX\n");
};
cgdotfieldhardstop(c, fi.tnode);
let uns34: bool = false;
if (fi.tnode != nil) { if (fi.tnode.type_ != nil) { uns34 = typeisunsigned(fi.tnode.type_: *tinfo); }; };
cgdotfieldcombine(c, n.op, uns34);
};
if (n.op == tkind.TK_ASSIGN) {
// str/slice field via *struct: str IS []u8, so both
@@ -10116,11 +10194,10 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("\tPOPQ\tBX\n");
// PLUSEQ commutes; MINUSEQ needs
// lhs-rhs (BX old lhs, AX rhs).
if (n.op == tkind.TK_PLUSEQ) { emitline("\tADDQ\tBX, AX\n"); };
if (n.op == tkind.TK_MINUSEQ) {
emitline("\tSUBQ\tAX, BX\n");
emitline("\tMOVQ\tBX, AX\n");
};
cgdotfieldhardstop(c, fi.tnode);
let uns34: bool = false;
if (fi.tnode != nil) { if (fi.tnode.type_ != nil) { uns34 = typeisunsigned(fi.tnode.type_: *tinfo); }; };
cgdotfieldcombine(c, n.op, uns34);
};
// str/slice field direct: str IS []u8, so both store the
// full 3-word {ptr,len,cap} from (AX,BX,CX) at +0/+8/+16.
@@ -10191,11 +10268,7 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("\tPOPQ\tBX\n");
// PLUSEQ is commutative; MINUSEQ
// needs lhs - rhs.
if (n.op == tkind.TK_PLUSEQ) { emitline("\tADDQ\tBX, AX\n"); };
if (n.op == tkind.TK_MINUSEQ) {
emitline("\tSUBQ\tAX, BX\n");
emitline("\tMOVQ\tBX, AX\n");
};
cgdotfieldcombine(c, n.op, false);
emitline("\tMOVQ\t");
emitoff(lc.off: i64);
emitline("(BP), BX\n");
@@ -10224,11 +10297,7 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("\tPUSHQ\tBX\n");
cgexpr(c, n.rhs);
emitline("\tPOPQ\tBX\n");
if (n.op == tkind.TK_PLUSEQ) { emitline("\tADDQ\tBX, AX\n"); };
if (n.op == tkind.TK_MINUSEQ) {
emitline("\tSUBQ\tAX, BX\n");
emitline("\tMOVQ\tBX, AX\n");
};
cgdotfieldcombine(c, n.op, false);
emitline("\tMOVQ\tAX, ");
emitoff((lc.off + delta): i64);
emitline("(BP)\n");
@@ -10479,11 +10548,10 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("\tPUSHQ\tBX\n");
cgexpr(c, n.rhs);
emitline("\tPOPQ\tBX\n");
if (n.op == tkind.TK_PLUSEQ) { emitline("\tADDQ\tBX, AX\n"); };
if (n.op == tkind.TK_MINUSEQ) {
emitline("\tSUBQ\tAX, BX\n");
emitline("\tMOVQ\tBX, AX\n");
};
cgdotfieldhardstop(c, fi.tnode);
let uns34: bool = false;
if (fi.tnode != nil) { if (fi.tnode.type_ != nil) { uns34 = typeisunsigned(fi.tnode.type_: *tinfo); }; };
cgdotfieldcombine(c, n.op, uns34);
let sop: str = fieldstoreop(c, fi);
emitline("\tLEAQ\t");
emitsymname(c, bn);

View File

@@ -23409,6 +23409,85 @@ fn cgtrytaggedshift(c: *cgen, n: *node) bool = {
// INDEX/DOT) from the box address cgexpr leaves in AX. Both were
// silent word0 unwraps pre-#35. >32B non-call stays loud (the cursor
// cannot carry it; #40 family). Mirrors cstage N_TRYPROP/N_TRYUNW.
// cgdotfieldcombine — single-dot field compound combine. The old
// field value is in BX, the rhs in AX; the result is left in AX.
// PLUSEQ/MINUSEQ preserve the pre-#34 emission (byte-id); the other 8
// ops were silently DROPPED (the arm fell through to a plain store of
// the rhs → `s.f = rhs`, #34/#263). SLASHEQ/PERCENTEQ/LSHIFTEQ/RSHIFTEQ
// need the lhs in AX and the divisor/count in CX, so swap (rhs AX→CX,
// old BX→AX) first. Signed RSHIFTEQ uses SARQ, unsigned SHRQ (#136).
// Mirrors cstage cg_dotfield_combine — both stages emit identical asm.
fn cgdotfieldcombine(c: *cgen, op: tkind, unsignd: bool) void = {
if (op == tkind.TK_PLUSEQ) { emitline("\tADDQ\tBX, AX\n"); return; };
if (op == tkind.TK_MINUSEQ) {
emitline("\tSUBQ\tAX, BX\n");
emitline("\tMOVQ\tBX, AX\n");
return;
};
if (op == tkind.TK_STAREQ) { emitline("\tIMULQ\tBX, AX\n"); return; };
if (op == tkind.TK_AMPEQ) { emitline("\tANDQ\tBX, AX\n"); return; };
if (op == tkind.TK_PIPEEQ) { emitline("\tORQ\tBX, AX\n"); return; };
if (op == tkind.TK_CARETEQ) { emitline("\tXORQ\tBX, AX\n"); return; };
if (op == tkind.TK_SLASHEQ) {
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"); };
return;
};
if (op == tkind.TK_PERCENTEQ) {
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"); };
emitline("\tMOVQ\tDX, AX\n");
return;
};
if (op == tkind.TK_LSHIFTEQ) {
emitline("\tMOVQ\tAX, CX\n");
emitline("\tMOVQ\tBX, AX\n");
emitline("\tSHLQ\tCX, AX\n");
return;
};
if (op == tkind.TK_RSHIFTEQ) {
emitline("\tMOVQ\tAX, CX\n");
emitline("\tMOVQ\tBX, AX\n");
if (unsignd) { emitline("\tSHRQ\tCX, AX\n"); }
else { emitline("\tSARQ\tCX, AX\n"); };
return;
};
let m: str = "single-dot field compound: unknown op (#34/rule-7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
// cgdotfieldhardstop — loud-stop a single-dot field compound on a
// non-integer field (float/str/slice/tagged): the combine arm only
// speaks integer ABI; pre-#34 these silently became `s.f = rhs`.
// Mirrors the cstage cg_dotfield_hardstop gate. (#34/rule-7)
fn cgdotfieldhardstop(c: *cgen, ftn: *node) void = {
if (istaggedtype(c, ftn)) {
let m: str = "single-dot field compound on tagged field not wired (#34/rule-7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
if (isstrtype(c, ftn)) {
let m: str = "single-dot field compound on str field not wired (#34/rule-7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
if (isslicetype(c, ftn)) {
let m: str = "single-dot field compound on slice field not wired (#34/rule-7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
if (isfloattype(c, ftn)) {
let m: str = "single-dot field compound on float field not wired (#34/rule-7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
};
fn cgtryunwcursor(c: *cgen, n: *node, opname: str) void = {
let u: *tinfo = nil;
if (n.lhs != nil) { u = n.lhs.type_: *tinfo; };
@@ -33041,11 +33120,10 @@ fn cgassign(c: *cgen, n: *node) void = {
// PLUSEQ is commutative; MINUSEQ
// needs lhs - rhs (BX is old lhs,
// AX is rhs).
if (n.op == tkind.TK_PLUSEQ) { emitline("\tADDQ\tBX, AX\n"); };
if (n.op == tkind.TK_MINUSEQ) {
emitline("\tSUBQ\tAX, BX\n");
emitline("\tMOVQ\tBX, AX\n");
};
cgdotfieldhardstop(c, fi.tnode);
let uns34: bool = false;
if (fi.tnode != nil) { if (fi.tnode.type_ != nil) { uns34 = typeisunsigned(fi.tnode.type_: *tinfo); }; };
cgdotfieldcombine(c, n.op, uns34);
};
if (n.op == tkind.TK_ASSIGN) {
// str/slice field via *struct: str IS []u8, so both
@@ -33267,11 +33345,10 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("\tPOPQ\tBX\n");
// PLUSEQ commutes; MINUSEQ needs
// lhs-rhs (BX old lhs, AX rhs).
if (n.op == tkind.TK_PLUSEQ) { emitline("\tADDQ\tBX, AX\n"); };
if (n.op == tkind.TK_MINUSEQ) {
emitline("\tSUBQ\tAX, BX\n");
emitline("\tMOVQ\tBX, AX\n");
};
cgdotfieldhardstop(c, fi.tnode);
let uns34: bool = false;
if (fi.tnode != nil) { if (fi.tnode.type_ != nil) { uns34 = typeisunsigned(fi.tnode.type_: *tinfo); }; };
cgdotfieldcombine(c, n.op, uns34);
};
// str/slice field direct: str IS []u8, so both store the
// full 3-word {ptr,len,cap} from (AX,BX,CX) at +0/+8/+16.
@@ -33342,11 +33419,7 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("\tPOPQ\tBX\n");
// PLUSEQ is commutative; MINUSEQ
// needs lhs - rhs.
if (n.op == tkind.TK_PLUSEQ) { emitline("\tADDQ\tBX, AX\n"); };
if (n.op == tkind.TK_MINUSEQ) {
emitline("\tSUBQ\tAX, BX\n");
emitline("\tMOVQ\tBX, AX\n");
};
cgdotfieldcombine(c, n.op, false);
emitline("\tMOVQ\t");
emitoff(lc.off: i64);
emitline("(BP), BX\n");
@@ -33375,11 +33448,7 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("\tPUSHQ\tBX\n");
cgexpr(c, n.rhs);
emitline("\tPOPQ\tBX\n");
if (n.op == tkind.TK_PLUSEQ) { emitline("\tADDQ\tBX, AX\n"); };
if (n.op == tkind.TK_MINUSEQ) {
emitline("\tSUBQ\tAX, BX\n");
emitline("\tMOVQ\tBX, AX\n");
};
cgdotfieldcombine(c, n.op, false);
emitline("\tMOVQ\tAX, ");
emitoff((lc.off + delta): i64);
emitline("(BP)\n");
@@ -33630,11 +33699,10 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("\tPUSHQ\tBX\n");
cgexpr(c, n.rhs);
emitline("\tPOPQ\tBX\n");
if (n.op == tkind.TK_PLUSEQ) { emitline("\tADDQ\tBX, AX\n"); };
if (n.op == tkind.TK_MINUSEQ) {
emitline("\tSUBQ\tAX, BX\n");
emitline("\tMOVQ\tBX, AX\n");
};
cgdotfieldhardstop(c, fi.tnode);
let uns34: bool = false;
if (fi.tnode != nil) { if (fi.tnode.type_ != nil) { uns34 = typeisunsigned(fi.tnode.type_: *tinfo); }; };
cgdotfieldcombine(c, n.op, uns34);
let sop: str = fieldstoreop(c, fi);
emitline("\tLEAQ\t");
emitsymname(c, bn);