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:
@@ -5911,60 +5911,19 @@ fn cgalloc(c: *cgen, n: *syntax.node) void = {
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
if (v.kind == syntax.nkind.N_STRUCTLIT) {
|
||||
if (si != nil) {
|
||||
let f: *syntax.node = v.list;
|
||||
for (f != nil) {
|
||||
if (f.kind == syntax.nkind.N_FIELD) {
|
||||
let fname: str = f.str;
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
let fn_: str = fi.fname;
|
||||
if (syntax.streq(fn_, fname)) {
|
||||
cgexpr(c, f.lhs);
|
||||
// alloc(T{ fval = v }) for f64/f32 field: cgexpr left
|
||||
// the value in X0, not AX — route the store via MOVSD/MOVSS.
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
||||
emitline("\tMOVQ\t(SP), BX\n");
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitdispreg(fi.foff: i64, "BX");
|
||||
emitline("\n");
|
||||
fi = nil;
|
||||
} else { if (isstrtype(c, fi.tnode)) {
|
||||
// str IS []u8: cgexpr leaves (AX=ptr,
|
||||
// BX=len, CX=cap). Route the heap base
|
||||
// through DX so all three survive — CX
|
||||
// holds cap, BX holds len (#1/Phase 3).
|
||||
emitline("\tMOVQ\t(SP), DX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg(fi.foff: i64, "DX");
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitdispreg((fi.foff + 8): i64, "DX");
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitdispreg((fi.foff + 16): i64, "DX");
|
||||
emitline("\n");
|
||||
fi = nil;
|
||||
} else {
|
||||
emitline("\tMOVQ\t(SP), BX\n");
|
||||
let sop: str = fieldstoreop(c, fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
emitdispreg(fi.foff: i64, "BX");
|
||||
emitline("\n");
|
||||
fi = nil;
|
||||
};};
|
||||
} else {
|
||||
fi = fi.finext;
|
||||
};
|
||||
};
|
||||
};
|
||||
f = f.next;
|
||||
};
|
||||
// C7c: route the heap field-fill through the shared
|
||||
// structlit helper in mode 3 (DST_PTR_SP) — 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 cstage's DST_PTR_SP arm. Byte-id to
|
||||
// the old inline loop for the float/str/scalar fields the
|
||||
// corpus actually allocs (same (SP) reload, disp=0).
|
||||
cgstructlitfill(c, si, v, 3, 0, "", 0);
|
||||
};
|
||||
} else {
|
||||
// #57 (retained cs!=ww, deferred): scalar/ptr alloc keeps the
|
||||
|
||||
@@ -5076,6 +5076,15 @@ export fn dotchainresolve(c: *cgen, n: *syntax.node,
|
||||
// 2 = DST_GLOBAL — base = BX, reloaded via `LEAQ srcname(SB),
|
||||
// BX` with the same cadence as DST_PTR_LOCAL.
|
||||
// srcoff unused.
|
||||
// 3 = DST_PTR_SP — base = BX, reloaded via `MOVQ (SP), BX` with
|
||||
// the same cadence as DST_PTR_LOCAL. dst is the
|
||||
// alloc-heap base saved by cgalloc's `PUSHQ AX`
|
||||
// (top-of-stack); cgexpr is stack-balanced so
|
||||
// (SP) keeps pointing at it across the walk.
|
||||
// srcoff/srcname unused. C7c: nested struct/
|
||||
// array/tuple field VALUE in alloc(Outer{x =
|
||||
// Inner{..}}) now writes the inner leaves
|
||||
// instead of storing AX=0 over the inner slot.
|
||||
//
|
||||
// Param semantics (locked in here so the recursion contract is
|
||||
// clear):
|
||||
@@ -5139,6 +5148,9 @@ fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *syntax.node,
|
||||
emitsymname(c, srcname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
if (mode == 3) {
|
||||
emitline("\tMOVQ\t(SP), BX\n");
|
||||
};
|
||||
let zi: i32 = 0;
|
||||
for (zi + 8 <= totsize) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
@@ -5198,6 +5210,9 @@ fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *syntax.node,
|
||||
emitsymname(c, srcname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
if (mode == 3) {
|
||||
emitline("\tMOVQ\t(SP), BX\n");
|
||||
};
|
||||
cgwidentaggedstore(c, fi.tnode.type_: *syntax.tinfo,
|
||||
fieldnode.lhs, basereg,
|
||||
disp + fi.foff, fi.fsz);
|
||||
@@ -5293,6 +5308,9 @@ fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *syntax.node,
|
||||
emitsymname(c, srcname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
if (mode == 3) {
|
||||
emitline("\tMOVQ\t(SP), BX\n");
|
||||
};
|
||||
let full: i32 = cfsz / 8;
|
||||
let ci: i32 = 0;
|
||||
for (ci < full) {
|
||||
@@ -5370,11 +5388,13 @@ fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *syntax.node,
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(srcoff: i64);
|
||||
emitline("(BP), DX\n");
|
||||
} else { if (mode == 3) {
|
||||
emitline("\tMOVQ\t(SP), DX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, srcname);
|
||||
emitline("(SB), DX\n");
|
||||
};
|
||||
}; };
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg((disp + fi.foff): i64, "DX");
|
||||
emitline("\n");
|
||||
@@ -5464,6 +5484,9 @@ fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *syntax.node,
|
||||
emitsymname(c, srcname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
if (mode == 3) {
|
||||
emitline("\tMOVQ\t(SP), BX\n");
|
||||
};
|
||||
let eoff: i32 = disp + fi.foff + idx * esz;
|
||||
if (isfloatel) {
|
||||
emitline("\t");
|
||||
@@ -5503,6 +5526,9 @@ fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *syntax.node,
|
||||
emitsymname(c, srcname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
if (mode == 3) {
|
||||
emitline("\tMOVQ\t(SP), BX\n");
|
||||
};
|
||||
let eoff: i32 = disp + fi.foff + idx * esz;
|
||||
if (isfloatel) {
|
||||
emitline("\t");
|
||||
@@ -5556,11 +5582,13 @@ fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *syntax.node,
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(srcoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else { if (mode == 3) {
|
||||
emitline("\tMOVQ\t(SP), BX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, srcname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
}; };
|
||||
if (disp + fi.foff != 0) {
|
||||
emitline("\tADDQ\t$");
|
||||
emitint((disp + fi.foff): i64);
|
||||
@@ -5589,6 +5617,9 @@ fn cgstructlitfill(c: *cgen, si: *structinfo, lit: *syntax.node,
|
||||
emitsymname(c, srcname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
if (mode == 3) {
|
||||
emitline("\tMOVQ\t(SP), BX\n");
|
||||
};
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, fi.tnode)) { mov = "MOVSS"; };
|
||||
|
||||
Reference in New Issue
Block a user