kit

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

commit 9bfc0800ac4b96f20ceeec6c00cc845e5ca51ef0
parent 84f2b286fdbb060a79474a30246c1eef2c1ba7ea
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu, 21 May 2026 10:45:58 -0700

Add stateful frontend vtable

Diffstat:
Mdriver/env.c | 3++-
Minclude/cfree/compile.h | 19++++++++++++++-----
Mlang/c/c.c | 43++++++++++++++++++++++++++++++++++++++-----
Mlang/c/c.h | 7++-----
Mlang/toy/compile.c | 41++++++++++++++++++++++++++++++++++++-----
Mlang/toy/toy.h | 5+----
Mlang/wasm/wasm.c | 43+++++++++++++++++++++++++++++++++++++------
Mlang/wasm/wasm.h | 4----
Msrc/api/compile.c | 218++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Msrc/core/core.h | 2+-
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*); };