wcc+w6c+w6c_ww: delete() range form delete(xs[lo:hi]) (fold-5a P2)
Hare's delete also takes a slicing place (harec check.c:1981-2027 EXPR_SLICE; Hare spells it delete(xs[i..j])): remove [lo, hi) — shift [hi..len) down count = hi-lo strides, len -= count, cap unchanged; lo defaults 0, hi defaults len, so delete(xs[:]) clears the slice with storage retained. Checker accepts N_SLICE next to N_INDEX (object must chase to a slice, harec :2024); the old range-unimplemented reject and its #35 cite drop. Lowering (both stages, converged byte-identical by construction) is the single-element arm's same-slice whole-stride word-copy loop with a DYNAMIC src offset (count*esz via a src register) instead of the constant one-stride. Base shapes: local slice ident, deref-of-local, plus NEW indexed local-slice base xs[g][lo:hi] — the fold-5a consumer shape (regex.ha:333 delete(jump_idxs[group_level][..]); outer stride off the type table). Bounds stay implicit, inheriting the documented single-element posture (no index checks anywhere in cgen). Operands evaluate left-to-right, exactly once, before the shift (harec order); only the header ADDRESS is taken before operand eval, so a bound expression's writes through the slice land before the copy. test/809: 64 fixtures — full/explicit/re-clear/head/mid/tail/empty a:a/end-boundary len:len/explicit 0:0 on a never-appended (nil-ptr) slice, single-vs-range equivalence, cap preservation, esz 1/2/4/8/16/24 copy tails against the dynamic src, operand order-of-eval (lo/hi CALLs fire once each, in order) + aliasing-visibility pins, the EXACT [][]size regex consumer shape, deref base, 2 reject rows w/ diagnostic text; every accept row cs==ww asm byte-id. test/804: reject_range row retired (form now accepted), reject_nonindex text follows the widened message.
This commit is contained in:
193
cmd/w6c/cgen.c
193
cmd/w6c/cgen.c
@@ -6853,7 +6853,8 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* ; del_e:
|
||||
* ; MOVQ (SP), DX ; SUBQ $1, 8(DX)
|
||||
* ; ADDQ $16, SP */
|
||||
Node *d = n->list; /* N_INDEX, checker-validated */
|
||||
Node *d = n->list; /* N_INDEX or N_SLICE,
|
||||
* checker-validated */
|
||||
Node *base = d->lhs;
|
||||
Type *su = type_chase_named(base->type);
|
||||
Type *esub = (su && su->sub)
|
||||
@@ -6862,6 +6863,196 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
if (esz <= 0)
|
||||
fatal("#35: delete() element size unresolved "
|
||||
"(rule-7)");
|
||||
if (d->kind == N_SLICE) {
|
||||
/* delete(xs[lo:hi]) — range slice removal
|
||||
* (fold-5a prereq P2; harec check.c:1994
|
||||
* EXPR_SLICE). Shift [hi..len) down count =
|
||||
* hi-lo strides, len -= count, cap
|
||||
* unchanged; lo defaults 0, hi defaults
|
||||
* len. delete(xs[:]) never enters the copy
|
||||
* loop (lo+count == len at entry) and
|
||||
* zeroes len. The per-element move is the
|
||||
* single-element arm's same-slice
|
||||
* whole-stride word copy with a DYNAMIC
|
||||
* src offset (count*esz, via a src
|
||||
* register) instead of the constant
|
||||
* one-stride. Ascending j keeps src >=
|
||||
* dst, the safe memmove-down direction.
|
||||
* Bounds are implicit (no range check,
|
||||
* matching the single-element arm and the
|
||||
* rest of cgen).
|
||||
*
|
||||
* ; &hdr -> AX (ident / *p / xs[g])
|
||||
* ; PUSHQ AX ; 16(SP)=&hdr
|
||||
* ; lo -> AX ($0 default) ; PUSHQ AX
|
||||
* ; ; 8(SP)=j
|
||||
* ; count = hi - lo (hi: cgexpr or len)
|
||||
* ; PUSHQ AX ; (SP)=count
|
||||
* ; rdl_l:
|
||||
* ; MOVQ 16(SP), DX ; MOVQ 8(SP), CX
|
||||
* ; MOVQ (SP), AX ; ADDQ CX, AX
|
||||
* ; MOVQ 8(DX), BX
|
||||
* ; CMPQ BX, AX ; JGE rdl_e
|
||||
* ; ; j+count>=len
|
||||
* ; [IMULQ esz, CX]
|
||||
* ; MOVQ (DX), BX ; ADDQ CX, BX ; dst
|
||||
* ; MOVQ (SP), CX ; [IMULQ esz, CX]
|
||||
* ; ADDQ BX, CX ; src
|
||||
* ; word-copy esz bytes (CX) -> (BX)
|
||||
* ; ADDQ $1, 8(SP) ; JMP rdl_l
|
||||
* ; rdl_e:
|
||||
* ; MOVQ 16(SP), DX ; MOVQ (SP), AX
|
||||
* ; MOVQ 8(DX), BX ; SUBQ AX, BX
|
||||
* ; MOVQ BX, 8(DX) ; len-=count
|
||||
* ; ADDQ $24, SP */
|
||||
int hdr_lea = 0;
|
||||
int hdr_off = 0;
|
||||
int hdr_ok = 0;
|
||||
int hdr_idx = 0;
|
||||
int osz = 0;
|
||||
if (base->kind == N_IDENT &&
|
||||
localfind(locals, base->str) != 0) {
|
||||
hdr_lea = 1;
|
||||
hdr_off = localfind(locals, base->str);
|
||||
hdr_ok = 1;
|
||||
}
|
||||
/* (*p)[lo:hi]: header behind a local
|
||||
* ptr-to-slice — the single-element arm'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;
|
||||
}
|
||||
/* xs[g][lo:hi]: the header IS element g of
|
||||
* an outer local slice — the fold-5a
|
||||
* consumer shape (ref/hare/regex/regex.ha:333
|
||||
* delete(jump_idxs[group_level][..])).
|
||||
* Outer stride = the inner header type's
|
||||
* own table size (su). */
|
||||
if (!hdr_ok && base->kind == N_INDEX &&
|
||||
base->lhs &&
|
||||
base->lhs->kind == N_IDENT &&
|
||||
localfind(locals, base->lhs->str) != 0) {
|
||||
hdr_idx = 1;
|
||||
hdr_off = localfind(locals,
|
||||
base->lhs->str);
|
||||
osz = su ? (int)su->size : 0;
|
||||
if (osz <= 0)
|
||||
fatal("#35: delete() outer "
|
||||
"element size unresolved "
|
||||
"(rule-7)");
|
||||
hdr_ok = 1;
|
||||
}
|
||||
if (!hdr_ok)
|
||||
fatal("#35: delete() range base "
|
||||
"shape unsupported (rule-7: "
|
||||
"local slice ident, "
|
||||
"deref-of-local, or indexed "
|
||||
"local slice only)");
|
||||
if (hdr_idx) {
|
||||
cgexpr(c, base->rhs, locals);
|
||||
if (osz > 1) {
|
||||
ins2(c, A_MOVQ, aimm(osz),
|
||||
areg(D_CX));
|
||||
ins2(c, A_IMULQ, areg(D_CX),
|
||||
areg(D_AX));
|
||||
}
|
||||
ins2(c, A_MOVQ,
|
||||
amem(D_BP, hdr_off), areg(D_CX));
|
||||
ins2(c, A_ADDQ, areg(D_CX),
|
||||
areg(D_AX));
|
||||
} else 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));
|
||||
if (d->rhs)
|
||||
cgexpr(c, d->rhs, locals);
|
||||
else
|
||||
ins2(c, A_MOVQ, aimm(0), areg(D_AX));
|
||||
ins1(c, A_PUSHQ, areg(D_AX));
|
||||
if (d->cond)
|
||||
cgexpr(c, d->cond, locals);
|
||||
else {
|
||||
ins2(c, A_MOVQ, amem(D_SP, 8),
|
||||
areg(D_CX));
|
||||
ins2(c, A_MOVQ, amem(D_CX, 8),
|
||||
areg(D_AX));
|
||||
}
|
||||
ins2(c, A_MOVQ, amem(D_SP, 0), areg(D_CX));
|
||||
ins2(c, A_SUBQ, areg(D_CX), areg(D_AX));
|
||||
ins1(c, A_PUSHQ, areg(D_AX));
|
||||
char *rll = mklabel(c, "rdl_l");
|
||||
char *rle = mklabel(c, "rdl_e");
|
||||
label(c, rll);
|
||||
ins2(c, A_MOVQ, amem(D_SP, 16), areg(D_DX));
|
||||
ins2(c, A_MOVQ, amem(D_SP, 8), areg(D_CX));
|
||||
ins2(c, A_MOVQ, amem(D_SP, 0), areg(D_AX));
|
||||
ins2(c, A_ADDQ, areg(D_CX), areg(D_AX));
|
||||
ins2(c, A_MOVQ, amem(D_DX, 8), areg(D_BX));
|
||||
ins2(c, A_CMPQ, areg(D_BX), areg(D_AX));
|
||||
ins1(c, A_JGE, abranch(rle));
|
||||
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));
|
||||
ins2(c, A_MOVQ, amem(D_SP, 0), 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_ADDQ, areg(D_BX), areg(D_CX));
|
||||
int rk = 0;
|
||||
for (; rk + 8 <= esz; rk += 8) {
|
||||
ins2(c, A_MOVQ, amem(D_CX, rk),
|
||||
areg(D_AX));
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BX, rk));
|
||||
}
|
||||
if (rk + 4 <= esz) {
|
||||
ins2(c, A_MOVL, amem(D_CX, rk),
|
||||
areg(D_AX));
|
||||
ins2(c, A_MOVL, areg(D_AX),
|
||||
amem(D_BX, rk));
|
||||
rk += 4;
|
||||
}
|
||||
if (rk + 2 <= esz) {
|
||||
ins2(c, A_MOVW, amem(D_CX, rk),
|
||||
areg(D_AX));
|
||||
ins2(c, A_MOVW, areg(D_AX),
|
||||
amem(D_BX, rk));
|
||||
rk += 2;
|
||||
}
|
||||
if (rk + 1 <= esz) {
|
||||
ins2(c, A_MOVB, amem(D_CX, rk),
|
||||
areg(D_AX));
|
||||
ins2(c, A_MOVB, areg(D_AX),
|
||||
amem(D_BX, rk));
|
||||
rk += 1;
|
||||
}
|
||||
ins2(c, A_ADDQ, aimm(1), amem(D_SP, 8));
|
||||
ins1(c, A_JMP, abranch(rll));
|
||||
label(c, rle);
|
||||
ins2(c, A_MOVQ, amem(D_SP, 16), areg(D_DX));
|
||||
ins2(c, A_MOVQ, amem(D_SP, 0), areg(D_AX));
|
||||
ins2(c, A_MOVQ, amem(D_DX, 8), areg(D_BX));
|
||||
ins2(c, A_SUBQ, areg(D_AX), areg(D_BX));
|
||||
ins2(c, A_MOVQ, areg(D_BX), amem(D_DX, 8));
|
||||
ins2(c, A_ADDQ, aimm(24), areg(D_SP));
|
||||
break;
|
||||
}
|
||||
int hdr_lea = 0;
|
||||
int hdr_off = 0;
|
||||
int hdr_ok = 0;
|
||||
|
||||
@@ -1479,12 +1479,14 @@ cexpr(Checker *c, Node *n)
|
||||
n->lhs->type = ty_err;
|
||||
return n->type;
|
||||
}
|
||||
/* delete(xs[i]) — single-element slice removal, the
|
||||
* 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);
|
||||
* loud-rejected below, the range form stays filed on #35. */
|
||||
/* delete(xs[i]) / delete(xs[lo:hi]) — slice removal, the
|
||||
* delete-half of #35 (insert() is the twin arm below).
|
||||
* harec ref/harec/src/check.c:1981-2027 accepts both an
|
||||
* indexing place (EXPR_ACCESS/ACCESS_INDEX) and a slicing
|
||||
* place (EXPR_SLICE — Hare spells it delete(xs[i..j]));
|
||||
* either way the OBJECT must be a slice. The range form
|
||||
* is the fold-5a prereq P2 (regex.ha:333
|
||||
* delete(jump_idxs[group_level][..])). */
|
||||
if (n->lhs && n->lhs->kind == N_IDENT &&
|
||||
n->lhs->str && strcmp(n->lhs->str, "delete") == 0) {
|
||||
Node *d = n->list;
|
||||
@@ -1492,16 +1494,17 @@ cexpr(Checker *c, Node *n)
|
||||
err(c, n->pos, "delete: takes exactly one argument");
|
||||
if (d != NULL) {
|
||||
(void)cexpr(c, d);
|
||||
if (d->kind == N_SLICE)
|
||||
err(c, n->pos, "delete: range form delete(xs[i..j]) unimplemented (task #35)");
|
||||
else if (d->kind != N_INDEX)
|
||||
err(c, n->pos, "delete: operand must be an indexing expression xs[i]");
|
||||
/* harec check.c:2016's reject; wording
|
||||
* adapted to ww's delete: prefix. */
|
||||
if (d->kind != N_INDEX && d->kind != N_SLICE)
|
||||
err(c, n->pos, "delete: operand must be an indexing or slicing expression");
|
||||
else {
|
||||
Type *bt = d->lhs ? d->lhs->type : NULL;
|
||||
while (bt && bt->kind == TY_NAMED)
|
||||
bt = bt->under;
|
||||
/* harec check.c:2024 wording; a
|
||||
* fixed-size [N]T base lands here. */
|
||||
* fixed-size [N]T base and a str
|
||||
* base land here. */
|
||||
if (bt == NULL || bt->kind != TY_SLICE)
|
||||
err(c, n->pos, "delete must operate on a slice");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user