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:
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)