commit 7387376e6968085c62386dbfb8a2c26ed6fa54d2
parent f0b8d11c52b728016a5533e5190309848e4e302b
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Mon, 15 Jun 2026 16:58:08 -0700
mc: reset MCEmitter labels per function
mc_begin_function now resets the label count and reuses the buffer instead
of letting it accumulate across the whole TU. The MCEmitter backs its label
array with the never-reset TU arena (mc->arena = c->tu), and labels are
function-local scratch (every reference resolves immediately for a backward
ref or at mc_label_place for a forward ref before the function ends;
cross-function references go through per-label SB_LOCAL object symbols, not
this array). Without the reset the array grew to the TU-wide label total,
doubling and abandoning each prior block in the TU arena.
On sqlite3.c -c -O0 this removed the 327K->655K->1.31M->2.62M label-array
cascade: total heap allocation 62.6 MB -> 57.4 MB (-5.2 MB), allocations
>=128 KiB (the mmap path) 36 -> 31. Output byte-identical; cg-api, toy
(1392/0), smoke-x64 all green.
Diffstat:
1 file changed, 9 insertions(+), 0 deletions(-)
diff --git a/src/arch/mc.c b/src/arch/mc.c
@@ -623,6 +623,15 @@ void mc_free(MCEmitter* m) {
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;
m->cur_func_sym = sym;
m->cur_func_section = section_id;
m->cur_func_start = start_offset;