kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit 261d004425ff5ab3be3242ace4927f15fe8f5ca6
parent b34c6beecd811a5aa7ac5bdbd2cc67c6ba99a032
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Mon, 15 Jun 2026 17:17:32 -0700

mc: give MCEmitter a per-function scratch arena

Generalizes the previous label-count reset: the MCEmitter now owns a
func_arena that backs all three per-function-transient allocations (the
label vector, forward-reference MCFixup nodes, and MCDataLabelRef nodes)
and is reset at every mc_begin_function. Previously these came from the
never-reset TU arena (c->tu), so the fixup/dataref nodes accumulated for
the whole TU and the label vector doubled-and-abandoned per function.

The whole-object CFI vectors (fdes/dirs) stay heap-realloc-backed (freed
in mc_free); only function-local scratch moves to func_arena. Safe because
labels/fixups/datarefs are all resolved within their producing function
(immediately for a backward ref, at mc_label_place for a forward one),
and cross-function references go through per-label SB_LOCAL object symbols
and emitted relocations in the ObjBuilder, not these structs.

Byte-identical (sqlite -O0 object bit-for-bit unchanged vs the prior
commit); cg-api, toy (1392/0), smoke-x64 green.

Diffstat:
Msrc/arch/mc.c | 38++++++++++++++++++++++++--------------
1 file changed, 24 insertions(+), 14 deletions(-)

diff --git a/src/arch/mc.c b/src/arch/mc.c @@ -111,7 +111,15 @@ typedef struct CfiFde { typedef struct MCImpl { MCEmitter base; - Arena* arena; + /* Per-function scratch: labels, forward-reference fixups, and data-label + * refs. All are resolved within the function that produced them (immediately + * for a backward ref, at mc_label_place for a forward one); cross-function + * references go through per-label SB_LOCAL object symbols, and the emitted + * bytes/relocations live in the ObjBuilder, not here. So this arena is reset + * at every mc_begin_function — it grows only to the largest single function's + * scratch instead of accumulating the whole TU's in c->tu. The whole-object + * CFI vectors (fdes/dirs) are heap-realloc'd separately, not from here. */ + Arena func_arena; /* `loc` lives on MCEmitter base now (so per-arch emit hooks can read it * to feed debug_emit_row). Use base.loc through impl_of(...)->base.loc * or directly mc->base.loc. */ @@ -143,7 +151,7 @@ static void labels_grow(MCImpl* mc, u32 want) { if (want <= mc->cap) return; u32 ncap = mc->cap ? mc->cap * 2 : 16; while (ncap < want) ncap *= 2; - MCLabelInfo* nbuf = arena_array(mc->arena, MCLabelInfo, ncap); + MCLabelInfo* nbuf = arena_array(&mc->func_arena, MCLabelInfo, ncap); if (mc->labels) memcpy(nbuf, mc->labels, sizeof(MCLabelInfo) * mc->nlabels); /* The grown tail is left uninitialized: mc_label_new fully assigns every * field of the one slot it hands out before any consumer indexes it, and @@ -399,7 +407,7 @@ void mc_emit_label_ref(MCEmitter* m, MCLabel id, RelocKind kind, compiler_panic(m->c, mc->base.loc, "MCEmitter: bad label %u", (unsigned)id); } MCLabelInfo* li = &mc->labels[id]; - MCFixup* fx = arena_new(mc->arena, MCFixup); + MCFixup* fx = arena_new(&mc->func_arena, MCFixup); fx->sec_id = m->section_id; fx->offset = obj_pos(m->obj, m->section_id) - width; /* fixup site is the just-emitted insn */ @@ -436,7 +444,7 @@ void mc_emit_label_data_reloc(MCEmitter* m, u32 data_sec, u32 data_offset, return; } { - MCDataLabelRef* r = arena_new(mc->arena, MCDataLabelRef); + MCDataLabelRef* r = arena_new(&mc->func_arena, MCDataLabelRef); r->data_sec = data_sec; r->data_offset = data_offset; r->kind = kind; @@ -579,7 +587,7 @@ MCEmitter* mc_new(Compiler* c, ObjBuilder* o) { base->cur_func_section = 0; base->cur_func_start = 0; - mc->arena = c->tu; + arena_init(&mc->func_arena, c->ctx->heap, 0); mc->labels = NULL; mc->nlabels = 0; mc->cap = 0; @@ -618,20 +626,22 @@ void mc_free(MCEmitter* m) { mc->fdes_cap = 0; mc->nfdes = 0; } + arena_fini(&mc->func_arena); } void mc_begin_function(MCEmitter* m, ObjSymId sym, u32 section_id, u32 start_offset) { if (!m) return; - /* Labels are function-local scratch: a reference is resolved immediately - * (backward) or at mc_label_place (forward) before the function ends, and - * cross-function references go through per-label SB_LOCAL object symbols, not - * this array. Reset the count and reuse the buffer across functions, so it - * grows only to the largest single function's label count. Without this it - * accumulated every function's labels into one array in the (never-reset) TU - * arena, doubling and abandoning each prior block (megabytes of dead arena + - * large mmap-backed allocations on label-heavy TUs). */ - impl_of(m)->nlabels = 0; + /* Reclaim the previous function's label/fixup/dataref scratch (see the + * func_arena note on MCImpl). The labels vector is re-grown from the reset + * arena on demand, so drop the stale pointer/count here. */ + { + MCImpl* mc = impl_of(m); + arena_reset(&mc->func_arena); + mc->labels = NULL; + mc->nlabels = 0; + mc->cap = 0; + } m->cur_func_sym = sym; m->cur_func_section = section_id; m->cur_func_start = start_offset;