wcc/cgen: #145 slice-copy-assign LHS s.arr[lo:hi]=bs — N_SLICE-LHS arm, runtime byte-copy loop, esz via type table (both stages)

Probe-first find for the path c2 appendlit (buf.buf[lo..hi]=bs): a
slice-copy-assign into a struct-field array sub-range emitted ZERO code —
silent NO-OP, both stages, both-wrong-identical (#263), so runtime is the
only net. N_ASSIGN gains an N_SLICE-LHS arm (cgen.c + cgenexpr.ww
slicebaseesz twin) reusing the N_SLICE-read base/esz cascade and copying
(hi-lo)*esz bytes from rhs.ptr via a runtime loop (len is runtime; no
REP/MOVSB). esz routed through the type table (rule 13; [N]u8->1). Hare
len(bs)==hi-lo assert deferred to #149.
This commit is contained in:
2026-06-08 09:58:22 +09:00
parent f1dcd4ecae
commit 6e1d958d9b
6 changed files with 541 additions and 0 deletions

View File

@@ -4771,6 +4771,57 @@ cgexpr(Cg *c, Node *n, Local *locals)
break;
}
case N_ASSIGN: {
/* #145 (c1.5a): bulk slice-copy-assign into a range place
* `s.arr[lo:hi] = bs` (LHS is N_SLICE). No legacy N_ASSIGN arm
* catches N_SLICE (they gate INDEX/STAR/DOT/ident), so the
* statement fell through to the scalar tail and emitted NOTHING
* — a silent no-op, both stages, byte-id-green (#263-class).
* Reuse the N_SLICE READ lowering: cgexpr(lhs) leaves AX = dst
* ptr (base+lo*esz), BX = hi-lo (element count), CX = cap. The
* element width is the SAME read-path esz (rule-13: chased base
* element tinfo, [N]u8 -> 1); multiply BX by it for the byte
* count, then a runtime-counted byte-granular copy from bs.ptr.
* Byte loop because the length is RUNTIME — the #265/#268 memcpy
* emitters are compile-time-sz unrolled and w6a has no REP/MOVSB.
* Only plain `=`; a compound op on a range place is meaningless.
*
* Hare asserts len(bs)==hi-lo (ref/hare/path/stack.ha:72,
* appendlit). We copy exactly hi-lo elems and do NOT runtime-
* check len(bs). The length-equality assert is task #149
* (rule-7: documented, not a c2 blocker — appendlit's lengths
* are equal by construction). */
if (n->op == TK_ASSIGN && n->lhs && n->lhs->kind == N_SLICE) {
Node *sl = n->lhs;
Node *sbase = sl->lhs;
Type *sbu = type_chase_named(sbase ? sbase->type : NULL);
int esz = (sbase && (sbase->kind == N_IDENT
|| sbase->kind == N_DOT || sbase->kind == N_ARRLIT)
&& sbu && sbu->sub) ? (int)sbu->sub->size : 1;
cgexpr(c, sl, locals); /* AX=dst ptr, BX=hi-lo */
if (esz > 1) {
ins2(c, A_MOVQ, aimm(esz), areg(D_DX));
ins2(c, A_IMULQ, areg(D_DX), areg(D_BX));
}
ins1(c, A_PUSHQ, areg(D_AX)); /* dst ptr */
ins1(c, A_PUSHQ, areg(D_BX)); /* byte count */
cgexpr(c, n->rhs, locals); /* AX=src ptr */
ins2(c, A_MOVQ, areg(D_AX), areg(D_SI));
ins1(c, A_POPQ, areg(D_CX)); /* byte count */
ins1(c, A_POPQ, areg(D_DI)); /* dst ptr */
char *top = mklabel(c, "scpy");
char *end = mklabel(c, "scpe");
label(c, top);
ins2(c, A_CMPQ, aimm(0), areg(D_CX));
ins1(c, A_JLE, abranch(end));
ins2(c, A_MOVB, amem(D_SI, 0), areg(D_AX));
ins2(c, A_MOVB, areg(D_AX), amem(D_DI, 0));
ins2(c, A_ADDQ, aimm(1), areg(D_SI));
ins2(c, A_ADDQ, aimm(1), areg(D_DI));
ins2(c, A_SUBQ, aimm(1), areg(D_CX));
ins1(c, A_JMP, abranch(top));
label(c, end);
break;
}
/* Discard lvalue `_ = expr;` — evaluate rhs for side effects,
* write nothing. */
if (n->lhs && n->lhs->kind == N_IDENT &&