diff --git a/selfhost/cmd/w6a/main.combined.ww b/selfhost/cmd/w6a/main.combined.ww index 2d48bd24..4ba02ea3 100644 --- a/selfhost/cmd/w6a/main.combined.ww +++ b/selfhost/cmd/w6a/main.combined.ww @@ -750,98 +750,6 @@ package rt; // a future task (task #39). ref/hare/rt/malloc.ha:27. @symbol("rt_malloc") export fn malloc(n: u64) *void; -// 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; - }; -}; - // types — integer limits. Mirrors Hare's types::limits (I8_MAX, …) // platform-fixed for amd64. Numeric helpers live in lib/math, matching // Hare's split between types::limits and math::. @@ -2778,8 +2686,6 @@ export fn rpad(s: str, p: rune, maxlen: i32) str = { package w6a; -import mem; - // ---- registers + operand kinds (from 6.out.h) ------------------------- // These must stay numerically aligned with the C enum so that ww-cgen // output (which reads them via `D_AX(SB)` etc.) lands on the same @@ -2965,7 +2871,6 @@ type afixup = struct { }; type asm_ = struct { - a: *arena, file: str, src: *u8, srclen: u64, @@ -3078,7 +2983,6 @@ export fn parsenum(p: *u8, n: u64) (i64, u64) = { package w6a; import os; -import mem; import strings; import lex; import opcodes; @@ -3203,8 +3107,7 @@ fn reglookup(p: *u8, n: u64) i32 = { return D_NONE; }; -export fn init(a: *asm_, ar: *arena, file: str, src: *u8, len: u64) void = { - a.a = ar; +export fn init(a: *asm_, file: str, src: *u8, len: u64) void = { a.file = file; a.src = src; a.srclen = len; @@ -3245,7 +3148,7 @@ fn perr(a: *asm_, msg: str) void = { }; // dupstr — copy n bytes from p into a fresh heap str. -fn dupstr(a: *arena, p: *u8, n: u64) str = { +fn dupstr(p: *u8, n: u64) str = { let view: str; view.ptr = p; view.len = n: i32; @@ -3397,7 +3300,7 @@ fn parseoperand(a: *asm_, p: *u8, offin: u64, n: u64, out: *aoperand) u64 = { let r: i32 = reglookup(p + rstart, rn); if (r == D_PSB) { out.atype = D_EXTERN; - out.asym = dupstr(a.a, p + istart, in_); + out.asym = dupstr(p + istart, in_); out.offset = symdisp; } else { out.atype = D_INDIR; @@ -3415,7 +3318,7 @@ fn parseoperand(a: *asm_, p: *u8, offin: u64, n: u64, out: *aoperand) u64 = { return off; }; out.atype = D_BRANCH; - out.asym = dupstr(a.a, p + istart, in_); + out.asym = dupstr(p + istart, in_); return off; }; // EOF inside ident @@ -3423,7 +3326,7 @@ fn parseoperand(a: *asm_, p: *u8, offin: u64, n: u64, out: *aoperand) u64 = { let r: i32 = reglookup(p + istart, in_); if (r != D_NONE) { out.atype = r; return off; }; out.atype = D_BRANCH; - out.asym = dupstr(a.a, p + istart, in_); + out.asym = dupstr(p + istart, in_); return off; }; @@ -3476,7 +3379,7 @@ export fn parse(a: *asm_) i32 = { else { scanid = false; }; }; }; if (q < n) { if (line[q] == 58u8) { // ':' - let nm: str = dupstr(a.a, line + i, q - i); + let nm: str = dupstr(line + i, q - i); // Pending label gets a NOP prog so addresses pin. if (pending.len > 0) { let np: *aprog = addprog(a, A_NOP, pending); @@ -3527,7 +3430,7 @@ export fn parse(a: *asm_) i32 = { }; let toop: *aoperand = pr.to; toop.atype = D_EXTERN; - toop.asym = dupstr(a.a, line + r0, commapos - r0); + toop.asym = dupstr(line + r0, commapos - r0); if (commapos < n) { let p2: u64 = commapos + 1u64; p2 = skipws(line, p2, n); @@ -3555,7 +3458,7 @@ export fn parse(a: *asm_) i32 = { }; let toop: *aoperand = pr.to; toop.atype = D_EXTERN; - toop.asym = dupstr(a.a, line + r0, lparen - r0); + toop.asym = dupstr(line + r0, lparen - r0); // Skip past `(SB)` to land just after ')'. let p2: u64 = lparen; let scand2: bool = true; @@ -3652,6 +3555,98 @@ export fn parse(a: *asm_) i32 = { return a.errs; }; +// 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; + }; +}; + // selfhost/cmd/w6a/asm.ww — port of cmd/w6a/asm.c. // // Encode the parsed aprog list into amd64 machine bytes, appending to @@ -4521,7 +4516,6 @@ export fn encode(a: *asm_) i32 = { package w6a; import os; -import mem; import opcodes; // Local wrappers around os.writeall's tagged return — collapse the @@ -4594,14 +4588,12 @@ fn wru64(p: *u8, off: u64, v: u64) void = { // ---- growable byte buffer --------------------------------------------- type buf = struct { - a: *arena, p: *u8, n: u64, cap: u64, }; -fn bufinit(b: *buf, a: *arena) void = { - b.a = a; +fn bufinit(b: *buf) void = { b.cap = 256u64; b.n = 0u64; let np: []u8 = alloc([], b.cap)!; @@ -4641,11 +4633,11 @@ fn bufputcstr(b: *buf, s: str) u32 = { // ---- emitelf --------------------------------------------------------- export fn emitelf(a: *asm_, fd: i32) i32 = { - let shstr: buf; bufinit(&shstr, a.a); - let str_: buf; bufinit(&str_, a.a); - let sym: buf; bufinit(&sym, a.a); - let rela: buf; bufinit(&rela, a.a); - let relad: buf; bufinit(&relad, a.a); + let shstr: buf; bufinit(&shstr); + let str_: buf; bufinit(&str_); + let sym: buf; bufinit(&sym); + let rela: buf; bufinit(&rela); + let relad: buf; bufinit(&relad); // Index 0 = empty. let zero: u8 = 0u8; @@ -4931,7 +4923,6 @@ package main; import os; import rt; -import mem; import strings; import opcodes; import lex; @@ -4957,7 +4948,7 @@ fn cstrlen(p: *u8) u64 = { return n; }; -// pathstr — view a NUL-terminated *u8 as a str. Bridges argv/arena +// pathstr — view a NUL-terminated *u8 as a str. Bridges argv-style // callers to lib/os entrypoints (str post-task-#23). fn pathstr(p: *u8) str = { let r: str; @@ -5035,14 +5026,13 @@ export fn main(argc: i32, argv: **u8) i32 = { return 1; }; - let ar: *arena = newarena(); let s: asm_; let nlen: u64 = cstrlen(src); let view: str; view.ptr = src; view.len = nlen: i32; let fname: str = strings.dup(view); - init(&s, ar, fname, buf, blen); + init(&s, fname, buf, blen); if (parse(&s) != 0) { return 1; }; if (encode(&s) != 0) { return 1; }; diff --git a/selfhost/cmd/w6a/main.ww b/selfhost/cmd/w6a/main.ww index 117b12b2..b9c9a2ef 100644 --- a/selfhost/cmd/w6a/main.ww +++ b/selfhost/cmd/w6a/main.ww @@ -8,7 +8,6 @@ package main; import os; import rt; -import mem; import strings; import opcodes; import lex; @@ -34,7 +33,7 @@ fn cstrlen(p: *u8) u64 = { return n; }; -// pathstr — view a NUL-terminated *u8 as a str. Bridges argv/arena +// pathstr — view a NUL-terminated *u8 as a str. Bridges argv-style // callers to lib/os entrypoints (str post-task-#23). fn pathstr(p: *u8) str = { let r: str; @@ -112,14 +111,13 @@ export fn main(argc: i32, argv: **u8) i32 = { return 1; }; - let ar: *arena = newarena(); let s: asm_; let nlen: u64 = cstrlen(src); let view: str; view.ptr = src; view.len = nlen: i32; let fname: str = strings.dup(view); - init(&s, ar, fname, buf, blen); + init(&s, fname, buf, blen); if (parse(&s) != 0) { return 1; }; if (encode(&s) != 0) { return 1; }; diff --git a/selfhost/cmd/w6a/obj.ww b/selfhost/cmd/w6a/obj.ww index 218ddc8f..bb014b2d 100644 --- a/selfhost/cmd/w6a/obj.ww +++ b/selfhost/cmd/w6a/obj.ww @@ -14,7 +14,6 @@ package w6a; import os; -import mem; import opcodes; // Local wrappers around os.writeall's tagged return — collapse the @@ -87,14 +86,12 @@ fn wru64(p: *u8, off: u64, v: u64) void = { // ---- growable byte buffer --------------------------------------------- type buf = struct { - a: *arena, p: *u8, n: u64, cap: u64, }; -fn bufinit(b: *buf, a: *arena) void = { - b.a = a; +fn bufinit(b: *buf) void = { b.cap = 256u64; b.n = 0u64; let np: []u8 = alloc([], b.cap)!; @@ -134,11 +131,11 @@ fn bufputcstr(b: *buf, s: str) u32 = { // ---- emitelf --------------------------------------------------------- export fn emitelf(a: *asm_, fd: i32) i32 = { - let shstr: buf; bufinit(&shstr, a.a); - let str_: buf; bufinit(&str_, a.a); - let sym: buf; bufinit(&sym, a.a); - let rela: buf; bufinit(&rela, a.a); - let relad: buf; bufinit(&relad, a.a); + let shstr: buf; bufinit(&shstr); + let str_: buf; bufinit(&str_); + let sym: buf; bufinit(&sym); + let rela: buf; bufinit(&rela); + let relad: buf; bufinit(&relad); // Index 0 = empty. let zero: u8 = 0u8; diff --git a/selfhost/cmd/w6a/opcodes.ww b/selfhost/cmd/w6a/opcodes.ww index 235d8260..7729a39d 100644 --- a/selfhost/cmd/w6a/opcodes.ww +++ b/selfhost/cmd/w6a/opcodes.ww @@ -3,8 +3,6 @@ package w6a; -import mem; - // ---- registers + operand kinds (from 6.out.h) ------------------------- // These must stay numerically aligned with the C enum so that ww-cgen // output (which reads them via `D_AX(SB)` etc.) lands on the same @@ -190,7 +188,6 @@ type afixup = struct { }; type asm_ = struct { - a: *arena, file: str, src: *u8, srclen: u64, diff --git a/selfhost/cmd/w6a/parse.ww b/selfhost/cmd/w6a/parse.ww index ff2e7f37..8e305524 100644 --- a/selfhost/cmd/w6a/parse.ww +++ b/selfhost/cmd/w6a/parse.ww @@ -13,7 +13,6 @@ package w6a; import os; -import mem; import strings; import lex; import opcodes; @@ -138,8 +137,7 @@ fn reglookup(p: *u8, n: u64) i32 = { return D_NONE; }; -export fn init(a: *asm_, ar: *arena, file: str, src: *u8, len: u64) void = { - a.a = ar; +export fn init(a: *asm_, file: str, src: *u8, len: u64) void = { a.file = file; a.src = src; a.srclen = len; @@ -180,7 +178,7 @@ fn perr(a: *asm_, msg: str) void = { }; // dupstr — copy n bytes from p into a fresh heap str. -fn dupstr(a: *arena, p: *u8, n: u64) str = { +fn dupstr(p: *u8, n: u64) str = { let view: str; view.ptr = p; view.len = n: i32; @@ -332,7 +330,7 @@ fn parseoperand(a: *asm_, p: *u8, offin: u64, n: u64, out: *aoperand) u64 = { let r: i32 = reglookup(p + rstart, rn); if (r == D_PSB) { out.atype = D_EXTERN; - out.asym = dupstr(a.a, p + istart, in_); + out.asym = dupstr(p + istart, in_); out.offset = symdisp; } else { out.atype = D_INDIR; @@ -350,7 +348,7 @@ fn parseoperand(a: *asm_, p: *u8, offin: u64, n: u64, out: *aoperand) u64 = { return off; }; out.atype = D_BRANCH; - out.asym = dupstr(a.a, p + istart, in_); + out.asym = dupstr(p + istart, in_); return off; }; // EOF inside ident @@ -358,7 +356,7 @@ fn parseoperand(a: *asm_, p: *u8, offin: u64, n: u64, out: *aoperand) u64 = { let r: i32 = reglookup(p + istart, in_); if (r != D_NONE) { out.atype = r; return off; }; out.atype = D_BRANCH; - out.asym = dupstr(a.a, p + istart, in_); + out.asym = dupstr(p + istart, in_); return off; }; @@ -411,7 +409,7 @@ export fn parse(a: *asm_) i32 = { else { scanid = false; }; }; }; if (q < n) { if (line[q] == 58u8) { // ':' - let nm: str = dupstr(a.a, line + i, q - i); + let nm: str = dupstr(line + i, q - i); // Pending label gets a NOP prog so addresses pin. if (pending.len > 0) { let np: *aprog = addprog(a, A_NOP, pending); @@ -462,7 +460,7 @@ export fn parse(a: *asm_) i32 = { }; let toop: *aoperand = pr.to; toop.atype = D_EXTERN; - toop.asym = dupstr(a.a, line + r0, commapos - r0); + toop.asym = dupstr(line + r0, commapos - r0); if (commapos < n) { let p2: u64 = commapos + 1u64; p2 = skipws(line, p2, n); @@ -490,7 +488,7 @@ export fn parse(a: *asm_) i32 = { }; let toop: *aoperand = pr.to; toop.atype = D_EXTERN; - toop.asym = dupstr(a.a, line + r0, lparen - r0); + toop.asym = dupstr(line + r0, lparen - r0); // Skip past `(SB)` to land just after ')'. let p2: u64 = lparen; let scand2: bool = true;