The final 2 astrndup callers in w6a (main.ww fname capture and the
dupstr wrapper in parse.ww) now use the uniform γ-1 shape:
let view: str;
view.ptr = src;
view.len = n: i32;
out = strings.dup(view);
With both call sites converted, wcc.astrndup is dead and removed
from selfhost/cmd/wcc/mem.ww. amalloc + arena bootstrap stay
(other callers; #7 Phase B/C territory).
The two `// astrndup until #11 (w6a types shadow) is fixed.`
WHY-pointers are obsolete (#11 landed in 6696e95) and dropped per
CLAUDE.md rule 8.
dupstr in parse.ww keeps its (*arena, *u8, u64) signature; the
vestigial *arena param is tracked by task #10.
Verified 132/132 incl. 991_w6a_ww + 995_self_rebuild byte-identity.
92 lines
2.2 KiB
Plaintext
92 lines
2.2 KiB
Plaintext
// 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;
|
|
};
|
|
|
|
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;
|
|
};
|
|
};
|