kit

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

core.c (6886B)


      1 /* Compiler lifecycle, panic, and cleanup-stack machinery. */
      2 
      3 #include "core/core.h"
      4 
      5 #include <stdarg.h>
      6 #include <stdlib.h>
      7 #include <string.h>
      8 
      9 #include "abi/abi.h"
     10 #include "api/lang_registry.h"
     11 #include "core/arena.h"
     12 #include "core/diag.h"
     13 #include "core/heap.h"
     14 #include "core/pool.h"
     15 
     16 #if defined(__GNUC__) || defined(__clang__) || defined(__kit__)
     17 __attribute__((weak))
     18 #endif
     19 void kit_debug_printf(const char* fmt, ...) {
     20   (void)fmt;
     21 }
     22 
     23 #if defined(__GNUC__) || defined(__clang__) || defined(__kit__)
     24 __attribute__((weak))
     25 #endif
     26 const char* kit_debug_getenv(const char* name) {
     27   (void)name;
     28   return NULL;
     29 }
     30 
     31 /* Weak fallbacks for the tracing seam (kit/trace.h). libkit holds no trace
     32  * state, so the library-side gate is always closed and the sink is a no-op;
     33  * hosted binaries (driver/env) override both with the KIT_TRACE-driven
     34  * implementation. */
     35 #if defined(__GNUC__) || defined(__clang__) || defined(__kit__)
     36 __attribute__((weak))
     37 #endif
     38 int kit_trace_enabled(const char* module, int level) {
     39   (void)module;
     40   (void)level;
     41   return 0;
     42 }
     43 
     44 #if defined(__GNUC__) || defined(__clang__) || defined(__kit__)
     45 __attribute__((weak))
     46 #endif
     47 void kit_trace_emit(const char* module, int level, const char* file, int line,
     48                     const char* fmt, ...) {
     49   (void)module;
     50   (void)level;
     51   (void)file;
     52   (void)line;
     53   (void)fmt;
     54 }
     55 
     56 /* Weak fallback for the <assert.h> failure hook (rt/include/assert.h). kit's
     57  * own code uses compiler_panic, not C assert(), but vendored code compiled into
     58  * libkit (the lz4 codecs) references __kit_assert_fail in non-NDEBUG builds.
     59  * A hidden weak trap lets every libkit.a consumer -- the kit binary, the
     60  * test harnesses that link the archive directly, a standalone embedder --
     61  * resolve it without pulling in the runtime. Hidden (the -fvisibility=hidden
     62  * default, NOT visibility-default) keeps it off libkit's public export
     63  * surface that scripts/lib_reloc_defined_prefixes.py guards. The freestanding
     64  * runtime ships its own weak __kit_assert_fail (rt/lib/assert) for programs
     65  * that link it. The contract is _Noreturn, so trap rather than return. */
     66 #if defined(__GNUC__) || defined(__clang__) || defined(__kit__)
     67 __attribute__((weak))
     68 #endif
     69 _Noreturn void __kit_assert_fail(const char* expr, const char* file, int line,
     70                                  const char* func) {
     71   (void)expr;
     72   (void)file;
     73   (void)line;
     74   (void)func;
     75   __builtin_trap();
     76   for (;;) {
     77   }
     78 }
     79 
     80 SourceManager* source_new(Compiler*);
     81 void source_free(SourceManager*);
     82 
     83 struct CompilerCleanup {
     84   void (*fn)(void*);
     85   void* arg;
     86   CompilerCleanup* prev;
     87 };
     88 
     89 KitStatus compiler_init(Compiler* c, const KitTarget* target,
     90                         const KitContext* ctx) {
     91   Heap* h = ctx->heap;
     92   KitStatus st;
     93 
     94   if (!c || !target || !ctx || !h) return KIT_INVALID;
     95   memset(c, 0, sizeof(*c));
     96   c->ctx = ctx;
     97   c->target_ref = target;
     98   c->target = target->spec;
     99 
    100   c->global = (Pool*)h->alloc(h, sizeof(Pool), _Alignof(Pool));
    101   if (!c->global) goto nomem;
    102   pool_init(c->global, h);
    103 
    104   c->tu = (Arena*)h->alloc(h, sizeof(Arena), _Alignof(Arena));
    105   if (!c->tu) goto nomem;
    106   arena_init(c->tu, h, 0);
    107 
    108   c->scratch = (Arena*)h->alloc(h, sizeof(Arena), _Alignof(Arena));
    109   if (!c->scratch) goto nomem;
    110   arena_init(c->scratch, h, 0);
    111 
    112   c->sources = source_new(c);
    113   if (!c->sources) goto nomem;
    114 
    115   c->abi = abi_new(c);
    116   if (!c->abi) goto nomem;
    117 
    118   c->cleanup = NULL;
    119 
    120   st = lang_registry_init(c);
    121   if (st != KIT_OK) goto err_status;
    122   return KIT_OK;
    123 
    124 nomem:
    125   compiler_fini(c);
    126   return KIT_NOMEM;
    127 
    128 err_status:
    129   compiler_fini(c);
    130   return st;
    131 }
    132 
    133 void compiler_fini(Compiler* c) {
    134   Heap* h = c->ctx->heap;
    135 
    136   compiler_run_cleanups(c);
    137 
    138   if (c->cg_api_free) {
    139     c->cg_api_free(c);
    140     c->cg_api_free = NULL;
    141   }
    142   if (c->frontends) {
    143     h->free(h, c->frontends, c->frontends_cap * sizeof(*c->frontends));
    144     c->frontends = NULL;
    145     c->nfrontends = 0;
    146     c->frontends_cap = 0;
    147   }
    148 
    149   if (c->abi) {
    150     abi_free(c->abi);
    151     c->abi = NULL;
    152   }
    153   if (c->sources) source_free(c->sources);
    154   if (c->scratch) {
    155     arena_fini(c->scratch);
    156     h->free(h, c->scratch, sizeof(Arena));
    157   }
    158   if (c->tu) {
    159     arena_fini(c->tu);
    160     h->free(h, c->tu, sizeof(Arena));
    161   }
    162   if (c->global) {
    163     pool_fini(c->global);
    164     h->free(h, c->global, sizeof(Pool));
    165   }
    166   c->global = NULL;
    167   c->tu = c->scratch = NULL;
    168   c->sources = NULL;
    169 }
    170 
    171 CompilerCleanup* compiler_defer(Compiler* c, void (*fn)(void*), void* arg) {
    172   CompilerCleanup* node;
    173   node = (CompilerCleanup*)arena_alloc(c->scratch, sizeof(*node),
    174                                        _Alignof(CompilerCleanup));
    175   if (!node) return NULL;
    176   node->fn = fn;
    177   node->arg = arg;
    178   node->prev = c->cleanup;
    179   c->cleanup = node;
    180   return node;
    181 }
    182 
    183 void compiler_undefer(Compiler* c, CompilerCleanup* node) {
    184   CompilerCleanup** link = &c->cleanup;
    185   while (*link) {
    186     if (*link == node) {
    187       *link = node->prev;
    188       return;
    189     }
    190     link = &(*link)->prev;
    191   }
    192 }
    193 
    194 void compiler_run_cleanups(Compiler* c) {
    195   while (c->cleanup) {
    196     CompilerCleanup* node = c->cleanup;
    197     c->cleanup = node->prev;
    198     node->fn(node->arg);
    199   }
    200 }
    201 
    202 void compiler_panic_push(Compiler* c, PanicFrame* frame) {
    203   frame->prev = c->panic_frame;
    204   c->panic_frame = frame;
    205 }
    206 
    207 void compiler_panic_pop(Compiler* c, PanicFrame* frame) {
    208   PanicFrame** link;
    209   if (!c || !frame) return;
    210   if (c->panic_frame == frame) {
    211     c->panic_frame = frame->prev;
    212     frame->prev = NULL;
    213     return;
    214   }
    215   link = &c->panic_frame;
    216   while (*link) {
    217     if (*link == frame) {
    218       *link = frame->prev;
    219       frame->prev = NULL;
    220       return;
    221     }
    222     link = &(*link)->prev;
    223   }
    224 }
    225 
    226 void compiler_panic(Compiler* c, SrcLoc loc, const char* fmt, ...) {
    227   va_list ap;
    228   va_start(ap, fmt);
    229   compiler_panicv(c, loc, fmt, ap);
    230   va_end(ap);
    231 }
    232 
    233 void compiler_panicv(Compiler* c, SrcLoc loc, const char* fmt, va_list ap) {
    234   /* Route through diag_emitv so the FATAL bumps the sink's error counter
    235    * before we longjmp away (diag_emitv tolerates a NULL emit slot). */
    236   if (c->ctx && c->ctx->diag) {
    237     diag_emitv(c->ctx->diag, DIAG_FATAL, loc, fmt, ap);
    238   }
    239   if (c->panic_frame) longjmp(c->panic_frame->env, 1);
    240   /* No active panic handler. Every compile/codegen entry point pushes a panic
    241    * frame (compiler_panic_push + setjmp) before reaching code that can panic, so
    242    * reaching here is an internal-consistency failure, not a recoverable error.
    243    * The legacy bare `c->panic` jmp_buf is never armed by anyone, so longjmp'ing
    244    * it would jump into garbage — a wild SEGV that hides the real fault. Trap
    245    * deterministically instead; the FATAL diagnostic above has already been
    246    * emitted, so the failing location is reported before we abort. (libkit is
    247    * freestanding — __builtin_trap is the same unrecoverable primitive
    248    * __kit_assert_fail uses; there is no exit() to call here.) */
    249   __builtin_trap();
    250 }