wcc+w6c_ww: append() struct-element sources via split place-resolve (#49)
#49 (#35's single-element sibling, tranche-C pre-check PC2): the
struct-element append arm dispatched on SOURCE node kind — N_STRUCTLIT
(literal fill) and N_IDENT (local word-copy) only; every
place-resolvable chain died on the rule-7 fatal in BOTH stages,
including search()'s result-build line
`append(res, threads[best_idx].root_capture)` (regex.ha:819).
Wire those shapes with a SPLIT resolve around the grow (the #49
ruling): the chain's rvalues — deref-root pointer expr, index expr —
evaluate exactly once PRE-grow into @appendsroot/@appendsoff (an index
reading the slice header sees the pre-append len, Hare's argument
order), then only the BASE re-derives POST-grow from the live storage
and the stashed offsets land back on top, so a self-append source
re-roots in the post-realloc buffer. harec resolves an aggregate
source address wholly PRE-grow (gen.c: gen_load returns the address
for STORAGE_STRUCT, gen_store copies after rt.ensure) — a
use-after-free under a reclaiming allocator; per #263 we align to the
runtime-correct side, not the reference. A pointer ALIASING the grown
buffer keeps Hare's own stale-base hole (sound today only because
rt/malloc.ww never reclaims). Supported shapes are bounded: root
(local/global ident | deref) + at most one index + trailing direct
fields; all else stays on the #34 fatal, including CALL rvalues (the
#42-style bound, new reject row pins the text in both stages). The
N_STRUCTLIT/N_IDENT fast-paths keep their emission byte-identical.
806_append_place grows eight rows: indexed-field 56B capture (the
ha:819 shape, header readback), computed-index whole element,
deref-spine param pair, deref source, self-append ×33 crossing three
cap-doubling reallocs, the split-order semantics pin (a CALLED index
helper reading len must run once and see the PRE-grow len — the
pre-split emission failed exactly there), an element-kind ×
place-source matrix row (scalar/narrow/str/slice/tagged route via the
pre-existing arms — regression net), and the CALL-source reject. The
six fix rows verified FAILING against a pristine 796d41b build on
both drivers (loud #34 fatal, identically in cstage and w6c_ww —
there was no silent path at master); 70/70 fixtures green here
including per-row cs/ww asm byte-cmp.
Unblocks regex fold-2b tranche C (search) — PC2 was the lone
pre-check failure; PC1/PC3/PC4 passed at base.
This commit is contained in:
179
cmd/w6c/cgen.c
179
cmd/w6c/cgen.c
@@ -102,6 +102,13 @@ static int cg_ntagscr;
|
||||
* 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;
|
||||
/* #49 split-resolve stashes: the source chain's PRE-grow rvalues —
|
||||
* deref-root pointer value (@appendsroot) and scaled index offset
|
||||
* (@appendsoff) — must survive rt_ensure so the POST-grow base
|
||||
* re-derivation can add them back. Same per-fn name-cache discipline
|
||||
* as @appendscr. */
|
||||
static int cg_appendsroot;
|
||||
static int cg_appendsoff;
|
||||
/* FA1 (#15) @apphdrscr — 8B spill of the resolver-derived slice-header
|
||||
* ADDRESS for append() through a non-ident-local target (`append(*p,
|
||||
* v)`). rt_ensure may realloc .ptr but never moves the header, so the
|
||||
@@ -7114,6 +7121,112 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* the dst pointer (tagged: the #12 widen
|
||||
* choke-point cgexprs the value internally;
|
||||
* struct: literal fill / ident word-copy). */
|
||||
/* #49 (#35's single-element sibling): a
|
||||
* place-chain source (indexed field
|
||||
* `threads[i].root_capture` regex.ha:819,
|
||||
* deref spine, computed index) SPLITS
|
||||
* around the grow per the #49 ruling:
|
||||
* the chain's rvalues (deref-root
|
||||
* pointer expr, index expr) evaluate
|
||||
* exactly once PRE-grow — an index
|
||||
* reading the slice header sees the
|
||||
* pre-append len, Hare's argument
|
||||
* order — and only the BASE re-derives
|
||||
* POST-grow from the live storage, so
|
||||
* a self-append source re-roots in the
|
||||
* post-realloc buffer. harec resolves
|
||||
* an aggregate source address wholly
|
||||
* PRE-grow (gen.c: gen_load returns
|
||||
* the address for STORAGE_STRUCT,
|
||||
* gen_store copies after rt.ensure) —
|
||||
* a use-after-free under a reclaiming
|
||||
* allocator; per #263 we align to the
|
||||
* runtime-correct side, not the
|
||||
* reference. A pointer ALIASING the
|
||||
* grown buffer keeps Hare's own
|
||||
* stale-base hole (sound today only
|
||||
* because rt/malloc.ww never
|
||||
* reclaims). Spec not vendored
|
||||
* (ref/hare/docs = man pages only),
|
||||
* spec-silence assumed — re-verify if
|
||||
* the spec is ever vendored.
|
||||
* Bounded shapes: root (local/global
|
||||
* ident | deref) + at most one index
|
||||
* + trailing direct fields; all else
|
||||
* stays on the #34 fatal (incl. CALL
|
||||
* rvalues, the #42-style bound). */
|
||||
int aplace = 0, afld = 0, aidx_esz = 0;
|
||||
int abase_slice = 0, aroot_off = 0;
|
||||
Node *aroot = NULL, *aidx = NULL;
|
||||
if (el_struct && vn->kind != N_STRUCTLIT
|
||||
&& vn->kind != N_IDENT) {
|
||||
Node *ch = vn;
|
||||
int aok = 1;
|
||||
while (aok && ch->kind == N_DOT) {
|
||||
Node *ab = ch->lhs;
|
||||
Type *abu = ab ? type_chase_named(ab->type) : NULL;
|
||||
Tfield *af = NULL;
|
||||
if (abu && abu->kind == TY_STRUCT)
|
||||
for (Tfield *fl = abu->fields; fl; fl = fl->next)
|
||||
if (strcmp(fl->name, ch->str) == 0) { af = fl; break; }
|
||||
if (af == NULL) { aok = 0; break; }
|
||||
afld += (int)af->offset;
|
||||
ch = ab;
|
||||
}
|
||||
if (aok && ch->kind == N_INDEX) {
|
||||
Node *ab = ch->lhs;
|
||||
Type *abu = ab ? type_chase_named(ab->type) : NULL;
|
||||
Type *aet = type_chase_named(ch->type);
|
||||
if (ab == NULL || abu == NULL || aet == NULL
|
||||
|| (abu->kind != TY_SLICE && abu->kind != TY_ARRAY)) {
|
||||
aok = 0;
|
||||
} else {
|
||||
abase_slice = abu->kind == TY_SLICE;
|
||||
aidx_esz = (int)aet->size;
|
||||
aidx = ch->rhs;
|
||||
ch = ab;
|
||||
}
|
||||
}
|
||||
if (aok) {
|
||||
if (ch->kind == N_IDENT) {
|
||||
aroot_off = localfind(locals, ch->str);
|
||||
if (aroot_off == 0
|
||||
&& !let_islet(ch->str)
|
||||
&& !def_isstructdef(ch->str)
|
||||
&& !def_isarraydef(ch->str))
|
||||
aok = 0;
|
||||
} else if (!(ch->kind == N_UN && ch->op == TK_STAR)) {
|
||||
aok = 0;
|
||||
}
|
||||
}
|
||||
if (!aok)
|
||||
fatal("#34: append() struct element source "
|
||||
"shape unsupported (rule-7)");
|
||||
aroot = ch;
|
||||
if (aroot->kind == N_UN) {
|
||||
if (cg_appendsroot == 0)
|
||||
cg_appendsroot = local_alloc(c,
|
||||
&locals, "@appendsroot", 8,
|
||||
cg_frame);
|
||||
cgexpr(c, aroot->lhs, locals);
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, cg_appendsroot));
|
||||
}
|
||||
if (aidx != NULL) {
|
||||
if (cg_appendsoff == 0)
|
||||
cg_appendsoff = local_alloc(c,
|
||||
&locals, "@appendsoff", 8,
|
||||
cg_frame);
|
||||
cgexpr(c, aidx, locals);
|
||||
if (aidx_esz > 1) {
|
||||
ins2(c, A_MOVQ, aimm(aidx_esz), areg(D_CX));
|
||||
ins2(c, A_IMULQ, areg(D_CX), areg(D_AX));
|
||||
}
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, cg_appendsoff));
|
||||
}
|
||||
aplace = 1;
|
||||
}
|
||||
cg_append_grow(c, sn_direct, sn_off,
|
||||
sn_scr, esz);
|
||||
cg_append_slot(c, sn_direct, sn_off,
|
||||
@@ -7163,6 +7276,70 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (aplace) {
|
||||
/* phase 2: dst slot to
|
||||
* @appendscr, base from the
|
||||
* live storage, stashed
|
||||
* offsets back on top. */
|
||||
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));
|
||||
if (aroot->kind == N_IDENT) {
|
||||
if (aroot_off != 0)
|
||||
ins2(c, A_LEAQ,
|
||||
amem(D_BP, aroot_off),
|
||||
areg(D_BX));
|
||||
else
|
||||
ins2(c, A_LEAQ,
|
||||
masym(c, aroot->str),
|
||||
areg(D_BX));
|
||||
} else {
|
||||
ins2(c, A_MOVQ,
|
||||
amem(D_BP, cg_appendsroot),
|
||||
areg(D_BX));
|
||||
}
|
||||
if (aidx != NULL) {
|
||||
if (abase_slice)
|
||||
ins2(c, A_MOVQ,
|
||||
amem(D_BX, 0),
|
||||
areg(D_BX));
|
||||
ins2(c, A_MOVQ,
|
||||
amem(D_BP, cg_appendsoff),
|
||||
areg(D_AX));
|
||||
ins2(c, A_ADDQ, areg(D_AX),
|
||||
areg(D_BX));
|
||||
}
|
||||
if (afld != 0)
|
||||
ins2(c, A_ADDQ, aimm(afld),
|
||||
areg(D_BX));
|
||||
ins2(c, A_MOVQ,
|
||||
amem(D_BP, cg_appendscr),
|
||||
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;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
fatal("#34: append() struct element source "
|
||||
"shape unsupported (rule-7)");
|
||||
}
|
||||
@@ -11999,6 +12176,8 @@ cgfn(Cg *c, FILE *out, Node *fn)
|
||||
cg_tagbase_sz = 0;
|
||||
cg_ntagscr = 0;
|
||||
cg_appendscr = 0;
|
||||
cg_appendsroot = 0;
|
||||
cg_appendsoff = 0;
|
||||
cg_sret_arg_off = 0;
|
||||
cg_sret_dest_off = 0;
|
||||
cg_sret_dest_sym = NULL;
|
||||
|
||||
Reference in New Issue
Block a user