cgen: store all eightbytes of a global/chained dot-field unwrap success (#16)

This commit is contained in:
2026-06-28 09:54:59 +09:00
parent 6b36b050d7
commit 3526725b5a
3 changed files with 216 additions and 0 deletions

View File

@@ -8353,6 +8353,86 @@ fn cgcall(c: *cgen, n: *syntax.node) void = {
fn cgassign(c: *cgen, n: *syntax.node) void = {
let lhs: *syntax.node = n.lhs;
// #16: a single-dot aggregate-field unwrap store whose base is a
// module-GLOBAL value-struct (`g.f = mk()!`) or a CHAINED struct
// field (`o.m.f = mk()!`). The #12 single-dot arm covered only a
// LOCAL / via-ptr base (its enclosing block requires localfindnode
// != nil), so the global case fell to the generic single-word store
// (DROPPED w1/w2) and the chained case never reached any field arm
// (SILENT both-stage, byte-id blind). Route the dest ADDRESS through
// cgplaceaddr (global LEAQ root + chained deref+offset spine) and
// feed the SAME {AX,DX,CX} producer-shift materialise. cgplaceaddr
// clobbers AX/CX, so it runs BEFORE cgexpr(rhs) and the address is
// saved across the call. In-cap struct field only (#12 scope);
// float/over-cap loud-stop at the producer. Local/via-ptr single-dot
// stays on the #12 arm. Mirrors cstage cgen.c N_ASSIGN #16 arm.
if (lhs != nil) { if (lhs.kind == syntax.nkind.N_DOT
&& lhs.lhs != nil && n.op == syntax.tkind.TK_ASSIGN
&& n.rhs != nil
&& (n.rhs.kind == syntax.nkind.N_TRYUNW
|| n.rhs.kind == syntax.nkind.N_TRYPROP)) {
let db: *syntax.node = lhs.lhs;
let chained: bool = db.kind == syntax.nkind.N_DOT;
let globalbase: bool = false;
if (db.kind == syntax.nkind.N_IDENT) {
if (localfindnode(c, db.str) == nil) {
if (isletvar(c, db.str)) {
let dbu: *syntax.tinfo = tichase(db.type_: *syntax.tinfo);
if (dbu != nil) { if (dbu.kind == syntax.tykind.TY_STRUCT) {
globalbase = true;
}; };
};
};
};
if (chained || globalbase) {
let fu: *syntax.tinfo = tichase(lhs.type_: *syntax.tinfo);
if (fu != nil) { if (fu.kind == syntax.tykind.TY_STRUCT) {
let ssz: i32 = fu.size: i32;
if (ssz <= 24) {
let tlm: i32 = ssz - (ssz / 8) * 8;
if (tlm == 0 || tlm == 1 || tlm == 2 || tlm == 4) {
if (!cgplaceaddr(c, lhs, "BX")) {
let m16: str = "#16: global/chained aggregate unwrap field dest unresolved (cgplaceaddr)\n";
os.write(2, m16.ptr, m16.len: u64);
os.exit(1);
};
emitline("\tPUSHQ\tBX\n");
cgexpr(c, n.rhs);
emitline("\tPOPQ\tBX\n");
let full: i32 = ssz / 8;
let i: i32 = 0;
for (i < full) {
let reg: str = "AX";
if (i == 1) { reg = "DX"; };
if (i == 2) { reg = "CX"; };
emitline("\tMOVQ\t");
emitline(reg);
emitline(", ");
emitdispreg((i * 8): i64, "BX");
emitline("\n");
i += 1;
};
if (tlm > 0) {
let top: str = "MOVB";
if (tlm == 4) { top = "MOVL"; };
if (tlm == 2) { top = "MOVW"; };
let treg: str = "AX";
if (full == 1) { treg = "DX"; };
if (full == 2) { treg = "CX"; };
emitline("\t");
emitline(top);
emitline("\t");
emitline(treg);
emitline(", ");
emitdispreg((full * 8): i64, "BX");
emitline("\n");
};
return;
};
};
}; };
};
}; };
// #145 (c1.5a): bulk slice-copy-assign `s.arr[lo:hi] = bs` (LHS is
// N_SLICE). No legacy cgassign arm catches N_SLICE — the statement
// silently emitted nothing (both stages, byte-id-green, #263-class).