session.c (19507B)
1 #include <kit/config.h> 2 3 #include "cg/internal.h" 4 5 #if KIT_OPT_ENABLED 6 #include "opt/opt.h" 7 #endif 8 9 #include "obj/symresolve.h" 10 #include "obj/wasm_imports.h" 11 12 static void cg_free_obj_state(KitCg* g) { 13 Heap* h; 14 if (!g) return; 15 h = g->c->ctx->heap; 16 if (g->stack) { 17 h->free(h, g->stack, sizeof(ApiSValue) * g->cap); 18 g->stack = NULL; 19 } 20 if (g->const_stack) { 21 h->free(h, g->const_stack, sizeof(ApiConstValue) * g->cap); 22 g->const_stack = NULL; 23 } 24 if (g->locals) { 25 h->free(h, g->locals, sizeof(*g->locals) * g->locals_cap); 26 g->locals = NULL; 27 } 28 if (g->scopes) { 29 h->free(h, g->scopes, sizeof(*g->scopes) * g->scopes_cap); 30 g->scopes = NULL; 31 } 32 if (g->sym_types) { 33 h->free(h, g->sym_types, sizeof(*g->sym_types) * g->sym_cap); 34 g->sym_types = NULL; 35 } 36 if (g->sym_attrs) { 37 h->free(h, g->sym_attrs, sizeof(*g->sym_attrs) * g->sym_cap); 38 g->sym_attrs = NULL; 39 } 40 if (g->debug_type_cache) { 41 h->free(h, g->debug_type_cache, 42 sizeof(*g->debug_type_cache) * g->debug_type_cap); 43 g->debug_type_cache = NULL; 44 } 45 if (g->debug_type_state) { 46 h->free(h, g->debug_type_state, 47 sizeof(*g->debug_type_state) * g->debug_type_cap); 48 g->debug_type_state = NULL; 49 } 50 if (g->sym_def_seq) { 51 h->free(h, g->sym_def_seq, sizeof(*g->sym_def_seq) * g->sym_def_seq_cap); 52 g->sym_def_seq = NULL; 53 g->sym_def_seq_cap = 0; 54 } 55 if (g->data_tls_collect) { 56 buf_fini(&g->data_tls_bytes); 57 g->data_tls_collect = 0; 58 } 59 if (g->data_tls_relocs) { 60 h->free(h, g->data_tls_relocs, 61 sizeof(*g->data_tls_relocs) * g->data_tls_relocs_cap); 62 g->data_tls_relocs = NULL; 63 } 64 if (g->delayed_arena_init) { 65 arena_fini(&g->delayed_arena); 66 g->delayed_arena_init = 0; 67 } 68 g->delayed_free = NULL; 69 g->sp = 0; 70 g->cap = 0; 71 g->unevaluated_depth = 0; 72 g->nlocals = 0; 73 g->const_head = KIT_CG_LOCAL_NONE; 74 g->locals_cap = 0; 75 g->scopes_cap = 0; 76 g->sym_cap = 0; 77 g->debug_type_cap = 0; 78 g->fn_ret_type = 0; 79 memset(&g->fn_desc, 0, sizeof(g->fn_desc)); 80 memset(g->fn_params, 0, sizeof(g->fn_params)); 81 g->nscopes = 0; 82 g->scope_generation = 0; 83 g->rodata_counter = 0; 84 g->function_sections = 0; 85 g->data_sections = 0; 86 g->data_sec = OBJ_SEC_NONE; 87 g->data_sym = OBJ_SYM_NONE; 88 g->data_base = 0; 89 g->data_size = 0; 90 g->data_local_static_target = 0; 91 g->data_discard = 0; 92 g->data_tls_zero_fill = 0; 93 g->data_tls_align = 0; 94 g->data_tls_nrelocs = 0; 95 g->data_tls_relocs_cap = 0; 96 } 97 98 KitStatus kit_cg_new(KitCompiler* c, KitCg** cg_out) { 99 Heap* h; 100 KitCg* g; 101 if (!cg_out) return KIT_INVALID; 102 *cg_out = NULL; 103 if (!c) return KIT_INVALID; 104 h = (Heap*)c->ctx->heap; 105 g = (KitCg*)h->alloc(h, sizeof(KitCg), _Alignof(KitCg)); 106 if (!g) return KIT_NOMEM; 107 memset(g, 0, sizeof *g); 108 g->c = (Compiler*)c; 109 g->data_sec = OBJ_SEC_NONE; 110 g->data_sym = OBJ_SYM_NONE; 111 *cg_out = g; 112 return KIT_OK; 113 } 114 115 KitStatus kit_cg_begin(KitCg* g, KitObjBuilder* out, 116 const KitCodeOptions* opts) { 117 KitCompiler* c; 118 CgTarget* target; 119 const CGBackend* backend; 120 int opt_level = opts ? opts->opt_level : 0; 121 if (!g || !g->c || !out) return KIT_INVALID; 122 if (g->obj || g->target || g->debug || g->unit_active || g->finished) 123 return KIT_INVALID; 124 c = (KitCompiler*)g->c; 125 if (opt_level < 0 || opt_level > 2) { 126 compiler_panic((Compiler*)c, api_no_loc(), 127 "KitCg: unsupported opt_level %d", opt_level); 128 } 129 if (opt_level > 1) opt_level = 1; 130 if (opts && opts->stack_protector > KIT_STACK_PROTECTOR_ALL) { 131 compiler_panic((Compiler*)c, api_no_loc(), 132 "KitCg: invalid stack protector mode %u", 133 (unsigned)opts->stack_protector); 134 } 135 /* Portable-C is a semantic replay backend, not a native optimizer target. 136 * Normalize centrally before either the optimizer wrapper or backend is 137 * constructed so every frontend/API caller gets the same safe behavior. */ 138 if (opts && opts->emit_c_source) opt_level = 0; 139 if (opts && opts->emit_ir && opt_level < 1) { 140 compiler_panic((Compiler*)c, api_no_loc(), 141 "KitCg: emit_ir requires opt_level >= 1 " 142 "(the IR tape is only recorded when the optimizer runs)"); 143 } 144 #if !KIT_OPT_ENABLED 145 if (opt_level > 0) { 146 compiler_panic((Compiler*)c, api_no_loc(), 147 "KitCg: opt_level %d requires KIT_OPT_ENABLED", opt_level); 148 } 149 #endif 150 backend = cg_backend_for_session((Compiler*)c, opts); 151 if (!backend) { 152 /* No CGBackend serves this request in this build. Distinguish the two 153 * causes so the failure is actionable instead of surfacing as a generic 154 * codegen error in each frontend. Consistent with the opt_level config 155 * panics above. */ 156 if (opts && opts->emit_c_source) 157 compiler_panic((Compiler*)c, api_no_loc(), 158 "KitCg: C-source backend not enabled in this build " 159 "(KIT_ARCH_C_TARGET_ENABLED)"); 160 compiler_panic((Compiler*)c, api_no_loc(), 161 "KitCg: no code generator for target arch '%s' — no " 162 "backend enabled in this build (KIT_ARCH_*_ENABLED)", 163 arch_kind_name(kit_compiler_target_spec(c).arch)); 164 } 165 target = backend->make((Compiler*)c, (ObjBuilder*)out, opts); 166 if (!target) return KIT_UNSUPPORTED; 167 /* The backend's make() creates the DWARF producer when opts->debug_info 168 * is set (NULL otherwise); capture it before any optimizer wrapper so the 169 * session can drive func/line/emit. The backend's MCEmitter holds the 170 * same Debug for line-row emission. */ 171 g->debug = target->debug; 172 #if KIT_OPT_ENABLED 173 if (opt_level > 0) { 174 target = opt_cgtarget_new((Compiler*)c, target, opt_level); 175 if (!target) return KIT_UNSUPPORTED; 176 if (opts && opts->emit_ir) 177 opt_set_dump_writer(target, (Writer*)opts->ir_dump_writer); 178 } 179 #endif 180 g->obj = (ObjBuilder*)out; 181 g->target = target; 182 g->opt_level = opt_level; 183 g->check_only = (opts && opts->check_only) ? 1u : 0u; 184 g->function_sections = (opts && opts->function_sections) ? 1u : 0u; 185 g->data_sections = (opts && opts->data_sections) ? 1u : 0u; 186 /* Stack instrumentation is owned by the native pipeline. Portable-C hands 187 * hardening policy to the compiler consuming the generated C, and Wasm has 188 * no native stack-guard ABI for this option. */ 189 g->stack_protector_mode = 190 (opts && !opts->emit_c_source && c->target.arch != KIT_ARCH_WASM) 191 ? opts->stack_protector 192 : 0u; 193 g->nsource_units = 0; 194 g->unit_active = 0; 195 g->finished = 0; 196 memset(&g->cur_unit, 0, sizeof(g->cur_unit)); 197 return KIT_OK; 198 } 199 200 KitStatus kit_cg_begin_unit(KitCg* g, const KitCgUnitOptions* opts) { 201 KitCgUnitOptions unit; 202 if (!g || !g->obj || !g->target) return KIT_INVALID; 203 if (g->finished || g->unit_active) return KIT_INVALID; 204 memset(&unit, 0, sizeof unit); 205 if (opts) { 206 if (opts->flags) return KIT_INVALID; 207 unit = *opts; 208 } 209 if (!unit.source_id) unit.source_id = g->nsource_units + 1u; 210 g->cur_unit = unit; 211 g->nsource_units++; 212 g->cur_unit_seq = g->nsource_units; /* nonzero, unique per unit */ 213 g->unit_active = 1; 214 return KIT_OK; 215 } 216 217 KitStatus kit_cg_end_unit(KitCg* g) { 218 if (!g || !g->obj || !g->target) return KIT_INVALID; 219 if (!g->unit_active) return KIT_INVALID; 220 g->unit_active = 0; 221 memset(&g->cur_unit, 0, sizeof(g->cur_unit)); 222 return KIT_OK; 223 } 224 225 KitStatus kit_cg_finish(KitCg* g, const KitCgFinishOptions* opts) { 226 CgFinishPolicy policy; 227 if (!g) return KIT_INVALID; 228 if (!g->obj || !g->target) return KIT_INVALID; 229 if (g->finished || g->unit_active) return KIT_INVALID; 230 memset(&policy, 0, sizeof policy); 231 if (opts) { 232 if (opts->output_kind > KIT_CG_OUTPUT_ARCHIVE_MEMBER) return KIT_INVALID; 233 if (opts->interposition_policy > KIT_CG_INTERPOSITION_DEFAULT_VISIBILITY) 234 return KIT_INVALID; 235 if (opts->npreserved_symbols && !opts->preserved_symbols) 236 return KIT_INVALID; 237 policy.output_kind = opts->output_kind; 238 policy.interposition_policy = opts->interposition_policy; 239 policy.preserved_symbols = (const ObjSymId*)opts->preserved_symbols; 240 policy.npreserved_symbols = opts->npreserved_symbols; 241 } 242 for (u32 i = 0; i < policy.npreserved_symbols; ++i) { 243 ObjSymId sym = policy.preserved_symbols[i]; 244 const ObjSym* os = obj_symbol_get(g->obj, sym); 245 if (sym == OBJ_SYM_NONE || !os || os->removed) return KIT_INVALID; 246 } 247 cgtarget_set_finish_policy(g->target, &policy); 248 #if KIT_OPT_ENABLED 249 /* opt_set_finish_policy treats the recorder's user as an OptImpl, which is 250 * only true when the optimizer wrapped the backend (opt_level > 0; see 251 * kit_cg_begin). At opt_level 0 g->target is the bare backend recorder — e.g. 252 * the C-source backend, whose user is a CTarget — so calling it there is a 253 * type confusion that corrupts the backend. Guard it the same way 254 * opt_set_dump_writer is guarded at its call site. */ 255 if (g->opt_level > 0) opt_set_finish_policy(g->target, &policy); 256 #endif 257 cgtarget_finalize(g->target); 258 if (g->debug) { 259 debug_emit(g->debug); 260 debug_free(g->debug); 261 g->debug = NULL; 262 } 263 g->finished = 1; 264 return KIT_OK; 265 } 266 267 KitStatus kit_cg_detach(KitCg* g) { 268 if (!g) return KIT_INVALID; 269 if (g->debug) { 270 debug_free(g->debug); 271 g->debug = NULL; 272 } 273 cgtarget_free(g->target); 274 g->obj = NULL; 275 g->target = NULL; 276 g->finished = 0; 277 g->unit_active = 0; 278 g->nsource_units = 0; 279 memset(&g->cur_unit, 0, sizeof(g->cur_unit)); 280 cg_free_obj_state(g); 281 return KIT_OK; 282 } 283 284 void kit_cg_free(KitCg* g) { 285 Heap* h; 286 if (!g) return; 287 h = g->c->ctx->heap; 288 (void)kit_cg_detach(g); 289 h->free(h, g, sizeof *g); 290 } 291 292 /* ============================================================ 293 * Source location 294 * ============================================================ */ 295 296 void kit_cg_set_loc(KitCg* g, KitSrcLoc loc) { 297 if (!g) return; 298 g->cur_loc = *(SrcLoc*)&loc; 299 if (g->debug) debug_set_pending_loc(g->debug, *(SrcLoc*)&loc); 300 if (g->target->set_loc) g->target->set_loc(g->target, *(SrcLoc*)&loc); 301 } 302 303 /* ============================================================ 304 * Function lifecycle 305 * ============================================================ */ 306 307 KitCgSym kit_cg_decl(KitCg* g, KitCgDecl decl) { 308 Compiler* c; 309 ObjBuilder* ob; 310 ObjSymId sym; 311 KitCgTypeId ty; 312 if (!g || !decl.linkage_name) return KIT_CG_SYM_NONE; 313 c = g->c; 314 ob = g->obj; 315 ty = resolve_type(c, decl.type); 316 if (!ty) return KIT_CG_SYM_NONE; 317 sym = (decl.sym.bind == KIT_SB_LOCAL) 318 ? OBJ_SYM_NONE 319 : obj_symbol_find(ob, (Sym)decl.linkage_name); 320 if (sym == OBJ_SYM_NONE) { 321 sym = obj_symbol_ex(ob, (Sym)decl.linkage_name, api_map_bind(decl.sym.bind), 322 api_map_vis(decl.sym.visibility), 323 api_decl_sym_kind(decl), OBJ_SEC_NONE, 0, 0, 0); 324 } else if (decl.sym.bind == KIT_SB_WEAK) { 325 /* C permits the `weak` attribute on any declaration of a symbol; a later 326 * weak (re)declaration demotes a previously-strong global to weak. Without 327 * this, a plain prototype followed by a `weak` definition would emit a 328 * strong global and collide with any strong override at link time. In a 329 * shared LTO builder, do not let a weak declaration from a later TU demote 330 * an already-defined strong symbol; merge policy handles that override. */ 331 const ObjSym* s = obj_symbol_get(ob, sym); 332 if (s && s->bind == SB_GLOBAL && !symresolve_sym_is_def(s)) 333 obj_symbol_set_bind(ob, sym, SB_WEAK); 334 } 335 if (decl.sym.flags) { 336 obj_symbol_set_flags(ob, sym, (u16)decl.sym.flags); 337 } 338 api_remember_sym(g, sym, ty, decl); 339 /* Forward wasm import overrides onto the ObjBuilder side-table so the wasm 340 * backend can find them when promoting an undefined function symbol into a 341 * `(import ...)` entry. Honored only by the wasm target — the storage cost 342 * for other backends is one heap allocation that no one reads. */ 343 if (decl.kind == KIT_CG_DECL_FUNC && 344 (decl.as.func.wasm_import_module || decl.as.func.wasm_import_name)) { 345 wasm_imports_set(ob, (Sym)decl.linkage_name, 346 (Sym)decl.as.func.wasm_import_module, 347 (Sym)decl.as.func.wasm_import_name); 348 } 349 return (KitCgSym)sym; 350 } 351 352 KitCgSym kit_cg_alias(KitCg* g, KitCgAlias alias) { 353 ObjBuilder* ob; 354 ObjSymId sym; 355 const ObjSym* ts; 356 KitCgDecl decl_attrs; 357 if (!g || !alias.linkage_name || alias.target == KIT_CG_SYM_NONE) { 358 return KIT_CG_SYM_NONE; 359 } 360 ob = g->obj; 361 sym = obj_symbol_find(ob, (Sym)alias.linkage_name); 362 ts = obj_symbol_get(ob, (ObjSymId)alias.target); 363 if (!ts) return KIT_CG_SYM_NONE; 364 if (sym == OBJ_SYM_NONE) { 365 sym = 366 obj_symbol_ex(ob, (Sym)alias.linkage_name, api_map_bind(alias.sym.bind), 367 api_map_vis(alias.sym.visibility), (SymKind)ts->kind, 368 ts->section_id, ts->value, ts->size, ts->common_align); 369 } else if (ts->section_id != OBJ_SEC_NONE) { 370 obj_symbol_define(ob, sym, ts->section_id, ts->value, ts->size); 371 } 372 if (alias.sym.flags) obj_symbol_set_flags(ob, sym, (u16)alias.sym.flags); 373 decl_attrs = api_sym_attrs(g, alias.target); 374 decl_attrs.sym = alias.sym; 375 api_remember_sym(g, sym, api_sym_type(g, alias.target), decl_attrs); 376 /* Notify the backend so it can mirror the alias in any output form that 377 * isn't a relocatable obj — e.g. the C-source target. Native machine-code 378 * backends leave this hook NULL because obj_symbol_define above already 379 * aliased the underlying bytes. */ 380 if (g->target && g->target->alias) { 381 g->target->alias(g->target, sym, (ObjSymId)alias.target, 382 api_sym_type(g, alias.target)); 383 } 384 return (KitCgSym)sym; 385 } 386 387 void kit_cg_func_begin_attrs(KitCg* g, KitCgSym cg_sym, 388 KitCgFuncAttrs begin_attrs) { 389 Compiler* c; 390 ObjBuilder* ob; 391 CgTarget* T; 392 ObjSymId sym; 393 ObjSecId text_sec; 394 KitCgTypeId fty; 395 KitCgDecl attrs; 396 Sym sec_name; 397 if (!g) return; 398 c = g->c; 399 ob = g->obj; 400 T = g->target; 401 sym = (ObjSymId)cg_sym; 402 fty = api_sym_type(g, cg_sym); 403 if (!fty) return; 404 attrs = api_sym_attrs(g, cg_sym); 405 406 sec_name = begin_attrs.section ? (Sym)begin_attrs.section 407 : (Sym)attrs.as.func.section; 408 int atomize = 0; 409 if (!sec_name && g->function_sections) { 410 atomize = obj_format_split_sections_as_atoms(c); 411 if (!atomize) { 412 sec_name = 413 api_cg_symbol_section_name(g, SLICE_LIT(".text"), attrs.linkage_name); 414 } 415 } 416 if (!sec_name) sec_name = pool_intern_slice(c->global, SLICE_LIT(".text")); 417 text_sec = obj_section(ob, sec_name, SEC_TEXT, SF_EXEC | SF_ALLOC, 4); 418 419 if (sym != OBJ_SYM_NONE) { 420 obj_symbol_define(ob, sym, text_sec, 0, 0); 421 } 422 423 memset(&g->fn_desc, 0, sizeof g->fn_desc); 424 g->fn_desc.sym = sym; 425 g->fn_desc.text_section_id = text_sec; 426 g->fn_desc.group_id = OBJ_GROUP_NONE; 427 g->fn_desc.fn_type = fty; 428 g->fn_desc.result_type = cg_type_func_result_id(c, fty); 429 g->fn_desc.loc = g->cur_loc; 430 g->fn_desc.sym_bind = api_map_bind(attrs.sym.bind); 431 g->fn_desc.sym_kind = SK_FUNC; 432 g->fn_desc.sym_vis = api_map_vis(attrs.sym.visibility); 433 g->fn_desc.atomize = atomize ? 1u : 0u; 434 if (attrs.as.func.flags & KIT_CG_FUNC_NORETURN) { 435 g->fn_desc.flags |= CGFD_NORETURN; 436 } 437 g->fn_desc.inline_policy = attrs.as.func.inline_policy; 438 if (begin_attrs.inline_policy != KIT_CG_INLINE_DEFAULT) 439 g->fn_desc.inline_policy = begin_attrs.inline_policy; 440 if (begin_attrs.flags & KIT_CG_FUNC_NORETURN) 441 g->fn_desc.flags |= CGFD_NORETURN; 442 443 g->fn_ret_type = cg_type_func_ret_id(c, fty); 444 g->nlocals = 0; 445 g->const_head = KIT_CG_LOCAL_NONE; 446 g->sp = 0; 447 g->stack_protected = 0; 448 g->stack_guard_tls = 0; 449 g->stack_guard_type = KIT_CG_TYPE_NONE; 450 g->stack_guard_local = KIT_CG_LOCAL_NONE; 451 g->stack_guard_sym = KIT_CG_SYM_NONE; 452 g->stack_fail_sym = KIT_CG_SYM_NONE; 453 /* New generation: stale local_temp_gen entries from the previous function are 454 * now != func_gen, so its temp handles read as non-temp without any clear. */ 455 g->func_gen++; 456 /* Drop the previous function's delayed cmp/arith payloads along with its 457 * value stack — neither outlives the function. */ 458 api_delayed_reset(g); 459 460 if (g->debug) { 461 KitCgDebugType supplied_dt = begin_attrs.debug_type 462 ? begin_attrs.debug_type 463 : attrs.as.func.debug_type; 464 DebugTypeId dt = api_debug_type(g, supplied_dt, fty); 465 if (dt != DEBUG_TYPE_NONE) debug_func_begin(g->debug, sym, dt, g->cur_loc); 466 } 467 T->func_begin(T, &g->fn_desc); 468 } 469 470 void kit_cg_func_begin(KitCg* g, KitCgSym cg_sym) { 471 KitCgFuncAttrs attrs; 472 memset(&attrs, 0, sizeof attrs); 473 kit_cg_func_begin_attrs(g, cg_sym, attrs); 474 } 475 476 static int api_source_local_debug_visible(const ApiSourceLocal* rec) { 477 u32 hidden_flags = KIT_CG_LOCAL_ARTIFICIAL | KIT_CG_LOCAL_OPTIMIZED_OUT | 478 KIT_CG_LOCAL_COMPILER_TEMP; 479 return rec && rec->name && (rec->attrs.flags & hidden_flags) == 0; 480 } 481 482 static int api_debug_var_loc_from_cg(CGDebugLoc in, DebugVarLoc* out) { 483 if (!out) return 0; 484 memset(out, 0, sizeof *out); 485 switch ((CGDebugLocKind)in.kind) { 486 case CG_DEBUG_LOC_FRAME: 487 out->kind = DVL_FRAME; 488 out->v.frame_ofs = in.v.frame_ofs; 489 return 1; 490 case CG_DEBUG_LOC_REG: 491 out->kind = DVL_REG; 492 out->v.reg = in.v.reg; 493 return 1; 494 case CG_DEBUG_LOC_GLOBAL: 495 out->kind = DVL_GLOBAL; 496 out->v.global = in.v.global; 497 return 1; 498 default: 499 return 0; 500 } 501 } 502 503 static void api_debug_emit_source_locals(KitCg* g) { 504 u32 i; 505 if (!g || !g->debug || !g->target || !g->target->local_debug_loc) return; 506 for (i = 0; i < g->nlocals; ++i) { 507 ApiSourceLocal* rec = &g->locals[i]; 508 CGDebugLoc cg_loc; 509 DebugVarLoc dbg_loc; 510 DebugTypeId dbg_type; 511 if (!api_source_local_debug_visible(rec)) continue; 512 memset(&cg_loc, 0, sizeof cg_loc); 513 if (!g->target->local_debug_loc(g->target, rec->storage, &cg_loc)) continue; 514 if (!api_debug_var_loc_from_cg(cg_loc, &dbg_loc)) continue; 515 dbg_type = api_debug_type(g, rec->attrs.debug_type, rec->type); 516 if (dbg_type == DEBUG_TYPE_NONE) continue; 517 if (rec->kind == API_SOURCE_LOCAL_PARAM) { 518 debug_param(g->debug, (Sym)rec->name, dbg_type, rec->loc, 519 rec->param_index, dbg_loc); 520 } else { 521 debug_local(g->debug, (Sym)rec->name, dbg_type, rec->loc, dbg_loc); 522 } 523 } 524 } 525 526 void kit_cg_func_end(KitCg* g) { 527 if (!g) return; 528 g->target->func_end(g->target); 529 api_debug_emit_source_locals(g); 530 if (g->debug) debug_func_end(g->debug); 531 g->fn_ret_type = KIT_CG_TYPE_NONE; 532 g->stack_protected = 0; 533 g->stack_guard_local = KIT_CG_LOCAL_NONE; 534 g->nscopes = 0; 535 /* Clear active/generation on the now-dead scope slots so a stray handle 536 * from this function is caught as stale in the next one. */ 537 if (g->scopes) memset(g->scopes, 0, sizeof(*g->scopes) * g->scopes_cap); 538 } 539 540 void kit_cg_reclaim_temps(KitCg* g) { 541 if (!g) return; 542 /* Only safe when the value stack is empty: every transient temp is then 543 * provably dead. The guard also makes this a no-op inside any future 544 * construct (e.g. a statement-expression) that leaves a value live across a 545 * boundary. */ 546 if (g->sp != 0) return; 547 if (g->target && g->target->reclaim_temps) 548 g->target->reclaim_temps(g->target); 549 } 550 551 void api_call_symbol_common(KitCg* g, KitCgSym sym, uint32_t nargs, 552 KitCgCallAttrs attrs);