wcc/cgen: #59 append/insert struct-literal value eval-order — eval-to-scratch pre-grow + precise copy (both-stage)
append/insert of a struct-LITERAL value evaluated the literal's field
exprs AFTER the grow, so a field reading the destination (e.g. len(xs))
saw the grown length. Both stages, #263 gate-blind (cs==ww byte-identical,
both wrong — runtime is the only net). #50 fixed the scalar/boxing value
arm; the struct-lit arm still post-grew.
Fix (mirror #50, both stages): resolve the struct, fill the literal into a
fresh per-site scratch (@appendstructscr, sized esz, survives rt_ensure +
nested-append clobber) BEFORE the grow, then copy scratch -> post-grow slot.
The copy uses the precise descending 8/4/2/1 ladder (the proven N_IDENT
struct arm directly below), NOT a raw 8B-word block copy: a struct's size
rounds to maxalign (check.c:916), so a sub-8B struct packs at a 4/2/1B
slice stride and an 8B copy over-writes past the slot — at a power-of-2
capacity boundary that clobbers the adjacent allocation (heap corruption,
both stages). The ladder never reads past esz (no uninit high bytes) nor
writes past the slot; esz=8 stays a single MOVQ (byte-id preserved).
insert() rides by construction: both stages desugar it to append and
re-dispatch into this arm. The #49 aplace path already uses the precise
ladder (verified, not exposed). #59 closes the last composite-value
eval-order hole in append/insert.
Pin: 946_append_structlit_evalorder_run — append / insert / narrow-neighbor
(i32-field at the cap boundary with an adjacent-allocation survival assert)
rows, each base-fail at 39432f7 and post-pass with cs==ww byte-id.
This commit is contained in:
@@ -8570,26 +8570,62 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (vn->kind == N_STRUCTLIT) {
|
||||
/* #59 (#50's eval-order kin):
|
||||
* fill the literal into a fresh
|
||||
* per-SITE scratch (must survive
|
||||
* rt_ensure + a nested append in a
|
||||
* field expr; the @apptagscr
|
||||
* rationale, #25/#31) BEFORE the
|
||||
* grow, so the field exprs see the
|
||||
* pre-grow len. Then grow, slot,
|
||||
* raw-copy scratch->slot (mirror
|
||||
* the #50 tagged arm above). */
|
||||
int st_scr = local_alloc(c,
|
||||
&locals, "@appendstructscr",
|
||||
esz, cg_frame);
|
||||
cg_structlit_fill_bp(c, &locals,
|
||||
esubu, vn, st_scr);
|
||||
cg_append_grow(c, sn_direct, sn_off,
|
||||
sn_scr, esz);
|
||||
cg_append_slot(c, sn_direct, sn_off,
|
||||
sn_scr, esz, D_BX);
|
||||
/* Descending 8/4/2/1 ladder, not an
|
||||
* 8B-word loop: a plain struct's esz
|
||||
* rounds to maxalign (check.c:916),
|
||||
* not 8, so a sub-8B / non-8B-multiple
|
||||
* element packs at its own stride —
|
||||
* an 8B copy of the last slot writes
|
||||
* past the slice buffer (the tagged
|
||||
* arm above is safe only because boxes
|
||||
* are 8B-padded; #59). Mirrors the
|
||||
* N_IDENT source arm below. */
|
||||
int ck = 0;
|
||||
for (; ck + 8 <= esz; ck += 8) {
|
||||
ins2(c, A_MOVQ, amem(D_BP, st_scr + ck), areg(D_AX));
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BX, ck));
|
||||
}
|
||||
if (ck + 4 <= esz) {
|
||||
ins2(c, A_MOVL, amem(D_BP, st_scr + ck), areg(D_AX));
|
||||
ins2(c, A_MOVL, areg(D_AX), amem(D_BX, ck));
|
||||
ck += 4;
|
||||
}
|
||||
if (ck + 2 <= esz) {
|
||||
ins2(c, A_MOVW, amem(D_BP, st_scr + ck), areg(D_AX));
|
||||
ins2(c, A_MOVW, areg(D_AX), amem(D_BX, ck));
|
||||
ck += 2;
|
||||
}
|
||||
if (ck + 1 <= esz) {
|
||||
ins2(c, A_MOVB, amem(D_BP, st_scr + ck), areg(D_AX));
|
||||
ins2(c, A_MOVB, areg(D_AX), amem(D_BX, ck));
|
||||
ck += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
cg_append_grow(c, sn_direct, sn_off,
|
||||
sn_scr, esz);
|
||||
cg_append_slot(c, sn_direct, sn_off,
|
||||
sn_scr, esz, D_BX);
|
||||
if (vn->kind == N_STRUCTLIT) {
|
||||
/* #59 (#50's eval-order kin):
|
||||
* the literal's field exprs
|
||||
* still eval POST-grow here —
|
||||
* filed, not folded. */
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user