cgen: #50 append/insert tagged-dst VALUE boxes PRE-grow, both stages
The #34 value-store dispatch's tagged arm ran the widen choke-point (cg_widen_tagged_store / cgwidentaggedstore — which cgexprs the value internally) AFTER cg_append_grow, so a value expression reading the destination (xs.len) saw post-grow state. Hare evaluates arguments before the call's effects; the scalar/str/slice arms already ordered value-first. insert() inherited the bug through its desugar-to-append (9861f73) — one boxing choke-point, so this is the whole fix. Box the value into a fresh per-site @apptagscr frame scratch pre-grow (zero, BP-rooted widen store), then raw-copy the finished box into the new slot post-grow. Consumer evidence: regex fold-5b's {,0} rows (reviewer-5b's mutant; ken's corrected-root matrix /tmp/ken_silent, f50v4_tagged exit 15 both stages, byte-id, gate-blind). lib/regex.ww:643-647's pre-bound split_target workaround comment GRADUATES with this commit; the comment update itself rides the next regex touch per the standing rule. test/807: 57 -> 84 fixtures. New rows: tagged_pregrow_val (f50v4 exact), scalar_pregrow_val (no-regress control), tagged_selfref_val, tagged_str_payload, tagged_regex_minrep (the {,0} shape standalone), tagged_append_pregrow_val (direct append, the fix site), tagged_realloc_selfref_loop (ken k50a: old-base read across actual rt_ensure base moves), tagged_seq_positions (ken k50b: sequenced inserts at 0/mid, each len-reading) and tagged_void_variant (ken k50c: tag-only box through the fresh scratch). Mutation ate8977a4fails exactly the five eval-order-discriminating tagged rows, both stages. The STRUCT-LITERAL value arm keeps its post-grow field-expr fill — same eval-order class, different arm; filed (ww-core #59), cited at-site, not folded.
This commit is contained in:
@@ -5399,10 +5399,14 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
continue;
|
||||
};
|
||||
if (eltagged || elstruct) {
|
||||
// #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).
|
||||
// #34: no register form survives rt_ensure for these.
|
||||
// struct: grow FIRST, then fill through the dst pointer
|
||||
// (literal fill / ident word-copy). tagged: #50 — the
|
||||
// #12 widen choke-point cgexprs the value internally,
|
||||
// so boxing must run PRE-grow (Hare's argument order:
|
||||
// a `xs.len` read in v sees the pre-append len, like
|
||||
// the scalar arm); box into a frame scratch, grow,
|
||||
// raw-copy the finished box in.
|
||||
// #49 (#35's single-element sibling): a place-chain
|
||||
// source (indexed field `threads[i].root_capture`
|
||||
// regex.ha:819, deref spine, computed index) SPLITS
|
||||
@@ -5528,14 +5532,43 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
};
|
||||
aplace = true;
|
||||
};
|
||||
cgappendgrow(c, sndirect, sn_off, snscr, esz);
|
||||
cgappendslot(c, sndirect, sn_off, snscr, esz, "BX");
|
||||
if (eltagged) {
|
||||
cgwidentaggedstore(c, esubti, vn, "BX", 0, esz);
|
||||
// Fresh slot per SITE, not the shared per-size
|
||||
// scratch: the box must stay live across
|
||||
// rt_ensure, and a nested append inside the
|
||||
// value expression would clobber a dedup'd
|
||||
// slot (the @apphdrscr rationale; #25/#31).
|
||||
let tgscr: i32 = localalloc(c, "@apptagscr", esz, nil);
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
let zk: i32 = 0;
|
||||
for (zk < esz) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((tgscr + zk): i64);
|
||||
emitline("(BP)\n");
|
||||
zk += 8;
|
||||
};
|
||||
cgwidentaggedstore(c, esubti, vn, "BP", tgscr, esz);
|
||||
cgappendgrow(c, sndirect, sn_off, snscr, esz);
|
||||
cgappendslot(c, sndirect, sn_off, snscr, esz, "BX");
|
||||
let ck: i32 = 0;
|
||||
for (ck < esz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((tgscr + ck): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg(ck: i64, "BX");
|
||||
emitline("\n");
|
||||
ck += 8;
|
||||
};
|
||||
vn = vn.next;
|
||||
continue;
|
||||
};
|
||||
cgappendgrow(c, sndirect, sn_off, snscr, esz);
|
||||
cgappendslot(c, sndirect, sn_off, snscr, esz, "BX");
|
||||
if (vn.kind == nkind.N_STRUCTLIT) {
|
||||
// #59 (#50's eval-order kin): the literal's field
|
||||
// exprs still eval POST-grow here — filed, not
|
||||
// folded.
|
||||
let scroff: i32 = localadd(c, "@appendscr", 8, nil);
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff(scroff: i64);
|
||||
|
||||
Reference in New Issue
Block a user