// selfhost/cmd/wcc/mem.ww — port of cmd/wcc/mem.c. // // Bump arena allocator. Backed by the runtime page allocator // (rt_malloc / rt_free), no libc. Each chunk is mmap'd; when the // current chunk runs out we link a fresh one. Freeing the arena // unmaps the chain. // // Memory handed out is 16-byte aligned. The C version under // cmd/wcc/ is retained until the three-stage bootstrap diffs clean. package wcc; import os; import rt; def ALIGN: u64 = 16u64; def INIT_CHUNK: u64 = 65536u64; def MAX_CHUNK: u64 = 4194304u64; def ARENA_SZ: u64 = 48u64; // sizeof(arena), kept in sync below type arena = struct { buf: *u8, off: u64, cap: u64, next: *arena, total: u64, }; fn roundup(n: u64, a: u64) u64 = { return (n + a - 1u64) & ~(a - 1u64); }; export fn newarena() *arena = { let a: *arena = rt.malloc(ARENA_SZ): *arena; a.buf = rt.malloc(INIT_CHUNK): *u8; a.off = 0u64; a.cap = INIT_CHUNK; a.next = nil; a.total = 0u64; return a; }; // Grow: link a fresh chunk in front of the head. We push the old // chunk into `next` so the head always describes the current bump // region. Chunk size doubles up to MAX_CHUNK. fn grow(a: *arena, need: u64) bool = { let want: u64 = a.cap * 2u64; if (want < need) { want = need; }; if (want > MAX_CHUNK) { want = MAX_CHUNK; }; if (want < need) { return false; }; // single allocation too big let old: *arena = rt.malloc(ARENA_SZ): *arena; old.buf = a.buf; old.off = a.off; old.cap = a.cap; old.next = a.next; old.total = 0u64; a.buf = rt.malloc(want): *u8; a.off = 0u64; a.cap = want; a.next = old; return true; }; export fn amalloc(a: *arena, n: u64) *void = { let need: u64 = roundup(n, ALIGN); if (need > a.cap - a.off) { if (!grow(a, need)) { return nil; }; }; let p: *u8 = a.buf + a.off; a.off += need; a.total += need; // Zero the region. Plan 9 amalloc zeroes; we mirror that here so // the checker can assume freshly allocated nodes start at 0. let i: u64 = 0u64; for (i < need) { p[i] = 0u8; i += 1u64; }; return p: *void; }; // astrndup — copy `n` bytes into the arena and produce a NUL-terminated // view. Returns a `str` whose ptr is arena-owned and whose len is `n` // (the trailing NUL is past `len`, so callers reading exactly n bytes // see no padding). Used by the lexer to capture token text. export fn astrndup(a: *arena, src: *u8, n: u64) str = { let p: *u8 = amalloc(a, n + 1u64): *u8; let i: u64 = 0u64; for (i < n) { p[i] = src[i]; i += 1u64; }; p[n] = 0u8; let r: str; r.ptr = p; r.len = n: i32; return r; }; export fn freearena(a: *arena) void = { for (a != nil) { let next: *arena = a.next; os.free(a.buf: *void, a.cap); os.free(a: *void, ARENA_SZ); a = next; }; };