core.c (12007B)
1 /* Public core API bridge. */ 2 3 #include "core/core.h" 4 #include "api/lang_registry.h" 5 6 #include <kit/core.h> 7 #include <string.h> 8 9 #include "arch/arch.h" 10 #include "core/diag.h" 11 #include "core/heap.h" 12 #include "core/pool.h" 13 #include "core/slice.h" 14 15 static void bitset_set(u64* words, u32 nwords, u32 idx, int enabled) { 16 u32 w = idx / 64u; 17 u64 bit = 1ull << (idx % 64u); 18 if (!words || w >= nwords) return; 19 if (enabled) 20 words[w] |= bit; 21 else 22 words[w] &= ~bit; 23 } 24 25 static int bitset_get(const u64* words, u32 nwords, u32 idx) { 26 u32 w = idx / 64u; 27 if (!words || w >= nwords) return 0; 28 return (words[w] & (1ull << (idx % 64u))) != 0; 29 } 30 31 KitStatus kit_target_new(const KitContext* ctx, const KitTargetOptions* opts, 32 KitTarget** out) { 33 const ArchImpl* arch; 34 KitTarget* t; 35 Heap* h; 36 u32 nwords; 37 u32 i; 38 39 if (!out) return KIT_INVALID; 40 *out = NULL; 41 if (!ctx || !ctx->heap || !opts) return KIT_INVALID; 42 arch = arch_lookup(opts->spec.arch); 43 if (!arch) { 44 kit_ctx_diagf(ctx, "unsupported target architecture: %u", 45 (unsigned)opts->spec.arch); 46 return KIT_UNSUPPORTED; 47 } 48 if (opts->spec.arch == KIT_ARCH_WASM && opts->spec.ptr_size != 4u) { 49 kit_ctx_diagf(ctx, 50 "wasm64 is not supported in v1; use a wasm32 target"); 51 return KIT_UNSUPPORTED; 52 } 53 54 h = ctx->heap; 55 t = (KitTarget*)h->alloc(h, sizeof(*t), _Alignof(KitTarget)); 56 if (!t) return KIT_NOMEM; 57 memset(t, 0, sizeof(*t)); 58 t->ctx = ctx; 59 t->spec = opts->spec; 60 61 /* Resolve the data-model / ABI properties once, here, from arch/os/ptr_size. 62 * These mirror the predicates the frontend/backend used to re-derive by 63 * identity (kit_target_uses_lp64, the Windows wchar width, the binary128 64 * long-double psABI test, and the freestanding eh_frame gate). */ 65 /* #33 LP64 vs LLP64/ILP32: long is 8 bytes only for 64-bit non-Windows. */ 66 t->spec.long_size = 67 (t->spec.ptr_size == 8u && t->spec.os != KIT_OS_WINDOWS) ? 8u : 4u; 68 /* #32/#20 wchar_t: 2 bytes on Windows, 4 elsewhere. */ 69 t->spec.wchar_size = (t->spec.os == KIT_OS_WINDOWS) ? 2u : 4u; 70 /* #3/#20 long double: binary128 on the quad-psABI targets (rv64, non-Apple/ 71 * non-Windows aarch64, wasm), else aliases double. Replicates 72 * kit_target_long_double_is_binary128 exactly. */ 73 if (t->spec.arch == KIT_ARCH_RV64 || 74 (t->spec.arch == KIT_ARCH_ARM_64 && t->spec.obj != KIT_OBJ_MACHO && 75 t->spec.os != KIT_OS_WINDOWS) || 76 t->spec.arch == KIT_ARCH_WASM) { 77 t->spec.long_double_format = (uint8_t)KIT_LDBL_BINARY128; 78 } else { 79 t->spec.long_double_format = (uint8_t)KIT_LDBL_DOUBLE; 80 } 81 /* #17 eh_frame: hosted ELF/COFF targets emit .eh_frame for the host 82 * unwinder; freestanding does not (no unwinder, and it would orphan an 83 * ALLOC section). Mach-O is also excluded: ld64 / ld64.lld reject kit's 84 * raw __eh_frame — arm64 Mach-O has no pcrel-32 data relocation for the 85 * FDE pc-begin, and ld64's legacy __eh_frame path is effectively unused 86 * (clang emits __compact_unwind instead). kit's macOS backtrace walks 87 * the frame-pointer chain, so dropping __eh_frame costs no capability 88 * while making kit objects linkable by the system linker. */ 89 t->spec.emits_eh_frame = 90 (t->spec.os != KIT_OS_FREESTANDING && t->spec.obj != KIT_OBJ_MACHO) ? 1u 91 : 0u; 92 93 nwords = (arch->ntarget_features + 63u) / 64u; 94 if (nwords) { 95 t->feature_words = 96 (u64*)h->alloc(h, sizeof(*t->feature_words) * nwords, _Alignof(u64)); 97 if (!t->feature_words) { 98 h->free(h, t, sizeof(*t)); 99 return KIT_NOMEM; 100 } 101 memset(t->feature_words, 0, sizeof(*t->feature_words) * nwords); 102 t->nfeature_words = nwords; 103 arch_target_feature_defaults(arch, &t->spec, t->feature_words, nwords); 104 } 105 106 if (opts->isa.s && opts->isa.len) { 107 KitStatus st = arch_target_feature_apply_isa( 108 arch, &t->spec, opts->isa, t->feature_words, t->nfeature_words); 109 if (st != KIT_OK) { 110 kit_ctx_diagf(ctx, "unsupported ISA/profile for %s: %.*s", arch->name, 111 KIT_SLICE_ARG(opts->isa)); 112 kit_target_free(t); 113 return st == KIT_UNSUPPORTED ? KIT_INVALID : st; 114 } 115 } 116 117 /* -mcpu= after -march= so a CPU selector refines (or, for ARM, re-pins) the 118 * ISA profile; explicit -mattr/-m<feature> overrides come last. */ 119 if (opts->cpu.s && opts->cpu.len) { 120 KitStatus st = arch_target_feature_apply_cpu( 121 arch, &t->spec, opts->cpu, t->feature_words, t->nfeature_words); 122 if (st != KIT_OK) { 123 kit_ctx_diagf(ctx, "unsupported CPU for %s: %.*s", arch->name, 124 KIT_SLICE_ARG(opts->cpu)); 125 kit_target_free(t); 126 return st == KIT_UNSUPPORTED ? KIT_INVALID : st; 127 } 128 } 129 130 for (i = 0; i < opts->nfeatures; ++i) { 131 u32 idx; 132 KitSlice name = opts->features[i].name; 133 if (!arch_target_feature_index(arch, name, &idx)) { 134 kit_ctx_diagf(ctx, "unknown target feature for %s: %.*s", arch->name, 135 KIT_SLICE_ARG(name)); 136 kit_target_free(t); 137 return KIT_INVALID; 138 } 139 bitset_set(t->feature_words, t->nfeature_words, idx, 140 opts->features[i].enabled); 141 } 142 143 /* Resolve & validate the float ABI by arch capability. Arches with no 144 * float-ABI axis (everything but RISC-V) leave float_abi at 145 * KIT_FLOAT_ABI_DEFAULT via the no-op hook. The RISC-V hook produces the 146 * same byte-identical diagnostics the old inline block emitted. */ 147 { 148 char errbuf[256]; 149 errbuf[0] = '\0'; 150 if (arch_resolve_float_abi(arch, &t->spec, t->feature_words, 151 t->nfeature_words, opts->abi, errbuf, 152 sizeof errbuf) == KIT_INVALID) { 153 kit_ctx_diagf(ctx, "%s", errbuf); 154 kit_target_free(t); 155 return KIT_INVALID; 156 } 157 } 158 159 *out = t; 160 return KIT_OK; 161 } 162 163 void kit_target_free(KitTarget* t) { 164 Heap* h; 165 if (!t) return; 166 h = t->ctx ? t->ctx->heap : NULL; 167 if (!h) return; 168 if (t->feature_words) 169 h->free(h, t->feature_words, sizeof(*t->feature_words) * t->nfeature_words); 170 h->free(h, t, sizeof(*t)); 171 } 172 173 KitTargetSpec kit_target_spec(const KitTarget* t) { 174 KitTargetSpec spec; 175 memset(&spec, 0, sizeof spec); 176 return t ? t->spec : spec; 177 } 178 179 int kit_target_has_feature(const KitTarget* t, KitSlice name) { 180 const ArchImpl* arch; 181 u32 idx; 182 if (!t) return 0; 183 arch = arch_lookup(t->spec.arch); 184 if (!arch_target_feature_index(arch, name, &idx)) return 0; 185 return bitset_get(t->feature_words, t->nfeature_words, idx); 186 } 187 188 KitStatus kit_compiler_new(const KitTarget* target, const KitContext* ctx, 189 KitCompiler** out) { 190 return kit_compiler_new_ex(target, ctx, NULL, out); 191 } 192 193 KitStatus kit_compiler_new_ex(const KitTarget* target, const KitContext* ctx, 194 const KitCompilerOptions* opts, 195 KitCompiler** out) { 196 Heap* h; 197 Compiler* c; 198 KitStatus st; 199 200 if (!out) return KIT_INVALID; 201 if (!target || !ctx || !ctx->heap) return KIT_INVALID; 202 h = ctx->heap; 203 c = h->alloc(h, sizeof(*c), _Alignof(Compiler)); 204 if (!c) return KIT_NOMEM; 205 st = compiler_init(c, target, ctx); 206 if (st != KIT_OK) { 207 h->free(h, c, sizeof(*c)); 208 return st; 209 } 210 if (opts && opts->frontends) { 211 st = kit_compiler_install_frontend_registry(c, opts->frontends); 212 if (st != KIT_OK) { 213 compiler_fini(c); 214 h->free(h, c, sizeof(*c)); 215 if (out) *out = NULL; 216 return st; 217 } 218 } 219 *out = c; 220 return KIT_OK; 221 } 222 223 void kit_compiler_free(KitCompiler* c) { 224 Heap* h; 225 if (!c) return; 226 h = c->ctx->heap; 227 compiler_fini(c); 228 h->free(h, c, sizeof(*c)); 229 } 230 231 const KitTarget* kit_compiler_target(KitCompiler* c) { 232 return c ? c->target_ref : NULL; 233 } 234 235 KitTargetSpec kit_compiler_target_spec(KitCompiler* c) { 236 KitTargetSpec t; 237 memset(&t, 0, sizeof t); 238 if (!c) return t; 239 return c->target; 240 } 241 242 const KitContext* kit_compiler_context(KitCompiler* c) { 243 return (c && c->ctx) ? c->ctx : NULL; 244 } 245 246 KitSlice kit_compiler_file_name(KitCompiler* c, uint32_t file_id) { 247 const SourceFile* f; 248 if (!c) return SLICE_NULL; 249 f = source_file(c->sources, file_id); 250 if (!f) return SLICE_NULL; 251 return pool_slice(c->global, f->name); 252 } 253 254 KitSym kit_sym_intern(KitCompiler* c, KitSlice s) { 255 if (!c) return 0; 256 return pool_intern_slice(c->global, s); 257 } 258 259 KitSlice kit_sym_str(KitCompiler* c, KitSym sym) { 260 if (!c) return SLICE_NULL; 261 return pool_slice(c->global, (Sym)sym); 262 } 263 264 KitSym kit_cg_c_linkage_name(KitCompiler* c, KitSym source_name) { 265 const char* name; 266 size_t len; 267 char* buf; 268 char stackbuf[256]; /* C linkage names are short identifiers; this covers 269 * all but pathological cases without touching the heap. 270 * Called once per declared symbol, so a per-call malloc 271 * here scales with declaration count -- avoid it. */ 272 int heaped; 273 KitSym out; 274 Heap* h; 275 Slice nslice; 276 277 if (!c || !source_name) return 0; 278 nslice = pool_slice(c->global, (Sym)source_name); 279 name = nslice.s; 280 len = nslice.len; 281 if (!name) return 0; 282 if (c->target.obj != KIT_OBJ_MACHO) return source_name; 283 284 h = c->ctx->heap; 285 heaped = len + 2u > sizeof stackbuf; 286 buf = heaped ? (char*)h->alloc(h, len + 2u, 1) : stackbuf; 287 if (!buf) return 0; 288 buf[0] = '_'; 289 if (len) memcpy(buf + 1, name, len); 290 buf[len + 1u] = '\0'; 291 out = pool_intern_slice(c->global, (Slice){.s = buf, .len = (u32)(len + 1u)}); 292 if (heaped) h->free(h, buf, len + 2u); 293 return out; 294 } 295 296 typedef struct MemWriter { 297 KitWriter base; 298 Heap* heap; 299 u8* data; 300 size_t cap; 301 size_t len; 302 size_t pos; 303 KitStatus status; 304 } MemWriter; 305 306 static KitStatus mw_grow(MemWriter* mw, size_t needed) { 307 size_t new_cap; 308 u8* p; 309 310 if (needed <= mw->cap) return KIT_OK; 311 new_cap = mw->cap ? mw->cap : 64; 312 while (new_cap < needed) { 313 size_t doubled = new_cap * 2; 314 if (doubled <= new_cap) { 315 mw->status = KIT_NOMEM; 316 return KIT_NOMEM; 317 } 318 new_cap = doubled; 319 } 320 321 p = (u8*)mw->heap->realloc(mw->heap, mw->data, mw->cap, new_cap, 1); 322 if (!p) { 323 mw->status = KIT_NOMEM; 324 return KIT_NOMEM; 325 } 326 if (new_cap > mw->cap) memset(p + mw->cap, 0, new_cap - mw->cap); 327 mw->data = p; 328 mw->cap = new_cap; 329 return KIT_OK; 330 } 331 332 static KitStatus mw_write(KitWriter* w, const void* data, size_t n) { 333 MemWriter* mw = (MemWriter*)w; 334 size_t end; 335 KitStatus st; 336 337 if (mw->status != KIT_OK) return mw->status; 338 if (n == 0) return KIT_OK; 339 end = mw->pos + n; 340 if (end < mw->pos) { 341 mw->status = KIT_NOMEM; 342 return KIT_NOMEM; 343 } 344 st = mw_grow(mw, end); 345 if (st != KIT_OK) return st; 346 memcpy(mw->data + mw->pos, data, n); 347 mw->pos = end; 348 if (mw->pos > mw->len) mw->len = mw->pos; 349 return KIT_OK; 350 } 351 352 static KitStatus mw_seek(KitWriter* w, uint64_t off) { 353 MemWriter* mw = (MemWriter*)w; 354 if (mw->status != KIT_OK) return mw->status; 355 mw->pos = (size_t)off; 356 return KIT_OK; 357 } 358 359 static uint64_t mw_tell(KitWriter* w) { return ((MemWriter*)w)->pos; } 360 361 static KitStatus mw_status(KitWriter* w) { return ((MemWriter*)w)->status; } 362 363 static void mw_close(KitWriter* w) { 364 MemWriter* mw = (MemWriter*)w; 365 Heap* h = mw->heap; 366 if (mw->data) h->free(h, mw->data, mw->cap); 367 h->free(h, mw, sizeof(*mw)); 368 } 369 370 KitStatus kit_writer_mem(KitHeap* heap, KitWriter** out) { 371 MemWriter* mw; 372 if (!out) return KIT_INVALID; 373 if (!heap) return KIT_INVALID; 374 mw = (MemWriter*)heap->alloc(heap, sizeof(*mw), _Alignof(MemWriter)); 375 if (!mw) return KIT_NOMEM; 376 mw->base.write = mw_write; 377 mw->base.seek = mw_seek; 378 mw->base.tell = mw_tell; 379 mw->base.status = mw_status; 380 mw->base.close = mw_close; 381 mw->heap = heap; 382 mw->data = NULL; 383 mw->cap = 0; 384 mw->len = 0; 385 mw->pos = 0; 386 mw->status = KIT_OK; 387 *out = &mw->base; 388 return KIT_OK; 389 } 390 391 const uint8_t* kit_writer_mem_bytes(KitWriter* w, size_t* len_out) { 392 MemWriter* mw = (MemWriter*)w; 393 if (len_out) *len_out = mw ? mw->len : 0; 394 return mw ? mw->data : NULL; 395 }