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 }