kit

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

compile.c (22141B)


      1 /* libkit's top-level compile entry points. Status-returning shapes
      2  * that drive the C, asm, and registered-frontend paths. */
      3 
      4 #include <kit/compile.h>
      5 #include <kit/cg.h>
      6 #include <kit/core.h>
      7 #include <string.h>
      8 
      9 #include "api/lang_registry.h"
     10 #include "arch/arch.h"
     11 #include "asm/asm.h"
     12 #include "core/core.h"
     13 #include "core/heap.h"
     14 #include "core/metrics.h"
     15 #include "core/pool.h"
     16 #include "core/slice.h"
     17 #include "obj/obj.h"
     18 
     19 typedef struct AsmFrontend {
     20   Compiler* c;
     21 } AsmFrontend;
     22 
     23 struct KitFrontend {
     24   KitCompiler* c;
     25   KitLanguage lang;
     26   const KitFrontendVTable* vtable;
     27   KitFrontendState* state;
     28 };
     29 
     30 struct KitCompileSession {
     31   KitCompiler* c;
     32   KitLanguage lang;
     33   KitFrontend* frontend;
     34   KitFrontendCompileOptions opts;
     35 };
     36 
     37 static KitFrontendState* asm_frontend_new(KitCompiler* c);
     38 static KitStatus asm_frontend_compile(KitFrontendState* fe,
     39                                       const KitFrontendCompileOptions* opts,
     40                                       const KitSourceInput* input,
     41                                       KitObjBuilder* out);
     42 static void asm_frontend_free(KitFrontendState* fe);
     43 
     44 static const KitSlice asm_extensions[] = {KIT_SLICE_LIT("s")};
     45 /* Canonical `-x` name plus alias; mirrors the driver's "asm"/"s" spellings. */
     46 static const KitSlice asm_names[] = {KIT_SLICE_LIT("asm"), KIT_SLICE_LIT("s")};
     47 
     48 const KitFrontendVTable kit_asm_frontend_vtable = {
     49     asm_frontend_new,
     50     NULL, /* compile_cg: asm participates in LTO as an opaque object */
     51     asm_frontend_compile,
     52     asm_frontend_free,
     53     asm_extensions,
     54     (uint32_t)(sizeof asm_extensions / sizeof asm_extensions[0]),
     55     asm_names,
     56     (uint32_t)(sizeof asm_names / sizeof asm_names[0]),
     57     NULL, /* commit: asm has no durable cross-compile state */
     58     NULL, /* abort */
     59     {false, KIT_FRONTEND_LTO_OPAQUE, false},
     60     NULL, /* parse_options: no asm-specific flags */
     61     NULL, /* free_options */
     62 };
     63 
     64 static _Noreturn void panic_bad_options(Compiler* c, const char* msg) {
     65   compiler_panic(c, SRCLOC_NONE, "bad kit options: %.*s",
     66                  SLICE_ARG(slice_from_cstr(msg)));
     67 }
     68 
     69 /* Compare `ext` to `pat` letter-for-letter, lowercasing ASCII A-Z in
     70  * the path side so `.S` matches asm's `"s"` entry. `ext` is a borrowed
     71  * slice over the path tail; `pat` is the frontend's extension slice.
     72  * Returns nonzero on full match. */
     73 static int ext_eq_ci(KitSlice ext, KitSlice pat) {
     74   size_t i;
     75   if (ext.len != pat.len) return 0;
     76   for (i = 0; i < ext.len; ++i) {
     77     char a = ext.s[i];
     78     char b = pat.s[i];
     79     if (a >= 'A' && a <= 'Z') a = (char)(a - 'A' + 'a');
     80     if (a != b) return 0;
     81   }
     82   return 1;
     83 }
     84 
     85 /* Resolve `lang`'s frontend vtable: from the compiler's per-instance table
     86  * when a compiler is supplied, else from the compile-time default registry so
     87  * the language resolvers work with c==NULL (e.g. at CLI arg-parse time, before
     88  * a KitCompiler exists). Out-of-range `lang` yields NULL via lang_registry. */
     89 static const KitFrontendVTable* frontend_for(KitCompiler* c, unsigned lang) {
     90   if (c) {
     91     if (lang >= KIT_LANG_COUNT) return NULL;
     92     return c->frontends[lang];
     93   }
     94   return lang_registry_vtable((KitLanguage)lang);
     95 }
     96 
     97 KitLanguage kit_language_for_path(KitCompiler* c, const char* path) {
     98   size_t len;
     99   size_t i;
    100   KitSlice ext = SLICE_NULL;
    101   int have_ext = 0;
    102   unsigned lang;
    103 
    104   if (!path) return KIT_LANG_UNKNOWN;
    105   for (len = 0; path[len]; ++len) {
    106   }
    107   /* Strip back to the last `.` after the final `/`. No dot → no
    108    * extension → no language claims it. */
    109   i = len;
    110   while (i > 0) {
    111     --i;
    112     if (path[i] == '/') break;
    113     if (path[i] == '.') {
    114       ext.s = path + i + 1;
    115       ext.len = len - (i + 1);
    116       have_ext = 1;
    117       break;
    118     }
    119   }
    120   if (!have_ext) return KIT_LANG_UNKNOWN;
    121 
    122   for (lang = 0; lang < KIT_LANG_COUNT; ++lang) {
    123     const KitFrontendVTable* v = frontend_for(c, lang);
    124     uint32_t e;
    125     if (!v || !v->extensions) continue;
    126     for (e = 0; e < v->nextensions; ++e) {
    127       if (ext_eq_ci(ext, v->extensions[e])) return (KitLanguage)lang;
    128     }
    129   }
    130   /* No registered frontend claims this extension. C is not special — it does
    131    * not absorb unrecognized inputs. */
    132   return KIT_LANG_UNKNOWN;
    133 }
    134 
    135 /* Compare a NUL-terminated name to a frontend's name slice, byte-for-byte
    136  * (case-sensitive, mirroring the driver's exact `-x` spellings). Returns
    137  * nonzero on a full match. */
    138 static int name_eq(const char* name, KitSlice pat) {
    139   size_t i;
    140   for (i = 0; i < pat.len; ++i) {
    141     if (name[i] == '\0' || name[i] != pat.s[i]) return 0;
    142   }
    143   return name[pat.len] == '\0';
    144 }
    145 
    146 KitLanguage kit_language_for_name(KitCompiler* c, const char* name) {
    147   unsigned lang;
    148   if (!name) return KIT_LANG_UNKNOWN;
    149   for (lang = 0; lang < KIT_LANG_COUNT; ++lang) {
    150     const KitFrontendVTable* v = frontend_for(c, lang);
    151     uint32_t n;
    152     if (!v || !v->names) continue;
    153     for (n = 0; n < v->nnames; ++n) {
    154       if (name_eq(name, v->names[n])) return (KitLanguage)lang;
    155     }
    156   }
    157   return KIT_LANG_UNKNOWN;
    158 }
    159 
    160 const char* kit_language_name(KitCompiler* c, KitLanguage lang) {
    161   const KitFrontendVTable* v;
    162   if ((unsigned)lang >= KIT_LANG_COUNT) return NULL;
    163   v = frontend_for(c, (unsigned)lang);
    164   if (!v || !v->names || v->nnames == 0) return NULL;
    165   /* The first name is the canonical one. Name tables are KIT_SLICE_LIT over
    166    * string literals, so the slice's bytes are NUL-terminated just past .len. */
    167   return v->names[0].s;
    168 }
    169 
    170 const char* kit_language_default_extension(KitCompiler* c, KitLanguage lang) {
    171   const KitFrontendVTable* v;
    172   if ((unsigned)lang >= KIT_LANG_COUNT) return NULL;
    173   v = frontend_for(c, (unsigned)lang);
    174   if (!v || !v->extensions || v->nextensions == 0) return NULL;
    175   /* The first extension is the canonical one. Extension tables are
    176    * KIT_SLICE_LIT over string literals, so the slice's bytes are
    177    * NUL-terminated just past .len. */
    178   return v->extensions[0].s;
    179 }
    180 
    181 KitStatus kit_register_frontend(KitCompiler* c, KitLanguage lang,
    182                                 const KitFrontendVTable* vtable) {
    183   if (!c) return KIT_INVALID;
    184   if ((unsigned)lang >= KIT_LANG_COUNT) return KIT_INVALID;
    185   if (vtable) {
    186     uint8_t mode = vtable->caps.lto_mode;
    187     if (!vtable->new_frontend || !vtable->free_frontend ||
    188         mode > KIT_FRONTEND_LTO_OPAQUE) {
    189       return KIT_INVALID;
    190     }
    191     if (mode == KIT_FRONTEND_LTO_CG) {
    192       if (!vtable->compile_cg) return KIT_INVALID;
    193     } else if (!vtable->compile_obj) {
    194       return KIT_INVALID;
    195     }
    196   }
    197   c->frontends[lang] = vtable;
    198   return KIT_OK;
    199 }
    200 
    201 KitStatus kit_frontend_caps(KitCompiler* c, KitLanguage lang,
    202                             KitFrontendCaps* out) {
    203   const KitFrontendVTable* v;
    204   if (!c || !out || (unsigned)lang >= KIT_LANG_COUNT) return KIT_INVALID;
    205   v = c->frontends[lang];
    206   if (!v) return KIT_INVALID;
    207   *out = v->caps;
    208   return KIT_OK;
    209 }
    210 
    211 KitStatus kit_frontend_parse_options(KitCompiler* c, KitLanguage lang, int argc,
    212                                      char** argv, void** out_opts) {
    213   const KitFrontendVTable* v;
    214   if (out_opts) *out_opts = NULL;
    215   if (!c || !out_opts || (unsigned)lang >= KIT_LANG_COUNT) return KIT_INVALID;
    216   v = c->frontends[lang];
    217   if (!v || !v->parse_options) return KIT_INVALID;
    218   return v->parse_options(c, argc, argv, out_opts);
    219 }
    220 
    221 void kit_frontend_free_options(KitCompiler* c, KitLanguage lang, void* opts) {
    222   const KitFrontendVTable* v;
    223   if (!c || !opts || (unsigned)lang >= KIT_LANG_COUNT) return;
    224   v = c->frontends[lang];
    225   if (v && v->free_options) v->free_options(c, opts);
    226 }
    227 
    228 uint32_t kit_compiler_arch_predefines(KitCompiler* c,
    229                                       const KitPredefinedMacro** out) {
    230   const ArchImpl* arch;
    231   if (out) *out = NULL;
    232   if (!c) return 0;
    233   arch = arch_for_compiler((Compiler*)c);
    234   if (!arch || !arch->predefined_macros || arch->npredefined_macros == 0)
    235     return 0;
    236   if (out) *out = arch->predefined_macros;
    237   return arch->npredefined_macros;
    238 }
    239 
    240 static void validate_bytes(Compiler* c, const KitSourceInput* in) {
    241   if (!in->name.s) panic_bad_options(c, "input name is NULL");
    242   if (!in->bytes.data && in->bytes.len != 0) {
    243     panic_bad_options(c, "input data is NULL but len > 0");
    244   }
    245 }
    246 
    247 static const KitFrontendVTable* frontend_for_language(Compiler* c,
    248                                                       KitLanguage lang) {
    249   if ((unsigned)lang >= KIT_LANG_COUNT) return NULL;
    250   return c->frontends[lang];
    251 }
    252 
    253 static KitStatus compile_obj_finalize(Compiler* c, ObjBuilder* ob);
    254 static KitStatus compile_frontend_state_obj_into(
    255     Compiler* c, const KitFrontendVTable* vtable, KitFrontendState* frontend,
    256     const KitFrontendCompileOptions* opts, const KitSourceInput* input,
    257     ObjBuilder* ob);
    258 
    259 static KitStatus kit_frontend_new(KitCompiler* c, KitLanguage lang,
    260                                   KitFrontend** out) {
    261   const KitFrontendVTable* vtable;
    262   KitFrontendState* state;
    263   KitFrontend* frontend;
    264   Heap* h;
    265 
    266   if (!out) return KIT_INVALID;
    267   *out = NULL;
    268   if (!c || (unsigned)lang >= KIT_LANG_COUNT) return KIT_INVALID;
    269   vtable = frontend_for_language((Compiler*)c, lang);
    270   if (!vtable) return KIT_UNSUPPORTED;
    271   state = vtable->new_frontend(c);
    272   if (!state) return KIT_NOMEM;
    273   h = (Heap*)c->ctx->heap;
    274   frontend =
    275       (KitFrontend*)h->alloc(h, sizeof(*frontend), _Alignof(KitFrontend));
    276   if (!frontend) {
    277     vtable->free_frontend(state);
    278     return KIT_NOMEM;
    279   }
    280   frontend->c = c;
    281   frontend->lang = lang;
    282   frontend->vtable = vtable;
    283   frontend->state = state;
    284   *out = frontend;
    285   return KIT_OK;
    286 }
    287 
    288 static void kit_frontend_commit(KitFrontend* frontend) {
    289   if (frontend && frontend->vtable && frontend->vtable->commit) {
    290     frontend->vtable->commit(frontend->state);
    291   }
    292 }
    293 
    294 static void kit_frontend_abort(KitFrontend* frontend) {
    295   if (frontend && frontend->vtable && frontend->vtable->abort) {
    296     frontend->vtable->abort(frontend->state);
    297   }
    298 }
    299 
    300 static KitStatus kit_frontend_compile_obj(KitFrontend* frontend,
    301                                           const KitFrontendCompileOptions* opts,
    302                                           const KitSourceInput* input,
    303                                           KitObjBuilder* out) {
    304   Compiler* c;
    305   PanicFrame panic;
    306   KitStatus st;
    307 
    308   if (!frontend || !frontend->c || !frontend->vtable || !frontend->state ||
    309       !opts || !input || !out) {
    310     return KIT_INVALID;
    311   }
    312   if (input->lang != frontend->lang) return KIT_INVALID;
    313   if (!frontend->vtable->compile_obj) return KIT_UNSUPPORTED;
    314   c = (Compiler*)frontend->c;
    315   compiler_panic_push(c, &panic);
    316   if (setjmp(panic.env)) {
    317     /* A genuine internal panic (CG/backend) longjmp'd here. Run cleanups, then
    318      * roll back any durable frontend state staged during this compile, and
    319      * propagate as a soft error. Ordinary diagnostic failures do NOT take this
    320      * path: the frontend returns KIT_ERR below without panicking. */
    321     compiler_run_cleanups(c);
    322     kit_frontend_abort(frontend);
    323     compiler_panic_pop(c, &panic);
    324     return KIT_ERR;
    325   }
    326   validate_bytes(c, input);
    327   metrics_scope_begin(c, "compile.tu");
    328   metrics_count(c, "compile.input_bytes", (u64)input->bytes.len);
    329   st = compile_frontend_state_obj_into(c, frontend->vtable, frontend->state,
    330                                        opts, input, (ObjBuilder*)out);
    331   metrics_scope_end(c, "compile.tu");
    332   /* On a soft diagnostic failure, roll back the staged transaction here so the
    333    * frontend is left exactly as it was before this compile. On success the
    334    * transaction is left open for the caller to commit or abort. */
    335   if (st != KIT_OK) kit_frontend_abort(frontend);
    336   compiler_panic_pop(c, &panic);
    337   return st;
    338 }
    339 
    340 static KitStatus kit_frontend_compile_cg(KitFrontend* frontend,
    341                                          const KitFrontendCompileOptions* opts,
    342                                          const KitSourceInput* input,
    343                                          KitCg* cg) {
    344   Compiler* c;
    345   PanicFrame panic;
    346   KitStatus st;
    347 
    348   if (!frontend || !frontend->c || !frontend->vtable || !frontend->state ||
    349       !opts || !input || !cg) {
    350     return KIT_INVALID;
    351   }
    352   if (input->lang != frontend->lang) return KIT_INVALID;
    353   if (frontend->vtable->caps.lto_mode != KIT_FRONTEND_LTO_CG ||
    354       !frontend->vtable->compile_cg) {
    355     return KIT_UNSUPPORTED;
    356   }
    357   c = (Compiler*)frontend->c;
    358   compiler_panic_push(c, &panic);
    359   if (setjmp(panic.env)) {
    360     compiler_run_cleanups(c);
    361     kit_frontend_abort(frontend);
    362     compiler_panic_pop(c, &panic);
    363     return KIT_ERR;
    364   }
    365   validate_bytes(c, input);
    366   metrics_scope_begin(c, "compile.tu");
    367   metrics_count(c, "compile.input_bytes", (u64)input->bytes.len);
    368   metrics_scope_begin(c, "compile.frontend");
    369   st = frontend->vtable->compile_cg(frontend->state, opts, input, cg);
    370   metrics_scope_end(c, "compile.frontend");
    371   metrics_scope_end(c, "compile.tu");
    372   if (st != KIT_OK) kit_frontend_abort(frontend);
    373   compiler_panic_pop(c, &panic);
    374   return st;
    375 }
    376 
    377 static void kit_frontend_free(KitFrontend* frontend) {
    378   Heap* h;
    379   if (!frontend) return;
    380   if (frontend->vtable && frontend->state) {
    381     frontend->vtable->free_frontend(frontend->state);
    382   }
    383   h = (Heap*)frontend->c->ctx->heap;
    384   h->free(h, frontend, sizeof(*frontend));
    385 }
    386 
    387 KitStatus kit_compile_session_new(KitCompiler* c,
    388                                   const KitCompileSessionOptions* opts,
    389                                   KitCompileSession** out) {
    390   KitCompileSession* s;
    391   KitFrontend* frontend = NULL;
    392   KitStatus st;
    393   Heap* h;
    394 
    395   if (!out) return KIT_INVALID;
    396   *out = NULL;
    397   if (!c || !opts || (unsigned)opts->lang >= KIT_LANG_COUNT) return KIT_INVALID;
    398   st = kit_frontend_new(c, opts->lang, &frontend);
    399   if (st != KIT_OK) return st;
    400   h = (Heap*)c->ctx->heap;
    401   s = (KitCompileSession*)h->alloc(h, sizeof(*s), _Alignof(KitCompileSession));
    402   if (!s) {
    403     kit_frontend_free(frontend);
    404     return KIT_NOMEM;
    405   }
    406   memset(s, 0, sizeof(*s));
    407   s->c = c;
    408   s->lang = opts->lang;
    409   s->frontend = frontend;
    410   s->opts = opts->compile;
    411   *out = s;
    412   return KIT_OK;
    413 }
    414 
    415 /* Shared object-producing compile path. Opaque frontends compile directly into
    416  * the object builder; semantic frontends use the same borrowed KitCg lifecycle
    417  * as LTO with a single source unit. On failure the frontend transaction is
    418  * rolled back and *out is NULL. On success, when commit_on_success is set (the
    419  * batch path), the transaction is committed before returning; otherwise it is
    420  * left open for the caller to resolve. */
    421 static KitStatus compile_session_run(KitCompileSession* s,
    422                                      const KitSourceInput* input,
    423                                      KitObjBuilder** out,
    424                                      int commit_on_success) {
    425   ObjBuilder* ob;
    426   KitFrontendCompileOptions opts;
    427   KitStatus st;
    428 
    429   if (!out) return KIT_INVALID;
    430   *out = NULL;
    431   if (!s || !s->c || !s->frontend || !input) return KIT_INVALID;
    432   if (input->lang != s->lang) return KIT_INVALID;
    433   ob = obj_new((Compiler*)s->c);
    434   if (!ob) return KIT_NOMEM;
    435   opts = s->opts;
    436   opts.input_kind = input->input_kind;
    437   opts.repl_entry_name = input->repl_entry_name;
    438   if (s->frontend->vtable->caps.lto_mode == KIT_FRONTEND_LTO_CG) {
    439     KitCg* cg = NULL;
    440     KitCgUnitOptions uopts;
    441     st = kit_cg_new(s->c, &cg);
    442     if (st == KIT_OK) st = kit_cg_begin(cg, (KitObjBuilder*)ob, &opts.code);
    443     memset(&uopts, 0, sizeof uopts);
    444     uopts.source_name = input->name;
    445     if (st == KIT_OK) st = kit_cg_begin_unit(cg, &uopts);
    446     if (st == KIT_OK)
    447       st = kit_frontend_compile_cg(s->frontend, &opts, input, cg);
    448     if (st == KIT_OK) st = kit_cg_end_unit(cg);
    449     if (st == KIT_OK) st = kit_cg_finish(cg, NULL);
    450     if (st == KIT_OK) st = kit_cg_detach(cg);
    451     kit_cg_free(cg);
    452     if (st == KIT_OK) st = compile_obj_finalize((Compiler*)s->c, ob);
    453   } else {
    454     st = kit_frontend_compile_obj(s->frontend, &opts, input,
    455                                   (KitObjBuilder*)ob);
    456   }
    457   if (st != KIT_OK) {
    458     kit_frontend_abort(s->frontend);
    459     obj_free(ob);
    460     return st;
    461   }
    462   if (commit_on_success) kit_frontend_commit(s->frontend);
    463   *out = (KitObjBuilder*)ob;
    464   return KIT_OK;
    465 }
    466 
    467 KitStatus kit_compile_session_compile(KitCompileSession* s,
    468                                       const KitSourceInput* input,
    469                                       KitObjBuilder** out) {
    470   return compile_session_run(s, input, out, /*commit_on_success=*/1);
    471 }
    472 
    473 KitStatus kit_compile_session_compile_cg(KitCompileSession* s,
    474                                          const KitSourceInput* input,
    475                                          KitCg* cg) {
    476   KitFrontendCompileOptions opts;
    477   KitStatus st;
    478   int unit_open = 0;
    479 
    480   if (!s || !s->c || !s->frontend || !input || !cg) return KIT_INVALID;
    481   if (input->lang != s->lang) return KIT_INVALID;
    482   opts = s->opts;
    483   opts.input_kind = input->input_kind;
    484   opts.repl_entry_name = input->repl_entry_name;
    485   {
    486     KitCgUnitOptions uopts;
    487     memset(&uopts, 0, sizeof uopts);
    488     uopts.source_name = input->name;
    489     st = kit_cg_begin_unit(cg, &uopts);
    490   }
    491   if (st == KIT_OK) unit_open = 1;
    492   if (st == KIT_OK) st = kit_frontend_compile_cg(s->frontend, &opts, input, cg);
    493   if (st == KIT_OK) st = kit_cg_end_unit(cg);
    494   if (st == KIT_OK) {
    495     unit_open = 0;
    496     kit_frontend_commit(s->frontend);
    497   } else if (unit_open) {
    498     (void)kit_cg_abort(cg);
    499   }
    500   return st;
    501 }
    502 
    503 KitStatus kit_compile_session_stage(KitCompileSession* s,
    504                                     const KitSourceInput* input,
    505                                     KitObjBuilder** out) {
    506   return compile_session_run(s, input, out, /*commit_on_success=*/0);
    507 }
    508 
    509 void kit_compile_session_commit(KitCompileSession* s) {
    510   if (s && s->frontend) kit_frontend_commit(s->frontend);
    511 }
    512 
    513 void kit_compile_session_abort(KitCompileSession* s) {
    514   if (s && s->frontend) kit_frontend_abort(s->frontend);
    515 }
    516 
    517 void kit_compile_session_free(KitCompileSession* s) {
    518   Heap* h;
    519   if (!s) return;
    520   h = (Heap*)s->c->ctx->heap;
    521   kit_frontend_free(s->frontend);
    522   h->free(h, s, sizeof(*s));
    523 }
    524 
    525 static KitStatus compile_obj_finalize(Compiler* c, ObjBuilder* ob) {
    526   metrics_scope_begin(c, "compile.obj_finalize");
    527   obj_finalize(ob);
    528   metrics_scope_end(c, "compile.obj_finalize");
    529   metrics_count(c, "compile.obj_sections", obj_section_count(ob));
    530   metrics_count(c, "compile.obj_relocs", obj_reloc_total(ob));
    531   return KIT_OK;
    532 }
    533 
    534 static KitStatus compile_frontend_state_obj_into(
    535     Compiler* c, const KitFrontendVTable* vtable, KitFrontendState* frontend,
    536     const KitFrontendCompileOptions* opts, const KitSourceInput* input,
    537     ObjBuilder* ob) {
    538   KitStatus st;
    539 
    540   metrics_scope_begin(c, "compile.frontend");
    541   st = vtable->compile_obj(frontend, opts, input, ob);
    542   metrics_scope_end(c, "compile.frontend");
    543   /* Ordinary diagnostic failure: fail softly with the status the frontend
    544    * already reported. No synthetic fatal, and do not finalize a half-built
    545    * object. Genuine internal failures panic from inside compile_obj and
    546    * never reach here. */
    547   if (st != KIT_OK) return st;
    548   return compile_obj_finalize(c, ob);
    549 }
    550 
    551 /* ============================================================
    552  * Asm
    553  * ============================================================ */
    554 
    555 static KitFrontendState* asm_frontend_new(KitCompiler* c) {
    556   Heap* h;
    557   AsmFrontend* fe;
    558   if (!c) return NULL;
    559   h = (Heap*)c->ctx->heap;
    560   fe = (AsmFrontend*)h->alloc(h, sizeof(*fe), _Alignof(AsmFrontend));
    561   if (!fe) return NULL;
    562   fe->c = c;
    563   return (KitFrontendState*)fe;
    564 }
    565 
    566 static KitStatus asm_frontend_compile(KitFrontendState* frontend,
    567                                       const KitFrontendCompileOptions* opts,
    568                                       const KitSourceInput* input,
    569                                       KitObjBuilder* out) {
    570   AsmFrontend* fe = (AsmFrontend*)frontend;
    571   Compiler* c;
    572   AsmLexer* lex;
    573   MCEmitter* mc;
    574   (void)opts;
    575   if (!fe || !fe->c || !input || !out) return KIT_INVALID;
    576   c = fe->c;
    577   metrics_scope_begin(c, "compile.asm.lex_open");
    578   lex = asm_lex_open_mem(c, input->name.s, input->bytes.s, input->bytes.len);
    579   metrics_scope_end(c, "compile.asm.lex_open");
    580   metrics_scope_begin(c, "compile.asm.mc_new");
    581   mc = mc_new(c, (ObjBuilder*)out);
    582   metrics_scope_end(c, "compile.asm.mc_new");
    583   metrics_scope_begin(c, "compile.asm.parse");
    584   asm_parse(c, lex, mc);
    585   metrics_scope_end(c, "compile.asm.parse");
    586   metrics_scope_begin(c, "compile.asm.mc_free");
    587   mc_free(mc);
    588   metrics_scope_end(c, "compile.asm.mc_free");
    589   return KIT_OK;
    590 }
    591 
    592 static void asm_frontend_free(KitFrontendState* frontend) {
    593   AsmFrontend* fe = (AsmFrontend*)frontend;
    594   Heap* h;
    595   if (!fe) return;
    596   h = fe->c->ctx->heap;
    597   h->free(h, fe, sizeof(*fe));
    598 }
    599 
    600 struct KitDepIter {
    601   Compiler* c;
    602   SourceDepIter* inner;
    603 };
    604 
    605 KitStatus kit_dep_iter_new(KitCompiler* c, KitDepIter** out) {
    606   Heap* h;
    607   KitDepIter* it;
    608   if (!out) return KIT_INVALID;
    609   if (!c || !c->sources) return KIT_INVALID;
    610   h = c->ctx->heap;
    611   it = (KitDepIter*)h->alloc(h, sizeof(*it), _Alignof(KitDepIter));
    612   if (!it) return KIT_NOMEM;
    613   it->c = c;
    614   it->inner = source_depiter_new(c->sources);
    615   if (!it->inner) {
    616     h->free(h, it, sizeof(*it));
    617     return KIT_NOMEM;
    618   }
    619   *out = it;
    620   return KIT_OK;
    621 }
    622 
    623 KitIterResult kit_dep_iter_next(KitDepIter* it, KitDepEdge* out) {
    624   const SourceInclude* edge;
    625   const SourceFile* includer;
    626   const SourceFile* included;
    627   if (!it || !out) return KIT_ITER_ERROR;
    628   edge = source_depiter_next(it->inner);
    629   if (!edge) return KIT_ITER_END;
    630   includer = source_file(it->c->sources, edge->includer_file_id);
    631   included = source_file(it->c->sources, edge->included_file_id);
    632   out->includer_name =
    633       includer ? pool_slice(it->c->global, includer->name) : KIT_SLICE_NULL;
    634   out->included_name =
    635       included ? pool_slice(it->c->global, included->name) : KIT_SLICE_NULL;
    636   out->include_loc = edge->include_loc;
    637   out->from_system_path = (uint8_t)(edge->system ? 1 : 0);
    638   out->bracketed = (uint8_t)(edge->system ? 1 : 0);
    639   out->pad[0] = 0;
    640   out->pad[1] = 0;
    641   return KIT_ITER_ITEM;
    642 }
    643 
    644 void kit_dep_iter_free(KitDepIter* it) {
    645   Heap* h;
    646   if (!it) return;
    647   h = it->c->ctx->heap;
    648   if (it->inner) source_depiter_free(it->inner);
    649   h->free(h, it, sizeof(*it));
    650 }