w6c: lower append() to rt_ensure + inline store (hare model)
This commit is contained in:
2
Makefile
2
Makefile
@@ -39,7 +39,7 @@ W6L_SRC = cmd/w6l/main.c cmd/w6l/obj.c cmd/w6l/sym.c cmd/w6l/pass.c cmd/w6l/out.
|
||||
W6L_OBJ = $(W6L_SRC:cmd/w6l/%.c=$(OBJ)/w6l/%.o)
|
||||
|
||||
RT_S = rt/start.s rt/syscall.s rt/alloc.s rt/streq.s rt/abort.s
|
||||
RT_WW = rt/append.ww
|
||||
RT_WW = rt/ensure.ww
|
||||
RT_OBJ = $(RT_S:rt/%.s=$(OBJ)/rt/%.o) $(RT_WW:rt/%.ww=$(OBJ)/rt/%.o)
|
||||
|
||||
# Cstage: one-time C bootstrap (see BOOTSTRAP.md). Built by `cc`.
|
||||
|
||||
@@ -1304,23 +1304,36 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
if (n->lhs && n->lhs->kind == N_IDENT && n->lhs->str &&
|
||||
strcmp(n->lhs->str, "append") == 0 && n->list &&
|
||||
n->list->next) {
|
||||
/* append(s, v1, v2, ...) — one CALL per item.
|
||||
* append(s, items...) — runtime loop over items. */
|
||||
/* append(s, v) lowering — Hare's rt::ensure model.
|
||||
* ; AX = value
|
||||
* ; PUSHQ AX ; save
|
||||
* ; ADDQ $1, sn_off+8(BP) ; s.len += 1
|
||||
* ; LEAQ sn_off(BP), DI ; arg1 = &s
|
||||
* ; MOVQ esz, SI ; arg2 = membsz
|
||||
* ; CALL rt_ensure(SB) ; may realloc s.ptr
|
||||
* ; MOVQ sn_off+8(BP), CX ; CX = new len
|
||||
* ; SUBQ $1, CX ; slot index
|
||||
* ; [IMULQ esz, CX] ; byte offset (esz>1)
|
||||
* ; MOVQ sn_off(BP), BX ; reread s.ptr
|
||||
* ; ADDQ CX, BX ; BX = target
|
||||
* ; POPQ AX ; v
|
||||
* ; MOV* AX, (BX) ; store (MOVB / MOVQ)
|
||||
*
|
||||
* Spread form `append(s, items...)` runs this same body
|
||||
* in a counted loop over items. */
|
||||
Node *sn = n->list;
|
||||
Type *st = sn->type;
|
||||
Type *su = (st && st->kind == TY_NAMED) ? st->under : st;
|
||||
int esz = (su && su->sub) ? (int)su->sub->size : 1;
|
||||
const char *fn = (esz == 1) ? "appendu8" : "appendi64";
|
||||
int sn_off = (sn->kind == N_IDENT)
|
||||
? localfind(locals, sn->str) : 0;
|
||||
int store_op = (esz == 1) ? A_MOVB : A_MOVQ;
|
||||
for (Node *vn = sn->next; vn; vn = vn->next) {
|
||||
if (vn->kind == N_SPREAD &&
|
||||
vn->lhs && vn->lhs->kind == N_IDENT) {
|
||||
/* iterate items; items_off is the
|
||||
* spread-source slice's BP offset. */
|
||||
int it_off = localfind(locals, vn->lhs->str);
|
||||
int load_op = (esz == 1) ? A_MOVZBQ : A_MOVQ;
|
||||
/* push counter on stack */
|
||||
/* push counter (i) on stack */
|
||||
ins2(c, A_SUBQ, aimm(8), areg(D_SP));
|
||||
ins2(c, A_MOVQ, aimm(0), amem(D_SP, 0));
|
||||
char *ll = mklabel(c, "spr_l");
|
||||
@@ -1330,6 +1343,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
ins2(c, A_MOVQ, amem(D_BP, it_off + 8), areg(D_DX));
|
||||
ins2(c, A_CMPQ, areg(D_DX), areg(D_CX));
|
||||
ins1(c, A_JGE, abranch(le));
|
||||
/* AX = items.ptr[i] */
|
||||
ins2(c, A_MOVQ, amem(D_BP, it_off), areg(D_BX));
|
||||
if (esz > 1) {
|
||||
ins2(c, A_MOVQ, aimm(esz), areg(D_AX));
|
||||
@@ -1337,11 +1351,23 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
}
|
||||
ins2(c, A_ADDQ, areg(D_CX), areg(D_BX));
|
||||
ins2(c, load_op, amem(D_BX, 0), areg(D_AX));
|
||||
/* ensure + store one element */
|
||||
ins1(c, A_PUSHQ, areg(D_AX));
|
||||
ins2(c, A_LEAQ, amem(D_BP, sn_off), areg(D_AX));
|
||||
ins1(c, A_POPQ, areg(D_SI));
|
||||
ins2(c, A_MOVQ, areg(D_AX), areg(D_DI));
|
||||
ins1(c, A_CALL, masym(c, fn));
|
||||
ins2(c, A_ADDQ, aimm(1), amem(D_BP, sn_off + 8));
|
||||
ins2(c, A_LEAQ, amem(D_BP, sn_off), areg(D_DI));
|
||||
ins2(c, A_MOVQ, aimm(esz), areg(D_SI));
|
||||
ins1(c, A_CALL, masym(c, "rt_ensure"));
|
||||
ins2(c, A_MOVQ, amem(D_BP, sn_off + 8), areg(D_CX));
|
||||
ins2(c, A_SUBQ, aimm(1), 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_MOVQ, amem(D_BP, sn_off), areg(D_BX));
|
||||
ins2(c, A_ADDQ, areg(D_CX), areg(D_BX));
|
||||
ins1(c, A_POPQ, areg(D_AX));
|
||||
ins2(c, store_op, areg(D_AX), amem(D_BX, 0));
|
||||
/* loop tail */
|
||||
ins2(c, A_ADDQ, aimm(1), amem(D_SP, 0));
|
||||
ins1(c, A_JMP, abranch(ll));
|
||||
label(c, le);
|
||||
@@ -1350,11 +1376,20 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
}
|
||||
cgexpr(c, vn, locals); /* val → AX */
|
||||
ins1(c, A_PUSHQ, areg(D_AX));
|
||||
if (sn->kind == N_IDENT)
|
||||
ins2(c, A_LEAQ, amem(D_BP, sn_off), areg(D_AX));
|
||||
ins1(c, A_POPQ, areg(D_SI));
|
||||
ins2(c, A_MOVQ, areg(D_AX), areg(D_DI));
|
||||
ins1(c, A_CALL, masym(c, fn));
|
||||
ins2(c, A_ADDQ, aimm(1), amem(D_BP, sn_off + 8));
|
||||
ins2(c, A_LEAQ, amem(D_BP, sn_off), areg(D_DI));
|
||||
ins2(c, A_MOVQ, aimm(esz), areg(D_SI));
|
||||
ins1(c, A_CALL, masym(c, "rt_ensure"));
|
||||
ins2(c, A_MOVQ, amem(D_BP, sn_off + 8), areg(D_CX));
|
||||
ins2(c, A_SUBQ, aimm(1), 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_MOVQ, amem(D_BP, sn_off), areg(D_BX));
|
||||
ins2(c, A_ADDQ, areg(D_CX), areg(D_BX));
|
||||
ins1(c, A_POPQ, areg(D_AX));
|
||||
ins2(c, store_op, areg(D_AX), amem(D_BX, 0));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
49
rt/append.ww
49
rt/append.ww
@@ -1,49 +0,0 @@
|
||||
// rt/append.ww — slice runtime helpers, archived into libwwrt.a.
|
||||
//
|
||||
// The compiler lowers `append(s, v)` to a CALL to one of these by
|
||||
// element size (1 byte → appendu8, else → appendi64). User code
|
||||
// never `use`s this — the symbol comes in via the runtime archive,
|
||||
// like rt_alloc and rt_streq.
|
||||
|
||||
@symbol("rt_alloc") fn alloc(n: u64) *void;
|
||||
@symbol("rt_free") fn free(p: *void, n: u64) void;
|
||||
|
||||
export fn appendu8(s: *[]u8, v: u8) void = {
|
||||
if (s.len >= s.cap) {
|
||||
let nc: i32 = s.cap * 2;
|
||||
if (nc < 8) { nc = 8; };
|
||||
let np: *u8 = alloc(nc: u64): *u8;
|
||||
let i: i32 = 0;
|
||||
for (i < s.len) {
|
||||
np[i] = s.ptr[i];
|
||||
i += 1;
|
||||
};
|
||||
if (s.cap > 0) {
|
||||
free(s.ptr: *void, s.cap: u64);
|
||||
};
|
||||
s.ptr = np;
|
||||
s.cap = nc;
|
||||
};
|
||||
s.ptr[s.len] = v;
|
||||
s.len += 1;
|
||||
};
|
||||
|
||||
export fn appendi64(s: *[]i64, v: i64) void = {
|
||||
if (s.len >= s.cap) {
|
||||
let nc: i32 = s.cap * 2;
|
||||
if (nc < 8) { nc = 8; };
|
||||
let np: *i64 = alloc((nc * 8): u64): *i64;
|
||||
let i: i32 = 0;
|
||||
for (i < s.len) {
|
||||
np[i] = s.ptr[i];
|
||||
i += 1;
|
||||
};
|
||||
if (s.cap > 0) {
|
||||
free(s.ptr: *void, (s.cap * 8): u64);
|
||||
};
|
||||
s.ptr = np;
|
||||
s.cap = nc;
|
||||
};
|
||||
s.ptr[s.len] = v;
|
||||
s.len += 1;
|
||||
};
|
||||
49
rt/ensure.ww
Normal file
49
rt/ensure.ww
Normal file
@@ -0,0 +1,49 @@
|
||||
// rt/ensure.ww — slice growth helper, archived into libwwrt.a.
|
||||
//
|
||||
// Companion to the `append(s, v)` builtin. The compiler lowers
|
||||
// `append(s, v)` to:
|
||||
//
|
||||
// ; push v
|
||||
// ; s.len += 1
|
||||
// ; CALL rt_ensure(&s, sizeof(elem))
|
||||
// ; ; ensure may have realloc'd, so re-read s.ptr
|
||||
// ; pop v
|
||||
// ; *(s.ptr + (s.len - 1) * elem_size) = v
|
||||
//
|
||||
// One helper handles every element width via the membsz parameter —
|
||||
// no per-type wrapper functions (appendu8 / appendi64) needed.
|
||||
//
|
||||
// User code never `use`s this — the symbol is resolved at link time
|
||||
// from libwwrt.a, like rt_alloc and rt_streq.
|
||||
|
||||
@symbol("rt_alloc") fn alloc(n: u64) *void;
|
||||
@symbol("rt_free") fn free(p: *void, n: u64) void;
|
||||
|
||||
// Mirrors ww's []T header layout: 24 bytes with 8-byte slots.
|
||||
// ww's source uses i32 for len/cap but the compiler stores them in
|
||||
// 8-byte slots; declaring as i64 here keeps the field offsets right
|
||||
// for this polymorphic alias.
|
||||
type slice = struct {
|
||||
ptr: *u8,
|
||||
len: i64,
|
||||
cap: i64,
|
||||
};
|
||||
|
||||
export fn rt_ensure(s: *slice, membsz: u64) void = {
|
||||
if (s.cap >= s.len) { return; };
|
||||
let nc: i64 = s.cap * 2i64;
|
||||
if (nc < 8i64) { nc = 8i64; };
|
||||
for (nc < s.len) { nc *= 2i64; };
|
||||
let np: *u8 = alloc((nc: u64) * membsz): *u8;
|
||||
let n: u64 = (s.cap: u64) * membsz;
|
||||
let i: u64 = 0u64;
|
||||
for (i < n) {
|
||||
np[i] = s.ptr[i];
|
||||
i += 1u64;
|
||||
};
|
||||
if (s.cap > 0i64) {
|
||||
free(s.ptr: *void, (s.cap: u64) * membsz);
|
||||
};
|
||||
s.ptr = np;
|
||||
s.cap = nc;
|
||||
};
|
||||
Reference in New Issue
Block a user