w6c: slice reassignment — full triple flow through N_IDENT/N_SLICE/N_ASSIGN

N_IDENT for a slice local now loads (AX=ptr, BX=len, CX=cap), matching
the existing global-slice load.

cgexpr learns N_SLICE: `base[lo:hi]` leaves the same triple in
registers, so callers (return, arg push, reassignment) all share
one shape. The let-init's pre-existing N_SLICE direct-store path
stays as a specialisation; the new generic slice let-init catches
fn-returning-slice and slice-ident initialisers.

N_ASSIGN gains a TY_SLICE branch parallel to TY_STR: store all
three halves to the local slot or, for globals, stash CX into DI
before LEAQ-ing the address (CX is both the new cap and the
address scratch).
This commit is contained in:
2026-05-12 13:37:31 +09:00
parent 328a53de5b
commit 548547a1d0
2 changed files with 141 additions and 5 deletions

View File

@@ -785,6 +785,14 @@ cgexpr(Cg *c, Node *n, Local *locals)
* call-arg stack uniformly. */
ins2(c, A_MOVQ, amem(D_BP, off), areg(D_AX));
ins2(c, A_MOVQ, amem(D_BP, off + 8), areg(D_BX));
} else if (node_isslice(n)) {
/* slice values flow as (AX=ptr, BX=len, CX=cap)
* — mirror the global-slice load so a slice
* local can be reassigned, returned, or copied
* with the same triple convention. */
ins2(c, A_MOVQ, amem(D_BP, off + 0), areg(D_AX));
ins2(c, A_MOVQ, amem(D_BP, off + 8), areg(D_BX));
ins2(c, A_MOVQ, amem(D_BP, off + 16), areg(D_CX));
} else {
ins2(c, A_MOVQ, amem(D_BP, off), areg(D_AX));
}
@@ -1577,6 +1585,31 @@ cgexpr(Cg *c, Node *n, Local *locals)
}
break;
}
/* Slice reassignment: cgexpr produces (AX=ptr, BX=len,
* CX=cap). Store all three at off+0/+8/+16 (local) or
* via &name(SB) → DI scratch (global — CX holds the
* cap, so we need a different address register). */
if (lu && lu->kind == TY_SLICE) {
int off = localfind(locals, n->lhs->str);
if (off != 0) {
cgexpr(c, n->rhs, locals);
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, off + 0));
ins2(c, A_MOVQ, areg(D_BX), amem(D_BP, off + 8));
ins2(c, A_MOVQ, areg(D_CX), amem(D_BP, off + 16));
break;
}
if (let_islet(n->lhs->str)) {
cgexpr(c, n->rhs, locals);
ins2(c, A_MOVQ, areg(D_CX), areg(D_DI));
ins2(c, A_LEAQ, masym(c, n->lhs->str),
areg(D_CX));
ins2(c, A_MOVQ, areg(D_AX), amem(D_CX, 0));
ins2(c, A_MOVQ, areg(D_BX), amem(D_CX, 8));
ins2(c, A_MOVQ, areg(D_DI), amem(D_CX, 16));
break;
}
break;
}
}
if (n->lhs->kind == N_IDENT) {
int off = localfind(locals, n->lhs->str);
@@ -2895,6 +2928,52 @@ cgexpr(Cg *c, Node *n, Local *locals)
}
break;
}
case N_SLICE: {
/* base[lo:hi] as a slice value. Leaves the triple in
* (AX=base+lo, BX=hi-lo, CX=hi-lo) so callers can route
* to a slice slot, return, or arg with the same ABI. Cap
* defaults to the new length — there's no syntax for a
* larger cap yet. Element scaling on the ptr isn't wired
* (matches the let-init path), so non-u8 slices need a
* follow-up audit when fixtures exercise them. */
Node *base = n->lhs;
Node *lo = n->rhs;
Node *hi = n->cond;
Type *bt = base ? base->type : NULL;
Type *bu = (bt && bt->kind == TY_NAMED) ? bt->under : bt;
if (base && base->kind == N_IDENT) {
int boff = localfind(locals, base->str);
if (bu && bu->kind == TY_ARRAY) {
ins2(c, A_LEAQ, amem(D_BP, boff), areg(D_AX));
} else {
ins2(c, A_MOVQ, amem(D_BP, boff), areg(D_AX));
}
} else if (base) {
cgexpr(c, base, locals);
}
ins1(c, A_PUSHQ, areg(D_AX));
if (lo) cgexpr(c, lo, locals);
else cgexpr_int(c, 0);
ins1(c, A_PUSHQ, areg(D_AX));
if (hi) {
cgexpr(c, hi, locals);
} else if (bu && bu->kind == TY_ARRAY) {
cgexpr_int(c, (long long)bu->alen);
} else if (base && base->kind == N_IDENT && bu &&
(bu->kind == TY_SLICE || bu->kind == TY_STR)) {
int boff = localfind(locals, base->str);
ins2(c, A_MOVQ, amem(D_BP, boff + 8), areg(D_AX));
} else {
cgexpr_int(c, 0);
}
ins2(c, A_MOVQ, areg(D_AX), areg(D_BX));
ins1(c, A_POPQ, areg(D_CX));
ins1(c, A_POPQ, areg(D_AX));
ins2(c, A_ADDQ, areg(D_CX), areg(D_AX));
ins2(c, A_SUBQ, areg(D_CX), areg(D_BX));
ins2(c, A_MOVQ, areg(D_BX), areg(D_CX));
break;
}
default:
cgexpr_int(c, 0);
break;
@@ -3091,6 +3170,18 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
ins2(c, A_MOVQ, areg(D_BX), amem(D_BP, off + 16));
break;
}
/* Generic slice rhs (e.g. fn returning []u8, slice ident,
* slice-typed param). cgexpr leaves (AX=ptr, BX=len, CX=
* cap); store all three into the local slot. Runs after
* the alloc and N_SLICE specialisations above so they keep
* their direct-store shape. */
if (n->rhs && lu && lu->kind == TY_SLICE && sz == 24) {
cgexpr(c, n->rhs, *locals);
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, off + 0));
ins2(c, A_MOVQ, areg(D_BX), amem(D_BP, off + 8));
ins2(c, A_MOVQ, areg(D_CX), amem(D_BP, off + 16));
break;
}
/* struct literal initialiser: field-by-field store. The
* literal carries op == TK_ELLIPSIS when the source ends in
* `..., ...` — in that case zero-fill the entire slot first,