kit

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

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);