kit

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

commit 9437e8f1750cb6133f73a22b6efe18bdf3717181
parent 54c8a075d684c8901b0dd5e69625569d0a3f8137
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu, 14 May 2026 17:34:12 -0700

Add scoped timing metrics for run JIT path

Diffstat:
Mdriver/driver.h | 8++++++++
Mdriver/env.c | 20++++++++++++++++++++
Mdriver/run.c | 93+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Minclude/cfree.h | 8++++++++
Msrc/api/pipeline.c | 9+++++++++
Asrc/core/metrics.h | 25+++++++++++++++++++++++++
Msrc/link/link_jit.c | 16++++++++++++++++
Msrc/link/link_layout.c | 20++++++++++++++++++++
Msrc/opt/opt.c | 36++++++++++++++++++++++++++++++++++++
Msrc/opt/pass_lower.c | 11++++++++++-
10 files changed, 245 insertions(+), 1 deletion(-)

diff --git a/driver/driver.h b/driver/driver.h @@ -75,6 +75,7 @@ typedef struct DriverEnv { const CfreeExecMem* execmem; const CfreeDbgOs* dbg_os; /* NULL unless `cfree dbg` paths run */ const CfreeJitTls* jit_tls; /* NULL unless `cfree run` w/ TLV paths run */ + const CfreeMetrics* metrics; /* optional scoped metrics sink */ int64_t now; /* unix seconds; -1 = unknown */ } DriverEnv; @@ -153,9 +154,16 @@ int driver_path_exists(const char* path); /* Diagnostic printing to host stderr. Format is `"<tool>: <fmt>\n"`. */ void driver_errf(const char* tool, const char* fmt, ...); +/* Raw hosted stderr log. Used by optional metrics so libcfree stays callback- + * only and freestanding. */ +void driver_logf(const char* fmt, ...); + /* Formatted output to stdout. */ void driver_printf(const char* fmt, ...); +/* Monotonic host time in nanoseconds, or 0 if unavailable. */ +uint64_t driver_now_ns(void); + /* Lookup a process environment variable; returns NULL if unset. The returned * pointer aliases libc-owned storage and is valid until the next setenv/ * putenv from any caller. */ diff --git a/driver/env.c b/driver/env.c @@ -1129,6 +1129,8 @@ void driver_env_init(DriverEnv* e) { g_jit_tls_posix.user = NULL; e->jit_tls = &g_jit_tls_posix; + e->metrics = NULL; + /* Reproducible-build precedent: SOURCE_DATE_EPOCH wins over wall clock. * If neither is set or the env value doesn't parse, advertise -1 ("no * clock") and pp falls back to C11 placeholders. */ @@ -1158,6 +1160,7 @@ CfreeEnv driver_env_to_cfree(const DriverEnv* e) { ce.execmem = e->execmem; ce.dbg_os = e->dbg_os; ce.jit_tls = e->jit_tls; + ce.metrics = e->metrics; ce.now = e->now; return ce; } @@ -1218,6 +1221,14 @@ void driver_errf(const char* tool, const char* fmt, ...) { fputc('\n', stderr); } +void driver_logf(const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputc('\n', stderr); +} + void driver_printf(const char* fmt, ...) { va_list ap; va_start(ap, fmt); @@ -1225,6 +1236,15 @@ void driver_printf(const char* fmt, ...) { va_end(ap); } +uint64_t driver_now_ns(void) { +#if defined(CLOCK_MONOTONIC) + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + return (uint64_t)ts.tv_sec * 1000000000ull + (uint64_t)ts.tv_nsec; +#endif + return 0; +} + const char* driver_getenv(const char* name) { return getenv(name); } int driver_load_bytes(const CfreeFileIO* io, const char* tool, const char* path, diff --git a/driver/run.c b/driver/run.c @@ -23,6 +23,7 @@ typedef struct RunOptions { int opt_level; int debug_info; + int metrics; int warnings_are_errors; /* -Werror */ uint32_t max_errors; /* -fmax-errors=N */ const char* entry; /* -e, default "main" */ @@ -35,6 +36,73 @@ typedef struct RunOptions { uint32_t prog_argc; } RunOptions; +#define RUN_METRIC_MAX_DEPTH 64u + +typedef struct RunMetricFrame { + const char* name; + uint64_t start_ns; +} RunMetricFrame; + +typedef struct RunMetrics { + CfreeMetrics iface; + RunMetricFrame stack[RUN_METRIC_MAX_DEPTH]; + uint32_t depth; +} RunMetrics; + +static void run_metrics_scope_begin(void* user, const char* name) { + RunMetrics* m = (RunMetrics*)user; + if (!m || m->depth >= RUN_METRIC_MAX_DEPTH) return; + m->stack[m->depth].name = name; + m->stack[m->depth].start_ns = driver_now_ns(); + m->depth++; +} + +static void run_metrics_scope_end(void* user, const char* name) { + RunMetrics* m = (RunMetrics*)user; + RunMetricFrame f; + uint64_t end_ns; + uint64_t elapsed; + uint32_t depth; + if (!m || m->depth == 0) return; + m->depth--; + depth = m->depth; + f = m->stack[depth]; + end_ns = driver_now_ns(); + elapsed = (end_ns >= f.start_ns) ? (end_ns - f.start_ns) : 0; + driver_logf("%*s%s %.3f ms", (int)(depth * 2u), "", + f.name ? f.name : name, (double)elapsed / 1000000.0); +} + +static void run_metrics_count(void* user, const char* name, uint64_t value) { + RunMetrics* m = (RunMetrics*)user; + uint32_t depth = m ? m->depth : 0; + driver_logf("%*s%s=%llu", (int)(depth * 2u), "", name, + (unsigned long long)value); +} + +static void run_metrics_init(RunMetrics* m) { + m->iface.scope_begin = run_metrics_scope_begin; + m->iface.scope_end = run_metrics_scope_end; + m->iface.count = run_metrics_count; + m->iface.user = m; + m->depth = 0; +} + +static void run_metrics_begin(RunMetrics* m, const char* name) { + if (m) m->iface.scope_begin(m->iface.user, name); +} + +static void run_metrics_end(RunMetrics* m, const char* name) { + if (m) m->iface.scope_end(m->iface.user, name); +} + +static void run_metrics_finish(RunMetrics* m) { + while (m && m->depth) { + const char* name = m->stack[m->depth - 1u].name; + run_metrics_end(m, name); + } +} + static void run_usage(void) { driver_errf(RUN_TOOL, "%s", "usage: cfree run [options] inputs... [-- prog-arg...]\n" @@ -70,6 +138,7 @@ void driver_help_run(void) { "COMPILE OPTIONS\n" " -O0 -O1 -O2 Optimization level (default -O0)\n" " -g Emit DWARF debug info\n" + " --time, --metrics Emit scoped compile/link/JIT timing to stderr\n" " -e SYMBOL Entry symbol (default `main`)\n" " -target TRIPLE Cross-compile target (see `cfree cc --help`)\n" " -fPIC -fpic Position-independent code (no-op for the JIT)\n" @@ -202,6 +271,10 @@ static int run_parse(int argc, char** argv, RunOptions* o) { o->debug_info = 1; continue; } + if (driver_streq(a, "--time") || driver_streq(a, "--metrics")) { + o->metrics = 1; + continue; + } if (driver_streq(a, "-O0")) { o->opt_level = 0; continue; @@ -337,6 +410,8 @@ int driver_run(int argc, char** argv) { CfreeEnv cenv; CfreePipeline* pipe = NULL; CfreeJit* jit = NULL; + RunMetrics metrics_storage; + RunMetrics* metrics = NULL; void* sym; MainFn entry_fn; int rc; @@ -355,6 +430,14 @@ int driver_run(int argc, char** argv) { return 2; } + if (ro.metrics) { + run_metrics_init(&metrics_storage); + metrics = &metrics_storage; + env.metrics = &metrics->iface; + driver_logf("cfree metrics:"); + run_metrics_begin(metrics, "run.total"); + } + /* Pipeline owns the Compiler that backs the JIT image โ€” keep it alive * across cfree_jit_lookup and the entry call, free after cfree_jit_free. */ @@ -362,24 +445,31 @@ int driver_run(int argc, char** argv) { pipe = driver_pipeline_new(ro.target, &cenv); if (!pipe) { driver_errf(RUN_TOOL, "failed to initialize compiler"); + run_metrics_finish(metrics); run_options_release(&ro); driver_env_fini(&env); return 1; } + run_metrics_begin(metrics, "run.compile_and_jit"); rc = run_compile_and_jit(&ro, pipe, &jit); + run_metrics_end(metrics, "run.compile_and_jit"); if (rc != 0) { driver_pipeline_free(pipe); + run_metrics_finish(metrics); run_options_release(&ro); driver_env_fini(&env); return rc; } + run_metrics_begin(metrics, "run.jit_lookup"); sym = cfree_jit_lookup(jit, ro.entry); + run_metrics_end(metrics, "run.jit_lookup"); if (!sym) { driver_errf(RUN_TOOL, "entry symbol not found: %s", ro.entry); cfree_jit_free(jit); driver_pipeline_free(pipe); + run_metrics_finish(metrics); run_options_release(&ro); driver_env_fini(&env); return 1; @@ -398,10 +488,13 @@ int driver_run(int argc, char** argv) { entry_fn = u.fn; } + run_metrics_begin(metrics, "run.entry_call"); rc = entry_fn((int)ro.prog_argc, ro.prog_argv); + run_metrics_end(metrics, "run.entry_call"); cfree_jit_free(jit); driver_pipeline_free(pipe); + run_metrics_finish(metrics); run_options_release(&ro); driver_env_fini(&env); return rc; diff --git a/include/cfree.h b/include/cfree.h @@ -392,6 +392,13 @@ typedef struct CfreeJitTls { void* user; } CfreeJitTls; +typedef struct CfreeMetrics { + void (*scope_begin)(void* user, const char* name); + void (*scope_end)(void* user, const char* name); + void (*count)(void* user, const char* name, uint64_t value); + void* user; +} CfreeMetrics; + typedef struct CfreeEnv { CfreeHeap* heap; const CfreeFileIO* file_io; /* may be NULL for purely in-memory pipelines */ @@ -399,6 +406,7 @@ typedef struct CfreeEnv { const CfreeExecMem* execmem; /* NULL ok unless JIT/emu paths run */ const CfreeDbgOs* dbg_os; /* NULL ok unless `cfree dbg` paths run */ const CfreeJitTls* jit_tls; /* NULL ok unless JIT TLV paths run */ + const CfreeMetrics* metrics; /* optional scoped metrics sink */ /* Unix seconds since 1970-01-01 UTC, or negative for "no clock". Used * by the preprocessor for __DATE__ / __TIME__ (negative โ†’ C11 ยง6.10.8.1 * placeholders). The host decides the policy (SOURCE_DATE_EPOCH, diff --git a/src/api/pipeline.c b/src/api/pipeline.c @@ -10,6 +10,7 @@ #include "asm/asm.h" #include "core/arena.h" #include "core/heap.h" +#include "core/metrics.h" #include "core/pool.h" #include "link/link.h" #include "obj/obj.h" @@ -130,7 +131,10 @@ int cfree_compile_obj(CfreeCompiler* c, const CfreeCompileOptions* opts, } validate_bytes_input(c, input); ob = obj_new(c); + metrics_scope_begin(c, "compile.tu"); + metrics_count(c, "compile.input_bytes", (u64)input->len); compile_into(c, opts, input, ob); + metrics_scope_end(c, "compile.tu"); *out = ob; compiler_panic_restore(c, &saved); return 0; @@ -169,7 +173,10 @@ int cfree_compile_obj_emit(CfreeCompiler* c, const CfreeCompileOptions* opts, } validate_bytes_input(c, input); ob = obj_new(c); + metrics_scope_begin(c, "compile.tu"); + metrics_count(c, "compile.input_bytes", (u64)input->len); compile_into(c, opts, input, ob); + metrics_scope_end(c, "compile.tu"); emit_object_bytes(c, ob, out); obj_free(ob); compiler_panic_restore(c, &saved); @@ -299,8 +306,10 @@ int cfree_link_jit(CfreeCompiler* c, const CfreeLinkOptions* opts, linker = build_linker(c, &opts->inputs); link_set_gc_sections(linker, opts->gc_sections); link_set_jit_mode(linker, 1); + metrics_scope_begin(c, "link_jit.all"); image = link_resolve(linker); /* deferred-cleanup-registered */ *out_jit = cfree_jit_from_image(image); /* undefers + transfers ownership */ + metrics_scope_end(c, "link_jit.all"); link_free(linker); compiler_panic_restore(c, &saved); return 0; diff --git a/src/core/metrics.h b/src/core/metrics.h @@ -0,0 +1,25 @@ +#ifndef CFREE_METRICS_H +#define CFREE_METRICS_H + +#include "core/core.h" + +static inline const CfreeMetrics* metrics_sink(Compiler* c) { + return (c && c->env) ? c->env->metrics : NULL; +} + +static inline void metrics_scope_begin(Compiler* c, const char* name) { + const CfreeMetrics* m = metrics_sink(c); + if (m && m->scope_begin) m->scope_begin(m->user, name); +} + +static inline void metrics_scope_end(Compiler* c, const char* name) { + const CfreeMetrics* m = metrics_sink(c); + if (m && m->scope_end) m->scope_end(m->user, name); +} + +static inline void metrics_count(Compiler* c, const char* name, u64 value) { + const CfreeMetrics* m = metrics_sink(c); + if (m && m->count) m->count(m->user, name, value); +} + +#endif diff --git a/src/link/link_jit.c b/src/link/link_jit.c @@ -14,6 +14,7 @@ #include "core/bytes.h" #include "core/buf.h" #include "core/heap.h" +#include "core/metrics.h" #include "core/pool.h" #include "core/util.h" #include "jit/tlv_thunk.h" @@ -294,6 +295,8 @@ CfreeJit* cfree_jit_from_image(LinkImage* img) { compiler_panic(c, no_loc(), "cfree_jit_from_image: segment vaddr not page-aligned"); master_size = image_end - image_base; + metrics_count(c, "jit.master_size", master_size); + metrics_count(c, "jit.nsegments", img->nsegments); /* One reservation for the whole image. Requesting EXEC if any segment * is exec triggers the dual-mapping path on Apple silicon; non-exec @@ -302,10 +305,12 @@ CfreeJit* cfree_jit_from_image(LinkImage* img) { { int master_prot = CFREE_PROT_READ | CFREE_PROT_WRITE; if (needs_exec) master_prot |= CFREE_PROT_EXEC; + metrics_scope_begin(c, "jit.reserve"); if (mem->reserve(mem->user, (size_t)master_size, master_prot, &master) != 0) { compiler_panic(c, no_loc(), "cfree_jit_from_image: execmem.reserve failed"); } + metrics_scope_end(c, "jit.reserve"); } segs = (CfreeExecMemRegion*)heap->alloc(heap, sizeof(*segs) * img->nsegments, @@ -331,14 +336,18 @@ CfreeJit* cfree_jit_from_image(LinkImage* img) { /* Master reservation is zeroed; BSS is naturally zero. */ /* Copy each segment's file bytes to its write alias. */ + metrics_scope_begin(c, "jit.copy_segments"); for (i = 0; i < img->nsegments; ++i) { const LinkSegment* seg = &img->segments[i]; if (seg->file_size == 0) continue; + metrics_count(c, "jit.segment_bytes", seg->file_size); memcpy(segs[i].write, img->segment_bytes[i], (size_t)seg->file_size); } + metrics_scope_end(c, "jit.copy_segments"); /* Apply relocations. The patch site bytes go through the write * alias; PC-relative arithmetic uses the runtime alias address. */ + metrics_scope_begin(c, "jit.apply_relocs"); for (i = 0; i < LinkRelocs_count(&img->relocs); ++i) { const LinkRelocApply* r = LinkRelocs_at(&img->relocs, i); const LinkSymbol* tgt = LinkSyms_at(&img->syms, r->target - 1); @@ -404,6 +413,7 @@ CfreeJit* cfree_jit_from_image(LinkImage* img) { } link_reloc_apply(c, r->kind, P_bytes, S, r->addend, P); } + metrics_scope_end(c, "jit.apply_relocs"); /* Flip the runtime alias of each segment to its final perms. The * write alias is unaffected (still RW for any segment we'd want to @@ -411,6 +421,7 @@ CfreeJit* cfree_jit_from_image(LinkImage* img) { * orphaned after this point โ€” JITed code is not expected to write * to its own code). Each segs[i] is a sub-range of master; protect * accepts arbitrary [addr,size) inside the reservation. */ + metrics_scope_begin(c, "jit.protect"); for (i = 0; i < img->nsegments; ++i) { const LinkSegment* seg = &img->segments[i]; if (mem->protect(mem->user, segs[i].runtime, segs[i].size, @@ -421,15 +432,18 @@ CfreeJit* cfree_jit_from_image(LinkImage* img) { "cfree_jit_from_image: execmem.protect failed"); } } + metrics_scope_end(c, "jit.protect"); /* Flush only the segments that will be executed, against the * runtime alias (the address from which the CPU will fetch). */ if (mem->flush_icache) { + metrics_scope_begin(c, "jit.flush_icache"); for (i = 0; i < img->nsegments; ++i) { const LinkSegment* seg = &img->segments[i]; if (seg->flags & SF_EXEC) mem->flush_icache(mem->user, segs[i].runtime, segs[i].size); } + metrics_scope_end(c, "jit.flush_icache"); } /* IFUNC pre-resolution: now that code is executable and slots are @@ -491,8 +505,10 @@ CfreeJit* cfree_jit_from_image(LinkImage* img) { if (p_start && p_end) { VoidFn* fn = (VoidFn*)p_start; VoidFn* end = (VoidFn*)p_end; + metrics_scope_begin(c, "jit.ctors"); for (; fn != end; ++fn) if (*fn) (*fn)(); + metrics_scope_end(c, "jit.ctors"); } } diff --git a/src/link/link_layout.c b/src/link/link_layout.c @@ -15,6 +15,7 @@ #include "core/buf.h" #include "core/bytes.h" #include "core/heap.h" +#include "core/metrics.h" #include "core/pool.h" #include "core/util.h" #include "core/vec.h" @@ -941,13 +942,17 @@ LinkImage* link_resolve(Linker* l) { LinkImage* img; Heap* h; + metrics_scope_begin(l->c, "link.resolve.total"); + metrics_scope_begin(l->c, "link.ingest_archives"); link_ingest_archives(l); + metrics_scope_end(l->c, "link.ingest_archives"); img = link_image_alloc(l->c); h = img->heap; img->linker = l; img->ninput_maps = LinkInputs_count(&l->inputs); + metrics_count(l->c, "link.inputs", img->ninput_maps); img->input_maps = LinkInputs_count(&l->inputs) ? (InputMap*)h->alloc( @@ -960,14 +965,24 @@ LinkImage* link_resolve(Linker* l) { memset(img->input_maps, 0, sizeof(*img->input_maps) * LinkInputs_count(&l->inputs)); + metrics_scope_begin(l->c, "link.resolve_symbols"); link_resolve_symbols(l, img); + metrics_scope_end(l->c, "link.resolve_symbols"); { GcLive g = {0}; + metrics_scope_begin(l->c, "link.gc"); link_gc_live_alloc(&g, l, h); link_gc_compute(l, img, &g); + metrics_scope_end(l->c, "link.gc"); + metrics_scope_begin(l->c, "link.layout_sections"); link_layout_sections(l, img, &g); link_layout_commons(l, img); + metrics_count(l->c, "link.sections", img->nsections); + metrics_count(l->c, "link.segments", img->nsegments); + metrics_scope_end(l->c, "link.layout_sections"); + metrics_scope_begin(l->c, "link.emit_segment_bytes"); link_emit_segment_bytes(l, img); + metrics_scope_end(l->c, "link.emit_segment_bytes"); link_assign_symbol_vaddrs(l, img); link_emit_array_boundaries(l, img); link_emit_tls_boundaries(l, img); @@ -1000,7 +1015,11 @@ LinkImage* link_resolve(Linker* l) { link_layout_jit_stubs(l, img, map_size, &stub_map); if (l->c->target.obj != CFREE_OBJ_MACHO || !l->emit_static_exe) link_layout_got(l, img, map_size, &got_map); + metrics_scope_begin(l->c, "link.emit_relocations"); link_emit_relocations(l, img, got_map, stub_map); + metrics_count(l->c, "link.syms", LinkSyms_count(&img->syms)); + metrics_count(l->c, "link.relocs", LinkRelocs_count(&img->relocs)); + metrics_scope_end(l->c, "link.emit_relocations"); if (got_map) h->free(h, got_map, sizeof(*got_map) * map_size); if (stub_map) h->free(h, stub_map, sizeof(*stub_map) * map_size); } @@ -1011,5 +1030,6 @@ LinkImage* link_resolve(Linker* l) { link_capture_debug_inputs(l, img); + metrics_scope_end(l->c, "link.resolve.total"); return img; } diff --git a/src/opt/opt.c b/src/opt/opt.c @@ -21,6 +21,7 @@ #include "arch/regalloc.h" #include "core/arena.h" #include "core/core.h" +#include "core/metrics.h" #include "opt/ir.h" /* ---- wrapper state ---- */ @@ -1332,6 +1333,13 @@ void opt_emit(Compiler* c, Func* f, CGTarget* target) { replay_func_to(c, f, target, 1); } +static u64 func_inst_count(Func* f) { + u64 n = 0; + if (!f) return 0; + for (u32 b = 0; b < f->nblocks; ++b) n += f->blocks[b].ninsts; + return n; +} + /* ---- func_end: optionally run dry-run passes; replay; reset ---- */ static void w_func_end(CGTarget* t) { @@ -1339,16 +1347,44 @@ static void w_func_end(CGTarget* t) { if (!o->f) return; if (o->level == 1) { + metrics_scope_begin(o->c, "opt.o1.total"); + metrics_count(o->c, "opt.funcs", 1); + metrics_count(o->c, "opt.blocks", o->f->nblocks); + metrics_count(o->c, "opt.insts", func_inst_count(o->f)); + metrics_count(o->c, "opt.vals", o->f->nvals); + metrics_scope_begin(o->c, "opt.build_cfg"); opt_build_cfg(o->f); + metrics_scope_end(o->c, "opt.build_cfg"); + metrics_scope_begin(o->c, "opt.machinize"); opt_machinize(o->f, o->target); + metrics_scope_end(o->c, "opt.machinize"); + metrics_scope_begin(o->c, "opt.build_loop_tree"); opt_build_loop_tree(o->f); + metrics_scope_end(o->c, "opt.build_loop_tree"); + metrics_scope_begin(o->c, "opt.live_info.pre_dde"); opt_live_info(o->f); + metrics_count(o->c, "opt.live_words", o->f->opt_live_words); + metrics_count(o->c, "opt.conflict_bytes", + (u64)o->f->nvals * (u64)o->f->opt_conflict_words * + (u64)sizeof(u64)); + metrics_scope_end(o->c, "opt.live_info.pre_dde"); + metrics_scope_begin(o->c, "opt.dead_def_elim"); opt_dead_def_elim(o->f); + metrics_scope_end(o->c, "opt.dead_def_elim"); o->f->val_info = NULL; /* force opt_regalloc to recompute liveness */ + metrics_scope_begin(o->c, "opt.regalloc"); opt_regalloc(o->f, 0); + metrics_scope_end(o->c, "opt.regalloc"); + metrics_scope_begin(o->c, "opt.combine"); opt_combine(o->f); + metrics_scope_end(o->c, "opt.combine"); + metrics_scope_begin(o->c, "opt.dce"); opt_dce(o->f); + metrics_scope_end(o->c, "opt.dce"); + metrics_scope_begin(o->c, "opt.emit"); opt_emit(o->c, o->f, o->target); + metrics_scope_end(o->c, "opt.emit"); + metrics_scope_end(o->c, "opt.o1.total"); } else if (o->level >= 2) { opt_build_cfg(o->f); opt_build_ssa(o->f); diff --git a/src/opt/pass_lower.c b/src/opt/pass_lower.c @@ -4,6 +4,7 @@ #include "core/arena.h" #include "core/core.h" +#include "core/metrics.h" #include "core/pool.h" enum { @@ -1030,7 +1031,15 @@ void opt_dead_def_elim(Func* f) { void opt_regalloc(Func* f, int allow_live_range_split) { (void)allow_live_range_split; - if (!f->val_info) opt_live_info(f); + if (!f->val_info) { + metrics_scope_begin(f->c, "opt.live_info.regalloc"); + opt_live_info(f); + metrics_count(f->c, "opt.live_words", f->opt_live_words); + metrics_count(f->c, "opt.conflict_bytes", + (u64)f->nvals * (u64)f->opt_conflict_words * + (u64)sizeof(u64)); + metrics_scope_end(f->c, "opt.live_info.regalloc"); + } Val* order = arena_array(f->arena, Val, f->nvals ? f->nvals : 1u); u32 norder = 0; for (Val v = 1; v < f->nvals; ++v)