cgen: fix nested aggregate field dropped in alloc-heap structlit fill (C7c)
`alloc(Outer{ x = Inner{q=10} })` dropped the nested struct-literal field:
the alloc path had its own inline fill loop with only scalar/float/str
arms, so a field whose value is itself an N_STRUCTLIT fell to the scalar
tail and stored MOVQ $0 (cgexpr leaves a whole aggregate in no register)
over the inner slot. Both stages emitted the identical wrong fill, so the
byte-id gate was blind to it.
Route alloc's fill through the existing shared structlit-fill helper (the
one the BP-relative/global/local structlit sites already use -- it handles
nested-struct recursion, N_ARRLIT, str/slice and tagged) via a new 4th
destination mode DST_PTR_SP that reloads the heap base from (SP). This
deletes alloc's divergent inline loop, the lone site lacking the recursion.
As a side effect it also fixes a latent slice-field drop in the driver's
own alloc(sepgraph{...}) (pkg.len/.cap were dropped; the consumer reads
neither -- g.n is the count SSoT). Nested-array fields are closed in-class;
a nested tuple-LITERAL field now errors loudly and symmetrically (the #49
non-addressable gap, previously dropped silently at alloc only).
Surfaced by the codegen miscompile hunt (finding C7c). Pinned by
test/lang/alloc_nested_field_test.ww (nested struct depth 1+2, nested
array, adjacent multi-nested, sibling-no-clobber; reddens on revert).
Routing preservation proven: the whole test/lang corpus is byte-identical
HEAD vs fixed except the new pin; self-compile byte-id (990-996) green.
This commit is contained in:
@@ -2523,6 +2523,7 @@ enum {
|
||||
DST_BP = 0,
|
||||
DST_PTR_LOCAL = 1,
|
||||
DST_GLOBAL = 2,
|
||||
DST_PTR_SP = 3,
|
||||
};
|
||||
static void cg_structlit_fill(Cg*, Local**, Type*, Node*, int, int, const char*, int);
|
||||
static void cg_structlit_fill_bp(Cg*, Local**, Type*, Node*, int);
|
||||
@@ -3296,6 +3297,15 @@ cg_widen_tagged_push(Cg *c, Local **locals_p, Type *dst, Node *src, int sz)
|
||||
* DST_GLOBAL — base = BX, reloaded via `LEAQ name(SB), BX` with
|
||||
* the same reload cadence as DST_PTR_LOCAL.
|
||||
* srcoff unused.
|
||||
* DST_PTR_SP — base = BX, reloaded via `MOVQ (SP), BX` with the
|
||||
* same reload cadence as DST_PTR_LOCAL. The dst is the
|
||||
* alloc-heap base saved by the alloc(value) arm's
|
||||
* `PUSHQ AX` (top-of-stack); cgexpr is stack-balanced
|
||||
* so (SP) keeps pointing at it across the field walk.
|
||||
* srcoff/name unused. C7c: recurse into a nested
|
||||
* struct/array/tuple field VALUE so alloc(Outer{x =
|
||||
* Inner{..}}) writes the inner leaves instead of
|
||||
* storing AX=0 over the whole inner slot.
|
||||
*
|
||||
* Param semantics (locked in here so the recursion contract is clear):
|
||||
* - `disp` is the per-recursion accumulator — grows by `foff` as
|
||||
@@ -3341,6 +3351,8 @@ cg_structlit_fill(Cg *c, Local **locals_p, Type *lu, Node *lit,
|
||||
ins2(c, A_MOVQ, amem(D_BP, srcoff), areg(D_BX));
|
||||
else if (mode == DST_GLOBAL)
|
||||
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 zi = 0;
|
||||
while (zi + 8 <= sz) {
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
@@ -3384,6 +3396,8 @@ cg_structlit_fill(Cg *c, Local **locals_p, Type *lu, Node *lit,
|
||||
areg(D_BX));
|
||||
else if (mode == DST_GLOBAL)
|
||||
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));
|
||||
cg_widen_tagged_store(c, locals_p, fu, f->lhs,
|
||||
base_reg, disp + (int)foff, (int)fu->size);
|
||||
continue;
|
||||
@@ -3431,6 +3445,8 @@ cg_structlit_fill(Cg *c, Local **locals_p, Type *lu, Node *lit,
|
||||
areg(D_BX));
|
||||
else if (mode == DST_GLOBAL)
|
||||
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;
|
||||
@@ -3466,6 +3482,9 @@ cg_structlit_fill(Cg *c, Local **locals_p, Type *lu, Node *lit,
|
||||
if (mode == DST_PTR_LOCAL)
|
||||
ins2(c, A_MOVQ, amem(D_BP, srcoff),
|
||||
areg(D_DX));
|
||||
else if (mode == DST_PTR_SP)
|
||||
ins2(c, A_MOVQ, amem(D_SP, 0),
|
||||
areg(D_DX));
|
||||
else
|
||||
ins2(c, A_LEAQ, masym(c, name),
|
||||
areg(D_DX));
|
||||
@@ -3524,6 +3543,9 @@ cg_structlit_fill(Cg *c, Local **locals_p, Type *lu, Node *lit,
|
||||
else if (mode == DST_GLOBAL)
|
||||
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 eoff = disp + (int)foff + idx * esz;
|
||||
if (is_float_el)
|
||||
ins2(c, fmov, areg(D_X0),
|
||||
@@ -3543,6 +3565,9 @@ cg_structlit_fill(Cg *c, Local **locals_p, Type *lu, Node *lit,
|
||||
else if (mode == DST_GLOBAL)
|
||||
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 eoff = disp + (int)foff + idx * esz;
|
||||
if (is_float_el)
|
||||
ins2(c, fmov, areg(D_X0),
|
||||
@@ -3581,6 +3606,9 @@ cg_structlit_fill(Cg *c, Local **locals_p, Type *lu, Node *lit,
|
||||
if (mode == DST_PTR_LOCAL)
|
||||
ins2(c, A_MOVQ, amem(D_BP, srcoff),
|
||||
areg(D_BX));
|
||||
else if (mode == DST_PTR_SP)
|
||||
ins2(c, A_MOVQ, amem(D_SP, 0),
|
||||
areg(D_BX));
|
||||
else
|
||||
ins2(c, A_LEAQ, masym(c, name),
|
||||
areg(D_BX));
|
||||
@@ -3599,6 +3627,8 @@ cg_structlit_fill(Cg *c, Local **locals_p, Type *lu, Node *lit,
|
||||
ins2(c, A_MOVQ, amem(D_BP, srcoff), areg(D_BX));
|
||||
else if (mode == DST_GLOBAL)
|
||||
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 sl_isf32 = 0;
|
||||
if (fld_isfloat(ft, &sl_isf32)) {
|
||||
int mov = sl_isf32 ? A_MOVSS : A_MOVSD;
|
||||
@@ -8772,48 +8802,21 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
label(c, alloc_ok);
|
||||
ins1(c, A_PUSHQ, areg(D_AX)); /* save ptr */
|
||||
if (v->kind == N_STRUCTLIT && u && u->kind == TY_STRUCT) {
|
||||
for (Node *f = v->list; f; f = f->next) {
|
||||
u64 foff = 0;
|
||||
int fsz = 8;
|
||||
Type *ftype = NULL;
|
||||
for (Tfield *fl = u->fields; fl; fl = fl->next) {
|
||||
if (strcmp(fl->name, f->str) == 0) {
|
||||
foff = fl->offset;
|
||||
fsz = (int)(fl->type ? fl->type->size : 8);
|
||||
ftype = fl->type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
cgexpr(c, f->lhs, locals); /* AX or (AX,BX) or X0 */
|
||||
int f_isf32 = 0;
|
||||
if (fld_isfloat(ftype, &f_isf32)) {
|
||||
int mov = f_isf32 ? A_MOVSS : A_MOVSD;
|
||||
ins2(c, A_MOVQ, amem(D_SP, 0), areg(D_BX));
|
||||
ins2(c, mov, areg(D_X0),
|
||||
amem(D_BX, (int)foff));
|
||||
continue;
|
||||
}
|
||||
/* str IS []u8: cgexpr leaves (AX=ptr, BX=len,
|
||||
* CX=cap). Route the heap base through DX so all
|
||||
* three survive — CX now holds cap, BX holds len
|
||||
* (#1/Phase 3). */
|
||||
Type *fu = type_chase_named(ftype);
|
||||
if (fu && fu->kind == TY_STR) {
|
||||
ins2(c, A_MOVQ, amem(D_SP, 0), areg(D_DX));
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_DX, (int)foff + 0));
|
||||
ins2(c, A_MOVQ, areg(D_BX),
|
||||
amem(D_DX, (int)foff + 8));
|
||||
ins2(c, A_MOVQ, areg(D_CX),
|
||||
amem(D_DX, (int)foff + 16));
|
||||
continue;
|
||||
}
|
||||
ins2(c, A_MOVQ, amem(D_SP, 0), areg(D_BX));
|
||||
int op = A_MOVQ;
|
||||
if (fsz == 1) op = A_MOVB;
|
||||
else if (fsz == 4) op = A_MOVL;
|
||||
ins2(c, op, areg(D_AX), amem(D_BX, (int)foff));
|
||||
}
|
||||
/* C7c: route the heap field-fill through the shared
|
||||
* structlit helper in DST_PTR_SP mode — base reloaded
|
||||
* from the just-pushed heap ptr at (SP). The prior
|
||||
* inline loop had only float/str/scalar arms, so a
|
||||
* nested struct/array/tuple field VALUE (an inner
|
||||
* N_STRUCTLIT / N_ARRLIT) fell to the scalar tail and
|
||||
* stored AX=0 over the whole inner slot, dropping its
|
||||
* leaves. The helper recurses to arbitrary depth and
|
||||
* reuses the tagged/call/str/slice/array/agg arms,
|
||||
* closing the class symmetrically with the BP-rel and
|
||||
* global structlit sites. Byte-identical to the old
|
||||
* inline loop for the float/str/scalar fields the
|
||||
* corpus actually allocs (same (SP) reload, disp=0). */
|
||||
cg_structlit_fill(c, &locals, u, v, DST_PTR_SP,
|
||||
0, NULL, 0);
|
||||
} else {
|
||||
cgexpr(c, v, locals); /* AX = value */
|
||||
ins2(c, A_MOVQ, amem(D_SP, 0), areg(D_BX));
|
||||
|
||||
Reference in New Issue
Block a user