commit 9bfc0800ac4b96f20ceeec6c00cc845e5ca51ef0
parent 84f2b286fdbb060a79474a30246c1eef2c1ba7ea
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Thu, 21 May 2026 10:45:58 -0700
Add stateful frontend vtable
Diffstat:
10 files changed, 281 insertions(+), 104 deletions(-)
diff --git a/driver/env.c b/driver/env.c
@@ -150,7 +150,8 @@ CfreeStatus driver_compiler_new(CfreeTarget t, const CfreeContext *ctx,
return st;
}
cfree_c_register(c);
- (void)cfree_register_frontend(c, CFREE_LANG_TOY, cfree_toy_compile);
+ (void)cfree_register_frontend(c, CFREE_LANG_TOY,
+ &cfree_toy_frontend_vtable);
cfree_wasm_register(c);
driver_diag_set_compiler(c);
if (out) *out = c;
diff --git a/include/cfree/compile.h b/include/cfree/compile.h
@@ -66,14 +66,23 @@ typedef struct CfreeFrontendCompileOptions {
const void *language_options;
} CfreeFrontendCompileOptions;
-typedef CfreeStatus (*CfreeCompileFn)(CfreeCompiler *,
- const CfreeFrontendCompileOptions *,
- const CfreeSourceInput *,
- CfreeObjBuilder *out);
+typedef struct CfreeFrontend CfreeFrontend;
+
+typedef CfreeFrontend *(*CfreeFrontendNewFn)(CfreeCompiler *);
+typedef CfreeStatus (*CfreeFrontendCompileFn)(
+ CfreeFrontend *, const CfreeFrontendCompileOptions *,
+ const CfreeSourceInput *, CfreeObjBuilder *out);
+typedef void (*CfreeFrontendFreeFn)(CfreeFrontend *);
+
+typedef struct CfreeFrontendVTable {
+ CfreeFrontendNewFn new_frontend;
+ CfreeFrontendCompileFn compile;
+ CfreeFrontendFreeFn free_frontend;
+} CfreeFrontendVTable;
CfreeLanguage cfree_language_for_path(const char *path);
CfreeStatus cfree_register_frontend(CfreeCompiler *, CfreeLanguage,
- CfreeCompileFn);
+ const CfreeFrontendVTable *);
uint32_t cfree_compiler_arch_predefines(CfreeCompiler *,
const CfreePredefinedMacro **out);
diff --git a/lang/c/c.c b/lang/c/c.c
@@ -19,6 +19,10 @@ static _Noreturn void c_bad_options(Compiler* c, const char* msg) {
compiler_panic(c, c_no_loc(), "bad C frontend options: %s", msg);
}
+typedef struct CFrontend {
+ CfreeCompiler* c;
+} CFrontend;
+
static void c_apply_pp_options(Pp* pp, const CfreePreprocessOptions* opts) {
u32 i;
@@ -175,10 +179,23 @@ CfreeStatus cfree_c_dump_tokens(CfreeCompiler* c, const CfreeBytes* input,
return cfree_frontend_run(c, c_dump_tokens_body, &r);
}
-CfreeStatus cfree_c_compile(CfreeCompiler* c,
- const CfreeFrontendCompileOptions* fe_opts,
- const CfreeSourceInput* input,
- CfreeObjBuilder* out) {
+static CfreeFrontend* c_frontend_new(CfreeCompiler* c) {
+ CfreeHeap* h;
+ CFrontend* fe;
+ if (!c) return NULL;
+ h = cfree_compiler_context(c)->heap;
+ fe = (CFrontend*)h->alloc(h, sizeof(*fe), _Alignof(CFrontend));
+ if (!fe) return NULL;
+ fe->c = c;
+ return (CfreeFrontend*)fe;
+}
+
+static CfreeStatus c_frontend_compile(CfreeFrontend* frontend,
+ const CfreeFrontendCompileOptions* fe_opts,
+ const CfreeSourceInput* input,
+ CfreeObjBuilder* out) {
+ CFrontend* fe = (CFrontend*)frontend;
+ CfreeCompiler* c;
/* The libcfree pipeline plants the original CfreeCCompileOptions* in
* language_options. Recover it for preprocessor configuration. */
const CfreeCCompileOptions* opts;
@@ -190,6 +207,8 @@ CfreeStatus cfree_c_compile(CfreeCompiler* c,
CfreeCg* cg;
CfreeStatus cg_st;
+ if (!fe || !fe->c) return CFREE_INVALID;
+ c = fe->c;
if (!fe_opts || !input) c_bad_options(c, "compile args missing");
opts = (const CfreeCCompileOptions*)fe_opts->language_options;
if (!opts) c_bad_options(c, "C frontend missing CfreeCCompileOptions");
@@ -245,6 +264,20 @@ CfreeStatus cfree_c_compile(CfreeCompiler* c,
return CFREE_OK;
}
+static void c_frontend_free(CfreeFrontend* frontend) {
+ CFrontend* fe = (CFrontend*)frontend;
+ CfreeHeap* h;
+ if (!fe) return;
+ h = cfree_compiler_context(fe->c)->heap;
+ h->free(h, fe, sizeof(*fe));
+}
+
+static const CfreeFrontendVTable c_frontend_vtable = {
+ c_frontend_new,
+ c_frontend_compile,
+ c_frontend_free,
+};
+
void cfree_c_register(CfreeCompiler* c) {
- (void)cfree_register_frontend(c, CFREE_LANG_C, cfree_c_compile);
+ (void)cfree_register_frontend(c, CFREE_LANG_C, &c_frontend_vtable);
}
diff --git a/lang/c/c.h b/lang/c/c.h
@@ -4,9 +4,8 @@
/* Public surface for the cfree C frontend.
*
* The C frontend is registered with the compiler via cfree_c_register, which
- * installs cfree_c_compile under CFREE_LANG_C. cfree_c_compile is shaped to
- * match the public CfreeCompileFn signature so the libcfree pipeline can
- * dispatch it through the registered-frontend table.
+ * installs a CfreeFrontendVTable under CFREE_LANG_C so the libcfree pipeline
+ * can create a frontend instance, compile through it, and free it.
*
* The pipeline constructs its CfreeFrontendCompileOptions by copying
* CfreeCCompileOptions.code and .diagnostics, then planting the original
@@ -23,8 +22,6 @@
#include <cfree/frontend.h>
#include <cfree/object.h>
-CfreeStatus cfree_c_compile(CfreeCompiler*, const CfreeFrontendCompileOptions*,
- const CfreeSourceInput*, CfreeObjBuilder*);
CfreeStatus cfree_c_preprocess(CfreeCompiler*, const CfreePreprocessOptions*,
const CfreeBytes*, CfreeWriter*);
CfreeStatus cfree_c_dump_tokens(CfreeCompiler*, const CfreeBytes*,
diff --git a/lang/toy/compile.c b/lang/toy/compile.c
@@ -2,17 +2,34 @@
#include "internal.h"
-CfreeStatus cfree_toy_compile(CfreeCompiler* c,
- const CfreeFrontendCompileOptions* opts,
- const CfreeSourceInput* input,
- CfreeObjBuilder* out) {
+typedef struct ToyFrontend {
+ CfreeCompiler* c;
+} ToyFrontend;
+
+static CfreeFrontend* toy_frontend_new(CfreeCompiler* c) {
+ CfreeHeap* h;
+ ToyFrontend* fe;
+ if (!c) return NULL;
+ h = cfree_compiler_context(c)->heap;
+ fe = (ToyFrontend*)h->alloc(h, sizeof(*fe), _Alignof(ToyFrontend));
+ if (!fe) return NULL;
+ fe->c = c;
+ return (CfreeFrontend*)fe;
+}
+
+static CfreeStatus toy_frontend_compile(
+ CfreeFrontend* frontend, const CfreeFrontendCompileOptions* opts,
+ const CfreeSourceInput* input, CfreeObjBuilder* out) {
+ ToyFrontend* fe = (ToyFrontend*)frontend;
+ CfreeCompiler* c;
ToyParser p;
CfreeCg* cg;
const uint8_t* source;
size_t source_len;
CfreeStatus st;
- if (!c || !opts || !input || !out) return CFREE_INVALID;
+ if (!fe || !fe->c || !opts || !input || !out) return CFREE_INVALID;
+ c = fe->c;
(void)opts->language_options; /* toy frontend has no per-language options */
source = input->bytes.data ? input->bytes.data : (const uint8_t*)"";
@@ -38,3 +55,17 @@ CfreeStatus cfree_toy_compile(CfreeCompiler* c,
cfree_cg_free(cg);
return CFREE_OK;
}
+
+static void toy_frontend_free(CfreeFrontend* frontend) {
+ ToyFrontend* fe = (ToyFrontend*)frontend;
+ CfreeHeap* h;
+ if (!fe) return;
+ h = cfree_compiler_context(fe->c)->heap;
+ h->free(h, fe, sizeof(*fe));
+}
+
+const CfreeFrontendVTable cfree_toy_frontend_vtable = {
+ toy_frontend_new,
+ toy_frontend_compile,
+ toy_frontend_free,
+};
diff --git a/lang/toy/toy.h b/lang/toy/toy.h
@@ -4,9 +4,6 @@
#include <cfree/compile.h>
#include <cfree/frontend.h>
-CfreeStatus cfree_toy_compile(CfreeCompiler*,
- const CfreeFrontendCompileOptions*,
- const CfreeSourceInput* input,
- CfreeObjBuilder* out);
+extern const CfreeFrontendVTable cfree_toy_frontend_vtable;
#endif
diff --git a/lang/wasm/wasm.c b/lang/wasm/wasm.c
@@ -9,12 +9,29 @@ static void wasm_parse_any(CfreeCompiler* c, const CfreeBytes* input,
wasm_validate(m, c);
}
-CfreeStatus cfree_wasm_compile(CfreeCompiler* c,
- const CfreeFrontendCompileOptions* opts,
- const CfreeSourceInput* input,
- CfreeObjBuilder* out) {
+typedef struct WasmFrontend {
+ CfreeCompiler* c;
+} WasmFrontend;
+
+static CfreeFrontend* wasm_frontend_new(CfreeCompiler* c) {
+ CfreeHeap* h;
+ WasmFrontend* fe;
+ if (!c) return NULL;
+ h = cfree_compiler_context(c)->heap;
+ fe = (WasmFrontend*)h->alloc(h, sizeof(*fe), _Alignof(WasmFrontend));
+ if (!fe) return NULL;
+ fe->c = c;
+ return (CfreeFrontend*)fe;
+}
+
+static CfreeStatus wasm_frontend_compile(
+ CfreeFrontend* frontend, const CfreeFrontendCompileOptions* opts,
+ const CfreeSourceInput* input, CfreeObjBuilder* out) {
+ WasmFrontend* fe = (WasmFrontend*)frontend;
+ CfreeCompiler* c;
WasmModule m;
- if (!c || !opts || !input || !out) return CFREE_INVALID;
+ if (!fe || !fe->c || !opts || !input || !out) return CFREE_INVALID;
+ c = fe->c;
(void)opts->language_options; /* wasm frontend has no per-language options */
wasm_module_init(&m, cfree_compiler_context(c)->heap);
wasm_parse_any(c, &input->bytes, &m);
@@ -23,8 +40,22 @@ CfreeStatus cfree_wasm_compile(CfreeCompiler* c,
return CFREE_OK;
}
+static void wasm_frontend_free(CfreeFrontend* frontend) {
+ WasmFrontend* fe = (WasmFrontend*)frontend;
+ CfreeHeap* h;
+ if (!fe) return;
+ h = cfree_compiler_context(fe->c)->heap;
+ h->free(h, fe, sizeof(*fe));
+}
+
+static const CfreeFrontendVTable wasm_frontend_vtable = {
+ wasm_frontend_new,
+ wasm_frontend_compile,
+ wasm_frontend_free,
+};
+
void cfree_wasm_register(CfreeCompiler* c) {
- (void)cfree_register_frontend(c, CFREE_LANG_WASM, cfree_wasm_compile);
+ (void)cfree_register_frontend(c, CFREE_LANG_WASM, &wasm_frontend_vtable);
}
int cfree_wasm_wat_to_wasm(CfreeCompiler* c, const CfreeBytes* input,
diff --git a/lang/wasm/wasm.h b/lang/wasm/wasm.h
@@ -7,10 +7,6 @@
#include "runtime_abi.h"
-CfreeStatus cfree_wasm_compile(CfreeCompiler*,
- const CfreeFrontendCompileOptions*,
- const CfreeSourceInput* input,
- CfreeObjBuilder* out);
void cfree_wasm_register(CfreeCompiler*);
/* Internal test/developer helper: parse accepted WAT and write equivalent
diff --git a/src/api/compile.c b/src/api/compile.c
@@ -13,6 +13,23 @@
#include "core/pool.h"
#include "obj/obj.h"
+typedef struct AsmFrontend {
+ Compiler* c;
+} AsmFrontend;
+
+static CfreeFrontend* asm_frontend_new(CfreeCompiler* c);
+static CfreeStatus asm_frontend_compile(CfreeFrontend* fe,
+ const CfreeFrontendCompileOptions* opts,
+ const CfreeSourceInput* input,
+ CfreeObjBuilder* out);
+static void asm_frontend_free(CfreeFrontend* fe);
+
+static const CfreeFrontendVTable asm_frontend_vtable = {
+ asm_frontend_new,
+ asm_frontend_compile,
+ asm_frontend_free,
+};
+
static SrcLoc no_loc(void) {
SrcLoc loc;
loc.file_id = 0;
@@ -52,10 +69,14 @@ CfreeLanguage cfree_language_for_path(const char* path) {
}
CfreeStatus cfree_register_frontend(CfreeCompiler* c, CfreeLanguage lang,
- CfreeCompileFn fn) {
+ const CfreeFrontendVTable* vtable) {
if (!c) return CFREE_INVALID;
if ((unsigned)lang >= CFREE_LANG_COUNT) return CFREE_INVALID;
- c->frontends[lang] = fn;
+ if (vtable &&
+ (!vtable->new_frontend || !vtable->compile || !vtable->free_frontend)) {
+ return CFREE_INVALID;
+ }
+ c->frontends[lang] = vtable;
return CFREE_OK;
}
@@ -95,58 +116,85 @@ static void emit_object_bytes(Compiler* c, ObjBuilder* ob, Writer* w) {
}
}
-/* Run the source-input-shaped path. */
-static void compile_source_into(Compiler* c,
- const CfreeFrontendCompileOptions* opts,
- const CfreeSourceInput* input, ObjBuilder* ob) {
- CfreeCompileFn frontend = NULL;
- AsmLexer* lex;
- MCEmitter* mc;
+static const CfreeFrontendVTable* frontend_for_language(Compiler* c,
+ CfreeLanguage lang) {
+ if ((unsigned)lang >= CFREE_LANG_COUNT) return NULL;
+ if (c->frontends[lang]) return c->frontends[lang];
+ if (lang == CFREE_LANG_ASM) return &asm_frontend_vtable;
+ return NULL;
+}
+
+typedef struct FrontendCleanup {
+ const CfreeFrontendVTable* vtable;
+ CfreeFrontend* frontend;
+} FrontendCleanup;
- if ((unsigned)input->lang < CFREE_LANG_COUNT) {
- frontend = c->frontends[input->lang];
+static void frontend_cleanup_run(void* arg) {
+ FrontendCleanup* cleanup = (FrontendCleanup*)arg;
+ if (cleanup && cleanup->vtable && cleanup->frontend) {
+ cleanup->vtable->free_frontend(cleanup->frontend);
+ cleanup->frontend = NULL;
}
- if (frontend) {
- CfreeStatus st;
- metrics_scope_begin(c, "compile.frontend");
- st = frontend(c, opts, input, ob);
- metrics_scope_end(c, "compile.frontend");
- if (st != CFREE_OK) {
- compiler_panic(c, no_loc(), "frontend failed for input: %s",
- input->bytes.name);
- }
- metrics_scope_begin(c, "compile.obj_finalize");
- obj_finalize(ob);
- metrics_scope_end(c, "compile.obj_finalize");
- metrics_count(c, "compile.obj_sections", obj_section_count(ob));
- metrics_count(c, "compile.obj_relocs", obj_reloc_total(ob));
- return;
+}
+
+static void compile_frontend_into(Compiler* c,
+ const CfreeFrontendVTable* vtable,
+ const CfreeFrontendCompileOptions* opts,
+ const CfreeSourceInput* input,
+ ObjBuilder* ob) {
+ CfreeFrontend* frontend;
+ FrontendCleanup* cleanup;
+ CompilerCleanup* cleanup_node;
+ CfreeStatus st;
+
+ if (!vtable) {
+ compiler_panic(c, no_loc(), "no frontend registered for language: %u",
+ (u32)input->lang);
}
- if (input->lang == CFREE_LANG_ASM) {
- metrics_scope_begin(c, "compile.asm.lex_open");
- lex = asm_lex_open_mem(c, input->bytes.name, (const char*)input->bytes.data,
- input->bytes.len);
- metrics_scope_end(c, "compile.asm.lex_open");
- metrics_scope_begin(c, "compile.asm.mc_new");
- mc = mc_new(c, ob);
- metrics_scope_end(c, "compile.asm.mc_new");
- metrics_scope_begin(c, "compile.asm.parse");
- asm_parse(c, lex, mc);
- metrics_scope_end(c, "compile.asm.parse");
- metrics_scope_begin(c, "compile.obj_finalize");
- obj_finalize(ob);
- metrics_scope_end(c, "compile.obj_finalize");
- metrics_count(c, "compile.obj_sections", obj_section_count(ob));
- metrics_count(c, "compile.obj_relocs", obj_reloc_total(ob));
- metrics_scope_begin(c, "compile.asm.mc_free");
- mc_free(mc);
- metrics_scope_end(c, "compile.asm.mc_free");
- return;
+ frontend = vtable->new_frontend(c);
+ if (!frontend) {
+ compiler_panic(c, no_loc(), "frontend allocation failed for input: %s",
+ input->bytes.name);
+ }
+ cleanup = (FrontendCleanup*)arena_alloc(c->scratch, sizeof(*cleanup),
+ _Alignof(FrontendCleanup));
+ if (!cleanup) {
+ vtable->free_frontend(frontend);
+ compiler_panic(c, no_loc(), "frontend cleanup allocation failed");
}
+ cleanup->vtable = vtable;
+ cleanup->frontend = frontend;
+ cleanup_node = compiler_defer(c, frontend_cleanup_run, cleanup);
+ if (!cleanup_node) {
+ vtable->free_frontend(frontend);
+ compiler_panic(c, no_loc(), "frontend cleanup allocation failed");
+ }
+
+ metrics_scope_begin(c, "compile.frontend");
+ st = vtable->compile(frontend, opts, input, ob);
+ metrics_scope_end(c, "compile.frontend");
+ compiler_undefer(c, cleanup_node);
+ vtable->free_frontend(frontend);
+ cleanup->frontend = NULL;
+ if (st != CFREE_OK) {
+ compiler_panic(c, no_loc(), "frontend failed for input: %s",
+ input->bytes.name);
+ }
+
+ metrics_scope_begin(c, "compile.obj_finalize");
+ obj_finalize(ob);
+ metrics_scope_end(c, "compile.obj_finalize");
+ metrics_count(c, "compile.obj_sections", obj_section_count(ob));
+ metrics_count(c, "compile.obj_relocs", obj_reloc_total(ob));
+}
- compiler_panic(c, no_loc(), "no frontend registered for language: %u",
- (u32)input->lang);
+/* Run the source-input-shaped path. */
+static void compile_source_into(Compiler* c,
+ const CfreeFrontendCompileOptions* opts,
+ const CfreeSourceInput* input, ObjBuilder* ob) {
+ compile_frontend_into(c, frontend_for_language(c, input->lang), opts, input,
+ ob);
}
/* ============================================================
@@ -157,9 +205,9 @@ static CfreeStatus compile_c_into(Compiler* c, const CfreeCCompileOptions* opts,
const CfreeBytes* input, ObjBuilder* ob) {
CfreeFrontendCompileOptions fe;
CfreeSourceInput si;
- CfreeCompileFn frontend;
+ const CfreeFrontendVTable* frontend;
- frontend = c->frontends[CFREE_LANG_C];
+ frontend = frontend_for_language(c, CFREE_LANG_C);
if (!frontend) {
compiler_panic(c, no_loc(), "no C frontend registered");
}
@@ -170,15 +218,7 @@ static CfreeStatus compile_c_into(Compiler* c, const CfreeCCompileOptions* opts,
si.bytes = *input;
si.lang = CFREE_LANG_C;
- metrics_scope_begin(c, "compile.frontend");
- CfreeStatus st = frontend(c, &fe, &si, ob);
- metrics_scope_end(c, "compile.frontend");
- if (st != CFREE_OK) {
- compiler_panic(c, no_loc(), "C frontend failed for input: %s", input->name);
- }
- metrics_scope_begin(c, "compile.obj_finalize");
- obj_finalize(ob);
- metrics_scope_end(c, "compile.obj_finalize");
+ compile_frontend_into(c, frontend, &fe, &si, ob);
return CFREE_OK;
}
@@ -243,26 +283,50 @@ CfreeStatus cfree_compile_c_obj_emit(CfreeCompiler* c,
* Asm
* ============================================================ */
-static void compile_asm_into(Compiler* c, const CfreeAsmCompileOptions* opts,
- const CfreeBytes* input, ObjBuilder* ob) {
+static CfreeFrontend* asm_frontend_new(CfreeCompiler* c) {
+ Heap* h;
+ AsmFrontend* fe;
+ if (!c) return NULL;
+ h = (Heap*)c->ctx->heap;
+ fe = (AsmFrontend*)h->alloc(h, sizeof(*fe), _Alignof(AsmFrontend));
+ if (!fe) return NULL;
+ fe->c = c;
+ return (CfreeFrontend*)fe;
+}
+
+static CfreeStatus asm_frontend_compile(CfreeFrontend* frontend,
+ const CfreeFrontendCompileOptions* opts,
+ const CfreeSourceInput* input,
+ CfreeObjBuilder* out) {
+ AsmFrontend* fe = (AsmFrontend*)frontend;
+ Compiler* c;
AsmLexer* lex;
MCEmitter* mc;
(void)opts;
+ if (!fe || !fe->c || !input || !out) return CFREE_INVALID;
+ c = fe->c;
metrics_scope_begin(c, "compile.asm.lex_open");
- lex = asm_lex_open_mem(c, input->name, (const char*)input->data, input->len);
+ lex = asm_lex_open_mem(c, input->bytes.name, (const char*)input->bytes.data,
+ input->bytes.len);
metrics_scope_end(c, "compile.asm.lex_open");
metrics_scope_begin(c, "compile.asm.mc_new");
- mc = mc_new(c, ob);
+ mc = mc_new(c, (ObjBuilder*)out);
metrics_scope_end(c, "compile.asm.mc_new");
metrics_scope_begin(c, "compile.asm.parse");
asm_parse(c, lex, mc);
metrics_scope_end(c, "compile.asm.parse");
- metrics_scope_begin(c, "compile.obj_finalize");
- obj_finalize(ob);
- metrics_scope_end(c, "compile.obj_finalize");
metrics_scope_begin(c, "compile.asm.mc_free");
mc_free(mc);
metrics_scope_end(c, "compile.asm.mc_free");
+ return CFREE_OK;
+}
+
+static void asm_frontend_free(CfreeFrontend* frontend) {
+ AsmFrontend* fe = (AsmFrontend*)frontend;
+ Heap* h;
+ if (!fe) return;
+ h = fe->c->ctx->heap;
+ h->free(h, fe, sizeof(*fe));
}
CfreeStatus cfree_compile_asm_obj(CfreeCompiler* c,
@@ -285,7 +349,16 @@ CfreeStatus cfree_compile_asm_obj(CfreeCompiler* c,
ob = obj_new(c);
metrics_scope_begin(c, "compile.tu");
metrics_count(c, "compile.input_bytes", (u64)input->len);
- compile_asm_into(c, opts, input, ob);
+ {
+ CfreeFrontendCompileOptions fe;
+ CfreeSourceInput si;
+ fe.code = opts->code;
+ fe.diagnostics = opts->diagnostics;
+ fe.language_options = opts;
+ si.bytes = *input;
+ si.lang = CFREE_LANG_ASM;
+ compile_frontend_into(c, &asm_frontend_vtable, &fe, &si, ob);
+ }
metrics_scope_end(c, "compile.tu");
*out = ob;
compiler_panic_restore(c, &saved);
@@ -309,7 +382,16 @@ CfreeStatus cfree_compile_asm_obj_emit(CfreeCompiler* c,
ob = obj_new(c);
metrics_scope_begin(c, "compile.tu");
metrics_count(c, "compile.input_bytes", (u64)input->len);
- compile_asm_into(c, opts, input, ob);
+ {
+ CfreeFrontendCompileOptions fe;
+ CfreeSourceInput si;
+ fe.code = opts->code;
+ fe.diagnostics = opts->diagnostics;
+ fe.language_options = opts;
+ si.bytes = *input;
+ si.lang = CFREE_LANG_ASM;
+ compile_frontend_into(c, &asm_frontend_vtable, &fe, &si, ob);
+ }
emit_object_bytes(c, ob, out);
metrics_scope_end(c, "compile.tu");
obj_free(ob);
diff --git a/src/core/core.h b/src/core/core.h
@@ -117,7 +117,7 @@ struct CfreeCompiler {
TargetABI* abi;
Target target;
CompilerCleanup* cleanup;
- CfreeCompileFn frontends[CFREE_LANG_COUNT];
+ const CfreeFrontendVTable* frontends[CFREE_LANG_COUNT];
void* cg_api;
void (*cg_api_free)(Compiler*);
};