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:
@@ -785,6 +785,14 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
* call-arg stack uniformly. */
|
* call-arg stack uniformly. */
|
||||||
ins2(c, A_MOVQ, amem(D_BP, off), areg(D_AX));
|
ins2(c, A_MOVQ, amem(D_BP, off), areg(D_AX));
|
||||||
ins2(c, A_MOVQ, amem(D_BP, off + 8), areg(D_BX));
|
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 {
|
} else {
|
||||||
ins2(c, A_MOVQ, amem(D_BP, off), areg(D_AX));
|
ins2(c, A_MOVQ, amem(D_BP, off), areg(D_AX));
|
||||||
}
|
}
|
||||||
@@ -1577,6 +1585,31 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
}
|
}
|
||||||
break;
|
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) {
|
if (n->lhs->kind == N_IDENT) {
|
||||||
int off = localfind(locals, n->lhs->str);
|
int off = localfind(locals, n->lhs->str);
|
||||||
@@ -2895,6 +2928,52 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
}
|
}
|
||||||
break;
|
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:
|
default:
|
||||||
cgexpr_int(c, 0);
|
cgexpr_int(c, 0);
|
||||||
break;
|
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));
|
ins2(c, A_MOVQ, areg(D_BX), amem(D_BP, off + 16));
|
||||||
break;
|
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
|
/* struct literal initialiser: field-by-field store. The
|
||||||
* literal carries op == TK_ELLIPSIS when the source ends in
|
* literal carries op == TK_ELLIPSIS when the source ends in
|
||||||
* `..., ...` — in that case zero-fill the entire slot first,
|
* `..., ...` — in that case zero-fill the entire slot first,
|
||||||
|
|||||||
@@ -146,11 +146,10 @@ static const struct fixture fixtures[] = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slice-cap-via-raw-pointer",
|
"slice-cap-via-raw-pointer",
|
||||||
/* Drive the slice header through a raw u64 pointer cast
|
/* Drive the slice header through a raw u64 pointer cast.
|
||||||
* — the language has no slice-reassignment expression
|
* Pre-fix for slice reassignment this was the only way to
|
||||||
* yet, so this is the only way to fill the header from
|
* fill the header from ww source; kept because the path
|
||||||
* ww source today. .cap reads back as the value we
|
* still has to work. */
|
||||||
* wrote. */
|
|
||||||
"let buf: []u8;\n"
|
"let buf: []u8;\n"
|
||||||
"fn main() i32 = {\n"
|
"fn main() i32 = {\n"
|
||||||
"\tlet p: *u64 = (&buf): *u64;\n"
|
"\tlet p: *u64 = (&buf): *u64;\n"
|
||||||
@@ -159,6 +158,52 @@ static const struct fixture fixtures[] = {
|
|||||||
"};\n",
|
"};\n",
|
||||||
99,
|
99,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"slice-global-reassign-from-global",
|
||||||
|
/* `dst = src` for slice globals — both halves of the
|
||||||
|
* triple (ptr, len, cap) must propagate. */
|
||||||
|
"let dst: []u8;\n"
|
||||||
|
"let src: []u8;\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
"\tlet p: *u64 = (&src): *u64;\n"
|
||||||
|
"\tp[0] = 0x1000u64;\n"
|
||||||
|
"\tp[1] = 7u64;\n"
|
||||||
|
"\tp[2] = 99u64;\n"
|
||||||
|
"\tdst = src;\n"
|
||||||
|
"\treturn dst.len: i32 + dst.cap: i32;\n"
|
||||||
|
"};\n",
|
||||||
|
106,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slice-local-reassign-from-slice-expr",
|
||||||
|
/* `s = arr[lo:hi]` reassigns the slice local; cgexpr now
|
||||||
|
* emits N_SLICE as a triple. Previously the let-init
|
||||||
|
* specific N_SLICE path worked but reassignment dropped
|
||||||
|
* len/cap. */
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
"\tlet arr: [16]u8;\n"
|
||||||
|
"\tlet i: i32 = 0;\n"
|
||||||
|
"\tfor (i < 16) { arr[i] = i: u8; i += 1; };\n"
|
||||||
|
"\tlet s: []u8 = arr[3:10];\n"
|
||||||
|
"\ts = arr[1:5];\n"
|
||||||
|
"\treturn s.len: i32;\n"
|
||||||
|
"};\n",
|
||||||
|
4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slice-local-from-fn-return",
|
||||||
|
/* fn returning []u8 leaves (AX,BX,CX) at return; the let-
|
||||||
|
* init slice fallback stores the triple. */
|
||||||
|
"fn mkslice() []u8 = {\n"
|
||||||
|
"\tlet arr: [16]u8;\n"
|
||||||
|
"\treturn arr[3:9];\n"
|
||||||
|
"};\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
"\tlet s: []u8 = mkslice();\n"
|
||||||
|
"\treturn s.len: i32;\n"
|
||||||
|
"};\n",
|
||||||
|
6,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"struct-field-readwrite",
|
"struct-field-readwrite",
|
||||||
/* Top-level struct global. Field writes hit the global
|
/* Top-level struct global. Field writes hit the global
|
||||||
|
|||||||
Reference in New Issue
Block a user