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:
@@ -24965,6 +24965,131 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
// grow FIRST, then fill through 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 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);
|
||||
cgappendslot(c, sndirect, sn_off, snscr, esz, "BX");
|
||||
if (eltagged) {
|
||||
@@ -25043,6 +25168,86 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
vn = vn.next;
|
||||
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";
|
||||
os.write(2, m34e.ptr, m34e.len: u64);
|
||||
os.exit(1);
|
||||
|
||||
@@ -4845,6 +4845,131 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
// grow FIRST, then fill through 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 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);
|
||||
cgappendslot(c, sndirect, sn_off, snscr, esz, "BX");
|
||||
if (eltagged) {
|
||||
@@ -4923,6 +5048,86 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
vn = vn.next;
|
||||
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";
|
||||
os.write(2, m34e.ptr, m34e.len: u64);
|
||||
os.exit(1);
|
||||
|
||||
@@ -24965,6 +24965,131 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
// grow FIRST, then fill through 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 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);
|
||||
cgappendslot(c, sndirect, sn_off, snscr, esz, "BX");
|
||||
if (eltagged) {
|
||||
@@ -25043,6 +25168,86 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
vn = vn.next;
|
||||
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";
|
||||
os.write(2, m34e.ptr, m34e.len: u64);
|
||||
os.exit(1);
|
||||
|
||||
Reference in New Issue
Block a user