cgen + memio: cgoutarena → memio.dynamic, grow → dynamicgrow (β-3)
Phase 0 last β-shape site. Two concerns in one commit because the refactor surfaced the rename: - selfhost/cmd/wcc/cgen.ww cgout buffer (cgoutbuf/cap/len + arena + cgout_grow + CGOUT_INIT_CAP) → memio.state + io.stream behind a one-shot lazy-init guard. cgout_enable drops its *arena param; memio.reset in cgout_flush keeps the buffer sticky across fns so the arena's amortisation survives — re-init per fn would abandon the buffer and re-grow from 0 via the 8→…→65536 ladder for every function (no io.close path → no os.free). - lib/memio/memio.ww private fn grow → dynamicgrow. Symmetric with dynamicwrite / dynamicclose; required because cstage bundles all imported modules into a flat TU and resolves private fns by unqualified name, so the new `import memio;` in wcc's bundle collided with selfhost/cmd/wcc/mem.ww's arena `grow`. Module-aware private-fn scoping in cstage is task #9. @test fn dynamicgrow in memiotest.ww (same package as memio.ww) renamed to dynamicgrowcases to free the name; new suffix mirrors the file's existing fixedwritecases / borrowedreadcases convention. Lazy-init guard cgoutinit. memio.dynamic runs once on first cgout_enable; subsequent enables just set cgoutmode. Mirrors lib/log/log.ww:124 ensureinit. Without it, ~14 mmap syscalls per fn and ~100 MiB+ cumulative leak on a typical bootstrap. io.write bare discard in emitbytes mirrors lib/log/log.ww:169 — memio.dynamicwrite never returns io.closed (memio.ww:166). Verified 132/132 incl. 995_self_rebuild byte-identity.
This commit is contained in:
@@ -31,6 +31,8 @@ import tok;
|
||||
import typ;
|
||||
import sym;
|
||||
import strconv;
|
||||
import io;
|
||||
import memio;
|
||||
// Split files. Bundler pulls these in transitively so consumers only
|
||||
// need `use cgen;`. Order matters for the flat-bundle concat — utils
|
||||
// first so cgenexpr/stmt/decl can reference helpers defined here.
|
||||
@@ -678,60 +680,50 @@ fn localfind(c: *cgen, name: str) i32 = {
|
||||
// Cgfn defers its prologue (TEXT / SUBQ) until after the body so the
|
||||
// frame size reflects every emit-time localadd — the scanlocals pre-
|
||||
// pass that previously pre-computed it was dropped per #15/#26c. The
|
||||
// body is captured into cgoutbuf while cgoutmode != 0, then flushed
|
||||
// body is captured into cgoutstate while cgoutmode != 0, then flushed
|
||||
// after the prologue is written to stdout. Module-level state so the
|
||||
// existing emitline/emitint/emitlabel/emitsymname callers don't have
|
||||
// to thread a *cgen they don't already hold. Mirrors cstage's deferred
|
||||
// Prog-chain emit (cmd/w6c/cgen.c cgfn allocates `subsp`/`text` up
|
||||
// front and patches `from.offset` after the body finishes).
|
||||
let cgoutbuf: *u8 = nil;
|
||||
let cgoutbufcap: i32 = 0;
|
||||
let cgoutbuflen: i32 = 0;
|
||||
let cgoutmode: i32 = 0;
|
||||
let cgoutarena: *arena = nil;
|
||||
//
|
||||
// `cgoutinit` guards a one-shot [[memio.dynamic]] wiring so the
|
||||
// backing buffer is sticky across fns: [[cgout_flush]]'s
|
||||
// [[memio.reset]] rewinds `pos`/`len` without touching `cap`, so the
|
||||
// allocation amortises the same way the previous arena buffer did.
|
||||
// Re-init per fn would abandon the buffer (no [[io.close]] path → no
|
||||
// [[os.free]]) and re-grow from 0 via the 8→…→65536 ladder for every
|
||||
// function. Same idiom as lib/log/log.ww:124 `ensureinit`.
|
||||
let cgoutstate: memio.state;
|
||||
let cgoutstream: io.stream;
|
||||
let cgoutmode: i32 = 0;
|
||||
let cgoutinit: i32 = 0;
|
||||
|
||||
def CGOUT_INIT_CAP: i32 = 65536;
|
||||
|
||||
fn cgout_grow(need: i32) void = {
|
||||
if (need <= cgoutbufcap) { return; };
|
||||
let want: i32 = cgoutbufcap;
|
||||
if (want == 0) { want = CGOUT_INIT_CAP; };
|
||||
for (want < need) { want = want * 2; };
|
||||
let p: *u8 = amalloc(cgoutarena, want: u64): *u8;
|
||||
let i: i32 = 0;
|
||||
for (i < cgoutbuflen) {
|
||||
p[i] = cgoutbuf[i];
|
||||
i += 1;
|
||||
fn cgout_enable() void = {
|
||||
if (cgoutinit == 0) {
|
||||
memio.dynamic(&cgoutstate, &cgoutstream);
|
||||
cgoutinit = 1;
|
||||
};
|
||||
cgoutbuf = p;
|
||||
cgoutbufcap = want;
|
||||
};
|
||||
|
||||
fn cgout_enable(a: *arena) void = {
|
||||
cgoutarena = a;
|
||||
cgoutbuflen = 0;
|
||||
cgoutmode = 1;
|
||||
};
|
||||
|
||||
fn cgout_disable() void = { cgoutmode = 0; };
|
||||
|
||||
fn cgout_flush() void = {
|
||||
if (cgoutbuflen > 0) {
|
||||
os.write(1, cgoutbuf, cgoutbuflen: u64);
|
||||
cgoutbuflen = 0;
|
||||
if (cgoutstate.pos > 0) {
|
||||
os.write(1, cgoutstate.ptr, cgoutstate.pos: u64);
|
||||
memio.reset(&cgoutstate);
|
||||
};
|
||||
};
|
||||
|
||||
fn emitbytes(p: *u8, n: u64) void = {
|
||||
if (cgoutmode != 0) {
|
||||
let nn: i32 = n: i32;
|
||||
cgout_grow(cgoutbuflen + nn);
|
||||
let i: i32 = 0;
|
||||
for (i < nn) {
|
||||
cgoutbuf[cgoutbuflen + i] = p[i];
|
||||
i += 1;
|
||||
};
|
||||
cgoutbuflen += nn;
|
||||
let buf: []u8;
|
||||
buf.ptr = p;
|
||||
buf.len = n: i32;
|
||||
// memio.dynamicwrite never returns io.closed (memio.ww:166);
|
||||
// bare-discard mirrors lib/log/log.ww:169 fmt.fprintln.
|
||||
io.write(&cgoutstream, buf);
|
||||
} else {
|
||||
os.write(1, p, n);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user