cgen: extract the narrow-tail aggregate-register materialise into a shared helper (#14)

This commit is contained in:
2026-06-28 14:08:57 +09:00
parent ac6e86cefe
commit e7fefa3eb7
4 changed files with 159 additions and 432 deletions

View File

@@ -2341,6 +2341,62 @@ cg_aggcopy(Cg *c, int sz)
}
}
/* cg_agg_reg_store — the ONE register-cursor aggregate materialise: an
* in-cap (<=24B, GP-class) struct/array/tuple already held in the
* {AX,DX,CX} return cursor is stored into the place base_reg+disp.
* full = sz/8 exact-8B MOVQ words land straight in (safe into any dest);
* the sz%8 tail is one sized MOVB/MOVW/MOVL for {1,2,4} (BOTH branches).
* Extracted from site E (the #10 template) so every narrow-tail
* materialise funnels through one place — close-by-construction (#14).
*
* dest_padded forks ONLY the {3,5,6,7} tail (no GP sub-register exists
* for those widths and w6a has no shift):
* dest_padded == "the dest is a ceil-8/round8 slot (a scratch, or a
* #75 let/local aggregate slot) so an 8B tail over-store stays
* in-bounds." Post-#9: packed struct fields + array elements => 0;
* let/local/scratch slots => 1. It CANNOT be derived — paddedness is
* routing knowledge the caller owns.
* 1 -> a single full MOVQ of the cursor tail eightbyte (= E's #10
* template; the over-store lands in the slot's pad).
* 0 -> MOVQ the cursor tail eightbyte into the helper's own ceil-8
* @tagscr pad, then a sized aggcopy of the tail bytes into the
* dest so a packed field / array element is never overrun.
* PRECONDITION (caller-owned, stays per-site): the value is already in
* AX/DX/CX, the dest base is resolved into base_reg+disp, and the
* float-class (#165) / over-cap-sret (#234) loud-stops have already
* fired. Clobbers SI/BX/AX only on the dest_padded==0 detour (the value
* words are in memory by then). */
static void
cg_agg_reg_store(Cg *c, Local **locals, int base_reg, int disp, int sz,
int dest_padded)
{
int regs[3] = { D_AX, D_DX, D_CX };
int full = sz / 8;
int tail = sz % 8;
for (int i = 0; i < full; i++)
ins2(c, A_MOVQ, areg(regs[i]),
amem(base_reg, disp + i * 8));
if (tail == 0)
return;
if (tail == 1 || tail == 2 || tail == 4) {
int op = (tail == 1) ? A_MOVB
: (tail == 2) ? A_MOVW : A_MOVL;
ins2(c, op, areg(regs[full]),
amem(base_reg, disp + full * 8));
return;
}
if (dest_padded) {
ins2(c, A_MOVQ, areg(regs[full]),
amem(base_reg, disp + full * 8));
return;
}
int pad = cg_tagscr_slot(c, locals, 8);
ins2(c, A_MOVQ, areg(regs[full]), amem(D_BP, pad));
ins2(c, A_LEAQ, amem(D_BP, pad), areg(D_SI));
ins2(c, A_LEAQ, amem(base_reg, disp + full * 8), areg(D_BX));
cg_aggcopy(c, tail);
}
/* cgplaceaddr — compute the ADDRESS of an arbitrary place (lvalue)
* expression into dst_reg; returns 1 when the shape is wired, 0
* otherwise (the caller loud-stops — rule 7, never a silent drop).
@@ -3480,20 +3536,8 @@ cg_structlit_fill(Cg *c, Local **locals_p, Type *lu, Node *lit,
ins2(c, A_LEAQ, masym(c, name), areg(D_BX));
else if (mode == DST_PTR_SP)
ins2(c, A_MOVQ, amem(D_SP, 0), areg(D_BX));
int regs[3] = { D_AX, D_DX, D_CX };
int full = fsz / 8;
int tail = fsz % 8;
for (int i = 0; i < full; i++)
ins2(c, A_MOVQ, areg(regs[i]),
amem(base_reg,
disp + (int)foff + i * 8));
if (tail > 0) {
int op = (tail == 4) ? A_MOVL
: (tail == 2) ? A_MOVW : A_MOVB;
ins2(c, op, areg(regs[full]),
amem(base_reg,
disp + (int)foff + full * 8));
}
cg_agg_reg_store(c, locals_p, base_reg,
disp + (int)foff, fsz, 0);
continue;
}
/* str IS []u8: 3-word field (ptr,len,cap). cgexpr leaves
@@ -5252,18 +5296,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
ins1(c, A_PUSHQ, areg(D_BX));
cgexpr(c, n->rhs, locals);
ins1(c, A_POPQ, areg(D_BX));
int regs[3] = { D_AX, D_DX, D_CX };
int full = ssz / 8;
int tail = ssz % 8;
for (int i = 0; i < full; i++)
ins2(c, A_MOVQ, areg(regs[i]),
amem(D_BX, i * 8));
if (tail > 0) {
int op = (tail == 4) ? A_MOVL
: (tail == 2) ? A_MOVW : A_MOVB;
ins2(c, op, areg(regs[full]),
amem(D_BX, full * 8));
}
cg_agg_reg_store(c, &locals, D_BX, 0, ssz, 0);
break;
}
}
@@ -5495,9 +5528,6 @@ cgexpr(Cg *c, Node *n, Local *locals)
|| str_fu->size % 8 == 4)) {
int ssz = (int)str_fu->size;
cgexpr(c, n->rhs, locals);
int regs[3] = { D_AX, D_DX, D_CX };
int full = ssz / 8;
int tail = ssz % 8;
int base_reg, base_disp;
if (via_ptr || is_global) {
if (via_ptr)
@@ -5514,18 +5544,8 @@ cgexpr(Cg *c, Node *n, Local *locals)
base_reg = D_BP;
base_disp = boff + foff;
}
for (int i = 0; i < full; i++)
ins2(c, A_MOVQ, areg(regs[i]),
amem(base_reg,
base_disp + i * 8));
if (tail > 0) {
int op = (tail == 4) ? A_MOVL
: (tail == 2) ? A_MOVW
: A_MOVB;
ins2(c, op, areg(regs[full]),
amem(base_reg,
base_disp + full * 8));
}
cg_agg_reg_store(c, &locals, base_reg,
base_disp, ssz, 0);
break;
}
if (n->op == TK_ASSIGN && str_fu
@@ -6723,9 +6743,6 @@ cgexpr(Cg *c, Node *n, Local *locals)
&& (fsz % 8 == 0 || fsz % 8 == 1
|| fsz % 8 == 2 || fsz % 8 == 4)) {
cgexpr(c, n->rhs, locals);
int regs[3] = { D_AX, D_DX, D_CX };
int full = fsz / 8;
int tail = fsz % 8;
int base_reg, base_off;
if (via_cx) {
if (ptr_root)
@@ -6742,18 +6759,8 @@ cgexpr(Cg *c, Node *n, Local *locals)
base_reg = D_BP;
base_off = base_disp + total_off;
}
for (int i = 0; i < full; i++)
ins2(c, A_MOVQ, areg(regs[i]),
amem(base_reg,
base_off + i * 8));
if (tail > 0) {
int op = (tail == 4) ? A_MOVL
: (tail == 2) ? A_MOVW
: A_MOVB;
ins2(c, op, areg(regs[full]),
amem(base_reg,
base_off + full * 8));
}
cg_agg_reg_store(c, &locals,
base_reg, base_off, fsz, 0);
break;
}
if (fu && fu->kind == TY_STRUCT
@@ -7119,19 +7126,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
int scr = cg_tagscr_slot(c, &locals, esz);
cgexpr(c, n->rhs, locals); /* call → AX/DX/CX */
/* AX/DX/CX → scratch (mirror cgen.c:3434 receive). */
int regs[3] = { D_AX, D_DX, D_CX };
int full = esz / 8;
int tail = esz % 8;
for (int i = 0; i < full; i++)
ins2(c, A_MOVQ, areg(regs[i]),
amem(D_BP, scr + i * 8));
if (tail > 0) {
int op = (tail == 1) ? A_MOVB
: (tail == 2) ? A_MOVW
: (tail == 4) ? A_MOVL : A_MOVQ;
ins2(c, op, areg(regs[full]),
amem(D_BP, scr + full * 8));
}
cg_agg_reg_store(c, &locals, D_BP, scr, esz, 1);
/* dest &a[i] → BX (mirror #121 / #270-1b resolve) */
cgexpr(c, n->lhs->rhs, locals); /* idx → AX */
if (esz > 1) {
@@ -7954,19 +7949,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|| sz % 8 == 2
|| sz % 8 == 4)) {
cgexpr(c, n->rhs, locals);
int regs[3] = { D_AX, D_DX, D_CX };
int full = sz / 8;
int tail = sz % 8;
for (int i = 0; i < full; i++)
ins2(c, A_MOVQ, areg(regs[i]),
amem(D_BP, off + i * 8));
if (tail > 0) {
int op = (tail == 4) ? A_MOVL
: (tail == 2) ? A_MOVW
: A_MOVB;
ins2(c, op, areg(regs[full]),
amem(D_BP, off + full * 8));
}
cg_agg_reg_store(c, &locals, D_BP, off, sz, 0);
break;
}
if (isglob && au->kind == TY_ARRAY
@@ -13623,18 +13606,7 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
&& (sz % 8 == 0 || sz % 8 == 1
|| sz % 8 == 2 || sz % 8 == 4)) {
cgexpr(c, n->rhs, *locals);
int regs[3] = { D_AX, D_DX, D_CX };
int full = sz / 8;
int tail = sz % 8;
for (int i = 0; i < full; i++)
ins2(c, A_MOVQ, areg(regs[i]),
amem(D_BP, off + i * 8));
if (tail > 0) {
int op = (tail == 4) ? A_MOVL
: (tail == 2) ? A_MOVW : A_MOVB;
ins2(c, op, areg(regs[full]),
amem(D_BP, off + full * 8));
}
cg_agg_reg_store(c, locals, D_BP, off, sz, 1);
goto letlink;
}
/* array literal initialiser: `let xs: [N]T = [a, b, c];`.