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:
195
cmd/w6c/cgen.c
195
cmd/w6c/cgen.c
@@ -6839,6 +6839,201 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
ins2(c, A_ADDQ, aimm(16), areg(D_SP));
|
||||
break;
|
||||
}
|
||||
if (n->lhs && n->lhs->kind == N_IDENT && n->lhs->str &&
|
||||
strcmp(n->lhs->str, "insert") == 0 && n->list &&
|
||||
n->list->next && n->list->next->next == NULL) {
|
||||
/* 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):
|
||||
* the append arm below 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 delete'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).
|
||||
*
|
||||
* ; AX = idx (cgexpr) ; PUSHQ AX
|
||||
* ; ...append(xs, v) body (grow + store at end,
|
||||
* ; push-balanced)...
|
||||
* ; LEAQ/MOVQ off(BP), AX ; &hdr (ident / *p)
|
||||
* ; PUSHQ AX ; (SP)=&hdr 8(SP)=idx
|
||||
* ; save elem[len-1] -> @insscr (word copy)
|
||||
* ; MOVQ 8(DX), AX ; SUBQ $2, AX ; PUSHQ AX
|
||||
* ; ; (SP)=j=len-2
|
||||
* ; ins_l:
|
||||
* ; MOVQ (SP), CX ; MOVQ 16(SP), DX
|
||||
* ; CMPQ DX, CX ; JL ins_e ; j < idx
|
||||
* ; [IMULQ esz, CX]
|
||||
* ; MOVQ 8(SP), DX ; MOVQ (DX), BX ; ADDQ CX, BX
|
||||
* ; word-copy esz bytes (BX) -> esz(BX)
|
||||
* ; SUBQ $1, (SP) ; JMP ins_l
|
||||
* ; ins_e:
|
||||
* ; store @insscr -> elem[idx] (word copy)
|
||||
* ; ADDQ $24, SP */
|
||||
Node *d = n->list; /* N_INDEX, checker-validated */
|
||||
Node *base = d->lhs;
|
||||
Node *v = d->next;
|
||||
Type *su = type_chase_named(base->type);
|
||||
Type *esub = (su && su->sub)
|
||||
? type_chase_named(su->sub) : NULL;
|
||||
int esz = esub ? (int)esub->size : 0;
|
||||
if (esz <= 0)
|
||||
fatal("#35: insert() element size unresolved "
|
||||
"(rule-7)");
|
||||
int hdr_lea = 0;
|
||||
int hdr_off = 0;
|
||||
int hdr_ok = 0;
|
||||
if (base->kind == N_IDENT &&
|
||||
localfind(locals, base->str) != 0) {
|
||||
hdr_lea = 1;
|
||||
hdr_off = localfind(locals, base->str);
|
||||
hdr_ok = 1;
|
||||
}
|
||||
/* (*p)[i]: header behind a local ptr-to-slice —
|
||||
* delete's regex_shape twin. */
|
||||
if (!hdr_ok && base->kind == N_UN &&
|
||||
base->op == TK_STAR && base->lhs &&
|
||||
base->lhs->kind == N_IDENT &&
|
||||
localfind(locals, base->lhs->str) != 0) {
|
||||
hdr_off = localfind(locals, base->lhs->str);
|
||||
hdr_ok = 1;
|
||||
}
|
||||
if (!hdr_ok)
|
||||
fatal("#35: insert() base shape unsupported "
|
||||
"(rule-7: local slice ident or "
|
||||
"deref-of-local only)");
|
||||
/* Fresh esz-sized slot per SITE (esz varies; an
|
||||
* @-name dedup would mis-share across element
|
||||
* types). */
|
||||
int ins_scr = local_alloc(c, &locals, "@insscr",
|
||||
esz, cg_frame);
|
||||
cgexpr(c, d->rhs, locals); /* AX = idx */
|
||||
ins1(c, A_PUSHQ, areg(D_AX));
|
||||
/* Desugar in place and re-dispatch into the append
|
||||
* arm: 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. */
|
||||
n->lhs->str = "append";
|
||||
n->list = base;
|
||||
base->next = v;
|
||||
cgexpr(c, n, locals);
|
||||
if (hdr_lea)
|
||||
ins2(c, A_LEAQ, amem(D_BP, hdr_off), areg(D_AX));
|
||||
else
|
||||
ins2(c, A_MOVQ, amem(D_BP, hdr_off), areg(D_AX));
|
||||
ins1(c, A_PUSHQ, areg(D_AX));
|
||||
ins2(c, A_MOVQ, areg(D_AX), areg(D_DX));
|
||||
ins2(c, A_MOVQ, amem(D_DX, 8), areg(D_CX));
|
||||
ins2(c, A_SUBQ, aimm(1), areg(D_CX));
|
||||
if (esz > 1) {
|
||||
ins2(c, A_MOVQ, aimm(esz), areg(D_AX));
|
||||
ins2(c, A_IMULQ, areg(D_AX), areg(D_CX));
|
||||
}
|
||||
ins2(c, A_MOVQ, amem(D_DX, 0), areg(D_BX));
|
||||
ins2(c, A_ADDQ, areg(D_CX), areg(D_BX));
|
||||
int ik = 0;
|
||||
for (; ik + 8 <= esz; ik += 8) {
|
||||
ins2(c, A_MOVQ, amem(D_BX, ik), areg(D_AX));
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, ins_scr + ik));
|
||||
}
|
||||
if (ik + 4 <= esz) {
|
||||
ins2(c, A_MOVL, amem(D_BX, ik), areg(D_AX));
|
||||
ins2(c, A_MOVL, areg(D_AX), amem(D_BP, ins_scr + ik));
|
||||
ik += 4;
|
||||
}
|
||||
if (ik + 2 <= esz) {
|
||||
ins2(c, A_MOVW, amem(D_BX, ik), areg(D_AX));
|
||||
ins2(c, A_MOVW, areg(D_AX), amem(D_BP, ins_scr + ik));
|
||||
ik += 2;
|
||||
}
|
||||
if (ik + 1 <= esz) {
|
||||
ins2(c, A_MOVB, amem(D_BX, ik), areg(D_AX));
|
||||
ins2(c, A_MOVB, areg(D_AX), amem(D_BP, ins_scr + ik));
|
||||
ik += 1;
|
||||
}
|
||||
ins2(c, A_MOVQ, amem(D_DX, 8), areg(D_AX));
|
||||
ins2(c, A_SUBQ, aimm(2), areg(D_AX));
|
||||
ins1(c, A_PUSHQ, areg(D_AX));
|
||||
char *ill = mklabel(c, "ins_l");
|
||||
char *ile = mklabel(c, "ins_e");
|
||||
label(c, ill);
|
||||
ins2(c, A_MOVQ, amem(D_SP, 0), areg(D_CX));
|
||||
ins2(c, A_MOVQ, amem(D_SP, 16), areg(D_DX));
|
||||
ins2(c, A_CMPQ, areg(D_DX), areg(D_CX));
|
||||
ins1(c, A_JL, abranch(ile));
|
||||
if (esz > 1) {
|
||||
ins2(c, A_MOVQ, aimm(esz), areg(D_AX));
|
||||
ins2(c, A_IMULQ, areg(D_AX), areg(D_CX));
|
||||
}
|
||||
ins2(c, A_MOVQ, amem(D_SP, 8), areg(D_DX));
|
||||
ins2(c, A_MOVQ, amem(D_DX, 0), areg(D_BX));
|
||||
ins2(c, A_ADDQ, areg(D_CX), areg(D_BX));
|
||||
ik = 0;
|
||||
for (; ik + 8 <= esz; ik += 8) {
|
||||
ins2(c, A_MOVQ, amem(D_BX, ik), areg(D_AX));
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BX, esz + ik));
|
||||
}
|
||||
if (ik + 4 <= esz) {
|
||||
ins2(c, A_MOVL, amem(D_BX, ik), areg(D_AX));
|
||||
ins2(c, A_MOVL, areg(D_AX), amem(D_BX, esz + ik));
|
||||
ik += 4;
|
||||
}
|
||||
if (ik + 2 <= esz) {
|
||||
ins2(c, A_MOVW, amem(D_BX, ik), areg(D_AX));
|
||||
ins2(c, A_MOVW, areg(D_AX), amem(D_BX, esz + ik));
|
||||
ik += 2;
|
||||
}
|
||||
if (ik + 1 <= esz) {
|
||||
ins2(c, A_MOVB, amem(D_BX, ik), areg(D_AX));
|
||||
ins2(c, A_MOVB, areg(D_AX), amem(D_BX, esz + ik));
|
||||
ik += 1;
|
||||
}
|
||||
ins2(c, A_SUBQ, aimm(1), amem(D_SP, 0));
|
||||
ins1(c, A_JMP, abranch(ill));
|
||||
label(c, ile);
|
||||
ins2(c, A_MOVQ, amem(D_SP, 16), areg(D_CX));
|
||||
if (esz > 1) {
|
||||
ins2(c, A_MOVQ, aimm(esz), areg(D_AX));
|
||||
ins2(c, A_IMULQ, areg(D_AX), areg(D_CX));
|
||||
}
|
||||
ins2(c, A_MOVQ, amem(D_SP, 8), areg(D_DX));
|
||||
ins2(c, A_MOVQ, amem(D_DX, 0), areg(D_BX));
|
||||
ins2(c, A_ADDQ, areg(D_CX), areg(D_BX));
|
||||
ik = 0;
|
||||
for (; ik + 8 <= esz; ik += 8) {
|
||||
ins2(c, A_MOVQ, amem(D_BP, ins_scr + ik), areg(D_AX));
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BX, ik));
|
||||
}
|
||||
if (ik + 4 <= esz) {
|
||||
ins2(c, A_MOVL, amem(D_BP, ins_scr + ik), areg(D_AX));
|
||||
ins2(c, A_MOVL, areg(D_AX), amem(D_BX, ik));
|
||||
ik += 4;
|
||||
}
|
||||
if (ik + 2 <= esz) {
|
||||
ins2(c, A_MOVW, amem(D_BP, ins_scr + ik), areg(D_AX));
|
||||
ins2(c, A_MOVW, areg(D_AX), amem(D_BX, ik));
|
||||
ik += 2;
|
||||
}
|
||||
if (ik + 1 <= esz) {
|
||||
ins2(c, A_MOVB, amem(D_BP, ins_scr + ik), areg(D_AX));
|
||||
ins2(c, A_MOVB, areg(D_AX), amem(D_BX, ik));
|
||||
ik += 1;
|
||||
}
|
||||
ins2(c, A_ADDQ, aimm(24), areg(D_SP));
|
||||
break;
|
||||
}
|
||||
if (n->lhs && n->lhs->kind == N_IDENT &&
|
||||
n->lhs->type == ty_err &&
|
||||
n->lhs->str && strcmp(n->lhs->str, "alloc") == 0 &&
|
||||
|
||||
Reference in New Issue
Block a user