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 &&
|
||||
|
||||
@@ -1436,7 +1436,7 @@ cexpr(Checker *c, Node *n)
|
||||
return n->type;
|
||||
}
|
||||
/* delete(xs[i]) — single-element slice removal, the
|
||||
* delete-half of #35 (insert() stays deferred). harec
|
||||
* delete-half of #35 (insert() is the twin arm below). harec
|
||||
* ref/harec/src/check.c:1981-2027 also accepts the range
|
||||
* form delete(xs[i..j]) (EXPR_SLICE) — out of scope here
|
||||
* (regex fold-2b's consumers are all single-element);
|
||||
@@ -1466,6 +1466,50 @@ cexpr(Checker *c, Node *n)
|
||||
n->lhs->type = ty_err;
|
||||
return n->type;
|
||||
}
|
||||
/* insert(xs[idx], v) — single-element slice insertion
|
||||
* before idx, delete()'s twin (the insert-half of #35).
|
||||
* 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
|
||||
* ref/hare os/exec platform_cmd.ha:86 idiom). The spread
|
||||
* form insert(xs[i], vs...) and the with-length form
|
||||
* (harec :821/:837) stay filed on #35 — regex fold-3's
|
||||
* consumers are all single-value. A range PLACE is not
|
||||
* Hare (harec asserts ACCESS_INDEX at :784; the form
|
||||
* never parses there) — rejected, no task cite. */
|
||||
if (n->lhs && n->lhs->kind == N_IDENT &&
|
||||
n->lhs->str && strcmp(n->lhs->str, "insert") == 0) {
|
||||
Node *d = n->list;
|
||||
if (d == NULL || d->next == NULL ||
|
||||
d->next->next != NULL)
|
||||
err(c, n->pos, "insert: takes exactly two arguments");
|
||||
if (d != NULL) {
|
||||
(void)cexpr(c, d);
|
||||
if (d->kind == N_SLICE)
|
||||
err(c, n->pos, "insert: range place is invalid; operand must be an indexing expression xs[i]");
|
||||
else if (d->kind != N_INDEX)
|
||||
err(c, n->pos, "insert: operand must be an indexing expression xs[i]");
|
||||
else {
|
||||
Type *bt = d->lhs ? d->lhs->type : NULL;
|
||||
while (bt && bt->kind == TY_NAMED)
|
||||
bt = bt->under;
|
||||
/* harec check.c:807 wording; a
|
||||
* fixed-size [N]T base lands here. */
|
||||
if (bt == NULL || bt->kind != TY_SLICE)
|
||||
err(c, n->pos, "insert must operate on a slice");
|
||||
}
|
||||
if (d->next != NULL) {
|
||||
if (d->next->kind == N_SPREAD)
|
||||
err(c, n->pos, "insert: spread form insert(xs[i], vs...) unimplemented (task #35)");
|
||||
else
|
||||
(void)cexpr(c, d->next);
|
||||
}
|
||||
}
|
||||
n->type = ty_void;
|
||||
n->lhs->type = ty_err;
|
||||
return n->type;
|
||||
}
|
||||
/* assert(cond[, msg]) / abort([msg]) — runtime checks that
|
||||
* call into rt_abort. msg must be a str when present.
|
||||
* Only treated as builtins when no user symbol shadows the
|
||||
|
||||
Reference in New Issue
Block a user