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
|
* Cached per name per fn to mirror wwstage's localadd `@`-prefix
|
||||||
* dedup, else two struct appends in one fn diverge the frame. */
|
* dedup, else two struct appends in one fn diverge the frame. */
|
||||||
static int cg_appendscr;
|
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
|
/* FA1 (#15) @apphdrscr — 8B spill of the resolver-derived slice-header
|
||||||
* ADDRESS for append() through a non-ident-local target (`append(*p,
|
* ADDRESS for append() through a non-ident-local target (`append(*p,
|
||||||
* v)`). rt_ensure may realloc .ptr but never moves the header, so the
|
* 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
|
* the dst pointer (tagged: the #12 widen
|
||||||
* choke-point cgexprs the value internally;
|
* choke-point cgexprs the value internally;
|
||||||
* struct: literal fill / ident word-copy). */
|
* 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,
|
cg_append_grow(c, sn_direct, sn_off,
|
||||||
sn_scr, esz);
|
sn_scr, esz);
|
||||||
cg_append_slot(c, sn_direct, sn_off,
|
cg_append_slot(c, sn_direct, sn_off,
|
||||||
@@ -7163,6 +7276,70 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
}
|
}
|
||||||
continue;
|
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 "
|
fatal("#34: append() struct element source "
|
||||||
"shape unsupported (rule-7)");
|
"shape unsupported (rule-7)");
|
||||||
}
|
}
|
||||||
@@ -11999,6 +12176,8 @@ cgfn(Cg *c, FILE *out, Node *fn)
|
|||||||
cg_tagbase_sz = 0;
|
cg_tagbase_sz = 0;
|
||||||
cg_ntagscr = 0;
|
cg_ntagscr = 0;
|
||||||
cg_appendscr = 0;
|
cg_appendscr = 0;
|
||||||
|
cg_appendsroot = 0;
|
||||||
|
cg_appendsoff = 0;
|
||||||
cg_sret_arg_off = 0;
|
cg_sret_arg_off = 0;
|
||||||
cg_sret_dest_off = 0;
|
cg_sret_dest_off = 0;
|
||||||
cg_sret_dest_sym = NULL;
|
cg_sret_dest_sym = NULL;
|
||||||
|
|||||||
@@ -24965,6 +24965,131 @@ fn cgappend(c: *cgen, n: *node) void = {
|
|||||||
// grow FIRST, then fill through the dst pointer
|
// grow FIRST, then fill through the dst pointer
|
||||||
// (tagged: the #12 widen choke-point cgexprs the value
|
// (tagged: the #12 widen choke-point cgexprs the value
|
||||||
// internally; struct: literal fill / ident word-copy).
|
// 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 loud exit (incl. CALL rvalues, the
|
||||||
|
// #42-style bound).
|
||||||
|
let aplace: bool = false;
|
||||||
|
let afld: i32 = 0;
|
||||||
|
let aidxesz: i32 = 0;
|
||||||
|
let abaseslice: bool = false;
|
||||||
|
let arootoff: i32 = 0;
|
||||||
|
let asroot: i32 = 0;
|
||||||
|
let asoff: i32 = 0;
|
||||||
|
let aroot: *node = nil;
|
||||||
|
let aidx: *node = nil;
|
||||||
|
if (elstruct && vn.kind != nkind.N_STRUCTLIT && vn.kind != nkind.N_IDENT) {
|
||||||
|
let ch: *node = vn;
|
||||||
|
let aok: bool = true;
|
||||||
|
for (aok && ch.kind == nkind.N_DOT) {
|
||||||
|
let ab: *node = ch.lhs;
|
||||||
|
let af: *tfield = nil;
|
||||||
|
if (ab != nil) {
|
||||||
|
let abu: *tinfo = ab.type_: *tinfo;
|
||||||
|
for (abu != nil && abu.kind == tykind.TY_NAMED) { abu = abu.under; };
|
||||||
|
if (abu != nil && abu.kind == tykind.TY_STRUCT) {
|
||||||
|
let fl: *tfield = abu.fields;
|
||||||
|
for (fl != nil) {
|
||||||
|
if (streq(fl.name, ch.str)) { af = fl; break; };
|
||||||
|
fl = fl.tnext;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (af == nil) { aok = false; break; };
|
||||||
|
afld += af.offset: i32;
|
||||||
|
ch = ab;
|
||||||
|
};
|
||||||
|
if (aok && ch.kind == nkind.N_INDEX) {
|
||||||
|
let ab: *node = ch.lhs;
|
||||||
|
let aet: *tinfo = ch.type_: *tinfo;
|
||||||
|
for (aet != nil && aet.kind == tykind.TY_NAMED) { aet = aet.under; };
|
||||||
|
let abu: *tinfo = nil;
|
||||||
|
if (ab != nil) {
|
||||||
|
abu = ab.type_: *tinfo;
|
||||||
|
for (abu != nil && abu.kind == tykind.TY_NAMED) { abu = abu.under; };
|
||||||
|
};
|
||||||
|
if (ab == nil || abu == nil || aet == nil
|
||||||
|
|| (abu.kind != tykind.TY_SLICE && abu.kind != tykind.TY_ARRAY)) {
|
||||||
|
aok = false;
|
||||||
|
} else {
|
||||||
|
abaseslice = abu.kind == tykind.TY_SLICE;
|
||||||
|
aidxesz = aet.size: i32;
|
||||||
|
aidx = ch.rhs;
|
||||||
|
ch = ab;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (aok) {
|
||||||
|
if (ch.kind == nkind.N_IDENT) {
|
||||||
|
let rl: *local = localfindnode(c, ch.str);
|
||||||
|
if (rl != nil) {
|
||||||
|
arootoff = rl.off;
|
||||||
|
} else {
|
||||||
|
let gok: bool = isletvar(c, ch.str);
|
||||||
|
if (!gok) {
|
||||||
|
if (defvarstructinfo(c, ch.str) != nil) { gok = true; };
|
||||||
|
};
|
||||||
|
if (!gok) {
|
||||||
|
let dtn: *node = defvartnode(c, ch.str);
|
||||||
|
if (dtn != nil) {
|
||||||
|
if (dtn.kind == nkind.N_TARRAY) { gok = true; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (!gok) { aok = false; };
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
if (ch.kind != nkind.N_UN || ch.op != tkind.TK_STAR) {
|
||||||
|
aok = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (!aok) {
|
||||||
|
let m34p: str = "#34: append() struct element source shape unsupported (rule-7)\n";
|
||||||
|
os.write(2, m34p.ptr, m34p.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
aroot = ch;
|
||||||
|
if (aroot.kind == nkind.N_UN) {
|
||||||
|
asroot = localadd(c, "@appendsroot", 8, nil);
|
||||||
|
cgexpr(c, aroot.lhs);
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitoff(asroot: i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
};
|
||||||
|
if (aidx != nil) {
|
||||||
|
asoff = localadd(c, "@appendsoff", 8, nil);
|
||||||
|
cgexpr(c, aidx);
|
||||||
|
if (aidxesz > 1) {
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(aidxesz: i64);
|
||||||
|
emitline(", CX\n");
|
||||||
|
emitline("\tIMULQ\tCX, AX\n");
|
||||||
|
};
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitoff(asoff: i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
};
|
||||||
|
aplace = true;
|
||||||
|
};
|
||||||
cgappendgrow(c, sndirect, sn_off, snscr, esz);
|
cgappendgrow(c, sndirect, sn_off, snscr, esz);
|
||||||
cgappendslot(c, sndirect, sn_off, snscr, esz, "BX");
|
cgappendslot(c, sndirect, sn_off, snscr, esz, "BX");
|
||||||
if (eltagged) {
|
if (eltagged) {
|
||||||
@@ -25043,6 +25168,86 @@ fn cgappend(c: *cgen, n: *node) void = {
|
|||||||
vn = vn.next;
|
vn = vn.next;
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
if (aplace) {
|
||||||
|
// phase 2: dst slot to @appendscr, base from
|
||||||
|
// the live storage, stashed offsets back on
|
||||||
|
// top.
|
||||||
|
let pscroff: i32 = localadd(c, "@appendscr", 8, nil);
|
||||||
|
emitline("\tMOVQ\tBX, ");
|
||||||
|
emitoff(pscroff: i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
if (aroot.kind == nkind.N_IDENT) {
|
||||||
|
if (arootoff != 0) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitoff(arootoff: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, aroot.str);
|
||||||
|
emitline("(SB), BX\n");
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(asroot: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
};
|
||||||
|
if (aidx != nil) {
|
||||||
|
if (abaseslice) {
|
||||||
|
emitline("\tMOVQ\t(BX), BX\n");
|
||||||
|
};
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(asoff: i64);
|
||||||
|
emitline("(BP), AX\n");
|
||||||
|
emitline("\tADDQ\tAX, BX\n");
|
||||||
|
};
|
||||||
|
if (afld != 0) {
|
||||||
|
emitline("\tADDQ\t$");
|
||||||
|
emitint(afld: i64);
|
||||||
|
emitline(", BX\n");
|
||||||
|
};
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(pscroff: i64);
|
||||||
|
emitline("(BP), DX\n");
|
||||||
|
let pk: i32 = 0;
|
||||||
|
for (pk + 8 <= esz) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitdispreg(pk: i64, "BX");
|
||||||
|
emitline(", AX\n");
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitdispreg(pk: i64, "DX");
|
||||||
|
emitline("\n");
|
||||||
|
pk += 8;
|
||||||
|
};
|
||||||
|
if (pk + 4 <= esz) {
|
||||||
|
emitline("\tMOVL\t");
|
||||||
|
emitdispreg(pk: i64, "BX");
|
||||||
|
emitline(", AX\n");
|
||||||
|
emitline("\tMOVL\tAX, ");
|
||||||
|
emitdispreg(pk: i64, "DX");
|
||||||
|
emitline("\n");
|
||||||
|
pk += 4;
|
||||||
|
};
|
||||||
|
if (pk + 2 <= esz) {
|
||||||
|
emitline("\tMOVW\t");
|
||||||
|
emitdispreg(pk: i64, "BX");
|
||||||
|
emitline(", AX\n");
|
||||||
|
emitline("\tMOVW\tAX, ");
|
||||||
|
emitdispreg(pk: i64, "DX");
|
||||||
|
emitline("\n");
|
||||||
|
pk += 2;
|
||||||
|
};
|
||||||
|
if (pk + 1 <= esz) {
|
||||||
|
emitline("\tMOVB\t");
|
||||||
|
emitdispreg(pk: i64, "BX");
|
||||||
|
emitline(", AX\n");
|
||||||
|
emitline("\tMOVB\tAX, ");
|
||||||
|
emitdispreg(pk: i64, "DX");
|
||||||
|
emitline("\n");
|
||||||
|
pk += 1;
|
||||||
|
};
|
||||||
|
vn = vn.next;
|
||||||
|
continue;
|
||||||
|
};
|
||||||
let m34e: str = "#34: append() struct element source shape unsupported (rule-7)\n";
|
let m34e: str = "#34: append() struct element source shape unsupported (rule-7)\n";
|
||||||
os.write(2, m34e.ptr, m34e.len: u64);
|
os.write(2, m34e.ptr, m34e.len: u64);
|
||||||
os.exit(1);
|
os.exit(1);
|
||||||
|
|||||||
@@ -4845,6 +4845,131 @@ fn cgappend(c: *cgen, n: *node) void = {
|
|||||||
// grow FIRST, then fill through the dst pointer
|
// grow FIRST, then fill through the dst pointer
|
||||||
// (tagged: the #12 widen choke-point cgexprs the value
|
// (tagged: the #12 widen choke-point cgexprs the value
|
||||||
// internally; struct: literal fill / ident word-copy).
|
// 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 loud exit (incl. CALL rvalues, the
|
||||||
|
// #42-style bound).
|
||||||
|
let aplace: bool = false;
|
||||||
|
let afld: i32 = 0;
|
||||||
|
let aidxesz: i32 = 0;
|
||||||
|
let abaseslice: bool = false;
|
||||||
|
let arootoff: i32 = 0;
|
||||||
|
let asroot: i32 = 0;
|
||||||
|
let asoff: i32 = 0;
|
||||||
|
let aroot: *node = nil;
|
||||||
|
let aidx: *node = nil;
|
||||||
|
if (elstruct && vn.kind != nkind.N_STRUCTLIT && vn.kind != nkind.N_IDENT) {
|
||||||
|
let ch: *node = vn;
|
||||||
|
let aok: bool = true;
|
||||||
|
for (aok && ch.kind == nkind.N_DOT) {
|
||||||
|
let ab: *node = ch.lhs;
|
||||||
|
let af: *tfield = nil;
|
||||||
|
if (ab != nil) {
|
||||||
|
let abu: *tinfo = ab.type_: *tinfo;
|
||||||
|
for (abu != nil && abu.kind == tykind.TY_NAMED) { abu = abu.under; };
|
||||||
|
if (abu != nil && abu.kind == tykind.TY_STRUCT) {
|
||||||
|
let fl: *tfield = abu.fields;
|
||||||
|
for (fl != nil) {
|
||||||
|
if (streq(fl.name, ch.str)) { af = fl; break; };
|
||||||
|
fl = fl.tnext;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (af == nil) { aok = false; break; };
|
||||||
|
afld += af.offset: i32;
|
||||||
|
ch = ab;
|
||||||
|
};
|
||||||
|
if (aok && ch.kind == nkind.N_INDEX) {
|
||||||
|
let ab: *node = ch.lhs;
|
||||||
|
let aet: *tinfo = ch.type_: *tinfo;
|
||||||
|
for (aet != nil && aet.kind == tykind.TY_NAMED) { aet = aet.under; };
|
||||||
|
let abu: *tinfo = nil;
|
||||||
|
if (ab != nil) {
|
||||||
|
abu = ab.type_: *tinfo;
|
||||||
|
for (abu != nil && abu.kind == tykind.TY_NAMED) { abu = abu.under; };
|
||||||
|
};
|
||||||
|
if (ab == nil || abu == nil || aet == nil
|
||||||
|
|| (abu.kind != tykind.TY_SLICE && abu.kind != tykind.TY_ARRAY)) {
|
||||||
|
aok = false;
|
||||||
|
} else {
|
||||||
|
abaseslice = abu.kind == tykind.TY_SLICE;
|
||||||
|
aidxesz = aet.size: i32;
|
||||||
|
aidx = ch.rhs;
|
||||||
|
ch = ab;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (aok) {
|
||||||
|
if (ch.kind == nkind.N_IDENT) {
|
||||||
|
let rl: *local = localfindnode(c, ch.str);
|
||||||
|
if (rl != nil) {
|
||||||
|
arootoff = rl.off;
|
||||||
|
} else {
|
||||||
|
let gok: bool = isletvar(c, ch.str);
|
||||||
|
if (!gok) {
|
||||||
|
if (defvarstructinfo(c, ch.str) != nil) { gok = true; };
|
||||||
|
};
|
||||||
|
if (!gok) {
|
||||||
|
let dtn: *node = defvartnode(c, ch.str);
|
||||||
|
if (dtn != nil) {
|
||||||
|
if (dtn.kind == nkind.N_TARRAY) { gok = true; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (!gok) { aok = false; };
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
if (ch.kind != nkind.N_UN || ch.op != tkind.TK_STAR) {
|
||||||
|
aok = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (!aok) {
|
||||||
|
let m34p: str = "#34: append() struct element source shape unsupported (rule-7)\n";
|
||||||
|
os.write(2, m34p.ptr, m34p.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
aroot = ch;
|
||||||
|
if (aroot.kind == nkind.N_UN) {
|
||||||
|
asroot = localadd(c, "@appendsroot", 8, nil);
|
||||||
|
cgexpr(c, aroot.lhs);
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitoff(asroot: i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
};
|
||||||
|
if (aidx != nil) {
|
||||||
|
asoff = localadd(c, "@appendsoff", 8, nil);
|
||||||
|
cgexpr(c, aidx);
|
||||||
|
if (aidxesz > 1) {
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(aidxesz: i64);
|
||||||
|
emitline(", CX\n");
|
||||||
|
emitline("\tIMULQ\tCX, AX\n");
|
||||||
|
};
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitoff(asoff: i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
};
|
||||||
|
aplace = true;
|
||||||
|
};
|
||||||
cgappendgrow(c, sndirect, sn_off, snscr, esz);
|
cgappendgrow(c, sndirect, sn_off, snscr, esz);
|
||||||
cgappendslot(c, sndirect, sn_off, snscr, esz, "BX");
|
cgappendslot(c, sndirect, sn_off, snscr, esz, "BX");
|
||||||
if (eltagged) {
|
if (eltagged) {
|
||||||
@@ -4923,6 +5048,86 @@ fn cgappend(c: *cgen, n: *node) void = {
|
|||||||
vn = vn.next;
|
vn = vn.next;
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
if (aplace) {
|
||||||
|
// phase 2: dst slot to @appendscr, base from
|
||||||
|
// the live storage, stashed offsets back on
|
||||||
|
// top.
|
||||||
|
let pscroff: i32 = localadd(c, "@appendscr", 8, nil);
|
||||||
|
emitline("\tMOVQ\tBX, ");
|
||||||
|
emitoff(pscroff: i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
if (aroot.kind == nkind.N_IDENT) {
|
||||||
|
if (arootoff != 0) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitoff(arootoff: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, aroot.str);
|
||||||
|
emitline("(SB), BX\n");
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(asroot: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
};
|
||||||
|
if (aidx != nil) {
|
||||||
|
if (abaseslice) {
|
||||||
|
emitline("\tMOVQ\t(BX), BX\n");
|
||||||
|
};
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(asoff: i64);
|
||||||
|
emitline("(BP), AX\n");
|
||||||
|
emitline("\tADDQ\tAX, BX\n");
|
||||||
|
};
|
||||||
|
if (afld != 0) {
|
||||||
|
emitline("\tADDQ\t$");
|
||||||
|
emitint(afld: i64);
|
||||||
|
emitline(", BX\n");
|
||||||
|
};
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(pscroff: i64);
|
||||||
|
emitline("(BP), DX\n");
|
||||||
|
let pk: i32 = 0;
|
||||||
|
for (pk + 8 <= esz) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitdispreg(pk: i64, "BX");
|
||||||
|
emitline(", AX\n");
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitdispreg(pk: i64, "DX");
|
||||||
|
emitline("\n");
|
||||||
|
pk += 8;
|
||||||
|
};
|
||||||
|
if (pk + 4 <= esz) {
|
||||||
|
emitline("\tMOVL\t");
|
||||||
|
emitdispreg(pk: i64, "BX");
|
||||||
|
emitline(", AX\n");
|
||||||
|
emitline("\tMOVL\tAX, ");
|
||||||
|
emitdispreg(pk: i64, "DX");
|
||||||
|
emitline("\n");
|
||||||
|
pk += 4;
|
||||||
|
};
|
||||||
|
if (pk + 2 <= esz) {
|
||||||
|
emitline("\tMOVW\t");
|
||||||
|
emitdispreg(pk: i64, "BX");
|
||||||
|
emitline(", AX\n");
|
||||||
|
emitline("\tMOVW\tAX, ");
|
||||||
|
emitdispreg(pk: i64, "DX");
|
||||||
|
emitline("\n");
|
||||||
|
pk += 2;
|
||||||
|
};
|
||||||
|
if (pk + 1 <= esz) {
|
||||||
|
emitline("\tMOVB\t");
|
||||||
|
emitdispreg(pk: i64, "BX");
|
||||||
|
emitline(", AX\n");
|
||||||
|
emitline("\tMOVB\tAX, ");
|
||||||
|
emitdispreg(pk: i64, "DX");
|
||||||
|
emitline("\n");
|
||||||
|
pk += 1;
|
||||||
|
};
|
||||||
|
vn = vn.next;
|
||||||
|
continue;
|
||||||
|
};
|
||||||
let m34e: str = "#34: append() struct element source shape unsupported (rule-7)\n";
|
let m34e: str = "#34: append() struct element source shape unsupported (rule-7)\n";
|
||||||
os.write(2, m34e.ptr, m34e.len: u64);
|
os.write(2, m34e.ptr, m34e.len: u64);
|
||||||
os.exit(1);
|
os.exit(1);
|
||||||
|
|||||||
@@ -24965,6 +24965,131 @@ fn cgappend(c: *cgen, n: *node) void = {
|
|||||||
// grow FIRST, then fill through the dst pointer
|
// grow FIRST, then fill through the dst pointer
|
||||||
// (tagged: the #12 widen choke-point cgexprs the value
|
// (tagged: the #12 widen choke-point cgexprs the value
|
||||||
// internally; struct: literal fill / ident word-copy).
|
// 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 loud exit (incl. CALL rvalues, the
|
||||||
|
// #42-style bound).
|
||||||
|
let aplace: bool = false;
|
||||||
|
let afld: i32 = 0;
|
||||||
|
let aidxesz: i32 = 0;
|
||||||
|
let abaseslice: bool = false;
|
||||||
|
let arootoff: i32 = 0;
|
||||||
|
let asroot: i32 = 0;
|
||||||
|
let asoff: i32 = 0;
|
||||||
|
let aroot: *node = nil;
|
||||||
|
let aidx: *node = nil;
|
||||||
|
if (elstruct && vn.kind != nkind.N_STRUCTLIT && vn.kind != nkind.N_IDENT) {
|
||||||
|
let ch: *node = vn;
|
||||||
|
let aok: bool = true;
|
||||||
|
for (aok && ch.kind == nkind.N_DOT) {
|
||||||
|
let ab: *node = ch.lhs;
|
||||||
|
let af: *tfield = nil;
|
||||||
|
if (ab != nil) {
|
||||||
|
let abu: *tinfo = ab.type_: *tinfo;
|
||||||
|
for (abu != nil && abu.kind == tykind.TY_NAMED) { abu = abu.under; };
|
||||||
|
if (abu != nil && abu.kind == tykind.TY_STRUCT) {
|
||||||
|
let fl: *tfield = abu.fields;
|
||||||
|
for (fl != nil) {
|
||||||
|
if (streq(fl.name, ch.str)) { af = fl; break; };
|
||||||
|
fl = fl.tnext;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (af == nil) { aok = false; break; };
|
||||||
|
afld += af.offset: i32;
|
||||||
|
ch = ab;
|
||||||
|
};
|
||||||
|
if (aok && ch.kind == nkind.N_INDEX) {
|
||||||
|
let ab: *node = ch.lhs;
|
||||||
|
let aet: *tinfo = ch.type_: *tinfo;
|
||||||
|
for (aet != nil && aet.kind == tykind.TY_NAMED) { aet = aet.under; };
|
||||||
|
let abu: *tinfo = nil;
|
||||||
|
if (ab != nil) {
|
||||||
|
abu = ab.type_: *tinfo;
|
||||||
|
for (abu != nil && abu.kind == tykind.TY_NAMED) { abu = abu.under; };
|
||||||
|
};
|
||||||
|
if (ab == nil || abu == nil || aet == nil
|
||||||
|
|| (abu.kind != tykind.TY_SLICE && abu.kind != tykind.TY_ARRAY)) {
|
||||||
|
aok = false;
|
||||||
|
} else {
|
||||||
|
abaseslice = abu.kind == tykind.TY_SLICE;
|
||||||
|
aidxesz = aet.size: i32;
|
||||||
|
aidx = ch.rhs;
|
||||||
|
ch = ab;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (aok) {
|
||||||
|
if (ch.kind == nkind.N_IDENT) {
|
||||||
|
let rl: *local = localfindnode(c, ch.str);
|
||||||
|
if (rl != nil) {
|
||||||
|
arootoff = rl.off;
|
||||||
|
} else {
|
||||||
|
let gok: bool = isletvar(c, ch.str);
|
||||||
|
if (!gok) {
|
||||||
|
if (defvarstructinfo(c, ch.str) != nil) { gok = true; };
|
||||||
|
};
|
||||||
|
if (!gok) {
|
||||||
|
let dtn: *node = defvartnode(c, ch.str);
|
||||||
|
if (dtn != nil) {
|
||||||
|
if (dtn.kind == nkind.N_TARRAY) { gok = true; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (!gok) { aok = false; };
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
if (ch.kind != nkind.N_UN || ch.op != tkind.TK_STAR) {
|
||||||
|
aok = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (!aok) {
|
||||||
|
let m34p: str = "#34: append() struct element source shape unsupported (rule-7)\n";
|
||||||
|
os.write(2, m34p.ptr, m34p.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
aroot = ch;
|
||||||
|
if (aroot.kind == nkind.N_UN) {
|
||||||
|
asroot = localadd(c, "@appendsroot", 8, nil);
|
||||||
|
cgexpr(c, aroot.lhs);
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitoff(asroot: i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
};
|
||||||
|
if (aidx != nil) {
|
||||||
|
asoff = localadd(c, "@appendsoff", 8, nil);
|
||||||
|
cgexpr(c, aidx);
|
||||||
|
if (aidxesz > 1) {
|
||||||
|
emitline("\tMOVQ\t$");
|
||||||
|
emitint(aidxesz: i64);
|
||||||
|
emitline(", CX\n");
|
||||||
|
emitline("\tIMULQ\tCX, AX\n");
|
||||||
|
};
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitoff(asoff: i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
};
|
||||||
|
aplace = true;
|
||||||
|
};
|
||||||
cgappendgrow(c, sndirect, sn_off, snscr, esz);
|
cgappendgrow(c, sndirect, sn_off, snscr, esz);
|
||||||
cgappendslot(c, sndirect, sn_off, snscr, esz, "BX");
|
cgappendslot(c, sndirect, sn_off, snscr, esz, "BX");
|
||||||
if (eltagged) {
|
if (eltagged) {
|
||||||
@@ -25043,6 +25168,86 @@ fn cgappend(c: *cgen, n: *node) void = {
|
|||||||
vn = vn.next;
|
vn = vn.next;
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
if (aplace) {
|
||||||
|
// phase 2: dst slot to @appendscr, base from
|
||||||
|
// the live storage, stashed offsets back on
|
||||||
|
// top.
|
||||||
|
let pscroff: i32 = localadd(c, "@appendscr", 8, nil);
|
||||||
|
emitline("\tMOVQ\tBX, ");
|
||||||
|
emitoff(pscroff: i64);
|
||||||
|
emitline("(BP)\n");
|
||||||
|
if (aroot.kind == nkind.N_IDENT) {
|
||||||
|
if (arootoff != 0) {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitoff(arootoff: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
} else {
|
||||||
|
emitline("\tLEAQ\t");
|
||||||
|
emitsymname(c, aroot.str);
|
||||||
|
emitline("(SB), BX\n");
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(asroot: i64);
|
||||||
|
emitline("(BP), BX\n");
|
||||||
|
};
|
||||||
|
if (aidx != nil) {
|
||||||
|
if (abaseslice) {
|
||||||
|
emitline("\tMOVQ\t(BX), BX\n");
|
||||||
|
};
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(asoff: i64);
|
||||||
|
emitline("(BP), AX\n");
|
||||||
|
emitline("\tADDQ\tAX, BX\n");
|
||||||
|
};
|
||||||
|
if (afld != 0) {
|
||||||
|
emitline("\tADDQ\t$");
|
||||||
|
emitint(afld: i64);
|
||||||
|
emitline(", BX\n");
|
||||||
|
};
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitoff(pscroff: i64);
|
||||||
|
emitline("(BP), DX\n");
|
||||||
|
let pk: i32 = 0;
|
||||||
|
for (pk + 8 <= esz) {
|
||||||
|
emitline("\tMOVQ\t");
|
||||||
|
emitdispreg(pk: i64, "BX");
|
||||||
|
emitline(", AX\n");
|
||||||
|
emitline("\tMOVQ\tAX, ");
|
||||||
|
emitdispreg(pk: i64, "DX");
|
||||||
|
emitline("\n");
|
||||||
|
pk += 8;
|
||||||
|
};
|
||||||
|
if (pk + 4 <= esz) {
|
||||||
|
emitline("\tMOVL\t");
|
||||||
|
emitdispreg(pk: i64, "BX");
|
||||||
|
emitline(", AX\n");
|
||||||
|
emitline("\tMOVL\tAX, ");
|
||||||
|
emitdispreg(pk: i64, "DX");
|
||||||
|
emitline("\n");
|
||||||
|
pk += 4;
|
||||||
|
};
|
||||||
|
if (pk + 2 <= esz) {
|
||||||
|
emitline("\tMOVW\t");
|
||||||
|
emitdispreg(pk: i64, "BX");
|
||||||
|
emitline(", AX\n");
|
||||||
|
emitline("\tMOVW\tAX, ");
|
||||||
|
emitdispreg(pk: i64, "DX");
|
||||||
|
emitline("\n");
|
||||||
|
pk += 2;
|
||||||
|
};
|
||||||
|
if (pk + 1 <= esz) {
|
||||||
|
emitline("\tMOVB\t");
|
||||||
|
emitdispreg(pk: i64, "BX");
|
||||||
|
emitline(", AX\n");
|
||||||
|
emitline("\tMOVB\tAX, ");
|
||||||
|
emitdispreg(pk: i64, "DX");
|
||||||
|
emitline("\n");
|
||||||
|
pk += 1;
|
||||||
|
};
|
||||||
|
vn = vn.next;
|
||||||
|
continue;
|
||||||
|
};
|
||||||
let m34e: str = "#34: append() struct element source shape unsupported (rule-7)\n";
|
let m34e: str = "#34: append() struct element source shape unsupported (rule-7)\n";
|
||||||
os.write(2, m34e.ptr, m34e.len: u64);
|
os.write(2, m34e.ptr, m34e.len: u64);
|
||||||
os.exit(1);
|
os.exit(1);
|
||||||
|
|||||||
@@ -44,8 +44,25 @@
|
|||||||
* | @apphdrscr slot (a shared slot clobbers)|
|
* | @apphdrscr slot (a shared slot clobbers)|
|
||||||
* identroot_dot | h.xs via *holder param — C1.5's reject, | 73
|
* identroot_dot | h.xs via *holder param — C1.5's reject, | 73
|
||||||
* | graduated by C2's resolver ident root |
|
* | graduated by C2's resolver ident root |
|
||||||
|
* idxfield_struct | append(res, threads[i].root_capture) | 74
|
||||||
|
* | 56B indexed-FIELD source (#49, ha:819) |
|
||||||
|
* idx_computed_elem | append(ds, bs[i + 1]) computed-index | 75
|
||||||
|
* | whole-element source |
|
||||||
|
* derefspine_field | append(*r, (*q)[i].rc) param spines | 76
|
||||||
|
* | both sides |
|
||||||
|
* deref_src | append(*ds, *p) deref source | 77
|
||||||
|
* self_append_realloc | append(bs, bs[i]) ×33 — source inside | 78
|
||||||
|
* | the grown slice, resolve-after-grow |
|
||||||
|
* selfidx_oldlen_once | append(bs, bs[f(bs)]) — index expr runs | 80
|
||||||
|
* | ONCE, PRE-grow (sees old len): the #49 |
|
||||||
|
* | ruling's split-order semantics pin |
|
||||||
|
* elem_kinds_place | place-resolved SOURCE × every other | 79
|
||||||
|
* | element kind (scalar/narrow/str/slice/ |
|
||||||
|
* | tagged via the pre-existing arms) |
|
||||||
* reject_spread_src | non-ident spread SOURCE through a deref | BUILD_FAIL
|
* reject_spread_src | non-ident spread SOURCE through a deref | BUILD_FAIL
|
||||||
* | target (the #35 designed boundary) |
|
* | target (the #35 designed boundary) |
|
||||||
|
* reject_call_src | CALL rvalue element source — the #49 | BUILD_FAIL
|
||||||
|
* | loud tail (#42-style bound) |
|
||||||
*
|
*
|
||||||
* BUILD_FAIL rows also assert the diagnostic TEXT (stderr substring,
|
* BUILD_FAIL rows also assert the diagnostic TEXT (stderr substring,
|
||||||
* both stages) — a build that fails for any other reason (parse error,
|
* both stages) — a build that fails for any other reason (parse error,
|
||||||
@@ -395,6 +412,190 @@ static const struct row rows[] = {
|
|||||||
"};\n",
|
"};\n",
|
||||||
73, NULL },
|
73, NULL },
|
||||||
|
|
||||||
|
/* #49 (#35's single-element sibling): the struct-element append
|
||||||
|
* arm dispatched on SOURCE node kind — N_STRUCTLIT and N_IDENT
|
||||||
|
* only; every place-resolvable chain (the regex.ha:819
|
||||||
|
* result-build line `append(res, threads[best_idx].root_capture)`)
|
||||||
|
* died on the rule-7 fatal. The fix routes the remaining shapes
|
||||||
|
* through cgplaceaddr (the C1 resolver): dst slot spills to
|
||||||
|
* @appendscr, source resolves AFTER the grow, whole-width
|
||||||
|
* word-copy through DX. Five rows: indexed-field 56B capture,
|
||||||
|
* computed-index whole element, deref-spine through params,
|
||||||
|
* deref source, self-append across reallocs. */
|
||||||
|
{ "idxfield_struct",
|
||||||
|
"package main;\n"
|
||||||
|
"type capture = struct { content: str, start: size,\n"
|
||||||
|
"\tstart_bytesize: size, end: size, end_bytesize: size };\n"
|
||||||
|
"type thread = struct { pc: size, root_capture: capture,\n"
|
||||||
|
"\tmatched: bool };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet threads: []thread = [];\n"
|
||||||
|
"\tappend(threads, thread { pc = 7, ... });\n"
|
||||||
|
"\tappend(threads, thread { pc = 1, root_capture = capture {\n"
|
||||||
|
"\t\tcontent = \"bcd\", start = 1, start_bytesize = 2,\n"
|
||||||
|
"\t\tend = 4, end_bytesize = 5 }, ... });\n"
|
||||||
|
"\tlet res: []capture = [];\n"
|
||||||
|
"\tlet i: size = 1;\n"
|
||||||
|
"\tappend(res, threads[i].root_capture);\n"
|
||||||
|
"\tif (len(res) != 1) { return 1; };\n"
|
||||||
|
"\tif (res[0].start != 1) { return 2; };\n"
|
||||||
|
"\tif (res[0].start_bytesize != 2) { return 3; };\n"
|
||||||
|
"\tif (res[0].end != 4) { return 4; };\n"
|
||||||
|
"\tif (res[0].end_bytesize != 5) { return 5; };\n"
|
||||||
|
"\tif (res[0].content.len != 3) { return 6; };\n"
|
||||||
|
"\tif (threads[1].root_capture.end != 4) { return 7; };\n"
|
||||||
|
"\treturn 74;\n"
|
||||||
|
"};\n",
|
||||||
|
74, NULL },
|
||||||
|
|
||||||
|
{ "idx_computed_elem",
|
||||||
|
"package main;\n"
|
||||||
|
"type box = struct { pc: size, a: i64, b: i64, x: i64 };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet bs: []box = [];\n"
|
||||||
|
"\tappend(bs, box { pc = 1, a = 10, ... });\n"
|
||||||
|
"\tappend(bs, box { pc = 2, a = 20, ... });\n"
|
||||||
|
"\tappend(bs, box { pc = 3, a = 30, b = 7, x = 8 });\n"
|
||||||
|
"\tlet ds: []box = [];\n"
|
||||||
|
"\tlet i: i64 = 1;\n"
|
||||||
|
"\tappend(ds, bs[i + 1]);\n"
|
||||||
|
"\tif (len(ds) != 1) { return 1; };\n"
|
||||||
|
"\tif (ds[0].pc != 3) { return 2; };\n"
|
||||||
|
"\tif (ds[0].a != 30) { return 3; };\n"
|
||||||
|
"\tif (ds[0].b != 7 || ds[0].x != 8) { return 4; };\n"
|
||||||
|
"\treturn 75;\n"
|
||||||
|
"};\n",
|
||||||
|
75, NULL },
|
||||||
|
|
||||||
|
{ "derefspine_field",
|
||||||
|
"package main;\n"
|
||||||
|
"type cap = struct { s: size, e: size };\n"
|
||||||
|
"type th = struct { pc: size, rc: cap, m: bool };\n"
|
||||||
|
"fn pick(r: *[]cap, q: *[]th, i: size) void = {\n"
|
||||||
|
"\tappend(*r, (*q)[i].rc);\n"
|
||||||
|
"};\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet ts: []th = [];\n"
|
||||||
|
"\tappend(ts, th { pc = 1, rc = cap { s = 5, e = 9 }, ... });\n"
|
||||||
|
"\tlet rs: []cap = [];\n"
|
||||||
|
"\tpick(&rs, &ts, 0);\n"
|
||||||
|
"\tif (len(rs) != 1) { return 1; };\n"
|
||||||
|
"\tif (rs[0].s != 5) { return 2; };\n"
|
||||||
|
"\tif (rs[0].e != 9) { return 3; };\n"
|
||||||
|
"\treturn 76;\n"
|
||||||
|
"};\n",
|
||||||
|
76, NULL },
|
||||||
|
|
||||||
|
{ "deref_src",
|
||||||
|
"package main;\n"
|
||||||
|
"type box = struct { pc: size, a: i64, b: i64, x: i64 };\n"
|
||||||
|
"fn addsrc(ds: *[]box, p: *box) void = { append(*ds, *p); };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet b: box = box { pc = 4, a = 40, b = 41, x = 42 };\n"
|
||||||
|
"\tlet ds: []box = [];\n"
|
||||||
|
"\taddsrc(&ds, &b);\n"
|
||||||
|
"\tif (len(ds) != 1) { return 1; };\n"
|
||||||
|
"\tif (ds[0].pc != 4) { return 2; };\n"
|
||||||
|
"\tif (ds[0].a != 40 || ds[0].b != 41 || ds[0].x != 42) { return 3; };\n"
|
||||||
|
"\treturn 77;\n"
|
||||||
|
"};\n",
|
||||||
|
77, NULL },
|
||||||
|
|
||||||
|
/* Self-append: the source element lives in the SLICE BEING GROWN,
|
||||||
|
* so its address is only valid post-rt_ensure — pins the
|
||||||
|
* resolve-AFTER-grow order across several cap-doubling reallocs.
|
||||||
|
* Sentinels around the header pin the frame. */
|
||||||
|
{ "self_append_realloc",
|
||||||
|
"package main;\n"
|
||||||
|
"type box = struct { pc: size, a: i64, b: i64, x: i64 };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet lo: i64 = 111;\n"
|
||||||
|
"\tlet bs: []box = [];\n"
|
||||||
|
"\tlet hi: i64 = 222;\n"
|
||||||
|
"\tappend(bs, box { pc = 9, a = 100, b = 200, x = 300 });\n"
|
||||||
|
"\tlet i: i32 = 0;\n"
|
||||||
|
"\tfor (i < 33) {\n"
|
||||||
|
"\t\tappend(bs, bs[i]);\n"
|
||||||
|
"\t\ti += 1;\n"
|
||||||
|
"\t};\n"
|
||||||
|
"\tif (len(bs) != 34) { return 1; };\n"
|
||||||
|
"\tif (bs[0].pc != 9 || bs[33].pc != 9) { return 2; };\n"
|
||||||
|
"\tif (bs[17].a != 100 || bs[33].b != 200) { return 3; };\n"
|
||||||
|
"\tif (bs[33].x != 300) { return 4; };\n"
|
||||||
|
"\tif (lo != 111 || hi != 222) { return 5; };\n"
|
||||||
|
"\treturn 78;\n"
|
||||||
|
"};\n",
|
||||||
|
78, NULL },
|
||||||
|
|
||||||
|
/* The #49 ruling's order pin, positive semantics row (converged
|
||||||
|
* with Hare, not a documented divergence): the source chain's
|
||||||
|
* rvalues — here a CALLED index helper reading the slice's len —
|
||||||
|
* evaluate exactly ONCE, PRE-grow. calls != 1 catches a
|
||||||
|
* double-evaluation; bs[2] != old-last catches a post-grow
|
||||||
|
* resolution (the helper would see the bumped len and return the
|
||||||
|
* uninitialized new slot — the pre-split emission failed exactly
|
||||||
|
* there, exit 3). */
|
||||||
|
{ "selfidx_oldlen_once",
|
||||||
|
"package main;\n"
|
||||||
|
"type box = struct { pc: i64, a: i64 };\n"
|
||||||
|
"let calls: i64 = 0;\n"
|
||||||
|
"fn lastidx(xs: []box) i64 = {\n"
|
||||||
|
"\tcalls += 1;\n"
|
||||||
|
"\tlet n: i64 = len(xs): i64;\n"
|
||||||
|
"\treturn n - 1;\n"
|
||||||
|
"};\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet bs: []box = [];\n"
|
||||||
|
"\tappend(bs, box { pc = 11, a = 1 });\n"
|
||||||
|
"\tappend(bs, box { pc = 4242, a = 2 });\n"
|
||||||
|
"\tappend(bs, bs[lastidx(bs)]);\n"
|
||||||
|
"\tif (calls != 1) { return 1; };\n"
|
||||||
|
"\tif (len(bs) != 3) { return 2; };\n"
|
||||||
|
"\tif (bs[2].pc != 4242 || bs[2].a != 2) { return 3; };\n"
|
||||||
|
"\tif (bs[0].pc != 11) { return 4; };\n"
|
||||||
|
"\treturn 80;\n"
|
||||||
|
"};\n",
|
||||||
|
80, NULL },
|
||||||
|
|
||||||
|
/* Element-kind × place-source matrix: struct was the ONLY kind
|
||||||
|
* the source-shape dispatch rejected — scalar (wide + narrow),
|
||||||
|
* str-header, slice-header and tagged sources route through the
|
||||||
|
* pre-existing cgexpr-based arms and already worked at 796d41b.
|
||||||
|
* This row is the regression net pinning that matrix (runtime +
|
||||||
|
* byte-id, dual-driver), not a fix pin. */
|
||||||
|
{ "elem_kinds_place",
|
||||||
|
"package main;\n"
|
||||||
|
"type box = struct { n: i64, m: i32, name: str, xs: []u8 };\n"
|
||||||
|
"type tv = (i64 | void);\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet inner: []u8 = [];\n"
|
||||||
|
"\tappend(inner, 7u8);\n"
|
||||||
|
"\tappend(inner, 8u8);\n"
|
||||||
|
"\tlet bs: []box = [];\n"
|
||||||
|
"\tappend(bs, box { n = 41, m = 9, name = \"hello\", xs = inner });\n"
|
||||||
|
"\tlet ns: []i64 = [];\n"
|
||||||
|
"\tappend(ns, bs[0].n);\n"
|
||||||
|
"\tlet ms: []i32 = [];\n"
|
||||||
|
"\tappend(ms, bs[0].m);\n"
|
||||||
|
"\tlet ss: []str = [];\n"
|
||||||
|
"\tappend(ss, bs[0].name);\n"
|
||||||
|
"\tlet vs: [][]u8 = [];\n"
|
||||||
|
"\tappend(vs, bs[0].xs);\n"
|
||||||
|
"\tlet ts: []tv = [];\n"
|
||||||
|
"\tappend(ts, bs[0].n);\n"
|
||||||
|
"\tif (len(ns) != 1 || ns[0] != 41) { return 1; };\n"
|
||||||
|
"\tif (len(ms) != 1 || ms[0] != 9) { return 2; };\n"
|
||||||
|
"\tif (len(ss) != 1 || len(ss[0]) != 5) { return 3; };\n"
|
||||||
|
"\tif (len(vs) != 1 || len(vs[0]) != 2) { return 4; };\n"
|
||||||
|
"\tif (vs[0][1] != 8u8) { return 5; };\n"
|
||||||
|
"\tmatch (ts[0]) {\n"
|
||||||
|
"\tcase let v: i64 => { if (v != 41) { return 6; }; };\n"
|
||||||
|
"\tcase void => { return 7; };\n"
|
||||||
|
"\t};\n"
|
||||||
|
"\treturn 79;\n"
|
||||||
|
"};\n",
|
||||||
|
79, NULL },
|
||||||
|
|
||||||
/* The FA4 designed boundary (task #35, old #37): a spread SOURCE that is
|
/* The FA4 designed boundary (task #35, old #37): a spread SOURCE that is
|
||||||
* not an ident local must stay loud even now that the deref
|
* not an ident local must stay loud even now that the deref
|
||||||
* TARGET resolves. */
|
* TARGET resolves. */
|
||||||
@@ -411,6 +612,20 @@ static const struct row rows[] = {
|
|||||||
"\treturn 0;\n"
|
"\treturn 0;\n"
|
||||||
"};\n",
|
"};\n",
|
||||||
BUILD_FAIL, "#34: append() spread source shape unsupported (rule-7)" },
|
BUILD_FAIL, "#34: append() spread source shape unsupported (rule-7)" },
|
||||||
|
|
||||||
|
/* The #49 boundary: a CALL rvalue source has no place to resolve
|
||||||
|
* — stays loud (the #42-style bound) until a scratch-receive
|
||||||
|
* route lands. */
|
||||||
|
{ "reject_call_src",
|
||||||
|
"package main;\n"
|
||||||
|
"type box = struct { pc: size, a: i64 };\n"
|
||||||
|
"fn mk() box = { return box { pc = 1, a = 2 }; };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet bs: []box = [];\n"
|
||||||
|
"\tappend(bs, mk());\n"
|
||||||
|
"\treturn 0;\n"
|
||||||
|
"};\n",
|
||||||
|
BUILD_FAIL, "#34: append() struct element source shape unsupported (rule-7)" },
|
||||||
};
|
};
|
||||||
|
|
||||||
/* errlog_has — the build-failure stderr must carry the row's expected
|
/* errlog_has — the build-failure stderr must carry the row's expected
|
||||||
|
|||||||
Reference in New Issue
Block a user