wcc+w6c+w6c_ww: insert() builtin — single-element slice insertion (part of #35)
Hare's insert(xs[idx], v) (ref/harec/src/check.c:745 check_expr_append_insert — append/insert share the checker arm, "insert" at :786): checker accepts an INDEX place over a slice plus one value, stamps void; idx == len is a legal end-insert (the ref/hare os/exec/platform_cmd.ha:86 idiom). Loud-rejects with exact texts: spread form insert(xs[i], vs...) (filed, #35 — also covers harec's with-length form via the arity check), range place (not Hare; harec only parses ACCESS_INDEX, :784), non-index operands, array bases, wrong arity. delete()-parity throughout. Lowering (both stages, converged byte-identical by construction) is a DESUGAR: append(xs, v) — reusing append's grow (rt_ensure) and the entire #34 value-store dispatch (scalar / str-slice header / tagged widen / struct fill) verbatim, one boxing choke-point — lands v at slot len-1; then a rotate-right of [idx, len) moves it home through a fresh per-site esz frame scratch (@insscr). The rotate is delete's shift loop in reverse (descending j, the safe memmove-up direction) and is a same-slice whole-stride raw byte move — no boxing exists for any element kind. idx evaluates BEFORE the grow (Hare's left-to-right operand order — pinned by the pregrow_len_idx row, insert(xs[len(xs)-1], v): pre-grow [7,13,11] vs post-grow [7,11,13]; an idx==len(xs) end-insert cannot discriminate, the rotate degenerates either way). Base shapes: local slice ident (LEAQ) and deref-of-local ptr-to-slice (MOVQ); others rule-7 loud-stop, like delete. test/807: 57 fixtures — front/middle/end + idx==len via len(xs) + the pre-grow eval-order pin, esz 1/2/4/8/16/24/56 (MOVB/MOVW/MOVL tails, struct body, str header, 7-qword tagged from a typed local [the regex fold-3 ha:347 newinst shape] and from a cast rvalue [ha:419/441]), empty-slice grow, (*p)[i] deref base, front-insert loop, 6 checker reject rows with diagnostic-text checks; every accept row cs==ww asm byte-id.
This commit is contained in:
@@ -10424,14 +10424,15 @@ fn seedprimitives(c: *checker) void = {
|
||||
scopedefine(c.top, "nomem", skind.SK_TYPE, nil, nomemdecl);
|
||||
// `nil`, `true`, `false` are keywords — handled at the lex/parser
|
||||
// level, no symbol needed.
|
||||
// `len`, `alloc`, `free`, `append`, `delete` are pseudo-builtins;
|
||||
// scopedefine them so their use sites resolve. The actual semantics
|
||||
// live in cgen.
|
||||
// `len`, `alloc`, `free`, `append`, `delete`, `insert` are
|
||||
// pseudo-builtins; scopedefine them so their use sites resolve.
|
||||
// The actual semantics live in cgen.
|
||||
scopedefine(c.top, "len", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "alloc", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "free", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "append", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "delete", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "insert", skind.SK_FN, nil, nil);
|
||||
// #42: typed builtins folded to integer literals at check time —
|
||||
// `size(T)` / `align(T)` (arg is a type-expression planted by the
|
||||
// parser at lib/ww/parse/expr.ww:254-267) and `offset(e.f)` (arg is
|
||||
@@ -12919,7 +12920,7 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
|
||||
return tn;
|
||||
};
|
||||
// delete(xs[i]) — single-element slice removal, the
|
||||
// delete-half of #35 (insert() deferred). Mirrors
|
||||
// delete-half of #35 (insert() is the twin arm below). Mirrors
|
||||
// cstage cmd/wcc/check.c's delete arm. harec
|
||||
// ref/harec/src/check.c:1981-2027 also accepts the
|
||||
// range form delete(xs[i..j]) (EXPR_SLICE) —
|
||||
@@ -12956,6 +12957,56 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
|
||||
e.type_ = tinfofornode(c, tn): *void;
|
||||
return tn;
|
||||
};
|
||||
// insert(xs[idx], v) — single-element slice insertion
|
||||
// before idx, delete()'s twin (the insert-half of
|
||||
// #35). Mirrors cstage cmd/wcc/check.c's insert arm.
|
||||
// harec models append/insert in ONE checker arm
|
||||
// (ref/harec/src/check.c:745 check_expr_append_insert;
|
||||
// "insert" at :786): operand 1 must be an indexing
|
||||
// place over a slice; idx == len is a legal
|
||||
// end-insert. The spread form and the with-length
|
||||
// form (harec :821/:837) stay filed on #35; a range
|
||||
// PLACE is not Hare (harec asserts ACCESS_INDEX at
|
||||
// :784) — rejected, no task cite.
|
||||
if (streq(callee.str, "insert")) {
|
||||
let d: *node = e.list;
|
||||
if (d == nil || d.next == nil || d.next.next != nil) {
|
||||
cerr("insert: takes exactly two arguments\n");
|
||||
c.errs += 1;
|
||||
};
|
||||
if (d != nil) {
|
||||
exprtype(c, d, nil);
|
||||
if (d.kind == nkind.N_SLICE) {
|
||||
cerr("insert: range place is invalid; operand must be an indexing expression xs[i]\n");
|
||||
c.errs += 1;
|
||||
} else {
|
||||
if (d.kind != nkind.N_INDEX) {
|
||||
cerr("insert: operand must be an indexing expression xs[i]\n");
|
||||
c.errs += 1;
|
||||
} else {
|
||||
let basetn: *node = exprtype(c, d.lhs, nil);
|
||||
let u: *node = resolvealias(c, unwrapbang(basetn));
|
||||
// harec check.c:807 wording; a
|
||||
// fixed-size [N]T base lands here.
|
||||
if (u == nil || u.kind != nkind.N_TSLICE) {
|
||||
cerr("insert must operate on a slice\n");
|
||||
c.errs += 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (d.next != nil) {
|
||||
if (d.next.kind == nkind.N_SPREAD) {
|
||||
cerr("insert: spread form insert(xs[i], vs...) unimplemented (task #35)\n");
|
||||
c.errs += 1;
|
||||
} else {
|
||||
exprtype(c, d.next, nil);
|
||||
};
|
||||
};
|
||||
};
|
||||
let tn: *node = mktname(c, "void");
|
||||
e.type_ = tinfofornode(c, tn): *void;
|
||||
return tn;
|
||||
};
|
||||
};
|
||||
let nm: str;
|
||||
nm.ptr = nil; nm.len = 0;
|
||||
@@ -25439,6 +25490,250 @@ fn cgdelete(c: *cgen, n: *node) void = {
|
||||
emitline("\tADDQ\t$16, SP\n");
|
||||
};
|
||||
|
||||
// cginsert — Hare `insert(xs[idx], v)`: delete()'s twin, the insert-half
|
||||
// of #35. Insert v BEFORE idx; idx==len is a legal end-insert. Lowered
|
||||
// as a DESUGAR to append(xs, v) + a rotate-right of [idx, len): cgappend
|
||||
// contributes grow (rt_ensure) and the whole #34 value-store dispatch
|
||||
// (scalar / str-slice header / tagged widen / struct fill) verbatim —
|
||||
// one boxing choke-point, byte-id by construction — landing v at slot
|
||||
// len-1; the rotate then moves it home through an esz frame scratch.
|
||||
// The rotate is cgdelete's shift loop in reverse (descending j keeps
|
||||
// src j behind dst j+1, the safe memmove-up direction) and, like
|
||||
// delete's, is a same-slice whole-stride raw byte move — no boxing
|
||||
// exists for any element kind. idx evaluates BEFORE the grow (Hare's
|
||||
// left-to-right operand order: insert(xs[len(xs)], v) sees the pre-grow
|
||||
// len); v's evaluation point inherits append's per-kind rules. Bounds
|
||||
// are implicit (no index check, matching delete). Mirrors cmd/w6c/
|
||||
// cgen.c's N_CALL insert arm instruction-for-instruction (rule-10
|
||||
// byte-id).
|
||||
fn cginsert(c: *cgen, n: *node) void = {
|
||||
let d: *node = n.list; // N_INDEX, checker-validated
|
||||
let base: *node = d.lhs;
|
||||
let v: *node = d.next;
|
||||
// esz off the STAMPED base tinfo (#34/#48 discipline — never the
|
||||
// value node). Peel TY_NAMED on indexable AND element, mirroring
|
||||
// cstage's type_chase_named on both (#8 family).
|
||||
let sti: *tinfo = base.type_: *tinfo;
|
||||
for (sti != nil && sti.kind == tykind.TY_NAMED) { sti = sti.under; };
|
||||
let esub: *tinfo = nil;
|
||||
if (sti != nil) { esub = sti.sub; };
|
||||
for (esub != nil && esub.kind == tykind.TY_NAMED) { esub = esub.under; };
|
||||
let esz: i32 = 0;
|
||||
if (esub != nil) { esz = esub.size: i32; };
|
||||
if (esz <= 0) {
|
||||
let m35c: str = "#35: insert() element size unresolved (rule-7)\n";
|
||||
os.write(2, m35c.ptr, m35c.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let hdr_lea: bool = false;
|
||||
let hdr_off: i32 = 0;
|
||||
let hdr_ok: bool = false;
|
||||
if (base.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, base.str);
|
||||
if (lc != nil) {
|
||||
hdr_lea = true;
|
||||
hdr_off = lc.off;
|
||||
hdr_ok = true;
|
||||
};
|
||||
};
|
||||
// (*p)[i]: the header lives behind a local ptr-to-slice —
|
||||
// delete's regex_shape twin.
|
||||
if (!hdr_ok && base.kind == nkind.N_UN) {
|
||||
if (base.op == tkind.TK_STAR && base.lhs != nil) {
|
||||
if (base.lhs.kind == nkind.N_IDENT) {
|
||||
let pc: *local = localfindnode(c, base.lhs.str);
|
||||
if (pc != nil) {
|
||||
hdr_off = pc.off;
|
||||
hdr_ok = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (!hdr_ok) {
|
||||
let m35d: str = "#35: insert() base shape unsupported (rule-7: local slice ident or deref-of-local only)\n";
|
||||
os.write(2, m35d.ptr, m35d.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
// Fresh esz-sized slot per SITE (esz varies; an @-name dedup
|
||||
// would mis-share across element types). Allocated BEFORE the
|
||||
// cgappend body's own scratch allocs — cstage order.
|
||||
let insscr: i32 = localalloc(c, "@insscr", esz, nil);
|
||||
cgexpr(c, d.rhs); // AX = idx
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
// Desugar in place and route through cgappend: cgen is
|
||||
// single-pass, base is an lhs node (never on a sibling chain),
|
||||
// and the checker has already validated this call — the mutation
|
||||
// is dead after this emission. The callee rename keeps the AST
|
||||
// consistent with cstage's re-dispatch.
|
||||
n.lhs.str = "append";
|
||||
n.list = base;
|
||||
base.next = v;
|
||||
cgappend(c, n);
|
||||
if (hdr_lea) {
|
||||
emitline("\tLEAQ\t");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
};
|
||||
emitoff(hdr_off: i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
emitline("\tMOVQ\tAX, DX\n");
|
||||
emitline("\tMOVQ\t8(DX), CX\n");
|
||||
emitline("\tSUBQ\t$1, CX\n");
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", AX\n");
|
||||
emitline("\tIMULQ\tAX, CX\n");
|
||||
};
|
||||
emitline("\tMOVQ\t(DX), BX\n");
|
||||
emitline("\tADDQ\tCX, BX\n");
|
||||
let ik: i32 = 0;
|
||||
for (ik + 8 <= esz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((insscr + ik): i64);
|
||||
emitline("(BP)\n");
|
||||
ik += 8;
|
||||
};
|
||||
if (ik + 4 <= esz) {
|
||||
emitline("\tMOVL\t");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitoff((insscr + ik): i64);
|
||||
emitline("(BP)\n");
|
||||
ik += 4;
|
||||
};
|
||||
if (ik + 2 <= esz) {
|
||||
emitline("\tMOVW\t");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVW\tAX, ");
|
||||
emitoff((insscr + ik): i64);
|
||||
emitline("(BP)\n");
|
||||
ik += 2;
|
||||
};
|
||||
if (ik + 1 <= esz) {
|
||||
emitline("\tMOVB\t");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitoff((insscr + ik): i64);
|
||||
emitline("(BP)\n");
|
||||
ik += 1;
|
||||
};
|
||||
emitline("\tMOVQ\t8(DX), AX\n");
|
||||
emitline("\tSUBQ\t$2, AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
let ill: str = mklabel(c, "ins_l");
|
||||
let ile: str = mklabel(c, "ins_e");
|
||||
emitlabel(ill);
|
||||
emitline("\tMOVQ\t(SP), CX\n");
|
||||
emitline("\tMOVQ\t16(SP), DX\n");
|
||||
emitline("\tCMPQ\tDX, CX\n");
|
||||
emitline("\tJL\t"); emitline(ile); emitline("\n");
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", AX\n");
|
||||
emitline("\tIMULQ\tAX, CX\n");
|
||||
};
|
||||
emitline("\tMOVQ\t8(SP), DX\n");
|
||||
emitline("\tMOVQ\t(DX), BX\n");
|
||||
emitline("\tADDQ\tCX, BX\n");
|
||||
ik = 0;
|
||||
for (ik + 8 <= esz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg((esz + ik): i64, "BX");
|
||||
emitline("\n");
|
||||
ik += 8;
|
||||
};
|
||||
if (ik + 4 <= esz) {
|
||||
emitline("\tMOVL\t");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitdispreg((esz + ik): i64, "BX");
|
||||
emitline("\n");
|
||||
ik += 4;
|
||||
};
|
||||
if (ik + 2 <= esz) {
|
||||
emitline("\tMOVW\t");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVW\tAX, ");
|
||||
emitdispreg((esz + ik): i64, "BX");
|
||||
emitline("\n");
|
||||
ik += 2;
|
||||
};
|
||||
if (ik + 1 <= esz) {
|
||||
emitline("\tMOVB\t");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitdispreg((esz + ik): i64, "BX");
|
||||
emitline("\n");
|
||||
ik += 1;
|
||||
};
|
||||
emitline("\tSUBQ\t$1, (SP)\n");
|
||||
emitline("\tJMP\t"); emitline(ill); emitline("\n");
|
||||
emitlabel(ile);
|
||||
emitline("\tMOVQ\t16(SP), CX\n");
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", AX\n");
|
||||
emitline("\tIMULQ\tAX, CX\n");
|
||||
};
|
||||
emitline("\tMOVQ\t8(SP), DX\n");
|
||||
emitline("\tMOVQ\t(DX), BX\n");
|
||||
emitline("\tADDQ\tCX, BX\n");
|
||||
ik = 0;
|
||||
for (ik + 8 <= esz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((insscr + ik): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline("\n");
|
||||
ik += 8;
|
||||
};
|
||||
if (ik + 4 <= esz) {
|
||||
emitline("\tMOVL\t");
|
||||
emitoff((insscr + ik): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline("\n");
|
||||
ik += 4;
|
||||
};
|
||||
if (ik + 2 <= esz) {
|
||||
emitline("\tMOVW\t");
|
||||
emitoff((insscr + ik): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVW\tAX, ");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline("\n");
|
||||
ik += 2;
|
||||
};
|
||||
if (ik + 1 <= esz) {
|
||||
emitline("\tMOVB\t");
|
||||
emitoff((insscr + ik): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline("\n");
|
||||
ik += 1;
|
||||
};
|
||||
emitline("\tADDQ\t$24, SP\n");
|
||||
};
|
||||
|
||||
fn cgcall(c: *cgen, n: *node) void = {
|
||||
// Hare-style `append(s, v)` / `append(s, items...)` builtin —
|
||||
// special-cased before pushargsrev so the spread variant can run
|
||||
@@ -25644,6 +25939,18 @@ fn cgcall(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// insert(xs[idx], v) — #35 insert-half; mirror of
|
||||
// cstage cgen.c's N_CALL insert arm.
|
||||
if (streq(callee.str, "insert")) {
|
||||
if (n.list != nil) {
|
||||
if (n.list.next != nil) {
|
||||
if (n.list.next.next == nil) {
|
||||
cginsert(c, n);
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -5272,6 +5272,250 @@ fn cgdelete(c: *cgen, n: *node) void = {
|
||||
emitline("\tADDQ\t$16, SP\n");
|
||||
};
|
||||
|
||||
// cginsert — Hare `insert(xs[idx], v)`: delete()'s twin, the insert-half
|
||||
// of #35. Insert v BEFORE idx; idx==len is a legal end-insert. Lowered
|
||||
// as a DESUGAR to append(xs, v) + a rotate-right of [idx, len): cgappend
|
||||
// contributes grow (rt_ensure) and the whole #34 value-store dispatch
|
||||
// (scalar / str-slice header / tagged widen / struct fill) verbatim —
|
||||
// one boxing choke-point, byte-id by construction — landing v at slot
|
||||
// len-1; the rotate then moves it home through an esz frame scratch.
|
||||
// The rotate is cgdelete's shift loop in reverse (descending j keeps
|
||||
// src j behind dst j+1, the safe memmove-up direction) and, like
|
||||
// delete's, is a same-slice whole-stride raw byte move — no boxing
|
||||
// exists for any element kind. idx evaluates BEFORE the grow (Hare's
|
||||
// left-to-right operand order: insert(xs[len(xs)], v) sees the pre-grow
|
||||
// len); v's evaluation point inherits append's per-kind rules. Bounds
|
||||
// are implicit (no index check, matching delete). Mirrors cmd/w6c/
|
||||
// cgen.c's N_CALL insert arm instruction-for-instruction (rule-10
|
||||
// byte-id).
|
||||
fn cginsert(c: *cgen, n: *node) void = {
|
||||
let d: *node = n.list; // N_INDEX, checker-validated
|
||||
let base: *node = d.lhs;
|
||||
let v: *node = d.next;
|
||||
// esz off the STAMPED base tinfo (#34/#48 discipline — never the
|
||||
// value node). Peel TY_NAMED on indexable AND element, mirroring
|
||||
// cstage's type_chase_named on both (#8 family).
|
||||
let sti: *tinfo = base.type_: *tinfo;
|
||||
for (sti != nil && sti.kind == tykind.TY_NAMED) { sti = sti.under; };
|
||||
let esub: *tinfo = nil;
|
||||
if (sti != nil) { esub = sti.sub; };
|
||||
for (esub != nil && esub.kind == tykind.TY_NAMED) { esub = esub.under; };
|
||||
let esz: i32 = 0;
|
||||
if (esub != nil) { esz = esub.size: i32; };
|
||||
if (esz <= 0) {
|
||||
let m35c: str = "#35: insert() element size unresolved (rule-7)\n";
|
||||
os.write(2, m35c.ptr, m35c.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let hdr_lea: bool = false;
|
||||
let hdr_off: i32 = 0;
|
||||
let hdr_ok: bool = false;
|
||||
if (base.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, base.str);
|
||||
if (lc != nil) {
|
||||
hdr_lea = true;
|
||||
hdr_off = lc.off;
|
||||
hdr_ok = true;
|
||||
};
|
||||
};
|
||||
// (*p)[i]: the header lives behind a local ptr-to-slice —
|
||||
// delete's regex_shape twin.
|
||||
if (!hdr_ok && base.kind == nkind.N_UN) {
|
||||
if (base.op == tkind.TK_STAR && base.lhs != nil) {
|
||||
if (base.lhs.kind == nkind.N_IDENT) {
|
||||
let pc: *local = localfindnode(c, base.lhs.str);
|
||||
if (pc != nil) {
|
||||
hdr_off = pc.off;
|
||||
hdr_ok = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (!hdr_ok) {
|
||||
let m35d: str = "#35: insert() base shape unsupported (rule-7: local slice ident or deref-of-local only)\n";
|
||||
os.write(2, m35d.ptr, m35d.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
// Fresh esz-sized slot per SITE (esz varies; an @-name dedup
|
||||
// would mis-share across element types). Allocated BEFORE the
|
||||
// cgappend body's own scratch allocs — cstage order.
|
||||
let insscr: i32 = localalloc(c, "@insscr", esz, nil);
|
||||
cgexpr(c, d.rhs); // AX = idx
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
// Desugar in place and route through cgappend: cgen is
|
||||
// single-pass, base is an lhs node (never on a sibling chain),
|
||||
// and the checker has already validated this call — the mutation
|
||||
// is dead after this emission. The callee rename keeps the AST
|
||||
// consistent with cstage's re-dispatch.
|
||||
n.lhs.str = "append";
|
||||
n.list = base;
|
||||
base.next = v;
|
||||
cgappend(c, n);
|
||||
if (hdr_lea) {
|
||||
emitline("\tLEAQ\t");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
};
|
||||
emitoff(hdr_off: i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
emitline("\tMOVQ\tAX, DX\n");
|
||||
emitline("\tMOVQ\t8(DX), CX\n");
|
||||
emitline("\tSUBQ\t$1, CX\n");
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", AX\n");
|
||||
emitline("\tIMULQ\tAX, CX\n");
|
||||
};
|
||||
emitline("\tMOVQ\t(DX), BX\n");
|
||||
emitline("\tADDQ\tCX, BX\n");
|
||||
let ik: i32 = 0;
|
||||
for (ik + 8 <= esz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((insscr + ik): i64);
|
||||
emitline("(BP)\n");
|
||||
ik += 8;
|
||||
};
|
||||
if (ik + 4 <= esz) {
|
||||
emitline("\tMOVL\t");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitoff((insscr + ik): i64);
|
||||
emitline("(BP)\n");
|
||||
ik += 4;
|
||||
};
|
||||
if (ik + 2 <= esz) {
|
||||
emitline("\tMOVW\t");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVW\tAX, ");
|
||||
emitoff((insscr + ik): i64);
|
||||
emitline("(BP)\n");
|
||||
ik += 2;
|
||||
};
|
||||
if (ik + 1 <= esz) {
|
||||
emitline("\tMOVB\t");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitoff((insscr + ik): i64);
|
||||
emitline("(BP)\n");
|
||||
ik += 1;
|
||||
};
|
||||
emitline("\tMOVQ\t8(DX), AX\n");
|
||||
emitline("\tSUBQ\t$2, AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
let ill: str = mklabel(c, "ins_l");
|
||||
let ile: str = mklabel(c, "ins_e");
|
||||
emitlabel(ill);
|
||||
emitline("\tMOVQ\t(SP), CX\n");
|
||||
emitline("\tMOVQ\t16(SP), DX\n");
|
||||
emitline("\tCMPQ\tDX, CX\n");
|
||||
emitline("\tJL\t"); emitline(ile); emitline("\n");
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", AX\n");
|
||||
emitline("\tIMULQ\tAX, CX\n");
|
||||
};
|
||||
emitline("\tMOVQ\t8(SP), DX\n");
|
||||
emitline("\tMOVQ\t(DX), BX\n");
|
||||
emitline("\tADDQ\tCX, BX\n");
|
||||
ik = 0;
|
||||
for (ik + 8 <= esz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg((esz + ik): i64, "BX");
|
||||
emitline("\n");
|
||||
ik += 8;
|
||||
};
|
||||
if (ik + 4 <= esz) {
|
||||
emitline("\tMOVL\t");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitdispreg((esz + ik): i64, "BX");
|
||||
emitline("\n");
|
||||
ik += 4;
|
||||
};
|
||||
if (ik + 2 <= esz) {
|
||||
emitline("\tMOVW\t");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVW\tAX, ");
|
||||
emitdispreg((esz + ik): i64, "BX");
|
||||
emitline("\n");
|
||||
ik += 2;
|
||||
};
|
||||
if (ik + 1 <= esz) {
|
||||
emitline("\tMOVB\t");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitdispreg((esz + ik): i64, "BX");
|
||||
emitline("\n");
|
||||
ik += 1;
|
||||
};
|
||||
emitline("\tSUBQ\t$1, (SP)\n");
|
||||
emitline("\tJMP\t"); emitline(ill); emitline("\n");
|
||||
emitlabel(ile);
|
||||
emitline("\tMOVQ\t16(SP), CX\n");
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", AX\n");
|
||||
emitline("\tIMULQ\tAX, CX\n");
|
||||
};
|
||||
emitline("\tMOVQ\t8(SP), DX\n");
|
||||
emitline("\tMOVQ\t(DX), BX\n");
|
||||
emitline("\tADDQ\tCX, BX\n");
|
||||
ik = 0;
|
||||
for (ik + 8 <= esz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((insscr + ik): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline("\n");
|
||||
ik += 8;
|
||||
};
|
||||
if (ik + 4 <= esz) {
|
||||
emitline("\tMOVL\t");
|
||||
emitoff((insscr + ik): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline("\n");
|
||||
ik += 4;
|
||||
};
|
||||
if (ik + 2 <= esz) {
|
||||
emitline("\tMOVW\t");
|
||||
emitoff((insscr + ik): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVW\tAX, ");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline("\n");
|
||||
ik += 2;
|
||||
};
|
||||
if (ik + 1 <= esz) {
|
||||
emitline("\tMOVB\t");
|
||||
emitoff((insscr + ik): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline("\n");
|
||||
ik += 1;
|
||||
};
|
||||
emitline("\tADDQ\t$24, SP\n");
|
||||
};
|
||||
|
||||
fn cgcall(c: *cgen, n: *node) void = {
|
||||
// Hare-style `append(s, v)` / `append(s, items...)` builtin —
|
||||
// special-cased before pushargsrev so the spread variant can run
|
||||
@@ -5477,6 +5721,18 @@ fn cgcall(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// insert(xs[idx], v) — #35 insert-half; mirror of
|
||||
// cstage cgen.c's N_CALL insert arm.
|
||||
if (streq(callee.str, "insert")) {
|
||||
if (n.list != nil) {
|
||||
if (n.list.next != nil) {
|
||||
if (n.list.next.next == nil) {
|
||||
cginsert(c, n);
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -97,14 +97,15 @@ fn seedprimitives(c: *checker) void = {
|
||||
scopedefine(c.top, "nomem", skind.SK_TYPE, nil, nomemdecl);
|
||||
// `nil`, `true`, `false` are keywords — handled at the lex/parser
|
||||
// level, no symbol needed.
|
||||
// `len`, `alloc`, `free`, `append`, `delete` are pseudo-builtins;
|
||||
// scopedefine them so their use sites resolve. The actual semantics
|
||||
// live in cgen.
|
||||
// `len`, `alloc`, `free`, `append`, `delete`, `insert` are
|
||||
// pseudo-builtins; scopedefine them so their use sites resolve.
|
||||
// The actual semantics live in cgen.
|
||||
scopedefine(c.top, "len", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "alloc", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "free", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "append", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "delete", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "insert", skind.SK_FN, nil, nil);
|
||||
// #42: typed builtins folded to integer literals at check time —
|
||||
// `size(T)` / `align(T)` (arg is a type-expression planted by the
|
||||
// parser at lib/ww/parse/expr.ww:254-267) and `offset(e.f)` (arg is
|
||||
@@ -2592,7 +2593,7 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
|
||||
return tn;
|
||||
};
|
||||
// delete(xs[i]) — single-element slice removal, the
|
||||
// delete-half of #35 (insert() deferred). Mirrors
|
||||
// delete-half of #35 (insert() is the twin arm below). Mirrors
|
||||
// cstage cmd/wcc/check.c's delete arm. harec
|
||||
// ref/harec/src/check.c:1981-2027 also accepts the
|
||||
// range form delete(xs[i..j]) (EXPR_SLICE) —
|
||||
@@ -2629,6 +2630,56 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
|
||||
e.type_ = tinfofornode(c, tn): *void;
|
||||
return tn;
|
||||
};
|
||||
// insert(xs[idx], v) — single-element slice insertion
|
||||
// before idx, delete()'s twin (the insert-half of
|
||||
// #35). Mirrors cstage cmd/wcc/check.c's insert arm.
|
||||
// harec models append/insert in ONE checker arm
|
||||
// (ref/harec/src/check.c:745 check_expr_append_insert;
|
||||
// "insert" at :786): operand 1 must be an indexing
|
||||
// place over a slice; idx == len is a legal
|
||||
// end-insert. The spread form and the with-length
|
||||
// form (harec :821/:837) stay filed on #35; a range
|
||||
// PLACE is not Hare (harec asserts ACCESS_INDEX at
|
||||
// :784) — rejected, no task cite.
|
||||
if (streq(callee.str, "insert")) {
|
||||
let d: *node = e.list;
|
||||
if (d == nil || d.next == nil || d.next.next != nil) {
|
||||
cerr("insert: takes exactly two arguments\n");
|
||||
c.errs += 1;
|
||||
};
|
||||
if (d != nil) {
|
||||
exprtype(c, d, nil);
|
||||
if (d.kind == nkind.N_SLICE) {
|
||||
cerr("insert: range place is invalid; operand must be an indexing expression xs[i]\n");
|
||||
c.errs += 1;
|
||||
} else {
|
||||
if (d.kind != nkind.N_INDEX) {
|
||||
cerr("insert: operand must be an indexing expression xs[i]\n");
|
||||
c.errs += 1;
|
||||
} else {
|
||||
let basetn: *node = exprtype(c, d.lhs, nil);
|
||||
let u: *node = resolvealias(c, unwrapbang(basetn));
|
||||
// harec check.c:807 wording; a
|
||||
// fixed-size [N]T base lands here.
|
||||
if (u == nil || u.kind != nkind.N_TSLICE) {
|
||||
cerr("insert must operate on a slice\n");
|
||||
c.errs += 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (d.next != nil) {
|
||||
if (d.next.kind == nkind.N_SPREAD) {
|
||||
cerr("insert: spread form insert(xs[i], vs...) unimplemented (task #35)\n");
|
||||
c.errs += 1;
|
||||
} else {
|
||||
exprtype(c, d.next, nil);
|
||||
};
|
||||
};
|
||||
};
|
||||
let tn: *node = mktname(c, "void");
|
||||
e.type_ = tinfofornode(c, tn): *void;
|
||||
return tn;
|
||||
};
|
||||
};
|
||||
let nm: str;
|
||||
nm.ptr = nil; nm.len = 0;
|
||||
|
||||
@@ -10424,14 +10424,15 @@ fn seedprimitives(c: *checker) void = {
|
||||
scopedefine(c.top, "nomem", skind.SK_TYPE, nil, nomemdecl);
|
||||
// `nil`, `true`, `false` are keywords — handled at the lex/parser
|
||||
// level, no symbol needed.
|
||||
// `len`, `alloc`, `free`, `append`, `delete` are pseudo-builtins;
|
||||
// scopedefine them so their use sites resolve. The actual semantics
|
||||
// live in cgen.
|
||||
// `len`, `alloc`, `free`, `append`, `delete`, `insert` are
|
||||
// pseudo-builtins; scopedefine them so their use sites resolve.
|
||||
// The actual semantics live in cgen.
|
||||
scopedefine(c.top, "len", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "alloc", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "free", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "append", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "delete", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "insert", skind.SK_FN, nil, nil);
|
||||
// #42: typed builtins folded to integer literals at check time —
|
||||
// `size(T)` / `align(T)` (arg is a type-expression planted by the
|
||||
// parser at lib/ww/parse/expr.ww:254-267) and `offset(e.f)` (arg is
|
||||
@@ -12919,7 +12920,7 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
|
||||
return tn;
|
||||
};
|
||||
// delete(xs[i]) — single-element slice removal, the
|
||||
// delete-half of #35 (insert() deferred). Mirrors
|
||||
// delete-half of #35 (insert() is the twin arm below). Mirrors
|
||||
// cstage cmd/wcc/check.c's delete arm. harec
|
||||
// ref/harec/src/check.c:1981-2027 also accepts the
|
||||
// range form delete(xs[i..j]) (EXPR_SLICE) —
|
||||
@@ -12956,6 +12957,56 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
|
||||
e.type_ = tinfofornode(c, tn): *void;
|
||||
return tn;
|
||||
};
|
||||
// insert(xs[idx], v) — single-element slice insertion
|
||||
// before idx, delete()'s twin (the insert-half of
|
||||
// #35). Mirrors cstage cmd/wcc/check.c's insert arm.
|
||||
// harec models append/insert in ONE checker arm
|
||||
// (ref/harec/src/check.c:745 check_expr_append_insert;
|
||||
// "insert" at :786): operand 1 must be an indexing
|
||||
// place over a slice; idx == len is a legal
|
||||
// end-insert. The spread form and the with-length
|
||||
// form (harec :821/:837) stay filed on #35; a range
|
||||
// PLACE is not Hare (harec asserts ACCESS_INDEX at
|
||||
// :784) — rejected, no task cite.
|
||||
if (streq(callee.str, "insert")) {
|
||||
let d: *node = e.list;
|
||||
if (d == nil || d.next == nil || d.next.next != nil) {
|
||||
cerr("insert: takes exactly two arguments\n");
|
||||
c.errs += 1;
|
||||
};
|
||||
if (d != nil) {
|
||||
exprtype(c, d, nil);
|
||||
if (d.kind == nkind.N_SLICE) {
|
||||
cerr("insert: range place is invalid; operand must be an indexing expression xs[i]\n");
|
||||
c.errs += 1;
|
||||
} else {
|
||||
if (d.kind != nkind.N_INDEX) {
|
||||
cerr("insert: operand must be an indexing expression xs[i]\n");
|
||||
c.errs += 1;
|
||||
} else {
|
||||
let basetn: *node = exprtype(c, d.lhs, nil);
|
||||
let u: *node = resolvealias(c, unwrapbang(basetn));
|
||||
// harec check.c:807 wording; a
|
||||
// fixed-size [N]T base lands here.
|
||||
if (u == nil || u.kind != nkind.N_TSLICE) {
|
||||
cerr("insert must operate on a slice\n");
|
||||
c.errs += 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (d.next != nil) {
|
||||
if (d.next.kind == nkind.N_SPREAD) {
|
||||
cerr("insert: spread form insert(xs[i], vs...) unimplemented (task #35)\n");
|
||||
c.errs += 1;
|
||||
} else {
|
||||
exprtype(c, d.next, nil);
|
||||
};
|
||||
};
|
||||
};
|
||||
let tn: *node = mktname(c, "void");
|
||||
e.type_ = tinfofornode(c, tn): *void;
|
||||
return tn;
|
||||
};
|
||||
};
|
||||
let nm: str;
|
||||
nm.ptr = nil; nm.len = 0;
|
||||
@@ -25439,6 +25490,250 @@ fn cgdelete(c: *cgen, n: *node) void = {
|
||||
emitline("\tADDQ\t$16, SP\n");
|
||||
};
|
||||
|
||||
// cginsert — Hare `insert(xs[idx], v)`: delete()'s twin, the insert-half
|
||||
// of #35. Insert v BEFORE idx; idx==len is a legal end-insert. Lowered
|
||||
// as a DESUGAR to append(xs, v) + a rotate-right of [idx, len): cgappend
|
||||
// contributes grow (rt_ensure) and the whole #34 value-store dispatch
|
||||
// (scalar / str-slice header / tagged widen / struct fill) verbatim —
|
||||
// one boxing choke-point, byte-id by construction — landing v at slot
|
||||
// len-1; the rotate then moves it home through an esz frame scratch.
|
||||
// The rotate is cgdelete's shift loop in reverse (descending j keeps
|
||||
// src j behind dst j+1, the safe memmove-up direction) and, like
|
||||
// delete's, is a same-slice whole-stride raw byte move — no boxing
|
||||
// exists for any element kind. idx evaluates BEFORE the grow (Hare's
|
||||
// left-to-right operand order: insert(xs[len(xs)], v) sees the pre-grow
|
||||
// len); v's evaluation point inherits append's per-kind rules. Bounds
|
||||
// are implicit (no index check, matching delete). Mirrors cmd/w6c/
|
||||
// cgen.c's N_CALL insert arm instruction-for-instruction (rule-10
|
||||
// byte-id).
|
||||
fn cginsert(c: *cgen, n: *node) void = {
|
||||
let d: *node = n.list; // N_INDEX, checker-validated
|
||||
let base: *node = d.lhs;
|
||||
let v: *node = d.next;
|
||||
// esz off the STAMPED base tinfo (#34/#48 discipline — never the
|
||||
// value node). Peel TY_NAMED on indexable AND element, mirroring
|
||||
// cstage's type_chase_named on both (#8 family).
|
||||
let sti: *tinfo = base.type_: *tinfo;
|
||||
for (sti != nil && sti.kind == tykind.TY_NAMED) { sti = sti.under; };
|
||||
let esub: *tinfo = nil;
|
||||
if (sti != nil) { esub = sti.sub; };
|
||||
for (esub != nil && esub.kind == tykind.TY_NAMED) { esub = esub.under; };
|
||||
let esz: i32 = 0;
|
||||
if (esub != nil) { esz = esub.size: i32; };
|
||||
if (esz <= 0) {
|
||||
let m35c: str = "#35: insert() element size unresolved (rule-7)\n";
|
||||
os.write(2, m35c.ptr, m35c.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let hdr_lea: bool = false;
|
||||
let hdr_off: i32 = 0;
|
||||
let hdr_ok: bool = false;
|
||||
if (base.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, base.str);
|
||||
if (lc != nil) {
|
||||
hdr_lea = true;
|
||||
hdr_off = lc.off;
|
||||
hdr_ok = true;
|
||||
};
|
||||
};
|
||||
// (*p)[i]: the header lives behind a local ptr-to-slice —
|
||||
// delete's regex_shape twin.
|
||||
if (!hdr_ok && base.kind == nkind.N_UN) {
|
||||
if (base.op == tkind.TK_STAR && base.lhs != nil) {
|
||||
if (base.lhs.kind == nkind.N_IDENT) {
|
||||
let pc: *local = localfindnode(c, base.lhs.str);
|
||||
if (pc != nil) {
|
||||
hdr_off = pc.off;
|
||||
hdr_ok = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (!hdr_ok) {
|
||||
let m35d: str = "#35: insert() base shape unsupported (rule-7: local slice ident or deref-of-local only)\n";
|
||||
os.write(2, m35d.ptr, m35d.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
// Fresh esz-sized slot per SITE (esz varies; an @-name dedup
|
||||
// would mis-share across element types). Allocated BEFORE the
|
||||
// cgappend body's own scratch allocs — cstage order.
|
||||
let insscr: i32 = localalloc(c, "@insscr", esz, nil);
|
||||
cgexpr(c, d.rhs); // AX = idx
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
// Desugar in place and route through cgappend: cgen is
|
||||
// single-pass, base is an lhs node (never on a sibling chain),
|
||||
// and the checker has already validated this call — the mutation
|
||||
// is dead after this emission. The callee rename keeps the AST
|
||||
// consistent with cstage's re-dispatch.
|
||||
n.lhs.str = "append";
|
||||
n.list = base;
|
||||
base.next = v;
|
||||
cgappend(c, n);
|
||||
if (hdr_lea) {
|
||||
emitline("\tLEAQ\t");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
};
|
||||
emitoff(hdr_off: i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
emitline("\tMOVQ\tAX, DX\n");
|
||||
emitline("\tMOVQ\t8(DX), CX\n");
|
||||
emitline("\tSUBQ\t$1, CX\n");
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", AX\n");
|
||||
emitline("\tIMULQ\tAX, CX\n");
|
||||
};
|
||||
emitline("\tMOVQ\t(DX), BX\n");
|
||||
emitline("\tADDQ\tCX, BX\n");
|
||||
let ik: i32 = 0;
|
||||
for (ik + 8 <= esz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((insscr + ik): i64);
|
||||
emitline("(BP)\n");
|
||||
ik += 8;
|
||||
};
|
||||
if (ik + 4 <= esz) {
|
||||
emitline("\tMOVL\t");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitoff((insscr + ik): i64);
|
||||
emitline("(BP)\n");
|
||||
ik += 4;
|
||||
};
|
||||
if (ik + 2 <= esz) {
|
||||
emitline("\tMOVW\t");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVW\tAX, ");
|
||||
emitoff((insscr + ik): i64);
|
||||
emitline("(BP)\n");
|
||||
ik += 2;
|
||||
};
|
||||
if (ik + 1 <= esz) {
|
||||
emitline("\tMOVB\t");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitoff((insscr + ik): i64);
|
||||
emitline("(BP)\n");
|
||||
ik += 1;
|
||||
};
|
||||
emitline("\tMOVQ\t8(DX), AX\n");
|
||||
emitline("\tSUBQ\t$2, AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
let ill: str = mklabel(c, "ins_l");
|
||||
let ile: str = mklabel(c, "ins_e");
|
||||
emitlabel(ill);
|
||||
emitline("\tMOVQ\t(SP), CX\n");
|
||||
emitline("\tMOVQ\t16(SP), DX\n");
|
||||
emitline("\tCMPQ\tDX, CX\n");
|
||||
emitline("\tJL\t"); emitline(ile); emitline("\n");
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", AX\n");
|
||||
emitline("\tIMULQ\tAX, CX\n");
|
||||
};
|
||||
emitline("\tMOVQ\t8(SP), DX\n");
|
||||
emitline("\tMOVQ\t(DX), BX\n");
|
||||
emitline("\tADDQ\tCX, BX\n");
|
||||
ik = 0;
|
||||
for (ik + 8 <= esz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg((esz + ik): i64, "BX");
|
||||
emitline("\n");
|
||||
ik += 8;
|
||||
};
|
||||
if (ik + 4 <= esz) {
|
||||
emitline("\tMOVL\t");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitdispreg((esz + ik): i64, "BX");
|
||||
emitline("\n");
|
||||
ik += 4;
|
||||
};
|
||||
if (ik + 2 <= esz) {
|
||||
emitline("\tMOVW\t");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVW\tAX, ");
|
||||
emitdispreg((esz + ik): i64, "BX");
|
||||
emitline("\n");
|
||||
ik += 2;
|
||||
};
|
||||
if (ik + 1 <= esz) {
|
||||
emitline("\tMOVB\t");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitdispreg((esz + ik): i64, "BX");
|
||||
emitline("\n");
|
||||
ik += 1;
|
||||
};
|
||||
emitline("\tSUBQ\t$1, (SP)\n");
|
||||
emitline("\tJMP\t"); emitline(ill); emitline("\n");
|
||||
emitlabel(ile);
|
||||
emitline("\tMOVQ\t16(SP), CX\n");
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", AX\n");
|
||||
emitline("\tIMULQ\tAX, CX\n");
|
||||
};
|
||||
emitline("\tMOVQ\t8(SP), DX\n");
|
||||
emitline("\tMOVQ\t(DX), BX\n");
|
||||
emitline("\tADDQ\tCX, BX\n");
|
||||
ik = 0;
|
||||
for (ik + 8 <= esz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((insscr + ik): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline("\n");
|
||||
ik += 8;
|
||||
};
|
||||
if (ik + 4 <= esz) {
|
||||
emitline("\tMOVL\t");
|
||||
emitoff((insscr + ik): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline("\n");
|
||||
ik += 4;
|
||||
};
|
||||
if (ik + 2 <= esz) {
|
||||
emitline("\tMOVW\t");
|
||||
emitoff((insscr + ik): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVW\tAX, ");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline("\n");
|
||||
ik += 2;
|
||||
};
|
||||
if (ik + 1 <= esz) {
|
||||
emitline("\tMOVB\t");
|
||||
emitoff((insscr + ik): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitdispreg(ik: i64, "BX");
|
||||
emitline("\n");
|
||||
ik += 1;
|
||||
};
|
||||
emitline("\tADDQ\t$24, SP\n");
|
||||
};
|
||||
|
||||
fn cgcall(c: *cgen, n: *node) void = {
|
||||
// Hare-style `append(s, v)` / `append(s, items...)` builtin —
|
||||
// special-cased before pushargsrev so the spread variant can run
|
||||
@@ -25644,6 +25939,18 @@ fn cgcall(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// insert(xs[idx], v) — #35 insert-half; mirror of
|
||||
// cstage cgen.c's N_CALL insert arm.
|
||||
if (streq(callee.str, "insert")) {
|
||||
if (n.list != nil) {
|
||||
if (n.list.next != nil) {
|
||||
if (n.list.next.next == nil) {
|
||||
cginsert(c, n);
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user