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:
@@ -5383,6 +5383,201 @@ fn cgdelete(c: *cgen, n: *node) void = {
|
||||
emitline("\tADDQ\t$16, SP\n");
|
||||
};
|
||||
|
||||
// cgdeleterange — Hare `delete(xs[lo..hi])` (ww spells the range
|
||||
// `xs[lo:hi]`): range slice removal, the fold-5a prereq P2
|
||||
// (ref/hare/regex/regex.ha:333 delete(jump_idxs[group_level][..]);
|
||||
// harec ref/harec/src/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 cgdelete'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 cgdelete and the rest
|
||||
// of cgen). Mirrors cmd/w6c/cgen.c's N_CALL delete range arm
|
||||
// instruction-for-instruction (rule-10 byte-id).
|
||||
fn cgdeleterange(c: *cgen, n: *node) void = {
|
||||
let d: *node = n.list; // N_SLICE, checker-validated
|
||||
let base: *node = d.lhs;
|
||||
// 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 m35a: str = "#35: delete() element size unresolved (rule-7)\n";
|
||||
os.write(2, m35a.ptr, m35a.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let hdr_lea: bool = false;
|
||||
let hdr_off: i32 = 0;
|
||||
let hdr_ok: bool = false;
|
||||
let hdr_idx: bool = false;
|
||||
let osz: i32 = 0;
|
||||
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)[lo:hi]: header behind a local ptr-to-slice — cgdelete'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;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
// 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 (sti).
|
||||
if (!hdr_ok && base.kind == nkind.N_INDEX) {
|
||||
if (base.lhs != nil) {
|
||||
if (base.lhs.kind == nkind.N_IDENT) {
|
||||
let oc: *local = localfindnode(c, base.lhs.str);
|
||||
if (oc != nil) {
|
||||
hdr_idx = true;
|
||||
hdr_off = oc.off;
|
||||
if (sti != nil) { osz = sti.size: i32; };
|
||||
if (osz <= 0) {
|
||||
let m35c: str = "#35: delete() outer element size unresolved (rule-7)\n";
|
||||
os.write(2, m35c.ptr, m35c.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
hdr_ok = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (!hdr_ok) {
|
||||
let m35b: str = "#35: delete() range base shape unsupported (rule-7: local slice ident, deref-of-local, or indexed local slice only)\n";
|
||||
os.write(2, m35b.ptr, m35b.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
if (hdr_idx) {
|
||||
cgexpr(c, base.rhs);
|
||||
if (osz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(osz: i64);
|
||||
emitline(", CX\n");
|
||||
emitline("\tIMULQ\tCX, AX\n");
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(hdr_off: i64);
|
||||
emitline("(BP), CX\n");
|
||||
emitline("\tADDQ\tCX, AX\n");
|
||||
} else {
|
||||
if (hdr_lea) {
|
||||
emitline("\tLEAQ\t");
|
||||
} else {
|
||||
emitline("\tMOVQ\t");
|
||||
};
|
||||
emitoff(hdr_off: i64);
|
||||
emitline("(BP), AX\n");
|
||||
};
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
if (d.rhs != nil) {
|
||||
cgexpr(c, d.rhs);
|
||||
} else {
|
||||
emitline("\tMOVQ\t$0, AX\n");
|
||||
};
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
if (d.cond != nil) {
|
||||
cgexpr(c, d.cond);
|
||||
} else {
|
||||
emitline("\tMOVQ\t8(SP), CX\n");
|
||||
emitline("\tMOVQ\t8(CX), AX\n");
|
||||
};
|
||||
emitline("\tMOVQ\t(SP), CX\n");
|
||||
emitline("\tSUBQ\tCX, AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
let rll: str = mklabel(c, "rdl_l");
|
||||
let rle: str = mklabel(c, "rdl_e");
|
||||
emitlabel(rll);
|
||||
emitline("\tMOVQ\t16(SP), DX\n");
|
||||
emitline("\tMOVQ\t8(SP), CX\n");
|
||||
emitline("\tMOVQ\t(SP), AX\n");
|
||||
emitline("\tADDQ\tCX, AX\n");
|
||||
emitline("\tMOVQ\t8(DX), BX\n");
|
||||
emitline("\tCMPQ\tBX, AX\n");
|
||||
emitline("\tJGE\t"); emitline(rle); emitline("\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");
|
||||
emitline("\tMOVQ\t(SP), CX\n");
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", AX\n");
|
||||
emitline("\tIMULQ\tAX, CX\n");
|
||||
};
|
||||
emitline("\tADDQ\tBX, CX\n");
|
||||
let rk: i32 = 0;
|
||||
for (rk + 8 <= esz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(rk: i64, "CX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg(rk: i64, "BX");
|
||||
emitline("\n");
|
||||
rk += 8;
|
||||
};
|
||||
if (rk + 4 <= esz) {
|
||||
emitline("\tMOVL\t");
|
||||
emitdispreg(rk: i64, "CX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitdispreg(rk: i64, "BX");
|
||||
emitline("\n");
|
||||
rk += 4;
|
||||
};
|
||||
if (rk + 2 <= esz) {
|
||||
emitline("\tMOVW\t");
|
||||
emitdispreg(rk: i64, "CX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVW\tAX, ");
|
||||
emitdispreg(rk: i64, "BX");
|
||||
emitline("\n");
|
||||
rk += 2;
|
||||
};
|
||||
if (rk + 1 <= esz) {
|
||||
emitline("\tMOVB\t");
|
||||
emitdispreg(rk: i64, "CX");
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitdispreg(rk: i64, "BX");
|
||||
emitline("\n");
|
||||
rk += 1;
|
||||
};
|
||||
emitline("\tADDQ\t$1, 8(SP)\n");
|
||||
emitline("\tJMP\t"); emitline(rll); emitline("\n");
|
||||
emitlabel(rle);
|
||||
emitline("\tMOVQ\t16(SP), DX\n");
|
||||
emitline("\tMOVQ\t(SP), AX\n");
|
||||
emitline("\tMOVQ\t8(DX), BX\n");
|
||||
emitline("\tSUBQ\tAX, BX\n");
|
||||
emitline("\tMOVQ\tBX, 8(DX)\n");
|
||||
emitline("\tADDQ\t$24, 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
|
||||
@@ -5901,11 +6096,17 @@ fn cgcall(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// delete(xs[i]) — #35 delete-half; mirror of cstage
|
||||
// cgen.c's N_CALL delete arm.
|
||||
// delete(xs[i]) / delete(xs[lo:hi]) — #35
|
||||
// delete-half + fold-5a P2 range form; mirror of
|
||||
// cstage cgen.c's N_CALL delete arm (which branches
|
||||
// on d->kind == N_SLICE internally).
|
||||
if (streq(callee.str, "delete")) {
|
||||
if (n.list != nil) {
|
||||
if (n.list.next == nil) {
|
||||
if (n.list.kind == nkind.N_SLICE) {
|
||||
cgdeleterange(c, n);
|
||||
return;
|
||||
};
|
||||
cgdelete(c, n);
|
||||
return;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user