selfhost/cmd/w6a: strip *arena cascade (γ-3)

amalloc has 0 callers post-γ-2; the *arena threaded through w6a's
init/dupstr/bufinit and the asm_.a / buf.a fields are vestigial.

Drop `import mem;` from opcodes/parse/obj/main, remove asm_.a and
buf.a struct fields, strip *arena from init/dupstr/bufinit
signatures, update 12 call sites. main.combined.ww auto-regenerated.

Comment at main.ww:36 retidied "argv/arena" → "argv-style" to match
post-strip reality.

Verified 132/132 incl. 991_w6a_ww + 995_self_rebuild byte-identity.
This commit is contained in:
2026-05-21 11:46:13 +09:00
parent 3dae4d9e9a
commit 12e0b4057f
5 changed files with 124 additions and 144 deletions

View File

@@ -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; };

View File

@@ -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; };

View File

@@ -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;

View File

@@ -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,

View File

@@ -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;