w6c+w6c_ww: append() stores the full element width per element kind (fix #34)
Both stages lowered the append element store as one sized mov from AX — correct only for scalars <= 8B. A str/slice element kept only .ptr (byte-id-blind), a tagged element got its raw payload written into the tag slot (the #12 pathology, no boxing), a struct element kept only its first qword. wwstage additionally fed rt_ensure membsz from bare elemsizeof, whose 8-sentinel under-allocated and mis-strided named tagged/struct elements (the #8 family; cs!=ww on the SI imm + stride). Fix, keyed on the DECLARED slice local's element type (cstage su->sub->size as before; wwstage elemsizeofc off the stamped tnode — never the value node, the #25/#31 esz=0 trap), applied to both the single-value and spread bodies (2 arms x 2 stages): - scalar 1/2/4/8: untouched (u8 asm byte-identical to pre-fix). - str/slice: AX/BX/CX pushed across rt_ensure, dst in DX (BX holds the element .len after the pops — the #24 register discipline), 3-word store. - tagged: grow first, dst -> BX, box via the #12 widen choke-point (cg_widen_tagged_store / cgwidentaggedstore via_outer). - struct: grow first; literal -> dst spilled to per-fn @appendscr (cached on cstage to mirror wwstage's @-prefix localadd dedup) + structlit fill DST_PTR_LOCAL; local ident -> word-copy; any other source shape is a rule-7 loud-stop, never a silent scalar fall-through. struct-from-call deferred. - spread: the source element is already a fully-formed T (tag included), so the wide arm grows first and whole-width word-copies &items[i] -> dst, recomputing both addresses from the slice headers after the possibly-reallocating rt_ensure. The elemsizeofc swap also corrects the named-scalar-alias membsz (wwstage fed SI=$8 where cstage fed $4); no in-tree consumer appended to such a slice, so nothing was riding the wrong 8 (lib/selfhost append sites are all u8). Test 800_append_wide_elem: 13 rows (runtime readback per kind, 2-append realloc survival, spread str+tagged, @appendscr dedup, enum-alias esz, loud-stop build-fail) + per-row cs==ww byte-id, which subsumes the frame canary.
This commit is contained in:
171
cmd/w6c/cgen.c
171
cmd/w6c/cgen.c
@@ -87,6 +87,12 @@ static int cg_tagbase;
|
||||
static int cg_tagbase_sz;
|
||||
static int cg_tagscr;
|
||||
static int cg_tagscr_sz;
|
||||
/* #34: per-fn @appendscr — 8B dst-pointer spill for the append()
|
||||
* struct-literal element fill (cg_structlit_fill DST_PTR_LOCAL needs
|
||||
* a BP-rooted slot to reload BX from across its internal cgexprs).
|
||||
* Cached per name per fn to mirror wwstage's localadd `@`-prefix
|
||||
* dedup, else two struct appends in one fn diverge the frame. */
|
||||
static int cg_appendscr;
|
||||
/* System V AMD64 sret discipline (task #23). Plain TY_STRUCT returns
|
||||
* with size > 24B are passed via a hidden first-arg pointer (RDI) to
|
||||
* a caller-prealloc dest; the callee writes through that pointer and
|
||||
@@ -6335,6 +6341,21 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
int sn_off = (sn->kind == N_IDENT)
|
||||
? localfind(locals, sn->str) : 0;
|
||||
int store_op = fldstoreop(esub, esz);
|
||||
/* #34 element-kind store dispatch: the scalar 1-word
|
||||
* store below silently gutted every wide element
|
||||
* (str/slice 24B header, tagged box, struct body).
|
||||
* Mirrors the #270/#12/#20 array-literal element
|
||||
* dispatch (cg_arrlit_fill_bp). */
|
||||
Type *esubu = type_chase_named(esub);
|
||||
int el_str = type_isstr(esub);
|
||||
int el_slice = type_isslice(esub);
|
||||
int el_tagged = esubu && esubu->kind == TY_TAGGED;
|
||||
int el_struct = esubu && esubu->kind == TY_STRUCT;
|
||||
int el_wide = el_str || el_slice || el_tagged ||
|
||||
el_struct;
|
||||
if (!el_wide && esz > 8)
|
||||
fatal("#34: append() element kind "
|
||||
"unsupported (rule-7)");
|
||||
for (Node *vn = sn->next; vn; vn = vn->next) {
|
||||
if (vn->kind == N_SPREAD &&
|
||||
vn->lhs && vn->lhs->kind == N_IDENT) {
|
||||
@@ -6350,6 +6371,61 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
ins2(c, A_MOVQ, amem(D_BP, it_off + 8), areg(D_DX));
|
||||
ins2(c, A_CMPQ, areg(D_DX), areg(D_CX));
|
||||
ins1(c, A_JGE, abranch(le));
|
||||
if (el_wide) {
|
||||
/* #34: a spread element is already a
|
||||
* fully-formed T in the source slice
|
||||
* (tag included), so a whole-width
|
||||
* word-copy is the store — no boxing.
|
||||
* Grow FIRST: rt_ensure may realloc,
|
||||
* so both addresses are recomputed
|
||||
* from the slice headers after the
|
||||
* call (i reloads from the counter
|
||||
* slot; CX was clobbered). */
|
||||
ins2(c, A_ADDQ, aimm(1), amem(D_BP, sn_off + 8));
|
||||
ins2(c, A_LEAQ, amem(D_BP, sn_off), areg(D_DI));
|
||||
ins2(c, A_MOVQ, aimm(esz), areg(D_SI));
|
||||
ins1(c, A_CALL, masym(c, "rt_ensure"));
|
||||
ins2(c, A_MOVQ, amem(D_SP, 0), areg(D_CX));
|
||||
if (esz > 1) {
|
||||
ins2(c, A_MOVQ, aimm(esz), areg(D_AX));
|
||||
ins2(c, A_IMULQ, areg(D_AX), areg(D_CX));
|
||||
}
|
||||
ins2(c, A_MOVQ, amem(D_BP, it_off), areg(D_BX));
|
||||
ins2(c, A_ADDQ, areg(D_CX), areg(D_BX));
|
||||
ins2(c, A_MOVQ, amem(D_BP, sn_off + 8), areg(D_CX));
|
||||
ins2(c, A_SUBQ, aimm(1), areg(D_CX));
|
||||
if (esz > 1) {
|
||||
ins2(c, A_MOVQ, aimm(esz), areg(D_AX));
|
||||
ins2(c, A_IMULQ, areg(D_AX), areg(D_CX));
|
||||
}
|
||||
ins2(c, A_MOVQ, amem(D_BP, sn_off), areg(D_DX));
|
||||
ins2(c, A_ADDQ, areg(D_CX), areg(D_DX));
|
||||
int k = 0;
|
||||
for (; k + 8 <= esz; k += 8) {
|
||||
ins2(c, A_MOVQ, amem(D_BX, k), areg(D_AX));
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_DX, k));
|
||||
}
|
||||
if (k + 4 <= esz) {
|
||||
ins2(c, A_MOVL, amem(D_BX, k), areg(D_AX));
|
||||
ins2(c, A_MOVL, areg(D_AX), amem(D_DX, k));
|
||||
k += 4;
|
||||
}
|
||||
if (k + 2 <= esz) {
|
||||
ins2(c, A_MOVW, amem(D_BX, k), areg(D_AX));
|
||||
ins2(c, A_MOVW, areg(D_AX), amem(D_DX, k));
|
||||
k += 2;
|
||||
}
|
||||
if (k + 1 <= esz) {
|
||||
ins2(c, A_MOVB, amem(D_BX, k), areg(D_AX));
|
||||
ins2(c, A_MOVB, areg(D_AX), amem(D_DX, k));
|
||||
k += 1;
|
||||
}
|
||||
ins2(c, A_ADDQ, aimm(1), amem(D_SP, 0));
|
||||
ins1(c, A_JMP, abranch(ll));
|
||||
label(c, le);
|
||||
ins2(c, A_ADDQ, aimm(8), areg(D_SP));
|
||||
continue;
|
||||
}
|
||||
/* AX = items.ptr[i] */
|
||||
ins2(c, A_MOVQ, amem(D_BP, it_off), areg(D_BX));
|
||||
if (esz > 1) {
|
||||
@@ -6381,6 +6457,100 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
ins2(c, A_ADDQ, aimm(8), areg(D_SP));
|
||||
continue;
|
||||
}
|
||||
if (el_str || el_slice) {
|
||||
/* #34: 24B {ptr,len,cap} header. cgexpr
|
||||
* leaves AX/BX/CX; all three must survive
|
||||
* rt_ensure. dst lands in DX, NOT BX — the
|
||||
* pops put the element .len back in BX
|
||||
* (the #24 register discipline). */
|
||||
cgexpr(c, vn, locals);
|
||||
ins1(c, A_PUSHQ, areg(D_AX));
|
||||
ins1(c, A_PUSHQ, areg(D_BX));
|
||||
ins1(c, A_PUSHQ, areg(D_CX));
|
||||
ins2(c, A_ADDQ, aimm(1), amem(D_BP, sn_off + 8));
|
||||
ins2(c, A_LEAQ, amem(D_BP, sn_off), areg(D_DI));
|
||||
ins2(c, A_MOVQ, aimm(esz), areg(D_SI));
|
||||
ins1(c, A_CALL, masym(c, "rt_ensure"));
|
||||
ins2(c, A_MOVQ, amem(D_BP, sn_off + 8), areg(D_CX));
|
||||
ins2(c, A_SUBQ, aimm(1), areg(D_CX));
|
||||
ins2(c, A_MOVQ, aimm(esz), areg(D_AX));
|
||||
ins2(c, A_IMULQ, areg(D_AX), areg(D_CX));
|
||||
ins2(c, A_MOVQ, amem(D_BP, sn_off), areg(D_DX));
|
||||
ins2(c, A_ADDQ, areg(D_CX), areg(D_DX));
|
||||
ins1(c, A_POPQ, areg(D_CX));
|
||||
ins1(c, A_POPQ, areg(D_BX));
|
||||
ins1(c, A_POPQ, areg(D_AX));
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_DX, 0));
|
||||
ins2(c, A_MOVQ, areg(D_BX), amem(D_DX, 8));
|
||||
ins2(c, A_MOVQ, areg(D_CX), amem(D_DX, 16));
|
||||
continue;
|
||||
}
|
||||
if (el_tagged || el_struct) {
|
||||
/* #34: no register form survives rt_ensure
|
||||
* for these — grow FIRST, then fill through
|
||||
* the dst pointer (tagged: the #12 widen
|
||||
* choke-point cgexprs the value internally;
|
||||
* struct: literal fill / ident word-copy). */
|
||||
ins2(c, A_ADDQ, aimm(1), amem(D_BP, sn_off + 8));
|
||||
ins2(c, A_LEAQ, amem(D_BP, sn_off), areg(D_DI));
|
||||
ins2(c, A_MOVQ, aimm(esz), areg(D_SI));
|
||||
ins1(c, A_CALL, masym(c, "rt_ensure"));
|
||||
ins2(c, A_MOVQ, amem(D_BP, sn_off + 8), areg(D_CX));
|
||||
ins2(c, A_SUBQ, aimm(1), areg(D_CX));
|
||||
if (esz > 1) {
|
||||
ins2(c, A_MOVQ, aimm(esz), areg(D_AX));
|
||||
ins2(c, A_IMULQ, areg(D_AX), areg(D_CX));
|
||||
}
|
||||
ins2(c, A_MOVQ, amem(D_BP, sn_off), areg(D_BX));
|
||||
ins2(c, A_ADDQ, areg(D_CX), areg(D_BX));
|
||||
if (el_tagged) {
|
||||
cg_widen_tagged_store(c, &locals,
|
||||
esub, vn, D_BX, 0, esz);
|
||||
continue;
|
||||
}
|
||||
if (vn->kind == N_STRUCTLIT) {
|
||||
if (cg_appendscr == 0)
|
||||
cg_appendscr = local_alloc(c,
|
||||
&locals, "@appendscr", 8,
|
||||
cg_frame);
|
||||
ins2(c, A_MOVQ, areg(D_BX),
|
||||
amem(D_BP, cg_appendscr));
|
||||
cg_structlit_fill(c, &locals, esubu,
|
||||
vn, DST_PTR_LOCAL, cg_appendscr,
|
||||
NULL, 0);
|
||||
continue;
|
||||
}
|
||||
if (vn->kind == N_IDENT) {
|
||||
int soff = localfind(locals, vn->str);
|
||||
if (soff == 0)
|
||||
fatal("#34: append() struct "
|
||||
"element source ident is "
|
||||
"not a local (rule-7)");
|
||||
int k = 0;
|
||||
for (; k + 8 <= esz; k += 8) {
|
||||
ins2(c, A_MOVQ, amem(D_BP, soff + k), areg(D_AX));
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BX, k));
|
||||
}
|
||||
if (k + 4 <= esz) {
|
||||
ins2(c, A_MOVL, amem(D_BP, soff + k), areg(D_AX));
|
||||
ins2(c, A_MOVL, areg(D_AX), amem(D_BX, k));
|
||||
k += 4;
|
||||
}
|
||||
if (k + 2 <= esz) {
|
||||
ins2(c, A_MOVW, amem(D_BP, soff + k), areg(D_AX));
|
||||
ins2(c, A_MOVW, areg(D_AX), amem(D_BX, k));
|
||||
k += 2;
|
||||
}
|
||||
if (k + 1 <= esz) {
|
||||
ins2(c, A_MOVB, amem(D_BP, soff + k), areg(D_AX));
|
||||
ins2(c, A_MOVB, areg(D_AX), amem(D_BX, k));
|
||||
k += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
fatal("#34: append() struct element source "
|
||||
"shape unsupported (rule-7)");
|
||||
}
|
||||
cgexpr(c, vn, locals); /* val → AX */
|
||||
ins1(c, A_PUSHQ, areg(D_AX));
|
||||
ins2(c, A_ADDQ, aimm(1), amem(D_BP, sn_off + 8));
|
||||
@@ -10839,6 +11009,7 @@ cgfn(Cg *c, FILE *out, Node *fn)
|
||||
cg_tagbase_sz = 0;
|
||||
cg_tagscr = 0;
|
||||
cg_tagscr_sz = 0;
|
||||
cg_appendscr = 0;
|
||||
cg_sret_arg_off = 0;
|
||||
cg_sret_dest_off = 0;
|
||||
cg_sret_dest_sym = NULL;
|
||||
|
||||
Reference in New Issue
Block a user