kit

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

c_emit.c (147437B)


      1 /* C-source emission core. See doc/CBACKEND.md.
      2  *
      3  * Output strategy
      4  * ---------------
      5  * Each function buffers two CBufs while CG walks the body:
      6  *   decls — variable declarations: "  long long v3;\n"
      7  *   body  — TU-wide running output; we accumulate signature/body/closing-brace
      8  *           across all functions; func_end splices decls in after the open
      9  *           brace using the recorded fn_body_start bookmark.
     10  *
     11  * c_emit_finalize flushes a tiny prologue + body to the writer.
     12  *
     13  * Local declaration is lazy: every operand emit goes through c_ensure_local,
     14  * which appends one declaration for each semantic local. */
     15 
     16 #include "arch/c_target/c_emit.h"
     17 
     18 #include <stdio.h>
     19 #include <string.h>
     20 
     21 #include "cg/type.h"
     22 #include "core/arena.h"
     23 #include "core/core.h"
     24 #include "core/heap.h"
     25 #include "core/pool.h"
     26 #include "core/slice.h"
     27 #include "core/vec.h"
     28 #include "obj/format.h"
     29 #include "obj/obj.h"
     30 
     31 /* Forward decls. */
     32 static void c_ensure_typedef(CTarget* t, KitCgTypeId tid);
     33 static const char* c_typedef_name(CTarget* t, KitCgTypeId tid);
     34 static const char* c_typename(CTarget* t, KitCgTypeId type);
     35 static KitCgTypeId c_local_type_or_panic(CTarget* t, CGLocal local);
     36 static Operand c_op_local(CGLocal local, KitCgTypeId type);
     37 static int c_type_is_aggregate(CTarget* t, KitCgTypeId type);
     38 static int c_type_is_bool(CTarget* t, KitCgTypeId type);
     39 static int c_type_is_ptr(CTarget* t, KitCgTypeId type);
     40 static int c_operand_is_ptr_typed(CTarget* t, Operand op);
     41 static void c_emit_addr_deref(CTarget* t, Operand addr,
     42                               KitCgTypeId access_type);
     43 static void c_emit_copy_addr(CTarget* t, Operand addr);
     44 CGLocal c_emit_local(CTarget* t, const CGLocalDesc* d);
     45 /* Private accessor on ObjBuilder (defined in obj/obj.c, not in obj.h).
     46  * Same forward-decl trick as obj_tls.c uses. */
     47 ObjSymId obj_tlv_bootstrap_get(const ObjBuilder*);
     48 
     49 /* === Growable-array helpers ===
     50  *
     51  * The C target's growable tables are heap-backed (allocated from
     52  * t->c->ctx->heap and freed in c_emit_destroy), so they route through
     53  * core/vec.h's VEC_GROW rather than hand-rolling the doubling realloc.
     54  *
     55  * c_vec_grow_or_panic: ensure capacity >= want, panicking on OOM. Used by
     56  * append-style tables (scopes, local_static_*) where the live region is
     57  * tracked by a separate count and the grown tail is never read before being
     58  * written.
     59  *
     60  * c_vec_grow_zeroed: same, but zero-fills the newly grown tail. Used by
     61  * index-addressed tables (type_state, local_*, sym_forwarded) that are read
     62  * at arbitrary indices and rely on a zero default. VEC_GROW does not zero,
     63  * so we capture the pre-grow cap and clear [old_cap, new_cap).
     64  *
     65  * Both are macros so VEC_GROW can derive element size/alignment from *ptr. */
     66 #define c_vec_grow_or_panic(t, ptr, cap, want)                              \
     67   do {                                                                      \
     68     if (VEC_GROW((t)->c->ctx->heap, (ptr), (cap), (want))) {                \
     69       compiler_panic((t)->c, (SrcLoc){0, 0, 0}, "C target: out of memory"); \
     70     }                                                                       \
     71   } while (0)
     72 
     73 #define c_vec_grow_zeroed(t, ptr, cap, want)                   \
     74   do {                                                         \
     75     u32 c_vgz_old_ = (cap);                                    \
     76     c_vec_grow_or_panic((t), (ptr), (cap), (want));            \
     77     if ((cap) > c_vgz_old_) {                                  \
     78       memset((ptr) + c_vgz_old_, 0,                            \
     79              ((size_t)((cap) - c_vgz_old_)) * sizeof(*(ptr))); \
     80     }                                                          \
     81   } while (0)
     82 
     83 /* === Target state === */
     84 
     85 void c_emit_target_init(CTarget* t, Compiler* c, ObjBuilder* o, KitWriter* w) {
     86   memset(t, 0, sizeof *t);
     87   t->c = c;
     88   t->obj = o;
     89   t->w = w;
     90   cbuf_init(&t->forwards, c->ctx->heap);
     91   cbuf_init(&t->typedefs, c->ctx->heap);
     92   cbuf_init(&t->data_defs, c->ctx->heap);
     93   cbuf_init(&t->decls, c->ctx->heap);
     94   cbuf_init(&t->body, c->ctx->heap);
     95 }
     96 
     97 CTarget* c_emit_target_new(Compiler* c, ObjBuilder* o, KitWriter* w) {
     98   CTarget* t = arena_new(c->tu, CTarget);
     99   if (!t) return NULL;
    100   c_emit_target_init(t, c, o, w);
    101   return t;
    102 }
    103 
    104 /* === Writer helpers === */
    105 
    106 void c_writer_write(CTarget* t, const void* data, size_t n) {
    107   KitStatus st = kit_writer_write(t->w, data, n);
    108   if (st != KIT_OK) {
    109     SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
    110     compiler_panic(t->c, loc, "C target: writer error %d", (int)st);
    111   }
    112 }
    113 
    114 void c_writer_puts(CTarget* t, const char* s) {
    115   size_t n = 0;
    116   while (s[n]) ++n;
    117   c_writer_write(t, s, n);
    118 }
    119 
    120 /* === CLocal / type emission === */
    121 
    122 static const char* c_int_type_name_for_width(u32 width, int signed_) {
    123   switch (width) {
    124     case 1:
    125     case 8:
    126       return signed_ ? "int8_t" : "uint8_t";
    127     case 16:
    128       return signed_ ? "int16_t" : "uint16_t";
    129     case 32:
    130       return signed_ ? "int32_t" : "uint32_t";
    131     case 64:
    132       return signed_ ? "int64_t" : "uint64_t";
    133     case 128:
    134       return signed_ ? "__int128" : "unsigned __int128";
    135     default:
    136       return NULL;
    137   }
    138 }
    139 
    140 /* Returns the integer width for sign-aware emission. 0 if the type isn't a
    141  * fixed-width integer (float, ptr, void, aggregate). */
    142 static u32 c_int_width_for_signedness(CTarget* t, KitCgTypeId type) {
    143   if (type == KIT_CG_TYPE_NONE) return 0;
    144   const CgType* ty = cg_type_get(t->c, type);
    145   if (!ty) return 0;
    146   if (ty->kind == KIT_CG_TYPE_INT) return ty->integer.width;
    147   if (ty->kind == KIT_CG_TYPE_BOOL) return 32; /* bool maps to int32_t */
    148   return 0;
    149 }
    150 
    151 /* === Typedef machinery ===
    152  *
    153  * Composite types (records, arrays, function types) are emitted as opaque
    154  * byte-storage typedefs in a TU-wide typedefs section. The typedef name is
    155  * `__ty_<id>` keyed on the unaliased type id; this is stable for the
    156  * compiler instance.
    157  *
    158  * For records and arrays the typedef wraps a single `_Alignas(A) uint8_t
    159  * raw[N];` member, so all field/element access is mediated by the existing
    160  * `(*(T*)((char*)addr + ofs))` path. This sidesteps any ABI ambiguity (C
    161  * bitfield rules, array decay, packed/aligned attribute interactions) and
    162  * keeps types orthogonal to access patterns.
    163  *
    164  * For function types we emit a function-pointer typedef `R (*__ty_N)(...)`,
    165  * used for indirect calls and function-pointer-typed values. */
    166 
    167 static void c_grow_type_state(CTarget* t, u32 needed) {
    168   c_vec_grow_zeroed(t, t->type_state, t->type_state_cap, needed);
    169 }
    170 
    171 static const char* c_typedef_name(CTarget* t, KitCgTypeId tid) {
    172   char buf[32];
    173   int n = snprintf(buf, sizeof buf, "__ty_%u", (unsigned)tid);
    174   Sym s = pool_intern_slice(t->c->global, (Slice){.s = buf, .len = (size_t)n});
    175   return pool_slice(t->c->global, s).s;
    176 }
    177 
    178 /* Forward decl. */
    179 static void c_emit_typedef_for_func(CTarget* t, KitCgTypeId tid,
    180                                     const CgType* ty);
    181 
    182 static void c_ensure_typedef(CTarget* t, KitCgTypeId tid) {
    183   KitCgTypeId u = tid;
    184   if ((u32)u >= t->type_state_cap) c_grow_type_state(t, (u32)u + 1u);
    185   if (t->type_state[u] >= 2) return;
    186   if (t->type_state[u] == 1) return; /* cyclic — emit forward-only */
    187   t->type_state[u] = 1;
    188   const CgType* ty = cg_type_get(t->c, u);
    189   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
    190   if (!ty)
    191     compiler_panic(t->c, loc, "C target: unknown type id %u", (unsigned)u);
    192   switch (ty->kind) {
    193     case KIT_CG_TYPE_FUNC:
    194       c_emit_typedef_for_func(t, u, ty);
    195       break;
    196     case KIT_CG_TYPE_RECORD: {
    197       /* Recurse on field types so any composite-typed field has its
    198        * typedef emitted first. (Records-by-value are accessed only via
    199        * pointer arithmetic in kit CG, but emitting deps first keeps the
    200        * output readable and stable.) */
    201       for (u32 i = 0; i < ty->record.nfields; ++i) {
    202         if (!(ty->record.fields[i].flags & KIT_CG_FIELD_BITFIELD)) {
    203           KitCgTypeId ft = ty->record.fields[i].type;
    204           KitCgTypeId ftu = ft;
    205           const CgType* fty = cg_type_get(t->c, ftu);
    206           if (fty && (fty->kind == KIT_CG_TYPE_RECORD ||
    207                       fty->kind == KIT_CG_TYPE_ARRAY ||
    208                       fty->kind == KIT_CG_TYPE_FUNC)) {
    209             c_ensure_typedef(t, ftu);
    210           }
    211         }
    212       }
    213       cbuf_puts(&t->typedefs, "typedef struct { _Alignas(");
    214       cbuf_put_u64(&t->typedefs, (u64)cg_type_align(t->c, u));
    215       cbuf_puts(&t->typedefs, ") uint8_t raw[");
    216       cbuf_put_u64(&t->typedefs, cg_type_size(t->c, u));
    217       cbuf_puts(&t->typedefs, "]; } __ty_");
    218       cbuf_put_u64(&t->typedefs, (u64)u);
    219       cbuf_puts(&t->typedefs, ";\n");
    220       break;
    221     }
    222     case KIT_CG_TYPE_ARRAY: {
    223       KitCgTypeId eu = ty->array.elem;
    224       const CgType* ety = cg_type_get(t->c, eu);
    225       if (ety &&
    226           (ety->kind == KIT_CG_TYPE_RECORD || ety->kind == KIT_CG_TYPE_ARRAY ||
    227            ety->kind == KIT_CG_TYPE_FUNC)) {
    228         c_ensure_typedef(t, eu);
    229       }
    230       cbuf_puts(&t->typedefs, "typedef struct { _Alignas(");
    231       cbuf_put_u64(&t->typedefs, (u64)cg_type_align(t->c, u));
    232       cbuf_puts(&t->typedefs, ") uint8_t raw[");
    233       cbuf_put_u64(&t->typedefs, cg_type_size(t->c, u));
    234       cbuf_puts(&t->typedefs, "]; } __ty_");
    235       cbuf_put_u64(&t->typedefs, (u64)u);
    236       cbuf_puts(&t->typedefs, ";\n");
    237       break;
    238     }
    239     default:
    240       compiler_panic(t->c, loc,
    241                      "C target: c_ensure_typedef on non-composite kind %d",
    242                      (int)ty->kind);
    243   }
    244   t->type_state[u] = 2;
    245 }
    246 
    247 static void c_emit_typedef_for_func(CTarget* t, KitCgTypeId tid,
    248                                     const CgType* ty) {
    249   /* Emit recursively for return and param types if they're composites. */
    250   KitCgTypeId ret = cg_func_ret_type(ty);
    251   const CgType* rty = cg_type_get(t->c, ret);
    252   if (rty &&
    253       (rty->kind == KIT_CG_TYPE_RECORD || rty->kind == KIT_CG_TYPE_ARRAY ||
    254        rty->kind == KIT_CG_TYPE_FUNC)) {
    255     c_ensure_typedef(t, ret);
    256   }
    257   for (u32 i = 0; i < ty->func.nparams; ++i) {
    258     KitCgTypeId pt = ty->func.params[i].type;
    259     const CgType* pty = cg_type_get(t->c, pt);
    260     if (pty &&
    261         (pty->kind == KIT_CG_TYPE_RECORD || pty->kind == KIT_CG_TYPE_ARRAY ||
    262          pty->kind == KIT_CG_TYPE_FUNC)) {
    263       c_ensure_typedef(t, pt);
    264     }
    265   }
    266   cbuf_puts(&t->typedefs, "typedef ");
    267   cbuf_puts(&t->typedefs, c_typename(t, cg_func_ret_type(ty)));
    268   cbuf_puts(&t->typedefs, " (*__ty_");
    269   cbuf_put_u64(&t->typedefs, (u64)tid);
    270   cbuf_puts(&t->typedefs, ")(");
    271   if (ty->func.nparams == 0 && !ty->func.abi_variadic) {
    272     cbuf_puts(&t->typedefs, "void");
    273   } else {
    274     for (u32 i = 0; i < ty->func.nparams; ++i) {
    275       if (i > 0) cbuf_puts(&t->typedefs, ", ");
    276       cbuf_puts(&t->typedefs, c_typename(t, ty->func.params[i].type));
    277     }
    278     if (ty->func.abi_variadic) {
    279       if (ty->func.nparams > 0) cbuf_puts(&t->typedefs, ", ");
    280       cbuf_puts(&t->typedefs, "...");
    281     }
    282   }
    283   cbuf_puts(&t->typedefs, ");\n");
    284 }
    285 
    286 static const char* c_int_type_for_width_panic(CTarget* t, u32 width,
    287                                               int signed_) {
    288   const char* s = c_int_type_name_for_width(width, signed_);
    289   if (!s) {
    290     SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
    291     compiler_panic(t->c, loc, "C target: int width %u not yet supported",
    292                    (unsigned)width);
    293   }
    294   return s;
    295 }
    296 
    297 static const char* c_float_type_name(u32 width) {
    298   switch (width) {
    299     case 32:
    300       return "float";
    301     case 64:
    302       return "double";
    303     case 80:
    304     case 128:
    305       return "long double";
    306     default:
    307       return NULL;
    308   }
    309 }
    310 
    311 /* Returns the C type name for a CG type id. Scalars map to fixed-width
    312  * <stdint.h> types or float/double/long double; pointers collapse to void*;
    313  * composites (records/arrays/funcs) emit an opaque-storage typedef on first
    314  * sighting and return the typedef name. */
    315 static const char* c_typename(CTarget* t, KitCgTypeId type) {
    316   KitCgTypeId resolved = type;
    317   const CgType* ty = cg_type_get(t->c, resolved);
    318   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
    319   if (!ty) {
    320     compiler_panic(t->c, loc, "C target: unknown type id %u", (unsigned)type);
    321   }
    322   switch (ty->kind) {
    323     case KIT_CG_TYPE_VOID:
    324       return "void";
    325     case KIT_CG_TYPE_BOOL:
    326       return "int32_t";
    327     case KIT_CG_TYPE_INT:
    328       return c_int_type_for_width_panic(t, ty->integer.width, 1);
    329     case KIT_CG_TYPE_FLOAT: {
    330       const char* s = c_float_type_name(ty->fp.width);
    331       if (!s) {
    332         compiler_panic(t->c, loc, "C target: fp width %u not yet supported",
    333                        (unsigned)ty->fp.width);
    334       }
    335       return s;
    336     }
    337     case KIT_CG_TYPE_PTR:
    338       return "void*";
    339     case KIT_CG_TYPE_ENUM:
    340       /* CG enums are width-only; treat as their underlying integer base. */
    341       return c_typename(t, ty->enum_.base);
    342     case KIT_CG_TYPE_VARARG_STATE:
    343       t->need_stdarg = 1;
    344       return "va_list";
    345     case KIT_CG_TYPE_RECORD:
    346     case KIT_CG_TYPE_ARRAY:
    347     case KIT_CG_TYPE_FUNC:
    348       c_ensure_typedef(t, resolved);
    349       return c_typedef_name(t, resolved);
    350     default:
    351       compiler_panic(t->c, loc, "C target: type kind %d not yet supported",
    352                      (int)ty->kind);
    353   }
    354 }
    355 
    356 void c_emit_type(CTarget* t, CBuf* b, KitCgTypeId type) {
    357   cbuf_puts(b, c_typename(t, type));
    358 }
    359 
    360 static KitCgTypeId c_local_type_or_panic(CTarget* t, CGLocal local) {
    361   if ((u32)local < t->local_cap && t->local_declared[local] &&
    362       t->local_type[local]) {
    363     return t->local_type[local];
    364   }
    365   compiler_panic(t->c, t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0},
    366                  "C target: unknown local type for v%u", (unsigned)local);
    367   return KIT_CG_TYPE_NONE;
    368 }
    369 
    370 static Operand c_op_local(CGLocal local, KitCgTypeId type) {
    371   Operand op;
    372   memset(&op, 0, sizeof op);
    373   op.kind = OPK_LOCAL;
    374   op.type = type;
    375   op.v.local = local;
    376   return op;
    377 }
    378 
    379 void c_local_name(CLocal r, char* out, size_t cap) {
    380   size_t i = 0;
    381   if (cap == 0) return;
    382   if (cap > 1) out[i++] = 'v';
    383   char tmp[16];
    384   size_t n = 0;
    385   u32 v = (u32)r;
    386   if (v == 0) {
    387     tmp[n++] = '0';
    388   } else {
    389     while (v) {
    390       tmp[n++] = (char)('0' + (v % 10));
    391       v /= 10;
    392     }
    393   }
    394   while (n && i + 1 < cap) out[i++] = tmp[--n];
    395   out[i] = '\0';
    396 }
    397 
    398 static void c_grow_local_table(CTarget* t, u32 needed) {
    399   /* Two parallel arrays kept at a single shared local_cap. Grow each from the
    400    * same old cap to the same new cap via independent cap trackers, then commit
    401    * the shared cap once. (They always grow together, so both reach the same
    402    * VEC_GROW-derived capacity.) */
    403   u32 declared_cap = t->local_cap;
    404   u32 type_cap = t->local_cap;
    405   c_vec_grow_zeroed(t, t->local_declared, declared_cap, needed);
    406   c_vec_grow_zeroed(t, t->local_type, type_cap, needed);
    407   t->local_cap = declared_cap;
    408 }
    409 
    410 /* Emit the trailing `__attribute__((unused)) = INIT;` for a local decl of
    411  * type `ty`. Scalars get `= 0` (readable); aggregates get `= {0}` (the only
    412  * form that compiles for record/array). va_list also takes `= {0}`: the host's
    413  * <stdarg.h> va_list is an aggregate (struct/array) on common ABIs (aarch64,
    414  * x86-64 SysV) where `= 0` is invalid, and `= {0}` is also valid for the
    415  * pointer form (e.g. Apple), so it is the portable choice. */
    416 static void c_emit_zero_init(CTarget* t, KitCgTypeId ty) {
    417   const CgType* cgt = ty ? cg_type_get(t->c, ty) : NULL;
    418   int braced = cgt && (cgt->kind == KIT_CG_TYPE_RECORD ||
    419                        cgt->kind == KIT_CG_TYPE_ARRAY ||
    420                        cgt->kind == KIT_CG_TYPE_VARARG_STATE);
    421   cbuf_puts(&t->decls, braced ? " __attribute__((unused)) = {0};\n"
    422                               : " __attribute__((unused)) = 0;\n");
    423 }
    424 
    425 void c_ensure_local(CTarget* t, CLocal r, KitCgTypeId type) {
    426   if (r == (CLocal)CG_LOCAL_NONE) {
    427     compiler_panic(t->c, (SrcLoc){0, 0, 0},
    428                    "C target: CG_LOCAL_NONE reached emission");
    429   }
    430   if ((u32)r >= t->local_cap) c_grow_local_table(t, (u32)r + 1u);
    431   if (t->local_declared[r]) {
    432     if (type && t->local_type[r] != type) {
    433       compiler_panic(t->c, t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0},
    434                      "C target: local v%u used with inconsistent type "
    435                      "(declared %u, used %u)",
    436                      (unsigned)r, (unsigned)t->local_type[r], (unsigned)type);
    437     }
    438     return;
    439   }
    440   t->local_declared[r] = 1;
    441   t->local_type[r] = type;
    442   cbuf_puts(&t->decls, "  ");
    443   c_emit_type(t, &t->decls, type);
    444   cbuf_puts(&t->decls, " ");
    445   char buf[24];
    446   c_local_name(r, buf, sizeof buf);
    447   cbuf_puts(&t->decls, buf);
    448   /* Zero-init kills -Wsometimes-uninitialized for control flow clang can't
    449    * reason through; the host C compiler DSEs the init when a real
    450    * assignment dominates. Scalars get `= 0`, aggregates `= {0}`. */
    451   c_emit_zero_init(t, type);
    452 }
    453 
    454 /* Emit a signed-int64 literal. INT64_MIN can't be written directly: clang
    455  * treats `-9223372036854775808` as `-(9223372036854775808)` with the inner
    456  * literal too large for any signed type, which trips
    457  * -Wimplicitly-unsigned-literal. The standard workaround is
    458  * `(-9223372036854775807LL - 1)`. */
    459 static void c_emit_imm_literal(CTarget* t, i64 v) {
    460   if (v == (i64)((u64)1u << 63u)) {
    461     cbuf_puts(&t->body, "(-9223372036854775807LL - 1)");
    462     return;
    463   }
    464   cbuf_put_i64(&t->body, v);
    465 }
    466 
    467 /* Address-mode tuple decoded from an OPK_INDIRECT operand. Mirrors the
    468  * `addr_mode` helper in the machine-code backends so all targets share a
    469  * single in-backend view of `base [+ index << log2_scale] + ofs`. */
    470 typedef struct CAddrMode {
    471   CLocal base;
    472   CLocal index;  /* CG_LOCAL_NONE when no index operand */
    473   u8 log2_scale; /* meaningful only when index != CG_LOCAL_NONE */
    474   i32 ofs;
    475 } CAddrMode;
    476 
    477 static CAddrMode c_addr_mode(Operand addr) {
    478   CAddrMode m;
    479   m.base = addr.v.ind.base;
    480   m.index = addr.v.ind.index;
    481   m.log2_scale = addr.v.ind.log2_scale;
    482   m.ofs = addr.v.ind.ofs;
    483   return m;
    484 }
    485 
    486 /* Emit `(char*)base [+ (uintptr_t)index * (1u << log2_scale)] [+ ofs]` into
    487  * the body, with each optional term suppressed when absent. Used by every
    488  * OPK_INDIRECT renderer; the caller wraps it with the appropriate
    489  * `(*(T*)(...))` or `((T)(...))` cast. */
    490 static void c_emit_indirect_addr_expr(CTarget* t, CAddrMode m) {
    491   char rbuf[24];
    492   cbuf_puts(&t->body, "(char*)");
    493   c_local_name(m.base, rbuf, sizeof rbuf);
    494   cbuf_puts(&t->body, rbuf);
    495   if (m.index != CG_LOCAL_NONE) {
    496     cbuf_puts(&t->body, " + (uintptr_t)");
    497     c_local_name(m.index, rbuf, sizeof rbuf);
    498     cbuf_puts(&t->body, rbuf);
    499     cbuf_puts(&t->body, " * ");
    500     /* Spell as the explicit 1/2/4/8 literal corresponding to log2_scale.
    501      * log2_scale is normalized to {0,1,2,3} by cg. */
    502     cbuf_put_u64(&t->body, (u64)(1u << m.log2_scale));
    503   }
    504   if (m.ofs != 0) {
    505     cbuf_puts(&t->body, " + ");
    506     cbuf_put_i64(&t->body, (i64)m.ofs);
    507   }
    508 }
    509 
    510 /* Assert that `addr`, if OPK_INDIRECT, has no index operand. Used by paths
    511  * the cg layer guarantees never carry the indexed shape (bitfield, atomics,
    512  * copy_bytes/set_bytes, inline asm). */
    513 static void c_assert_no_index(CTarget* t, Operand addr, const char* where) {
    514   if (addr.kind != OPK_INDIRECT) return;
    515   if (addr.v.ind.index == CG_LOCAL_NONE) return;
    516   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
    517   compiler_panic(t->c, loc,
    518                  "C target: %.*s: indexed OPK_INDIRECT not allowed here",
    519                  SLICE_ARG(slice_from_cstr(where)));
    520 }
    521 
    522 void c_emit_operand(CTarget* t, Operand op) {
    523   char buf[24];
    524   switch (op.kind) {
    525     case OPK_IMM:
    526       if (op.type == KIT_CG_TYPE_NONE) {
    527         /* Untyped IMM (e.g. memset byte value): emit the literal raw. */
    528         cbuf_putc(&t->body, '(');
    529         c_emit_imm_literal(t, op.v.imm);
    530         cbuf_putc(&t->body, ')');
    531       } else {
    532         cbuf_puts(&t->body, "((");
    533         c_emit_type(t, &t->body, op.type);
    534         cbuf_puts(&t->body, ")");
    535         c_emit_imm_literal(t, op.v.imm);
    536         cbuf_puts(&t->body, ")");
    537       }
    538       return;
    539     case OPK_LOCAL: {
    540       c_ensure_local(t, op.v.local, op.type);
    541       c_local_name(op.v.local, buf, sizeof buf);
    542       cbuf_puts(&t->body, buf);
    543       return;
    544     }
    545     case OPK_INDIRECT: {
    546       /* Used by call paths to pass aggregates by-address: the operand's type
    547        * is the aggregate, the storage is `base + index*scale + ofs`. Emit the
    548        * deref as a value expression. */
    549       cbuf_puts(&t->body, "(*(");
    550       c_emit_type(t, &t->body, op.type);
    551       cbuf_puts(&t->body, "*)(");
    552       c_emit_indirect_addr_expr(t, c_addr_mode(op));
    553       cbuf_puts(&t->body, "))");
    554       return;
    555     }
    556     case OPK_GLOBAL: {
    557       /* OPK_GLOBAL carries `&sym + addend`. How we spell it depends on
    558        * op.type:
    559        *   - pointer/scalar/void: the value IS the address, so cast through
    560        *     `((T)((char*)sym + addend))`.
    561        *   - aggregate (RECORD/ARRAY): the symbol's storage is an aggregate
    562        *     value; emit `(*(T*)((char*)sym + addend))` so the deref reads
    563        *     the aggregate value (used by call args that pass struct
    564        *     by-value via a global initialized buffer). */
    565       obj_sym_mark_referenced(t->obj, op.v.global.sym);
    566       const char* nm = c_sym_name(t, op.v.global.sym);
    567       const CgType* gty =
    568           (op.type != KIT_CG_TYPE_NONE) ? cg_type_get(t->c, op.type) : NULL;
    569       int is_aggregate = gty && (gty->kind == KIT_CG_TYPE_RECORD ||
    570                                  gty->kind == KIT_CG_TYPE_ARRAY);
    571       if (is_aggregate) {
    572         cbuf_puts(&t->body, "(*(");
    573         c_emit_type(t, &t->body, op.type);
    574         cbuf_puts(&t->body, "*)((char*)&");
    575         cbuf_puts(&t->body, nm);
    576         if (op.v.global.addend != 0) {
    577           cbuf_puts(&t->body, " + ");
    578           cbuf_put_i64(&t->body, op.v.global.addend);
    579         }
    580         cbuf_puts(&t->body, "))");
    581       } else {
    582         cbuf_puts(&t->body, "((");
    583         if (op.type != KIT_CG_TYPE_NONE) {
    584           c_emit_type(t, &t->body, op.type);
    585         } else {
    586           cbuf_puts(&t->body, "void*");
    587         }
    588         cbuf_puts(&t->body, ")((char*)&");
    589         cbuf_puts(&t->body, nm);
    590         if (op.v.global.addend != 0) {
    591           cbuf_puts(&t->body, " + ");
    592           cbuf_put_i64(&t->body, op.v.global.addend);
    593         }
    594         cbuf_puts(&t->body, "))");
    595       }
    596       return;
    597     }
    598     default: {
    599       SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
    600       compiler_panic(t->c, loc, "C target: operand kind %d not yet supported",
    601                      (int)op.kind);
    602     }
    603   }
    604 }
    605 
    606 static int c_type_is_float(CTarget* t, KitCgTypeId type) {
    607   if (type == KIT_CG_TYPE_NONE) return 0;
    608   const CgType* ty = cg_type_get(t->c, type);
    609   return ty && ty->kind == KIT_CG_TYPE_FLOAT;
    610 }
    611 
    612 /* True iff a and b name the same CG type. */
    613 static int c_types_equiv(CTarget* t, KitCgTypeId a, KitCgTypeId b) {
    614   (void)t;
    615   if (a == 0 || b == 0) return 0;
    616   return a == b;
    617 }
    618 
    619 /* Emit "  vN = " plus any cast needed for a C assignment expression.
    620  * Caller must then emit the RHS expression and call c_emit_local_assign_close.
    621  *
    622  * `rhs_ty` is the CG type the RHS expression will produce (or 0 if unknown).
    623  * Pointer/int crossings bridge through uintptr_t to keep host-C diagnostics
    624  * quiet. The outer `(...)` parens are kept so the closer's `);` stays
    625  * balanced. */
    626 static void c_emit_local_assign_open(CTarget* t, CLocal r, KitCgTypeId rhs_ty) {
    627   if ((u32)r >= t->local_cap || !t->local_declared[r]) {
    628     compiler_panic(t->c, t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0},
    629                    "C target: assign to undeclared local v%u", (unsigned)r);
    630   }
    631   KitCgTypeId decl = t->local_type[r];
    632   char buf[24];
    633   c_local_name(r, buf, sizeof buf);
    634   cbuf_puts(&t->body, "  ");
    635   cbuf_puts(&t->body, buf);
    636   cbuf_puts(&t->body, " = ");
    637   if (!c_types_equiv(t, rhs_ty, decl)) {
    638     cbuf_putc(&t->body, '(');
    639     c_emit_type(t, &t->body, decl);
    640     cbuf_putc(&t->body, ')');
    641     if (!c_type_is_float(t, decl) &&
    642         (!rhs_ty || c_type_is_ptr(t, decl) || c_type_is_ptr(t, rhs_ty))) {
    643       cbuf_puts(&t->body, "(uintptr_t)");
    644     }
    645   }
    646   cbuf_puts(&t->body, "(");
    647 }
    648 
    649 static void c_emit_local_assign_close(CTarget* t) {
    650   cbuf_puts(&t->body, ");\n");
    651 }
    652 
    653 void c_emit_operand_signed(CTarget* t, Operand op, int signed_) {
    654   u32 w = c_int_width_for_signedness(t, op.type);
    655   if (w == 0) {
    656     /* Not an integer — emit without sign cast. */
    657     c_emit_operand(t, op);
    658     return;
    659   }
    660   const char* tn = c_int_type_name_for_width(w, signed_);
    661   if (!tn) {
    662     c_emit_operand(t, op);
    663     return;
    664   }
    665   int via_uptr = c_operand_is_ptr_typed(t, op);
    666   /* CG ints are width-only; the C target declares every int local/IMM
    667    * as the signed `int{W}_t` of its width. So when `signed_` is true and
    668    * the operand's emit-width matches `w`, the explicit cast is redundant
    669    * with what c_emit_operand already produces. Skipping it cuts the
    670    * ubiquitous `((int32_t)((int32_t)23))` double-cast down to one. */
    671   if (!via_uptr && signed_) {
    672     KitCgTypeId et = op.type;
    673     if (c_int_width_for_signedness(t, et) == w) {
    674       c_emit_operand(t, op);
    675       return;
    676     }
    677   }
    678   cbuf_puts(&t->body, "((");
    679   cbuf_puts(&t->body, tn);
    680   cbuf_puts(&t->body, ")");
    681   if (via_uptr) {
    682     cbuf_puts(&t->body, "(uintptr_t)");
    683   }
    684   c_emit_operand(t, op);
    685   cbuf_puts(&t->body, ")");
    686 }
    687 
    688 /* Returns 1 if `type` is a pointer (or void*). */
    689 static int c_type_is_ptr(CTarget* t, KitCgTypeId type) {
    690   if (type == KIT_CG_TYPE_NONE) return 0;
    691   const CgType* ty = cg_type_get(t->c, type);
    692   return ty && ty->kind == KIT_CG_TYPE_PTR;
    693 }
    694 
    695 static int c_type_is_bool(CTarget* t, KitCgTypeId type) {
    696   if (type == KIT_CG_TYPE_NONE) return 0;
    697   const CgType* ty = cg_type_get(t->c, type);
    698   return ty && ty->kind == KIT_CG_TYPE_BOOL;
    699 }
    700 
    701 static int c_type_is_aggregate(CTarget* t, KitCgTypeId type) {
    702   if (type == KIT_CG_TYPE_NONE) return 0;
    703   const CgType* ty = cg_type_get(t->c, type);
    704   return ty &&
    705          (ty->kind == KIT_CG_TYPE_RECORD || ty->kind == KIT_CG_TYPE_ARRAY);
    706 }
    707 
    708 static int c_operand_is_ptr_typed(CTarget* t, Operand op) {
    709   if (c_type_is_ptr(t, op.type)) return 1;
    710   return 0;
    711 }
    712 
    713 /* Emit `(target_ty)(uintptr_t)(op)` (or `(target_ty)(op)` for float
    714  * target_ty). Used when the caller needs a specific C expression type.
    715  * Pointer/int crossings bridge through uintptr_t. */
    716 static void c_emit_operand_as(CTarget* t, Operand op, KitCgTypeId target_ty) {
    717   if (c_types_equiv(t, op.type, target_ty)) {
    718     c_emit_operand(t, op);
    719     return;
    720   }
    721   cbuf_puts(&t->body, "(");
    722   c_emit_type(t, &t->body, target_ty);
    723   cbuf_puts(&t->body, ")");
    724   if (!c_type_is_float(t, target_ty) &&
    725       (!op.type || c_type_is_ptr(t, op.type) || c_type_is_ptr(t, target_ty))) {
    726     cbuf_puts(&t->body, "(uintptr_t)");
    727   }
    728   cbuf_puts(&t->body, "(");
    729   c_emit_operand(t, op);
    730   cbuf_puts(&t->body, ")");
    731 }
    732 
    733 /* Emit an operand for use in a C binary arithmetic expression. Pointer-typed
    734  * operands are cast to uintptr_t so C arithmetic semantics apply uniformly
    735  * (kit IR carries byte offsets, not C-pointer-arith scaled indices). */
    736 static void c_emit_operand_arith(CTarget* t, Operand op) {
    737   if (c_operand_is_ptr_typed(t, op)) {
    738     cbuf_puts(&t->body, "((uintptr_t)");
    739     if (op.kind == OPK_IMM) {
    740       c_emit_imm_literal(t, op.v.imm);
    741     } else {
    742       c_emit_operand(t, op);
    743     }
    744     cbuf_puts(&t->body, ")");
    745     return;
    746   }
    747   c_emit_operand(t, op);
    748 }
    749 
    750 /* Same, but applies the requested signedness when the operand is an integer
    751  * (used for SDIV/UDIV/SREM/UREM/SHR_S/SHR_U). Pointer operands always go
    752  * through the uintptr_t cast regardless of the requested signedness. */
    753 static void c_emit_operand_arith_signed(CTarget* t, Operand op, int signed_) {
    754   if (c_operand_is_ptr_typed(t, op)) {
    755     cbuf_puts(&t->body, "((uintptr_t)");
    756     if (op.kind == OPK_IMM) {
    757       c_emit_imm_literal(t, op.v.imm);
    758     } else {
    759       c_emit_operand(t, op);
    760     }
    761     cbuf_puts(&t->body, ")");
    762     return;
    763   }
    764   c_emit_operand_signed(t, op, signed_);
    765 }
    766 
    767 /* Emit a C lvalue expression for an addr operand (OPK_LOCAL / OPK_GLOBAL /
    768  * OPK_INDIRECT) using `access_type` as the access type. The result is the
    769  * full `*(T*)(...)` dereference, or the C variable directly when the access
    770  * type matches the underlying local/global object. */
    771 static void c_emit_addr_deref(CTarget* t, Operand addr,
    772                               KitCgTypeId access_type) {
    773   char buf[24];
    774   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
    775   switch (addr.kind) {
    776     case OPK_LOCAL: {
    777       c_ensure_local(t, addr.v.local, addr.type);
    778       c_local_name(addr.v.local, buf, sizeof buf);
    779       if (access_type == 0 || addr.type == 0 || access_type == addr.type) {
    780         cbuf_puts(&t->body, buf);
    781       } else {
    782         cbuf_puts(&t->body, "(*(");
    783         c_emit_type(t, &t->body, access_type);
    784         cbuf_puts(&t->body, "*)&");
    785         cbuf_puts(&t->body, buf);
    786         cbuf_puts(&t->body, ")");
    787       }
    788       return;
    789     }
    790     case OPK_GLOBAL: {
    791       obj_sym_mark_referenced(t->obj, addr.v.global.sym);
    792       const char* nm = c_sym_name(t, addr.v.global.sym);
    793       cbuf_puts(&t->body, "(*(");
    794       c_emit_type(t, &t->body, access_type);
    795       cbuf_puts(&t->body, "*)((char*)&");
    796       cbuf_puts(&t->body, nm);
    797       if (addr.v.global.addend != 0) {
    798         cbuf_puts(&t->body, " + ");
    799         cbuf_put_i64(&t->body, addr.v.global.addend);
    800       }
    801       cbuf_puts(&t->body, "))");
    802       return;
    803     }
    804     case OPK_INDIRECT: {
    805       CAddrMode m = c_addr_mode(addr);
    806       if ((u32)m.base >= t->local_cap || !t->local_declared[m.base]) {
    807         compiler_panic(t->c, loc,
    808                        "C target: indirect on undeclared base local v%u",
    809                        (unsigned)m.base);
    810       }
    811       if (m.index != CG_LOCAL_NONE &&
    812           ((u32)m.index >= t->local_cap || !t->local_declared[m.index])) {
    813         compiler_panic(t->c, loc,
    814                        "C target: indirect on undeclared index local v%u",
    815                        (unsigned)m.index);
    816       }
    817       cbuf_puts(&t->body, "(*(");
    818       c_emit_type(t, &t->body, access_type);
    819       cbuf_puts(&t->body, "*)(");
    820       c_emit_indirect_addr_expr(t, m);
    821       cbuf_puts(&t->body, "))");
    822       return;
    823     }
    824     default:
    825       compiler_panic(t->c, loc,
    826                      "C target: addr-deref on operand kind %d not supported",
    827                      (int)addr.kind);
    828   }
    829 }
    830 
    831 /* Emit a C address-of expression for a lvalue operand. Output is a pointer
    832  * value (cast to dst_type). */
    833 static void c_emit_lvalue_addr(CTarget* t, Operand lv, KitCgTypeId dst_type) {
    834   char buf[24];
    835   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
    836   switch (lv.kind) {
    837     case OPK_LOCAL:
    838       cbuf_puts(&t->body, "((");
    839       c_emit_type(t, &t->body, dst_type);
    840       cbuf_puts(&t->body, ")");
    841       cbuf_puts(&t->body, "&");
    842       c_ensure_local(t, lv.v.local, lv.type);
    843       c_local_name(lv.v.local, buf, sizeof buf);
    844       cbuf_puts(&t->body, buf);
    845       cbuf_puts(&t->body, ")");
    846       return;
    847     case OPK_GLOBAL: {
    848       obj_sym_mark_referenced(t->obj, lv.v.global.sym);
    849       const char* nm = c_sym_name(t, lv.v.global.sym);
    850       cbuf_puts(&t->body, "((");
    851       c_emit_type(t, &t->body, dst_type);
    852       cbuf_puts(&t->body, ")((char*)&");
    853       cbuf_puts(&t->body, nm);
    854       if (lv.v.global.addend != 0) {
    855         cbuf_puts(&t->body, " + ");
    856         cbuf_put_i64(&t->body, lv.v.global.addend);
    857       }
    858       cbuf_puts(&t->body, ")");
    859       cbuf_puts(&t->body, ")");
    860       return;
    861     }
    862     case OPK_INDIRECT: {
    863       cbuf_puts(&t->body, "((");
    864       c_emit_type(t, &t->body, dst_type);
    865       cbuf_puts(&t->body, ")(");
    866       c_emit_indirect_addr_expr(t, c_addr_mode(lv));
    867       cbuf_puts(&t->body, "))");
    868       return;
    869     }
    870     default:
    871       compiler_panic(t->c, loc,
    872                      "C target: addr-of on operand kind %d not supported",
    873                      (int)lv.kind);
    874   }
    875 }
    876 
    877 /* === Symbol name lookup === */
    878 
    879 const char* c_sym_name(CTarget* t, ObjSymId sym) {
    880   const ObjSym* os = obj_symbol_get(t->obj, sym);
    881   if (!os) {
    882     compiler_panic(t->c, (SrcLoc){0, 0, 0}, "C target: unknown ObjSymId %u",
    883                    (unsigned)sym);
    884   }
    885   Slice nm = pool_slice(t->c->global, os->name);
    886   const char* s = nm.s;
    887   size_t n = nm.len;
    888   /* Linker symbols carry the active object format's C-mangle prefix (a leading
    889    * underscore on Mach-O); the host C compiler will re-add it on its own, so
    890    * strip when re-emitting source. */
    891   obj_format_demangle_c(t->c, &s, &n);
    892   /* Sanitize for C identifier rules: assemblers accept '.', '$', etc. in
    893    * symbol names; C does not. Replace each illegal byte with '_' and prepend
    894    * '_' if the first char isn't alpha/underscore. Local syms (SB_LOCAL) also
    895    * get an ObjSymId prefix: one C emit session may contain several original
    896    * source units, each of which may legally define the same internal-linkage
    897    * name. Globals are assumed to come in with C-safe names; if they don't, we
    898    * still rewrite — the resulting symbol won't link against other TUs that use
    899    * the asm spelling, but kit-produced code uses it consistently. */
    900   int is_local = os->bind == SB_LOCAL;
    901   int needs_rewrite = is_local;
    902   if (n == 0 && !is_local) {
    903     return s;
    904   }
    905   if (n != 0 && !is_local) {
    906     if (!((s[0] >= 'a' && s[0] <= 'z') || (s[0] >= 'A' && s[0] <= 'Z') ||
    907           s[0] == '_')) {
    908       needs_rewrite = 1;
    909     }
    910     for (size_t i = 0; i < n; ++i) {
    911       char ch = s[i];
    912       if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
    913             (ch >= '0' && ch <= '9') || ch == '_')) {
    914         needs_rewrite = 1;
    915         break;
    916       }
    917     }
    918   }
    919   if (!needs_rewrite) return s;
    920   char buf[256];
    921   size_t cap = sizeof(buf) - 1u;
    922   size_t out = 0;
    923   if (is_local) {
    924     static const char prefix[] = "__kit_local_";
    925     char digits[16];
    926     size_t ndigits = 0;
    927     u32 value = (u32)sym;
    928     for (size_t i = 0; i + 1u < sizeof prefix && out < cap; ++i)
    929       buf[out++] = prefix[i];
    930     do {
    931       digits[ndigits++] = (char)('0' + value % 10u);
    932       value /= 10u;
    933     } while (value != 0 && ndigits < sizeof digits);
    934     while (ndigits != 0 && out < cap) buf[out++] = digits[--ndigits];
    935     if (out < cap) buf[out++] = '_';
    936   } else {
    937     int first_alpha = (s[0] >= 'a' && s[0] <= 'z') ||
    938                       (s[0] >= 'A' && s[0] <= 'Z') || s[0] == '_';
    939     if (!first_alpha && out < cap) buf[out++] = '_';
    940   }
    941   for (size_t i = 0; i < n && out < cap; ++i) {
    942     char ch = s[i];
    943     int ok = (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
    944              (ch >= '0' && ch <= '9') || ch == '_';
    945     buf[out++] = ok ? ch : '_';
    946   }
    947   buf[out] = '\0';
    948   Sym interned = pool_intern_slice(t->c->global, (Slice){.s = buf, .len = out});
    949   return pool_slice(t->c->global, interned).s;
    950 }
    951 
    952 /* === Prologue / finalize === */
    953 
    954 void c_emit_prologue(CTarget* t) {
    955   if (t->prologue_emitted) return;
    956   t->prologue_emitted = 1;
    957   c_writer_puts(t,
    958                 "/* generated by kit --emit=c */\n"
    959                 "#include <stdint.h>\n"
    960                 "#include <stdalign.h>\n");
    961   /* Other headers are decided at finalize so include lines remain
    962    * deterministic regardless of when the type was first referenced.
    963    * Writer flushes are not stream-buffered, so we keep prologue compact and
    964    * tack the rest on at c_emit_finalize. */
    965   c_writer_puts(t, "\n");
    966 }
    967 
    968 /* === func_begin / func_end === */
    969 
    970 /* Write `RetT name(P0, P1, ...)` (without trailing `;` or `{`) to `b`. */
    971 static void c_emit_func_signature(CTarget* t, CBuf* b, const char* name,
    972                                   KitCgTypeId fn_type) {
    973   KitCgTypeId ret_type = cg_type_func_ret_id(t->c, fn_type);
    974   const CgType* fty = cg_type_get(t->c, fn_type);
    975   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
    976   if (!fty || fty->kind != KIT_CG_TYPE_FUNC) {
    977     compiler_panic(t->c, loc, "C target: fn_type is not a function type");
    978   }
    979   if (cg_type_is_void(t->c, ret_type)) {
    980     cbuf_puts(b, "void");
    981   } else {
    982     c_emit_type(t, b, ret_type);
    983   }
    984   cbuf_puts(b, " ");
    985   cbuf_puts(b, name);
    986   cbuf_puts(b, "(");
    987   if (fty->func.nparams == 0 && !fty->func.abi_variadic) {
    988     cbuf_puts(b, "void");
    989   } else {
    990     for (u32 i = 0; i < fty->func.nparams; ++i) {
    991       if (i > 0) cbuf_puts(b, ", ");
    992       c_emit_type(t, b, fty->func.params[i].type);
    993       cbuf_puts(b, " p");
    994       cbuf_put_u64(b, (u64)i);
    995     }
    996     if (fty->func.abi_variadic) {
    997       if (fty->func.nparams > 0) cbuf_puts(b, ", ");
    998       cbuf_puts(b, "...");
    999     }
   1000   }
   1001   cbuf_puts(b, ")");
   1002 }
   1003 
   1004 void c_emit_func_begin(CTarget* t, const CGFuncDesc* fd) {
   1005   c_emit_prologue(t);
   1006 
   1007   t->cur_fn = fd;
   1008   cbuf_reset(&t->decls);
   1009   for (u32 i = 0; i < t->local_cap; ++i) {
   1010     t->local_declared[i] = 0;
   1011     t->local_type[i] = 0;
   1012   }
   1013   t->next_label = 0;
   1014   t->next_local = 0;
   1015   t->next_tmp = 0;
   1016   t->nscopes = 0;
   1017   t->last_was_terminator = 0;
   1018   t->have_emitted_loc = 0;
   1019   t->emitted_loc = (SrcLoc){0, 0, 0};
   1020 
   1021   const char* name = c_sym_name(t, fd->sym);
   1022 
   1023   /* Forward-declare so out-of-order callers and same-TU references find the
   1024    * prototype regardless of definition order. */
   1025   c_ensure_forward_decl(t, fd->sym, fd->fn_type);
   1026 
   1027   {
   1028     const ObjSym* os = obj_symbol_get(t->obj, fd->sym);
   1029     if (os && os->bind == SB_LOCAL) cbuf_puts(&t->body, "static ");
   1030   }
   1031   c_emit_func_signature(t, &t->body, name, fd->fn_type);
   1032   cbuf_puts(&t->body, " {\n");
   1033   t->fn_body_start = t->body.len;
   1034 }
   1035 
   1036 /* Test-and-set on the sym_forwarded bitmap, growing it as needed. Returns 1 if
   1037  * `sym` was already marked (caller should skip re-emitting its forward decl),
   1038  * 0 after marking it for the first time. Shared by c_ensure_forward_decl and
   1039  * c_emit_alias, which both emit a decl that doubles as a forward prototype. */
   1040 static int c_sym_forwarded_test_and_set(CTarget* t, ObjSymId sym) {
   1041   c_vec_grow_zeroed(t, t->sym_forwarded, t->sym_forwarded_cap, (u32)sym + 1u);
   1042   if (t->sym_forwarded[sym]) return 1;
   1043   t->sym_forwarded[sym] = 1;
   1044   return 0;
   1045 }
   1046 
   1047 void c_ensure_forward_decl(CTarget* t, ObjSymId sym, KitCgTypeId fn_type) {
   1048   if (c_sym_forwarded_test_and_set(t, sym)) return;
   1049   const char* name = c_sym_name(t, sym);
   1050   const ObjSym* os = obj_symbol_get(t->obj, sym);
   1051   if ((os && (os->kind == SK_FUNC || os->kind == SK_IFUNC)) || fn_type != 0) {
   1052     if (os && os->bind == SB_LOCAL) cbuf_puts(&t->forwards, "static ");
   1053     c_emit_func_signature(t, &t->forwards, name, fn_type);
   1054     cbuf_puts(&t->forwards, ";\n");
   1055   } else {
   1056     if (os && os->bind == SB_LOCAL)
   1057       cbuf_puts(&t->forwards, "static ");
   1058     else
   1059       cbuf_puts(&t->forwards, "extern ");
   1060     if (os && os->section_id != OBJ_SEC_NONE) {
   1061       const Section* sec = obj_section_get(t->obj, os->section_id);
   1062       if (sec->kind == SEC_RODATA) cbuf_puts(&t->forwards, "const ");
   1063     }
   1064     cbuf_puts(&t->forwards, "struct __kit_data_");
   1065     cbuf_puts(&t->forwards, name);
   1066     cbuf_puts(&t->forwards, " ");
   1067     cbuf_puts(&t->forwards, name);
   1068     cbuf_puts(&t->forwards, ";\n");
   1069   }
   1070 }
   1071 
   1072 void c_emit_func_end(CTarget* t) {
   1073   size_t splice_at = t->fn_body_start;
   1074   size_t body_after = t->body.len;
   1075   size_t fn_body_len = body_after - splice_at;
   1076   Heap* h = t->c->ctx->heap;
   1077 
   1078   u8* tmp = NULL;
   1079   if (fn_body_len) {
   1080     tmp = (u8*)h->alloc(h, fn_body_len, 1);
   1081     if (!tmp) {
   1082       compiler_panic(t->c, t->cur_fn->loc, "C target: out of memory");
   1083     }
   1084     for (size_t i = 0; i < fn_body_len; ++i) {
   1085       tmp[i] = t->body.data[splice_at + i];
   1086     }
   1087   }
   1088 
   1089   t->body.len = splice_at;
   1090   if (t->decls.len)
   1091     cbuf_putn(&t->body, (const char*)t->decls.data, t->decls.len);
   1092   if (tmp) {
   1093     cbuf_putn(&t->body, (const char*)tmp, fn_body_len);
   1094     h->free(h, tmp, fn_body_len);
   1095   }
   1096   cbuf_puts(&t->body, "}\n\n");
   1097 
   1098   t->cur_fn = NULL;
   1099 }
   1100 
   1101 /* === locals, params === */
   1102 
   1103 void c_emit_param_bind(CTarget* t, CGLocal local, KitCgTypeId type, u32 index) {
   1104   char buf[24];
   1105   c_ensure_local(t, local, type);
   1106   c_local_name(local, buf, sizeof buf);
   1107   cbuf_puts(&t->body, "  ");
   1108   cbuf_puts(&t->body, buf);
   1109   cbuf_puts(&t->body, " = p");
   1110   cbuf_put_u64(&t->body, (u64)index);
   1111   cbuf_puts(&t->body, ";\n");
   1112 }
   1113 
   1114 CGLocal c_emit_param(CTarget* t, const CGParamDesc* pd) {
   1115   CGLocalDesc d;
   1116   memset(&d, 0, sizeof d);
   1117   d.type = pd->type;
   1118   d.name = pd->name;
   1119   d.loc = pd->loc;
   1120   d.size = pd->size;
   1121   d.align = pd->align;
   1122   d.flags = pd->flags;
   1123   CGLocal local = c_emit_local(t, &d);
   1124   c_emit_param_bind(t, local, pd->type, pd->index);
   1125   return local;
   1126 }
   1127 
   1128 /* === load_imm, copy, binop === */
   1129 
   1130 void c_emit_load_imm(CTarget* t, Operand dst, i64 imm) {
   1131   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
   1132   if (dst.kind != OPK_LOCAL) {
   1133     compiler_panic(t->c, loc, "C target: load_imm dst must be LOCAL");
   1134   }
   1135   c_ensure_local(t, dst.v.local, dst.type);
   1136   /* The literal is emitted bare; its C type is `long long`. We can drop
   1137    * the bridge cast iff the bare assignment compiles cleanly:
   1138    *   - integer dst: imm must fit in dst's signed range (else
   1139    *     -Wconstant-conversion). 64-bit dst always fits.
   1140    *   - pointer dst: only `0` (null pointer constant) is safe; any other
   1141    *     literal trips -Wint-conversion.
   1142    * Otherwise keep the bridge. */
   1143   u32 w = c_int_width_for_signedness(t, dst.type);
   1144   int can_drop_bridge;
   1145   if (w > 0) {
   1146     can_drop_bridge = (w >= 64) || (imm >= -((i64)1 << (w - 1)) &&
   1147                                     imm <= (((i64)1 << (w - 1)) - 1));
   1148   } else if (c_type_is_ptr(t, dst.type)) {
   1149     can_drop_bridge = (imm == 0);
   1150   } else {
   1151     can_drop_bridge = 0;
   1152   }
   1153   c_emit_local_assign_open(t, dst.v.local,
   1154                            can_drop_bridge ? dst.type : (KitCgTypeId)0);
   1155   c_emit_imm_literal(t, imm);
   1156   c_emit_local_assign_close(t);
   1157 }
   1158 
   1159 void c_emit_copy(CTarget* t, Operand dst, Operand src) {
   1160   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
   1161   if (dst.kind != OPK_LOCAL) {
   1162     compiler_panic(t->c, loc, "C target: copy dst must be LOCAL");
   1163   }
   1164   c_ensure_local(t, dst.v.local, dst.type);
   1165   c_emit_local_assign_open(t, dst.v.local, src.type);
   1166   c_emit_operand(t, src);
   1167   c_emit_local_assign_close(t);
   1168 }
   1169 
   1170 static const char* binop_to_c(BinOp op) {
   1171   switch (op) {
   1172     case BO_IADD:
   1173     case BO_FADD:
   1174       return "+";
   1175     case BO_ISUB:
   1176     case BO_FSUB:
   1177       return "-";
   1178     case BO_IMUL:
   1179     case BO_FMUL:
   1180       return "*";
   1181     case BO_SDIV:
   1182     case BO_UDIV:
   1183     case BO_FDIV:
   1184       return "/";
   1185     case BO_SREM:
   1186     case BO_UREM:
   1187       return "%";
   1188     case BO_AND:
   1189       return "&";
   1190     case BO_OR:
   1191       return "|";
   1192     case BO_XOR:
   1193       return "^";
   1194     case BO_SHL:
   1195       return "<<";
   1196     case BO_SHR_S:
   1197     case BO_SHR_U:
   1198       return ">>";
   1199   }
   1200   return NULL;
   1201 }
   1202 
   1203 /* For BinOp `op`, decide how to sign-cast the operands. Returns 0 for "no
   1204  * cast", 1 for "cast both to signed", 2 for "cast both to unsigned", 3 for
   1205  * "cast lhs only (signedness `lhs_signed`)" (used for shifts). */
   1206 typedef enum { BSC_NONE, BSC_SIGNED, BSC_UNSIGNED, BSC_SHIFT_LHS } BinSignCast;
   1207 
   1208 static BinSignCast binop_sign_kind(BinOp op, int* lhs_signed_out) {
   1209   *lhs_signed_out = 1;
   1210   switch (op) {
   1211     case BO_SDIV:
   1212     case BO_SREM:
   1213       return BSC_SIGNED;
   1214     case BO_UDIV:
   1215     case BO_UREM:
   1216       return BSC_UNSIGNED;
   1217     case BO_SHR_S:
   1218       *lhs_signed_out = 1;
   1219       return BSC_SHIFT_LHS;
   1220     case BO_SHR_U:
   1221       *lhs_signed_out = 0;
   1222       return BSC_SHIFT_LHS;
   1223     default:
   1224       return BSC_NONE;
   1225   }
   1226 }
   1227 
   1228 void c_emit_binop(CTarget* t, BinOp op, Operand dst, Operand a, Operand b) {
   1229   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
   1230   const char* sym = binop_to_c(op);
   1231   if (!sym) {
   1232     compiler_panic(t->c, loc, "C target: unknown binop %d", (int)op);
   1233   }
   1234   if (dst.kind != OPK_LOCAL) {
   1235     compiler_panic(t->c, loc, "C target: binop dst must be LOCAL");
   1236   }
   1237   c_ensure_local(t, dst.v.local, dst.type);
   1238   /* Pointer operands get cast to uintptr_t inside c_emit_operand_arith,
   1239    * so the binop's C result type is `uintptr_t`, not the original pointer
   1240    * type. Keep the bridge when dst or either operand is pointer-typed so
   1241    * the assignment back to a pointer dst doesn't trip -Wint-conversion. */
   1242   int has_ptr = c_operand_is_ptr_typed(t, dst) ||
   1243                 c_operand_is_ptr_typed(t, a) || c_operand_is_ptr_typed(t, b);
   1244   c_emit_local_assign_open(t, dst.v.local, has_ptr ? (KitCgTypeId)0 : dst.type);
   1245   int lhs_signed = 1;
   1246   BinSignCast bsc = binop_sign_kind(op, &lhs_signed);
   1247   switch (bsc) {
   1248     case BSC_NONE:
   1249       c_emit_operand_arith(t, a);
   1250       cbuf_puts(&t->body, " ");
   1251       cbuf_puts(&t->body, sym);
   1252       cbuf_puts(&t->body, " ");
   1253       c_emit_operand_arith(t, b);
   1254       break;
   1255     case BSC_SIGNED:
   1256       c_emit_operand_arith_signed(t, a, 1);
   1257       cbuf_puts(&t->body, " ");
   1258       cbuf_puts(&t->body, sym);
   1259       cbuf_puts(&t->body, " ");
   1260       c_emit_operand_arith_signed(t, b, 1);
   1261       break;
   1262     case BSC_UNSIGNED:
   1263       c_emit_operand_arith_signed(t, a, 0);
   1264       cbuf_puts(&t->body, " ");
   1265       cbuf_puts(&t->body, sym);
   1266       cbuf_puts(&t->body, " ");
   1267       c_emit_operand_arith_signed(t, b, 0);
   1268       break;
   1269     case BSC_SHIFT_LHS:
   1270       c_emit_operand_arith_signed(t, a, lhs_signed);
   1271       cbuf_puts(&t->body, " ");
   1272       cbuf_puts(&t->body, sym);
   1273       cbuf_puts(&t->body, " ");
   1274       c_emit_operand(t, b);
   1275       break;
   1276   }
   1277   c_emit_local_assign_close(t);
   1278 }
   1279 
   1280 /* ===== unop ===== */
   1281 
   1282 void c_emit_unop(CTarget* t, UnOp op, Operand dst, Operand a) {
   1283   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
   1284   if (dst.kind != OPK_LOCAL) {
   1285     compiler_panic(t->c, loc, "C target: unop dst must be LOCAL");
   1286   }
   1287   c_ensure_local(t, dst.v.local, dst.type);
   1288   const char* sym = NULL;
   1289   switch (op) {
   1290     case UO_NEG:
   1291     case UO_FNEG:
   1292       sym = "-";
   1293       break;
   1294     case UO_NOT:
   1295       sym = "!";
   1296       break;
   1297     case UO_BNOT:
   1298       sym = "~";
   1299       break;
   1300     default:
   1301       compiler_panic(t->c, loc, "C target: unknown unop %d", (int)op);
   1302   }
   1303   c_emit_local_assign_open(t, dst.v.local, dst.type);
   1304   cbuf_puts(&t->body, sym);
   1305   c_emit_operand(t, a);
   1306   c_emit_local_assign_close(t);
   1307 }
   1308 
   1309 /* ===== compare ops ===== */
   1310 
   1311 /* The single C operator for ops that lower to one relational/equality
   1312  * expression: all integer ops, plus the FP predicates whose plain C operator
   1313  * already has the right NaN behavior (<,<=,>,>= and == are ordered: false on
   1314  * NaN; != is unordered: true on NaN). The remaining FP predicates need a
   1315  * compound expression and are handled in c_emit_cmp_operands; they return NULL
   1316  * here. No `default:` so -Wswitch flags any unhandled enumerator. */
   1317 static const char* cmp_to_c(CmpOp op) {
   1318   switch (op) {
   1319     case CMP_EQ:
   1320     case CMP_OEQ_F:
   1321       return "==";
   1322     case CMP_NE:
   1323     case CMP_UNE_F:
   1324       return "!=";
   1325     case CMP_LT_S:
   1326     case CMP_LT_U:
   1327     case CMP_OLT_F:
   1328       return "<";
   1329     case CMP_LE_S:
   1330     case CMP_LE_U:
   1331     case CMP_OLE_F:
   1332       return "<=";
   1333     case CMP_GT_S:
   1334     case CMP_GT_U:
   1335     case CMP_OGT_F:
   1336       return ">";
   1337     case CMP_GE_S:
   1338     case CMP_GE_U:
   1339     case CMP_OGE_F:
   1340       return ">=";
   1341     /* Compound FP predicates — no single C operator (see c_emit_cmp_operands).
   1342      */
   1343     case CMP_ONE_F:
   1344     case CMP_UEQ_F:
   1345     case CMP_ULT_F:
   1346     case CMP_ULE_F:
   1347     case CMP_UGT_F:
   1348     case CMP_UGE_F:
   1349       return NULL;
   1350   }
   1351   return NULL;
   1352 }
   1353 
   1354 /* The 6 FP predicates with no single C operator: built from compound ordered
   1355  * comparisons (no isnan(); host must not be built with -ffast-math). */
   1356 static int cmp_is_fp_compound(CmpOp op) {
   1357   return op == CMP_ONE_F || op == CMP_UEQ_F || op == CMP_ULT_F ||
   1358          op == CMP_ULE_F || op == CMP_UGT_F || op == CMP_UGE_F;
   1359 }
   1360 
   1361 /* Returns 1 if cmp op needs unsigned operand cast. -1 if signed. 0 if no cast
   1362  * (EQ/NE — sign doesn't matter for integer equality at the same width — and
   1363  * float compares). */
   1364 static int cmp_signedness(CmpOp op) {
   1365   switch (op) {
   1366     case CMP_LT_S:
   1367     case CMP_LE_S:
   1368     case CMP_GT_S:
   1369     case CMP_GE_S:
   1370       return -1;
   1371     case CMP_LT_U:
   1372     case CMP_LE_U:
   1373     case CMP_GT_U:
   1374     case CMP_GE_U:
   1375       return 1;
   1376     default:
   1377       return 0;
   1378   }
   1379 }
   1380 
   1381 /* Emit one ordered comparison `<a> opstr <b>` (no signedness cast — FP). */
   1382 static void c_emit_fp_rel(CTarget* t, Operand a, const char* opstr, Operand b) {
   1383   c_emit_operand_arith(t, a);
   1384   cbuf_puts(&t->body, " ");
   1385   cbuf_puts(&t->body, opstr);
   1386   cbuf_puts(&t->body, " ");
   1387   c_emit_operand_arith(t, b);
   1388 }
   1389 
   1390 static void c_emit_cmp_operands(CTarget* t, CmpOp op, Operand a, Operand b) {
   1391   /* The 6 FP predicates without a single C operator. Composed from ordered
   1392    * comparisons via unordered-R == !(ordered-not-R); ONE/UEQ from a<b / a>b.
   1393    * Each `!(...)` / `(...)` wraps the full cast-bearing comparison. */
   1394   switch (op) {
   1395     case CMP_UGE_F: /* !(OLT) */
   1396       cbuf_puts(&t->body, "!(");
   1397       c_emit_fp_rel(t, a, "<", b);
   1398       cbuf_puts(&t->body, ")");
   1399       return;
   1400     case CMP_UGT_F: /* !(OLE) */
   1401       cbuf_puts(&t->body, "!(");
   1402       c_emit_fp_rel(t, a, "<=", b);
   1403       cbuf_puts(&t->body, ")");
   1404       return;
   1405     case CMP_ULE_F: /* !(OGT) */
   1406       cbuf_puts(&t->body, "!(");
   1407       c_emit_fp_rel(t, a, ">", b);
   1408       cbuf_puts(&t->body, ")");
   1409       return;
   1410     case CMP_ULT_F: /* !(OGE) */
   1411       cbuf_puts(&t->body, "!(");
   1412       c_emit_fp_rel(t, a, ">=", b);
   1413       cbuf_puts(&t->body, ")");
   1414       return;
   1415     case CMP_ONE_F: /* ordered & !=: a<b || a>b */
   1416       cbuf_puts(&t->body, "(");
   1417       c_emit_fp_rel(t, a, "<", b);
   1418       cbuf_puts(&t->body, " || ");
   1419       c_emit_fp_rel(t, a, ">", b);
   1420       cbuf_puts(&t->body, ")");
   1421       return;
   1422     case CMP_UEQ_F: /* unordered | ==: !(a<b) && !(a>b) */
   1423       cbuf_puts(&t->body, "(!(");
   1424       c_emit_fp_rel(t, a, "<", b);
   1425       cbuf_puts(&t->body, ") && !(");
   1426       c_emit_fp_rel(t, a, ">", b);
   1427       cbuf_puts(&t->body, "))");
   1428       return;
   1429     default:
   1430       break; /* integer ops + single-operator FP fall through */
   1431   }
   1432   int sg = cmp_signedness(op);
   1433   if (sg == 0) {
   1434     c_emit_operand_arith(t, a);
   1435     cbuf_puts(&t->body, " ");
   1436     cbuf_puts(&t->body, cmp_to_c(op));
   1437     cbuf_puts(&t->body, " ");
   1438     c_emit_operand_arith(t, b);
   1439   } else {
   1440     int signed_ = (sg < 0);
   1441     c_emit_operand_arith_signed(t, a, signed_);
   1442     cbuf_puts(&t->body, " ");
   1443     cbuf_puts(&t->body, cmp_to_c(op));
   1444     cbuf_puts(&t->body, " ");
   1445     c_emit_operand_arith_signed(t, b, signed_);
   1446   }
   1447 }
   1448 
   1449 void c_emit_cmp(CTarget* t, CmpOp op, Operand dst, Operand a, Operand b) {
   1450   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
   1451   if (dst.kind != OPK_LOCAL) {
   1452     compiler_panic(t->c, loc, "C target: cmp dst must be LOCAL");
   1453   }
   1454   if (!cmp_to_c(op) && !cmp_is_fp_compound(op)) {
   1455     compiler_panic(t->c, loc, "C target: unknown cmp %d", (int)op);
   1456   }
   1457   c_ensure_local(t, dst.v.local, dst.type);
   1458   /* Compare result is C `int` (0/1); assigning to integer dst.type narrows
   1459    * implicitly without -Wall complaint. The result of a `!(...)` / `||` / `&&`
   1460    * compound FP predicate is already an int 0/1. */
   1461   c_emit_local_assign_open(t, dst.v.local, dst.type);
   1462   c_emit_cmp_operands(t, op, a, b);
   1463   c_emit_local_assign_close(t);
   1464 }
   1465 
   1466 /* ===== labels, jump, cmp_branch ===== */
   1467 
   1468 static void c_label_name(Label l, char* out, size_t cap) {
   1469   size_t i = 0;
   1470   if (cap == 0) return;
   1471   const char* p = "L";
   1472   while (*p && i + 1 < cap) out[i++] = *p++;
   1473   char tmp[16];
   1474   size_t n = 0;
   1475   u32 v = (u32)l;
   1476   if (v == 0) {
   1477     tmp[n++] = '0';
   1478   } else {
   1479     while (v) {
   1480       tmp[n++] = (char)('0' + (v % 10));
   1481       v /= 10;
   1482     }
   1483   }
   1484   while (n && i + 1 < cap) out[i++] = tmp[--n];
   1485   out[i] = '\0';
   1486 }
   1487 
   1488 Label c_emit_label_new(CTarget* t) {
   1489   t->next_label += 1;
   1490   return (Label)t->next_label;
   1491 }
   1492 
   1493 void c_emit_label_place(CTarget* t, Label l) {
   1494   char buf[24];
   1495   c_label_name(l, buf, sizeof buf);
   1496   /* `Lk: __attribute__((unused));` — empty stmt keeps it valid at end-of-block,
   1497    * and the attribute silences -Wunused-label when the goto got folded away. */
   1498   cbuf_puts(&t->body, " ");
   1499   cbuf_puts(&t->body, buf);
   1500   cbuf_puts(&t->body, ": __attribute__((unused));\n");
   1501   t->last_was_terminator = 0;
   1502 }
   1503 
   1504 /* If `l` is the innermost structured scope's break/continue label, return
   1505  * the C keyword that exits/iterates that scope (a literal `break` or
   1506  * `continue`). NULL means "fall back to goto." Matches only the innermost
   1507  * scope because C `break`/`continue` only escape the nearest enclosing
   1508  * loop/switch — outer-scope targets must stay as goto. */
   1509 static const char* c_scope_kw_for_label(CTarget* t, Label l) {
   1510   if (t->nscopes == 0) return NULL;
   1511   const CScopeInfo* s = &t->scopes[t->nscopes - 1u];
   1512   if (!s->structured) return NULL;
   1513   if (l == s->break_label) return "break";
   1514   if (l == s->continue_label) return "continue";
   1515   return NULL;
   1516 }
   1517 
   1518 void c_emit_jump(CTarget* t, Label l) {
   1519   if (t->last_was_terminator) return;
   1520   const char* kw = c_scope_kw_for_label(t, l);
   1521   if (kw) {
   1522     cbuf_puts(&t->body, "  ");
   1523     cbuf_puts(&t->body, kw);
   1524     cbuf_puts(&t->body, ";\n");
   1525   } else {
   1526     char buf[24];
   1527     c_label_name(l, buf, sizeof buf);
   1528     cbuf_puts(&t->body, "  goto ");
   1529     cbuf_puts(&t->body, buf);
   1530     cbuf_puts(&t->body, ";\n");
   1531   }
   1532   t->last_was_terminator = 1;
   1533 }
   1534 
   1535 void c_emit_cmp_branch(CTarget* t, CmpOp op, Operand a, Operand b, Label l) {
   1536   if (t->last_was_terminator) return;
   1537   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
   1538   if (!cmp_to_c(op) && !cmp_is_fp_compound(op)) {
   1539     compiler_panic(t->c, loc, "C target: unknown cmp %d", (int)op);
   1540   }
   1541   const char* kw = c_scope_kw_for_label(t, l);
   1542   cbuf_puts(&t->body, "  if (");
   1543   c_emit_cmp_operands(t, op, a, b);
   1544   if (kw) {
   1545     cbuf_puts(&t->body, ") ");
   1546     cbuf_puts(&t->body, kw);
   1547     cbuf_puts(&t->body, ";\n");
   1548   } else {
   1549     char buf[24];
   1550     c_label_name(l, buf, sizeof buf);
   1551     cbuf_puts(&t->body, ") goto ");
   1552     cbuf_puts(&t->body, buf);
   1553     cbuf_puts(&t->body, ";\n");
   1554   }
   1555 }
   1556 
   1557 /* ===== scopes =====
   1558  *
   1559  * SCOPE_LOOP maps to C's `for (;;) { ... }`. CG places the continue label
   1560  * just before `scope_begin` and the break label just before `scope_end`
   1561  * (see src/cg/control.c:208,253). The C target leaves those label
   1562  * placements in the body — they sit just before `for (;;) {` and just
   1563  * after `}` respectively, so any outer-scope `goto continue_lbl` or
   1564  * `goto break_lbl` (e.g. a nested loop's `continue` targeting this
   1565  * outer loop) still resolves. Inside the `for` body, `c_jump` and
   1566  * `c_cmp_branch` translate jumps whose target is the *innermost* scope's
   1567  * break/continue label into `break;` / `continue;`; outer-scope targets
   1568  * fall back to `goto`. The redundant `Lk: ;` adjacent to the `for` is
   1569  * cosmetic; gcc/clang fold it. */
   1570 
   1571 static void c_grow_scopes(CTarget* t, u32 needed) {
   1572   c_vec_grow_or_panic(t, t->scopes, t->scopes_cap, needed);
   1573 }
   1574 
   1575 CGScope c_emit_scope_begin(CTarget* t, const CGScopeDesc* d) {
   1576   if (t->nscopes + 1u >= t->scopes_cap) c_grow_scopes(t, t->nscopes + 2u);
   1577   u32 idx = t->nscopes;
   1578   t->scopes[idx].kind = d->kind;
   1579   t->scopes[idx].break_label = d->break_label;
   1580   t->scopes[idx].continue_label = d->continue_label;
   1581   t->scopes[idx].structured = 0;
   1582   t->nscopes += 1u;
   1583   if (d->kind == SCOPE_LOOP) {
   1584     cbuf_puts(&t->body, "  for (;;) {\n");
   1585     t->scopes[idx].structured = 1;
   1586     t->last_was_terminator = 0;
   1587     return (CGScope)(idx + 1u);
   1588   }
   1589   return (CGScope)(idx + 1u);
   1590 }
   1591 
   1592 void c_emit_scope_end(CTarget* t, CGScope s) {
   1593   if (s == 0 || (u32)s > t->nscopes) {
   1594     compiler_panic(t->c, t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0},
   1595                    "C target: scope_end on invalid handle");
   1596   }
   1597   u32 idx = (u32)s - 1u;
   1598   if (t->scopes[idx].structured) {
   1599     /* CG places break_label just before scope_end, so the label sits
   1600      * inside the for-body. Anything that lands on it (including a
   1601      * `goto break_lbl` from a nested scope's labeled break) needs to
   1602      * exit the for — without an explicit `break;`, fall-through would
   1603      * iterate again. Always emit; if the body already terminated the
   1604      * defensive break is dead but harmless. */
   1605     cbuf_puts(&t->body, "  break;\n");
   1606     cbuf_puts(&t->body, "  }\n");
   1607     /* The closing brace is not a terminator; control can fall through it
   1608      * (e.g., off the end of a void function). */
   1609     t->last_was_terminator = 0;
   1610   }
   1611   t->nscopes -= 1u;
   1612 }
   1613 
   1614 void c_emit_break_to(CTarget* t, CGScope s) {
   1615   if (s == 0 || (u32)s > t->nscopes) {
   1616     compiler_panic(t->c, t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0},
   1617                    "C target: break_to on invalid handle");
   1618   }
   1619   c_emit_jump(t, t->scopes[s - 1u].break_label);
   1620 }
   1621 
   1622 void c_emit_continue_to(CTarget* t, CGScope s) {
   1623   if (s == 0 || (u32)s > t->nscopes) {
   1624     compiler_panic(t->c, t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0},
   1625                    "C target: continue_to on invalid handle");
   1626   }
   1627   c_emit_jump(t, t->scopes[s - 1u].continue_label);
   1628 }
   1629 
   1630 /* ===== switch dispatch ===== */
   1631 
   1632 /* Emit `case <value>:`. For an int32_t selector the bare literal is
   1633  * already the right type, so we skip the cast; for wider/narrower
   1634  * integers we wrap in `(T)` so the case constant matches the switch
   1635  * value's promoted type (avoids -Wswitch warnings on narrower
   1636  * selectors). */
   1637 static void c_emit_case_value(CTarget* t, KitCgTypeId sel_ty, u64 v) {
   1638   u32 w = c_int_width_for_signedness(t, sel_ty);
   1639   cbuf_puts(&t->body, "    case ");
   1640   if (w != 0 && w != 32) {
   1641     cbuf_putc(&t->body, '(');
   1642     c_emit_type(t, &t->body, sel_ty);
   1643     cbuf_puts(&t->body, ")");
   1644   }
   1645   c_emit_imm_literal(t, (i64)v);
   1646   cbuf_puts(&t->body, ":");
   1647 }
   1648 
   1649 void c_emit_switch_(
   1650     CTarget* t, const CGSwitchDesc* d) { /* gcc/clang ignore strategy hints and
   1651                                             pick their own dispatch shape. */
   1652   (void)d->hint;
   1653   if (t->last_was_terminator) return;
   1654   cbuf_puts(&t->body, "  switch (");
   1655   c_emit_operand(t, d->selector);
   1656   cbuf_puts(&t->body, ") {\n");
   1657   for (u32 i = 0; i < d->ncases; ++i) {
   1658     char buf[24];
   1659     c_label_name(d->cases[i].label, buf, sizeof buf);
   1660     c_emit_case_value(t, d->selector.type, d->cases[i].value);
   1661     cbuf_puts(&t->body, " goto ");
   1662     cbuf_puts(&t->body, buf);
   1663     cbuf_puts(&t->body, ";\n");
   1664   }
   1665   cbuf_puts(&t->body, "    default: ");
   1666   if (d->default_label != (Label)LABEL_NONE) {
   1667     char buf[24];
   1668     c_label_name(d->default_label, buf, sizeof buf);
   1669     cbuf_puts(&t->body, "goto ");
   1670     cbuf_puts(&t->body, buf);
   1671     cbuf_puts(&t->body, ";\n");
   1672   } else {
   1673     /* No default supplied — the kit IR's contract for that case is
   1674      * "if no case matches, fall through." `break;` does exactly that
   1675      * inside the for-wrapper around structured scopes. */
   1676     cbuf_puts(&t->body, "break;\n");
   1677   }
   1678   cbuf_puts(&t->body, "  }\n");
   1679   /* The switch always transfers control (every arm jumps or breaks).
   1680    * Mark as terminator so any frontend-emitted defensive jump after
   1681    * dispatch is dropped. */
   1682   t->last_was_terminator = 1;
   1683 }
   1684 
   1685 /* ===== load_label_addr / indirect_branch =====
   1686  * GCC computed-goto extension: `&&L` is the address of label L within
   1687  * the current function, and `goto *p;` jumps to such an address. This
   1688  * is the lowering every cc1-like backend uses (and what the toy
   1689  * frontend ultimately compiles to via the C target). */
   1690 void c_emit_load_label_addr(CTarget* t, Operand dst, Label l) {
   1691   char buf[24];
   1692   if (dst.kind != OPK_LOCAL) {
   1693     compiler_panic(t->c, t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0},
   1694                    "C target: load_label_addr dst must be LOCAL");
   1695   }
   1696   c_ensure_local(t, dst.v.local, dst.type);
   1697   c_emit_local_assign_open(t, dst.v.local, (KitCgTypeId)0);
   1698   cbuf_puts(&t->body, "(void*)&&");
   1699   c_label_name(l, buf, sizeof buf);
   1700   cbuf_puts(&t->body, buf);
   1701   c_emit_local_assign_close(t);
   1702 }
   1703 
   1704 void c_emit_indirect_branch(CTarget* t, Operand addr,
   1705                             const Label* valid_targets, u32 ntargets) {
   1706   (void)valid_targets;
   1707   (void)ntargets;
   1708   if (t->last_was_terminator) return;
   1709   cbuf_puts(&t->body, "  goto *");
   1710   c_emit_operand(t, addr);
   1711   cbuf_puts(&t->body, ";\n");
   1712   t->last_was_terminator = 1;
   1713 }
   1714 
   1715 /* ===== function-local static label-address data ===== */
   1716 
   1717 static int c_is_local_static_sym(CTarget* t, ObjSymId sym) {
   1718   for (u32 i = 0; i < t->local_static_nsyms; ++i) {
   1719     if (t->local_static_syms[i] == sym) return 1;
   1720   }
   1721   return 0;
   1722 }
   1723 
   1724 static void c_mark_local_static_sym(CTarget* t, ObjSymId sym) {
   1725   if (sym == OBJ_SYM_NONE || c_is_local_static_sym(t, sym)) return;
   1726   /* Append-style: VEC_GROW for the doubling realloc, but keep the
   1727    * cur_fn-relative panic location this path uses on OOM. */
   1728   if (VEC_GROW(t->c->ctx->heap, t->local_static_syms, t->local_static_syms_cap,
   1729                t->local_static_nsyms + 1u)) {
   1730     compiler_panic(t->c, t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0},
   1731                    "C target: out of memory");
   1732   }
   1733   t->local_static_syms[t->local_static_nsyms++] = sym;
   1734 }
   1735 
   1736 static void c_grow_local_static_entries(CTarget* t, u32 want) {
   1737   /* Append-style (the caller initializes the [0, count) entries after this
   1738    * grows): VEC_GROW for the doubling realloc, with the cur_fn-relative panic
   1739    * location this path uses on OOM. */
   1740   if (VEC_GROW(t->c->ctx->heap, t->local_static_entries,
   1741                t->local_static_entries_cap, want)) {
   1742     compiler_panic(t->c, t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0},
   1743                    "C target: out of memory");
   1744   }
   1745 }
   1746 
   1747 int c_emit_can_local_static_data(CTarget* t,
   1748                                  const CGLocalStaticDataDesc* desc) {
   1749   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
   1750   const CgType* ty = cg_type_get(t->c, desc->type);
   1751   if (!ty) {
   1752     compiler_panic(t->c, loc, "C target: unknown local static type %u",
   1753                    (unsigned)desc->type);
   1754   }
   1755   if (ty->kind == KIT_CG_TYPE_ARRAY) {
   1756     ty = cg_type_get(t->c, ty->array.elem);
   1757   }
   1758   return ty && ty->kind == KIT_CG_TYPE_PTR;
   1759 }
   1760 
   1761 int c_emit_local_static_data_begin(CTarget* t,
   1762                                    const CGLocalStaticDataDesc* desc) {
   1763   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
   1764   if (!t->cur_fn) {
   1765     compiler_panic(t->c, loc,
   1766                    "C target: function-local static data outside function");
   1767   }
   1768   if (t->local_static_active) {
   1769     compiler_panic(t->c, loc,
   1770                    "C target: nested function-local static data definition");
   1771   }
   1772   const CgType* ty = cg_type_get(t->c, desc->type);
   1773   if (!ty) {
   1774     compiler_panic(t->c, loc, "C target: unknown local static type %u",
   1775                    (unsigned)desc->type);
   1776   }
   1777 
   1778   u64 count = 1;
   1779   int is_array = 0;
   1780   KitCgTypeId elem = desc->type;
   1781   if (ty->kind == KIT_CG_TYPE_ARRAY) {
   1782     is_array = 1;
   1783     count = ty->array.count;
   1784     elem = ty->array.elem;
   1785     ty = cg_type_get(t->c, elem);
   1786   }
   1787   if (!c_emit_can_local_static_data(t, desc)) {
   1788     return 0;
   1789   }
   1790   if (count > UINT32_MAX) {
   1791     compiler_panic(t->c, loc, "C target: local static pointer table too large");
   1792   }
   1793 
   1794   c_grow_local_static_entries(t, (u32)count);
   1795   for (u32 i = 0; i < (u32)count; ++i) {
   1796     t->local_static_entries[i].label = LABEL_NONE;
   1797     t->local_static_entries[i].addend = 0;
   1798     t->local_static_entries[i].has_label = 0;
   1799   }
   1800   t->local_static_nentries = (u32)count;
   1801   t->local_static_sym = desc->sym;
   1802   t->local_static_type = desc->type;
   1803   t->local_static_count = count;
   1804   t->local_static_offset = 0;
   1805   t->local_static_ptr_width = (u32)cg_type_size(t->c, elem);
   1806   t->local_static_align =
   1807       desc->align ? desc->align : cg_type_align(t->c, desc->type);
   1808   t->local_static_active = 1;
   1809   t->local_static_is_array = (u8)is_array;
   1810   t->local_static_readonly =
   1811       (desc->attrs.flags & KIT_CG_DATADEF_READONLY) ? 1u : 0u;
   1812   c_mark_local_static_sym(t, desc->sym);
   1813   return 1;
   1814 }
   1815 
   1816 void c_emit_local_static_data_write(CTarget* t, const u8* data, u64 len) {
   1817   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
   1818   if (!t->local_static_active || !len) return;
   1819   if (data) {
   1820     for (u64 i = 0; i < len; ++i) {
   1821       if (data[i] != 0) {
   1822         compiler_panic(t->c, loc,
   1823                        "C target: function-local static label table supports "
   1824                        "only zero bytes and label addresses");
   1825       }
   1826     }
   1827   }
   1828   t->local_static_offset += len;
   1829 }
   1830 
   1831 void c_emit_local_static_data_label_addr(CTarget* t, Label target, i64 addend,
   1832                                          u32 width, u32 address_space) {
   1833   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
   1834   (void)address_space;
   1835   if (!t->local_static_active) {
   1836     compiler_panic(t->c, loc,
   1837                    "C target: label address outside local static data");
   1838   }
   1839   if (width != t->local_static_ptr_width) {
   1840     compiler_panic(t->c, loc,
   1841                    "C target: label address width %u does not match pointer "
   1842                    "width %u",
   1843                    (unsigned)width, (unsigned)t->local_static_ptr_width);
   1844   }
   1845   if ((t->local_static_offset % t->local_static_ptr_width) != 0) {
   1846     compiler_panic(t->c, loc,
   1847                    "C target: unaligned label address in local static data");
   1848   }
   1849   u64 idx = t->local_static_offset / t->local_static_ptr_width;
   1850   if (idx >= t->local_static_count) {
   1851     compiler_panic(t->c, loc,
   1852                    "C target: too many local static label table entries");
   1853   }
   1854   CLocalStaticLabelEntry* e = &t->local_static_entries[(u32)idx];
   1855   if (e->has_label) {
   1856     compiler_panic(t->c, loc,
   1857                    "C target: duplicate local static label table entry");
   1858   }
   1859   e->label = target;
   1860   e->addend = addend;
   1861   e->has_label = 1;
   1862   t->local_static_offset += width;
   1863 }
   1864 
   1865 static void c_emit_local_static_label_expr(CTarget* t,
   1866                                            const CLocalStaticLabelEntry* e) {
   1867   char lbuf[24];
   1868   if (!e->has_label) {
   1869     cbuf_puts(&t->decls, "(void*)0");
   1870     return;
   1871   }
   1872   if (e->addend == 0) {
   1873     cbuf_puts(&t->decls, "&&");
   1874     c_label_name(e->label, lbuf, sizeof lbuf);
   1875     cbuf_puts(&t->decls, lbuf);
   1876     return;
   1877   }
   1878   cbuf_puts(&t->decls, "(void*)((char*)&&");
   1879   c_label_name(e->label, lbuf, sizeof lbuf);
   1880   cbuf_puts(&t->decls, lbuf);
   1881   cbuf_puts(&t->decls, " + ");
   1882   cbuf_put_i64(&t->decls, e->addend);
   1883   cbuf_puts(&t->decls, ")");
   1884 }
   1885 
   1886 void c_emit_local_static_data_end(CTarget* t) {
   1887   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
   1888   if (!t->local_static_active) return;
   1889   u64 total_size = t->local_static_count * t->local_static_ptr_width;
   1890   if (t->local_static_offset > total_size) {
   1891     compiler_panic(t->c, loc,
   1892                    "C target: local static initializer exceeds object size");
   1893   }
   1894   const char* nm = c_sym_name(t, t->local_static_sym);
   1895   cbuf_puts(&t->decls, "  static __attribute__((unused)) ");
   1896   cbuf_puts(&t->decls, "_Alignas(");
   1897   cbuf_put_u64(&t->decls, t->local_static_align ? t->local_static_align : 1);
   1898   cbuf_puts(&t->decls, ") void* ");
   1899   if (t->local_static_readonly) cbuf_puts(&t->decls, "const ");
   1900   cbuf_puts(&t->decls, nm);
   1901   if (t->local_static_is_array) {
   1902     cbuf_puts(&t->decls, "[");
   1903     cbuf_put_u64(&t->decls, t->local_static_count);
   1904     cbuf_puts(&t->decls, "]");
   1905   }
   1906   cbuf_puts(&t->decls, " = {");
   1907   for (u32 i = 0; i < t->local_static_nentries; ++i) {
   1908     if (i > 0) cbuf_putc(&t->decls, ',');
   1909     if ((i & 3u) == 0) cbuf_puts(&t->decls, "\n    ");
   1910     c_emit_local_static_label_expr(t, &t->local_static_entries[i]);
   1911   }
   1912   cbuf_puts(&t->decls, "\n  };\n");
   1913 
   1914   t->local_static_active = 0;
   1915   t->local_static_sym = OBJ_SYM_NONE;
   1916   t->local_static_type = KIT_CG_TYPE_NONE;
   1917   t->local_static_count = 0;
   1918   t->local_static_offset = 0;
   1919   t->local_static_ptr_width = 0;
   1920   t->local_static_align = 0;
   1921   t->local_static_nentries = 0;
   1922   t->local_static_is_array = 0;
   1923   t->local_static_readonly = 0;
   1924 }
   1925 
   1926 /* ===== local, local_addr ===== */
   1927 
   1928 CGLocal c_emit_local(CTarget* t, const CGLocalDesc* d) {
   1929   t->next_local += 1u;
   1930   if (t->next_local == CG_LOCAL_NONE) {
   1931     compiler_panic(t->c, t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0},
   1932                    "C target: semantic local id exhausted");
   1933     return CG_LOCAL_NONE;
   1934   }
   1935   c_ensure_local(t, (CGLocal)t->next_local, d->type);
   1936   return (CGLocal)t->next_local;
   1937 }
   1938 
   1939 void c_emit_local_addr(CTarget* t, Operand dst, const CGLocalDesc* d,
   1940                        CGLocal s) {
   1941   (void)d;
   1942   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
   1943   if (dst.kind != OPK_LOCAL) {
   1944     compiler_panic(t->c, loc, "C target: local_addr dst must be LOCAL");
   1945   }
   1946   c_ensure_local(t, dst.v.local, dst.type);
   1947   c_ensure_local(t, s, d->type);
   1948   char buf[24];
   1949   c_emit_local_assign_open(t, dst.v.local, (KitCgTypeId)0);
   1950   cbuf_puts(&t->body, "&");
   1951   c_local_name(s, buf, sizeof buf);
   1952   cbuf_puts(&t->body, buf);
   1953   c_emit_local_assign_close(t);
   1954 }
   1955 
   1956 /* ===== convert ===== */
   1957 
   1958 void c_emit_convert(CTarget* t, ConvKind k, Operand dst, Operand src) {
   1959   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
   1960   if (dst.kind != OPK_LOCAL) {
   1961     compiler_panic(t->c, loc, "C target: convert dst must be LOCAL");
   1962   }
   1963   c_ensure_local(t, dst.v.local, dst.type);
   1964   char buf[24];
   1965   c_local_name(dst.v.local, buf, sizeof buf);
   1966 
   1967   if (k == CV_BITCAST) {
   1968     /* Same-size reinterpretation. Use __builtin_memcpy through a temp so
   1969      * neither aliasing nor representation assumptions creep in. The temp
   1970      * lives in its own `{ ... }` block, so no name collision tracking. */
   1971     u32 id = ++t->next_tmp;
   1972     cbuf_puts(&t->body, "  { ");
   1973     c_emit_type(t, &t->body, src.type);
   1974     cbuf_puts(&t->body, " __bc");
   1975     cbuf_put_u64(&t->body, (u64)id);
   1976     cbuf_puts(&t->body, " = ");
   1977     c_emit_operand(t, src);
   1978     cbuf_puts(&t->body, "; __builtin_memcpy(&");
   1979     cbuf_puts(&t->body, buf);
   1980     cbuf_puts(&t->body, ", &__bc");
   1981     cbuf_put_u64(&t->body, (u64)id);
   1982     cbuf_puts(&t->body, ", sizeof __bc");
   1983     cbuf_put_u64(&t->body, (u64)id);
   1984     cbuf_puts(&t->body, "); }\n");
   1985     return;
   1986   }
   1987 
   1988   if (c_type_is_bool(t, dst.type)) {
   1989     c_emit_local_assign_open(t, dst.v.local, dst.type);
   1990     cbuf_puts(&t->body, "(");
   1991     c_emit_type(t, &t->body, dst.type);
   1992     cbuf_puts(&t->body, ")(");
   1993     c_emit_operand(t, src);
   1994     cbuf_puts(&t->body, " != 0)");
   1995     c_emit_local_assign_close(t);
   1996     return;
   1997   }
   1998 
   1999   /* Integer and float conversions: a C cast does the right thing once the
   2000    * source is first cast to the appropriate signedness (for SEXT/ZEXT and
   2001    * ITOF_S/U / FTOI_S/U). */
   2002   int src_signed = 1;
   2003   switch (k) {
   2004     case CV_ZEXT:
   2005     case CV_ITOF_U:
   2006     case CV_FTOI_U:
   2007       src_signed = 0;
   2008       break;
   2009     default:
   2010       src_signed = 1;
   2011       break;
   2012   }
   2013 
   2014   /* The cast `(dst.type)(src)` produces a value of dst.type. */
   2015   c_emit_local_assign_open(t, dst.v.local, dst.type);
   2016   cbuf_puts(&t->body, "(");
   2017   c_emit_type(t, &t->body, dst.type);
   2018   cbuf_puts(&t->body, ")");
   2019   if (k == CV_SEXT || k == CV_ZEXT) {
   2020     c_emit_operand_signed(t, src, src_signed);
   2021   } else if (k == CV_TRUNC && c_operand_is_ptr_typed(t, src)) {
   2022     /* Casting a pointer directly to a narrower integer trips
   2023      * -Wvoid-pointer-to-int-cast (and -Wpointer-to-int-cast). Bridge
   2024      * through uintptr_t. */
   2025     cbuf_puts(&t->body, "((uintptr_t)");
   2026     c_emit_operand(t, src);
   2027     cbuf_puts(&t->body, ")");
   2028   } else {
   2029     /* TRUNC / FTOI / ITOF / FEXT / FTRUNC: rely on C cast semantics. */
   2030     c_emit_operand(t, src);
   2031   }
   2032   c_emit_local_assign_close(t);
   2033 }
   2034 
   2035 /* === call === */
   2036 
   2037 static KitCgTypeId c_call_arg_type(CTarget* t, const CgType* fty,
   2038                                    const CGCallDesc* d, u32 i) {
   2039   if (i < fty->func.nparams) return fty->func.params[i].type;
   2040   return c_local_type_or_panic(t, d->args[i]);
   2041 }
   2042 
   2043 static void c_emit_call_arg(CTarget* t, const CgType* fty, const CGCallDesc* d,
   2044                             u32 i) {
   2045   KitCgTypeId ty = c_call_arg_type(t, fty, d, i);
   2046   c_ensure_local(t, d->args[i], ty);
   2047   c_emit_operand(t, c_op_local(d->args[i], ty));
   2048 }
   2049 
   2050 /* Render call operand `i`, optionally cast to unsigned __int128 first (used by
   2051  * the unsigned i128 helpers below). */
   2052 static void c_emit_ti_operand(CTarget* t, const CgType* fty,
   2053                               const CGCallDesc* d, u32 i, int as_unsigned) {
   2054   if (as_unsigned) cbuf_puts(&t->body, "(unsigned __int128)(");
   2055   c_emit_call_arg(t, fty, d, i);
   2056   if (as_unsigned) cbuf_puts(&t->body, ")");
   2057 }
   2058 
   2059 /* The CG arithmetic layer (src/cg/arith.c) lowers 128-bit integer operations
   2060  * into calls to runtime helpers: compiler-rt-standard names for mul/div/mod/
   2061  * shift/neg, and __kit_*-prefixed ones for add/sub/bitwise/not/extend/compare
   2062  * — operations that real toolchains inline (so have no compiler-rt symbol), or
   2063  * that use kit's own -1/0/1 compare convention. A C compiler has native
   2064  * __int128, so the portable C backend re-expresses every such call as a native
   2065  * operator: the emitted source then needs neither kit's runtime nor the host's
   2066  * compiler-rt builtins. Returns 1 if it emitted the intrinsic, 0 to fall
   2067  * through to a normal call. */
   2068 static int c_try_emit_ti_intrinsic(CTarget* t, const CgType* fty,
   2069                                    const CGCallDesc* d) {
   2070   if (d->callee.kind != OPK_GLOBAL) return 0;
   2071   const char* n = c_sym_name(t, d->callee.v.global.sym);
   2072   if (!n) return 0;
   2073 
   2074   /* Symmetric binary ops over two i128 operands: (a) OP (b). `u` casts both
   2075    * operands to unsigned __int128 first (unsigned divide/remainder). */
   2076   static const struct {
   2077     const char* name;
   2078     const char* op;
   2079     int u;
   2080   } kBin[] = {
   2081       {"__kit_addti3", "+", 0}, {"__kit_subti3", "-", 0},
   2082       {"__multi3", "*", 0},     {"__kit_andti3", "&", 0},
   2083       {"__kit_orti3", "|", 0},  {"__kit_xorti3", "^", 0},
   2084       {"__divti3", "/", 0},     {"__modti3", "%", 0},
   2085       {"__udivti3", "/", 1},    {"__umodti3", "%", 1},
   2086   };
   2087   if (d->nargs == 2) {
   2088     for (size_t i = 0; i < sizeof kBin / sizeof kBin[0]; ++i) {
   2089       if (strcmp(n, kBin[i].name) != 0) continue;
   2090       cbuf_puts(&t->body, "(");
   2091       c_emit_ti_operand(t, fty, d, 0, kBin[i].u);
   2092       cbuf_puts(&t->body, " ");
   2093       cbuf_puts(&t->body, kBin[i].op);
   2094       cbuf_puts(&t->body, " ");
   2095       c_emit_ti_operand(t, fty, d, 1, kBin[i].u);
   2096       cbuf_puts(&t->body, ")");
   2097       return 1;
   2098     }
   2099   }
   2100 
   2101   /* Shifts: (value) OP (count). The count is a plain int operand, never cast;
   2102    * logical right shift takes an unsigned value. */
   2103   if (d->nargs == 2) {
   2104     const char* sop = NULL;
   2105     int uval = 0;
   2106     if (strcmp(n, "__ashlti3") == 0) {
   2107       sop = "<<";
   2108     } else if (strcmp(n, "__ashrti3") == 0) {
   2109       sop = ">>";
   2110     } else if (strcmp(n, "__lshrti3") == 0) {
   2111       sop = ">>";
   2112       uval = 1;
   2113     }
   2114     if (sop) {
   2115       cbuf_puts(&t->body, "(");
   2116       c_emit_ti_operand(t, fty, d, 0, uval);
   2117       cbuf_puts(&t->body, " ");
   2118       cbuf_puts(&t->body, sop);
   2119       cbuf_puts(&t->body, " ");
   2120       c_emit_call_arg(t, fty, d, 1);
   2121       cbuf_puts(&t->body, ")");
   2122       return 1;
   2123     }
   2124   }
   2125 
   2126   /* Unary ops and i64 -> i128 widening. */
   2127   if (d->nargs == 1) {
   2128     const char* uop = NULL;
   2129     if (strcmp(n, "__negti2") == 0)
   2130       uop = "-";
   2131     else if (strcmp(n, "__kit_notti3") == 0)
   2132       uop = "~";
   2133     if (uop) {
   2134       cbuf_puts(&t->body, "(");
   2135       cbuf_puts(&t->body, uop);
   2136       cbuf_puts(&t->body, "(");
   2137       c_emit_call_arg(t, fty, d, 0);
   2138       cbuf_puts(&t->body, "))");
   2139       return 1;
   2140     }
   2141     if (strcmp(n, "__kit_sext64ti") == 0) {
   2142       cbuf_puts(&t->body, "((__int128)(int64_t)(");
   2143       c_emit_call_arg(t, fty, d, 0);
   2144       cbuf_puts(&t->body, "))");
   2145       return 1;
   2146     }
   2147     if (strcmp(n, "__kit_zext64ti") == 0) {
   2148       cbuf_puts(&t->body, "((unsigned __int128)(uint64_t)(");
   2149       c_emit_call_arg(t, fty, d, 0);
   2150       cbuf_puts(&t->body, "))");
   2151       return 1;
   2152     }
   2153   }
   2154 
   2155   /* Compare: kit's helpers return -1/0/1 (the CG layer compares the result
   2156    * against zero), so reproduce that sign convention with native operators. */
   2157   if (d->nargs == 2) {
   2158     int usign = -1;
   2159     if (strcmp(n, "__kit_cmpti2") == 0)
   2160       usign = 0;
   2161     else if (strcmp(n, "__kit_ucmpti2") == 0)
   2162       usign = 1;
   2163     if (usign >= 0) {
   2164       cbuf_puts(&t->body, "(");
   2165       c_emit_ti_operand(t, fty, d, 0, usign);
   2166       cbuf_puts(&t->body, " < ");
   2167       c_emit_ti_operand(t, fty, d, 1, usign);
   2168       cbuf_puts(&t->body, " ? -1 : (");
   2169       c_emit_ti_operand(t, fty, d, 0, usign);
   2170       cbuf_puts(&t->body, " > ");
   2171       c_emit_ti_operand(t, fty, d, 1, usign);
   2172       cbuf_puts(&t->body, " ? 1 : 0))");
   2173       return 1;
   2174     }
   2175   }
   2176   return 0;
   2177 }
   2178 
   2179 static void c_emit_call_expr(CTarget* t, const CgType* fty,
   2180                              const CGCallDesc* d) {
   2181   if (c_try_emit_ti_intrinsic(t, fty, d)) return;
   2182   if (d->callee.kind == OPK_GLOBAL) {
   2183     c_ensure_forward_decl(t, d->callee.v.global.sym, d->fn_type);
   2184     cbuf_puts(&t->body, c_sym_name(t, d->callee.v.global.sym));
   2185   } else if (d->callee.kind == OPK_LOCAL) {
   2186     const char* fp = c_typedef_name(t, d->fn_type);
   2187     cbuf_puts(&t->body, "((");
   2188     c_ensure_typedef(t, d->fn_type);
   2189     cbuf_puts(&t->body, fp);
   2190     cbuf_puts(&t->body, ")");
   2191     c_emit_operand(t, d->callee);
   2192     cbuf_puts(&t->body, ")");
   2193   } else {
   2194     compiler_panic(t->c, t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0},
   2195                    "C target: callee kind %d not supported",
   2196                    (int)d->callee.kind);
   2197   }
   2198 
   2199   cbuf_puts(&t->body, "(");
   2200   for (u32 i = 0; i < d->nargs; ++i) {
   2201     if (i > 0) cbuf_puts(&t->body, ", ");
   2202     c_emit_call_arg(t, fty, d, i);
   2203   }
   2204   cbuf_puts(&t->body, ")");
   2205 }
   2206 
   2207 const char* c_emit_tail_call_unrealizable_reason(CTarget* t,
   2208                                                  const CGCallDesc* d) {
   2209   return c_emit_tail_call_unrealizable_reason_for(t, t->cur_fn, d);
   2210 }
   2211 
   2212 const char* c_emit_tail_call_unrealizable_reason_for(
   2213     CTarget* t, const CGFuncDesc* caller_fd, const CGCallDesc* d) {
   2214   SrcLoc loc = caller_fd ? caller_fd->loc : (SrcLoc){0, 0, 0};
   2215   const CgType* fty = cg_type_get(t->c, d->fn_type);
   2216   if (!fty || fty->kind != KIT_CG_TYPE_FUNC) {
   2217     compiler_panic(t->c, loc, "C target: tail call: bad fn_type");
   2218   }
   2219   const CgType* caller =
   2220       caller_fd ? cg_type_get(t->c, caller_fd->fn_type) : NULL;
   2221   if (!caller || caller->kind != KIT_CG_TYPE_FUNC) {
   2222     compiler_panic(t->c, loc, "C target: tail call outside function");
   2223   }
   2224   if (caller->func.abi_variadic) {
   2225     return "C target: caller variadic tail call not yet supported by clang "
   2226            "musttail";
   2227   }
   2228   if (fty->func.abi_variadic) {
   2229     return "C target: variadic tail call not yet supported by clang musttail";
   2230   }
   2231   if (caller->func.nparams != fty->func.nparams) {
   2232     return "C target: tail call with differing parameter counts not yet "
   2233            "supported by clang musttail";
   2234   }
   2235   return NULL;
   2236 }
   2237 
   2238 void c_emit_call(CTarget* t, const CGCallDesc* d) {
   2239   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
   2240 
   2241   const CgType* fty = cg_type_get(t->c, d->fn_type);
   2242   if (!fty || fty->kind != KIT_CG_TYPE_FUNC) {
   2243     compiler_panic(t->c, loc, "C target: call: bad fn_type");
   2244   }
   2245   KitCgTypeId ret_type = cg_func_ret_type(fty);
   2246   int is_tail = (d->flags & CG_CALL_TAIL) != 0;
   2247 
   2248   if (is_tail) {
   2249     cbuf_puts(&t->body, "  __attribute__((musttail)) return ");
   2250     c_emit_call_expr(t, fty, d);
   2251     cbuf_puts(&t->body, ";\n");
   2252     t->last_was_terminator = 1;
   2253   } else if (d->result == CG_LOCAL_NONE) {
   2254     cbuf_puts(&t->body, "  ");
   2255     c_emit_call_expr(t, fty, d);
   2256     cbuf_puts(&t->body, ";\n");
   2257   } else {
   2258     c_ensure_local(t, d->result, ret_type);
   2259     c_emit_local_assign_open(t, d->result, ret_type);
   2260     c_emit_call_expr(t, fty, d);
   2261     c_emit_local_assign_close(t);
   2262   }
   2263 }
   2264 
   2265 /* === load / store === */
   2266 
   2267 void c_emit_load(CTarget* t, Operand dst, Operand addr, MemAccess m) {
   2268   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
   2269   if (dst.kind != OPK_LOCAL) {
   2270     compiler_panic(t->c, loc, "C target: load dst must be LOCAL");
   2271   }
   2272   c_ensure_local(t, dst.v.local, dst.type);
   2273   KitCgTypeId access_ty = m.type ? m.type : dst.type;
   2274   if (c_type_is_aggregate(t, access_ty) && !c_type_is_aggregate(t, dst.type))
   2275     access_ty = dst.type;
   2276   /* The deref `*(access_ty*)addr` produces a value of access_ty. */
   2277   c_emit_local_assign_open(t, dst.v.local, access_ty);
   2278   c_emit_addr_deref(t, addr, access_ty);
   2279   c_emit_local_assign_close(t);
   2280 }
   2281 
   2282 void c_emit_store(CTarget* t, Operand addr, Operand src, MemAccess m) {
   2283   KitCgTypeId access_ty = m.type ? m.type : src.type;
   2284   if (c_type_is_aggregate(t, access_ty) && !c_type_is_aggregate(t, src.type))
   2285     access_ty = src.type;
   2286   cbuf_puts(&t->body, "  ");
   2287   c_emit_addr_deref(t, addr, access_ty);
   2288   /* c_emit_operand_as bridges int/ptr crossings through uintptr_t so
   2289    * roundtrips don't trip `-Wint-conversion`. */
   2290   cbuf_puts(&t->body, " = ");
   2291   c_emit_operand_as(t, src, access_ty);
   2292   cbuf_puts(&t->body, ";\n");
   2293 }
   2294 
   2295 void c_emit_addr_of(CTarget* t, Operand dst, Operand lv) {
   2296   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
   2297   if (dst.kind != OPK_LOCAL) {
   2298     compiler_panic(t->c, loc, "C target: addr_of dst must be LOCAL");
   2299   }
   2300   c_ensure_local(t, dst.v.local, dst.type);
   2301   /* `c_emit_lvalue_addr` casts its output to dst.type already. */
   2302   c_emit_local_assign_open(t, dst.v.local, dst.type);
   2303   c_emit_lvalue_addr(t, lv, dst.type);
   2304   c_emit_local_assign_close(t);
   2305 }
   2306 
   2307 void c_emit_ret(CTarget* t, CGLocal value) {
   2308   /* Already-terminated block: this ret is unreachable (the frontend's
   2309    * defensive `return 0;` epilogue lands here right after a user return). */
   2310   if (t->last_was_terminator) return;
   2311   /* CG emits a defensive void-return epilogue at the end of every function. For
   2312    * a non-void function that's unreachable; emitting a bare `return;` would
   2313    * trip -Wreturn-type. Spell it as `__builtin_unreachable()` so the host C
   2314    * compiler sees the path is dead without us inventing a fake value. A genuine
   2315    * void return (the function's result is the void builtin) must still emit
   2316    * `return;` -- testing against KIT_CG_TYPE_NONE here would misfire, since the
   2317    * cutover represents void as the void builtin, not NONE. */
   2318   if (value == CG_LOCAL_NONE && t->cur_fn) {
   2319     if (!cg_type_is_void(t->c, t->cur_fn->result_type)) {
   2320       cbuf_puts(&t->body, "  __builtin_unreachable();\n");
   2321       t->last_was_terminator = 1;
   2322       return;
   2323     }
   2324   }
   2325   cbuf_puts(&t->body, "  return");
   2326   if (value != CG_LOCAL_NONE) {
   2327     cbuf_puts(&t->body, " ");
   2328     KitCgTypeId ret_type = t->cur_fn ? t->cur_fn->result_type : (KitCgTypeId)0;
   2329     const CgType* rty = ret_type ? cg_type_get(t->c, ret_type) : NULL;
   2330     int is_aggregate = rty && (rty->kind == KIT_CG_TYPE_RECORD ||
   2331                                rty->kind == KIT_CG_TYPE_ARRAY);
   2332     if (ret_type && !is_aggregate) {
   2333       KitCgTypeId value_ty = c_local_type_or_panic(t, value);
   2334       c_emit_operand_as(t, c_op_local(value, value_ty), ret_type);
   2335     } else {
   2336       c_emit_operand(t, c_op_local(value, ret_type));
   2337     }
   2338   }
   2339   cbuf_puts(&t->body, ";\n");
   2340   t->last_was_terminator = 1;
   2341 }
   2342 
   2343 /* === unreachable ===
   2344  * Control terminator for statically-unreachable code (the C
   2345  * __builtin_unreachable point). Ends the basic block; emit the host
   2346  * compiler's `__builtin_unreachable()` so it sees the path is dead. */
   2347 void c_emit_unreachable(CTarget* t) {
   2348   if (t->last_was_terminator) return;
   2349   cbuf_puts(&t->body, "  __builtin_unreachable();\n");
   2350   t->last_was_terminator = 1;
   2351 }
   2352 
   2353 /* === alias ===
   2354  * `kit_cg_alias` makes alias_sym refer to target_sym's body. In obj-file
   2355  * land that's two ObjSyms sharing a (section_id, value); in C source we
   2356  * have to spell it out:
   2357  *
   2358  *   ELF/PE   → `Ret alias(args) __attribute__((alias("target")));`
   2359  *              Single definition, true aliasing, &alias == &target.
   2360  *   Mach-O   → emit a thunk `Ret alias(args) { return target(args); }`.
   2361  *              Clang on Darwin rejects __attribute__((alias)) outright,
   2362  *              so we fall back to a wrapper. Loses the `&alias==&target`
   2363  *              identity but preserves call-through semantics, which is
   2364  *              all the kit-emitted code path needs.
   2365  *
   2366  * The emitted decl serves as the alias definition AND a forward prototype
   2367  * for callers, so we mark sym_forwarded to dedup against a later c_call. */
   2368 void c_emit_alias(CTarget* t, ObjSymId alias_sym, ObjSymId target_sym,
   2369                   KitCgTypeId type) {
   2370   if (c_sym_forwarded_test_and_set(t, alias_sym)) return;
   2371   const char* alias_name = c_sym_name(t, alias_sym);
   2372   const char* target_name = c_sym_name(t, target_sym);
   2373   const CgType* fty = cg_type_get(t->c, type);
   2374   int is_func = fty && fty->kind == KIT_CG_TYPE_FUNC;
   2375 
   2376   const ObjFormatImpl* fmt = obj_format_lookup(t->c->target.obj);
   2377   if (!fmt || !fmt->alias_via_thunk) {
   2378     /* Attribute form. Works for both function and object aliases on ELF
   2379      * and PE/COFF. */
   2380     c_emit_func_signature(t, &t->forwards, alias_name, type);
   2381     cbuf_puts(&t->forwards, " __attribute__((alias(\"");
   2382     cbuf_puts(&t->forwards, target_name);
   2383     cbuf_puts(&t->forwards, "\")));\n");
   2384     return;
   2385   }
   2386 
   2387   /* Mach-O thunk fallback. Functions only for v1 — object aliases on
   2388    * Darwin would need a more elaborate scheme (see doc/CBACKEND.md). */
   2389   if (!is_func) {
   2390     compiler_panic(t->c, (SrcLoc){0, 0, 0},
   2391                    "C target: object alias on Mach-O not yet supported");
   2392   }
   2393   /* Forward prototype for the target (its full definition lands separately
   2394    * via c_func_begin). Also dedup that. */
   2395   c_ensure_forward_decl(t, target_sym, type);
   2396   /* `static`? No — alias must be externally visible. */
   2397   c_emit_func_signature(t, &t->forwards, alias_name, type);
   2398   cbuf_puts(&t->forwards, " { ");
   2399   KitCgTypeId ret_type = cg_type_func_ret_id(t->c, type);
   2400   if (!cg_type_is_void(t->c, ret_type)) cbuf_puts(&t->forwards, "return ");
   2401   cbuf_puts(&t->forwards, target_name);
   2402   cbuf_puts(&t->forwards, "(");
   2403   for (u32 i = 0; i < fty->func.nparams; ++i) {
   2404     if (i > 0) cbuf_puts(&t->forwards, ", ");
   2405     cbuf_puts(&t->forwards, "p");
   2406     cbuf_put_u64(&t->forwards, (u64)i);
   2407   }
   2408   cbuf_puts(&t->forwards, "); }\n");
   2409 }
   2410 
   2411 /* === intrinsic ===
   2412  *
   2413  * All kit IntrinKinds map onto gcc/clang `__builtin_*` builtins, which
   2414  * the host C compiler then turns into the appropriate sequence (inline op,
   2415  * libcall, runtime CAS, etc.). This is exactly the seam the doc described:
   2416  * kit records intent, the downstream toolchain picks the mechanism.
   2417  *
   2418  * Operand shapes follow arch.h §IntrinKind. */
   2419 
   2420 static const char* c_bitop_builtin(IntrinKind k, u32 width) {
   2421   switch (k) {
   2422     case INTRIN_POPCOUNT:
   2423       if (width == 32) return "__builtin_popcount";
   2424       if (width == 64) return "__builtin_popcountll";
   2425       if (width == 16 || width == 8) return "__builtin_popcount";
   2426       return NULL;
   2427     case INTRIN_CTZ:
   2428       if (width == 32) return "__builtin_ctz";
   2429       if (width == 64) return "__builtin_ctzll";
   2430       if (width == 16 || width == 8) return "__builtin_ctz";
   2431       return NULL;
   2432     case INTRIN_CLZ:
   2433       if (width == 32) return "__builtin_clz";
   2434       if (width == 64) return "__builtin_clzll";
   2435       if (width == 16 || width == 8) return "__builtin_clz";
   2436       return NULL;
   2437     case INTRIN_BSWAP:
   2438       if (width == 16) return "__builtin_bswap16";
   2439       if (width == 32) return "__builtin_bswap32";
   2440       if (width == 64) return "__builtin_bswap64";
   2441       return NULL;
   2442     default:
   2443       return NULL;
   2444   }
   2445 }
   2446 
   2447 static const char* c_overflow_builtin(IntrinKind k) {
   2448   switch (k) {
   2449     case INTRIN_SADD_OVERFLOW:
   2450     case INTRIN_UADD_OVERFLOW:
   2451       return "__builtin_add_overflow";
   2452     case INTRIN_SSUB_OVERFLOW:
   2453     case INTRIN_USUB_OVERFLOW:
   2454       return "__builtin_sub_overflow";
   2455     case INTRIN_SMUL_OVERFLOW:
   2456     case INTRIN_UMUL_OVERFLOW:
   2457       return "__builtin_mul_overflow";
   2458     default:
   2459       return NULL;
   2460   }
   2461 }
   2462 
   2463 void c_emit_intrinsic(CTarget* t, IntrinKind k, Operand* dsts, u32 ndst,
   2464                       const Operand* args, u32 narg) {
   2465   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
   2466   switch (k) {
   2467     case INTRIN_TRAP:
   2468       cbuf_puts(&t->body, "  __builtin_trap();\n");
   2469       return;
   2470     case INTRIN_PREFETCH: {
   2471       cbuf_puts(&t->body, "  __builtin_prefetch(");
   2472       for (u32 i = 0; i < narg; ++i) {
   2473         if (i > 0) cbuf_puts(&t->body, ", ");
   2474         c_emit_operand(t, args[i]);
   2475       }
   2476       cbuf_puts(&t->body, ");\n");
   2477       return;
   2478     }
   2479     case INTRIN_ASSUME_ALIGNED: {
   2480       /* dsts[0] is the result local (pointer); args = (ptr, align [, ofs]) */
   2481       if (ndst != 1) {
   2482         compiler_panic(t->c, loc,
   2483                        "C target: assume_aligned: expected 1 dst, got %u",
   2484                        (unsigned)ndst);
   2485       }
   2486       c_ensure_local(t, dsts[0].v.local, dsts[0].type);
   2487       /* Returns void*; bridge to dst pointer type. */
   2488       c_emit_local_assign_open(t, dsts[0].v.local, (KitCgTypeId)0);
   2489       cbuf_puts(&t->body, "__builtin_assume_aligned(");
   2490       for (u32 i = 0; i < narg; ++i) {
   2491         if (i > 0) cbuf_puts(&t->body, ", ");
   2492         c_emit_operand(t, args[i]);
   2493       }
   2494       cbuf_puts(&t->body, ")");
   2495       c_emit_local_assign_close(t);
   2496       return;
   2497     }
   2498     case INTRIN_EXPECT: {
   2499       /* dsts[0] = __builtin_expect(args[0], args[1]) but typed via long. */
   2500       if (ndst != 1 || narg != 2) {
   2501         compiler_panic(t->c, loc,
   2502                        "C target: expect: bad shape (ndst=%u narg=%u)",
   2503                        (unsigned)ndst, (unsigned)narg);
   2504       }
   2505       c_ensure_local(t, dsts[0].v.local, dsts[0].type);
   2506       /* Returns `long`; dst.type may be a narrower int — keep the bridge. */
   2507       c_emit_local_assign_open(t, dsts[0].v.local, (KitCgTypeId)0);
   2508       cbuf_puts(&t->body, "__builtin_expect((long)");
   2509       c_emit_operand(t, args[0]);
   2510       cbuf_puts(&t->body, ", (long)");
   2511       c_emit_operand(t, args[1]);
   2512       cbuf_puts(&t->body, ")");
   2513       c_emit_local_assign_close(t);
   2514       return;
   2515     }
   2516     case INTRIN_POPCOUNT:
   2517     case INTRIN_CTZ:
   2518     case INTRIN_CLZ:
   2519     case INTRIN_BSWAP: {
   2520       if (ndst != 1 || narg != 1) {
   2521         compiler_panic(t->c, loc,
   2522                        "C target: bit-intrin: bad shape (ndst=%u narg=%u)",
   2523                        (unsigned)ndst, (unsigned)narg);
   2524       }
   2525       /* bswap width is determined by the result type (in bytes -> bit-width
   2526        * bucket, matching the old per-width intrinsic split). The other bit
   2527        * ops keep deriving width from the operand. */
   2528       u32 w;
   2529       if (k == INTRIN_BSWAP) {
   2530         u32 bytes = (u32)cg_type_size(t->c, dsts[0].type);
   2531         w = bytes <= 2 ? 16u : (bytes <= 4 ? 32u : 64u);
   2532       } else {
   2533         w = c_int_width_for_signedness(t, args[0].type);
   2534       }
   2535       const char* fn = c_bitop_builtin(k, w);
   2536       if (!fn) {
   2537         compiler_panic(t->c, loc, "C target: bit-intrin width %u unsupported",
   2538                        (unsigned)w);
   2539       }
   2540       c_ensure_local(t, dsts[0].v.local, dsts[0].type);
   2541       /* __builtin_popcount/ctz/clz return `int`; bswap returns its input
   2542        * type. Narrow to dst.type via the bridge. */
   2543       c_emit_local_assign_open(t, dsts[0].v.local, (KitCgTypeId)0);
   2544       cbuf_puts(&t->body, fn);
   2545       cbuf_puts(&t->body, "(");
   2546       c_emit_operand(t, args[0]);
   2547       cbuf_puts(&t->body, ")");
   2548       c_emit_local_assign_close(t);
   2549       return;
   2550     }
   2551     case INTRIN_SMUL_HIGH:
   2552     case INTRIN_UMUL_HIGH: {
   2553       u32 width;
   2554       int is_signed = k == INTRIN_SMUL_HIGH;
   2555       if (ndst != 1 || narg != 2) {
   2556         compiler_panic(t->c, loc,
   2557                        "C target: mul-high: bad shape (ndst=%u narg=%u)",
   2558                        (unsigned)ndst, (unsigned)narg);
   2559       }
   2560       width = (u32)cg_type_size(t->c, dsts[0].type) * 8u;
   2561       c_ensure_local(t, dsts[0].v.local, dsts[0].type);
   2562       c_emit_local_assign_open(t, dsts[0].v.local, (KitCgTypeId)0);
   2563       if (width == 64u) {
   2564         cbuf_puts(&t->body, is_signed ? "((int64_t)(((__int128)(int64_t)("
   2565                                         : "((uint64_t)(((unsigned __int128)(uint64_t)(");
   2566         c_emit_operand(t, args[0]);
   2567         cbuf_puts(&t->body, is_signed ? ") * (__int128)(int64_t)("
   2568                                       : ") * (unsigned __int128)(uint64_t)(");
   2569         c_emit_operand(t, args[1]);
   2570         cbuf_puts(&t->body, ")) >> 64))");
   2571       } else {
   2572         cbuf_puts(&t->body, is_signed ? "((int32_t)(((int64_t)(int32_t)("
   2573                                         : "((uint32_t)(((uint64_t)(uint32_t)(");
   2574         c_emit_operand(t, args[0]);
   2575         cbuf_puts(&t->body, is_signed ? ") * (int64_t)(int32_t)("
   2576                                       : ") * (uint64_t)(uint32_t)(");
   2577         c_emit_operand(t, args[1]);
   2578         cbuf_puts(&t->body, is_signed ? ")) >> 32))" : ")) >> 32))");
   2579       }
   2580       c_emit_local_assign_close(t);
   2581       return;
   2582     }
   2583     case INTRIN_CPU_YIELD:
   2584       /* A portable relax hint may be discarded by the downstream C compiler. */
   2585       cbuf_puts(&t->body, "  (void)0;\n");
   2586       return;
   2587     case INTRIN_MEMMOVE: {
   2588       cbuf_puts(&t->body, "  __builtin_memmove(");
   2589       for (u32 i = 0; i < narg; ++i) {
   2590         if (i > 0) cbuf_puts(&t->body, ", ");
   2591         /* The pointer operands (dst and src) may be typed as a plain integer
   2592          * local when they come from address arithmetic, which the C target
   2593          * declares as int64_t. __builtin_memmove takes void*, so cast
   2594          * explicitly to avoid -Wint-conversion. */
   2595         int is_ptr_arg = (i == 0) || (i == 1);
   2596         if (is_ptr_arg) cbuf_puts(&t->body, "(void*)");
   2597         c_emit_operand(t, args[i]);
   2598       }
   2599       cbuf_puts(&t->body, ");\n");
   2600       return;
   2601     }
   2602     case INTRIN_SADD_OVERFLOW:
   2603     case INTRIN_UADD_OVERFLOW:
   2604     case INTRIN_SSUB_OVERFLOW:
   2605     case INTRIN_USUB_OVERFLOW:
   2606     case INTRIN_SMUL_OVERFLOW:
   2607     case INTRIN_UMUL_OVERFLOW: {
   2608       /* dsts[0] = value local, dsts[1] = i1 overflow flag.
   2609        *
   2610        * Signedness comes from the intrinsic kind, but kit's CG int type
   2611        * is width-only and the C target declares every result as a signed
   2612        * fixed-width (int{8,16,32,64}_t). __builtin_*_overflow keys its
   2613        * overflow check on the result type, so passing the signed local
   2614        * directly makes a UADD test as if it were signed and miss true
   2615        * unsigned overflow. Wrap the call in a block with a scratch result
   2616        * of the right signedness and copy it back through the int/uint
   2617        * bridge. */
   2618       if (ndst != 2 || narg != 2) {
   2619         compiler_panic(t->c, loc, "C target: overflow-intrin: bad shape");
   2620       }
   2621       int is_unsigned =
   2622           (k == INTRIN_UADD_OVERFLOW || k == INTRIN_USUB_OVERFLOW ||
   2623            k == INTRIN_UMUL_OVERFLOW);
   2624       const char* fn = c_overflow_builtin(k);
   2625       c_ensure_local(t, dsts[0].v.local, dsts[0].type);
   2626       c_ensure_local(t, dsts[1].v.local, dsts[1].type);
   2627       char vbuf[24], obuf[24];
   2628       c_local_name(dsts[0].v.local, vbuf, sizeof vbuf);
   2629       c_local_name(dsts[1].v.local, obuf, sizeof obuf);
   2630       u32 w = c_int_width_for_signedness(t, dsts[0].type);
   2631       const char* sty = c_int_type_name_for_width(w, !is_unsigned);
   2632       if (!sty) {
   2633         compiler_panic(t->c, loc,
   2634                        "C target: overflow-intrin: unsupported width %u",
   2635                        (unsigned)w);
   2636       }
   2637       cbuf_puts(&t->body, "  { ");
   2638       cbuf_puts(&t->body, sty);
   2639       cbuf_puts(&t->body, " __ovsc; ");
   2640       cbuf_puts(&t->body, obuf);
   2641       cbuf_puts(&t->body, " = (");
   2642       c_emit_type(t, &t->body, dsts[1].type);
   2643       cbuf_puts(&t->body, ")");
   2644       cbuf_puts(&t->body, fn);
   2645       cbuf_puts(&t->body, "((");
   2646       cbuf_puts(&t->body, sty);
   2647       cbuf_puts(&t->body, ")");
   2648       c_emit_operand(t, args[0]);
   2649       cbuf_puts(&t->body, ", (");
   2650       cbuf_puts(&t->body, sty);
   2651       cbuf_puts(&t->body, ")");
   2652       c_emit_operand(t, args[1]);
   2653       cbuf_puts(&t->body, ", &__ovsc); ");
   2654       cbuf_puts(&t->body, vbuf);
   2655       cbuf_puts(&t->body, " = (");
   2656       c_emit_type(t, &t->body, dsts[0].type);
   2657       cbuf_puts(&t->body, ")__ovsc; }\n");
   2658       return;
   2659     }
   2660     case INTRIN_SETJMP: {
   2661       t->need_setjmp = 1;
   2662       if (ndst != 1 || narg != 1) {
   2663         compiler_panic(t->c, loc, "C target: setjmp: bad shape");
   2664       }
   2665       c_ensure_local(t, dsts[0].v.local, dsts[0].type);
   2666       /* setjmp returns `int`; bridge to dst.type. */
   2667       c_emit_local_assign_open(t, dsts[0].v.local, (KitCgTypeId)0);
   2668       cbuf_puts(&t->body, "setjmp(*(jmp_buf*)(");
   2669       c_emit_operand(t, args[0]);
   2670       cbuf_puts(&t->body, "))");
   2671       c_emit_local_assign_close(t);
   2672       return;
   2673     }
   2674     case INTRIN_LONGJMP: {
   2675       t->need_setjmp = 1;
   2676       cbuf_puts(&t->body, "  longjmp(*(jmp_buf*)(");
   2677       c_emit_operand(t, args[0]);
   2678       cbuf_puts(&t->body, "), (int)");
   2679       c_emit_operand(t, args[1]);
   2680       cbuf_puts(&t->body, ");\n");
   2681       return;
   2682     }
   2683     case INTRIN_FRAME_ADDRESS:
   2684     case INTRIN_RETURN_ADDRESS: {
   2685       /* Forward straight to the host compiler's builtin. dsts[0] is the void*
   2686        * result; args[0] is the constant level. The builtin requires a bare
   2687        * integer constant, so emit the level as a plain decimal (not via
   2688        * c_emit_operand, which wraps IMMs in a cast). */
   2689       char nbuf[24];
   2690       unsigned level =
   2691           (narg >= 1 && args[0].kind == OPK_IMM) ? (unsigned)args[0].v.imm : 0u;
   2692       if (ndst != 1) {
   2693         compiler_panic(t->c, loc,
   2694                        "C target: frame/return address: expected 1 dst, got %u",
   2695                        (unsigned)ndst);
   2696       }
   2697       snprintf(nbuf, sizeof nbuf, "%u", level);
   2698       c_ensure_local(t, dsts[0].v.local, dsts[0].type);
   2699       c_emit_local_assign_open(t, dsts[0].v.local, (KitCgTypeId)0);
   2700       cbuf_puts(&t->body, k == INTRIN_FRAME_ADDRESS
   2701                               ? "__builtin_frame_address("
   2702                               : "__builtin_return_address(");
   2703       cbuf_puts(&t->body, nbuf);
   2704       cbuf_puts(&t->body, ")");
   2705       c_emit_local_assign_close(t);
   2706       return;
   2707     }
   2708     case INTRIN_READCYCLECOUNTER: {
   2709       /* Forward to the host compiler's builtin. dsts[0] is the u64 result. */
   2710       if (ndst != 1) {
   2711         compiler_panic(t->c, loc,
   2712                        "C target: readcyclecounter: expected 1 dst, got %u",
   2713                        (unsigned)ndst);
   2714       }
   2715       c_ensure_local(t, dsts[0].v.local, dsts[0].type);
   2716       c_emit_local_assign_open(t, dsts[0].v.local, (KitCgTypeId)0);
   2717       cbuf_puts(&t->body, "__builtin_readcyclecounter()");
   2718       c_emit_local_assign_close(t);
   2719       return;
   2720     }
   2721     case INTRIN_SYSCALL:
   2722       compiler_panic(t->c, loc, "C target: syscall intrinsic not supported");
   2723       return;
   2724     case INTRIN_NONE:
   2725     default:
   2726       compiler_panic(t->c, loc, "C target: intrinsic kind %d not handled",
   2727                      (int)k);
   2728   }
   2729 }
   2730 
   2731 /* === alloca === */
   2732 
   2733 void c_emit_alloca(CTarget* t, Operand dst, Operand size, u32 align) {
   2734   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
   2735   if (dst.kind != OPK_LOCAL) {
   2736     compiler_panic(t->c, loc, "C target: alloca dst must be LOCAL");
   2737   }
   2738   c_ensure_local(t, dst.v.local, dst.type);
   2739   /* __builtin_alloca returns `void*`; dst.type is typically void* too. */
   2740   c_emit_local_assign_open(t, dst.v.local, dst.type);
   2741   if (align > 1) {
   2742     /* gcc has __builtin_alloca_with_align taking bits, not bytes. */
   2743     cbuf_puts(&t->body, "__builtin_alloca_with_align(");
   2744     c_emit_operand(t, size);
   2745     cbuf_puts(&t->body, ", ");
   2746     cbuf_put_u64(&t->body, (u64)align * 8u);
   2747     cbuf_puts(&t->body, ")");
   2748   } else {
   2749     cbuf_puts(&t->body, "__builtin_alloca(");
   2750     c_emit_operand(t, size);
   2751     cbuf_puts(&t->body, ")");
   2752   }
   2753   c_emit_local_assign_close(t);
   2754 }
   2755 
   2756 /* === varargs ===
   2757  *
   2758  * The C-target va_list is the host toolchain's `va_list` from <stdarg.h>.
   2759  * The first arg of all va_* is `ap_addr` - the address of the va_list local.
   2760  * We deref to get the va_list lvalue C's macros expect. */
   2761 
   2762 void c_emit_va_start(CTarget* t, Operand ap_addr) {
   2763   t->need_stdarg = 1;
   2764   /* va_start needs the "last named parameter". CG doesn't pass that to the
   2765    * backend; gcc/clang accept any non-modified ident here for variadic
   2766    * compatibility — feed the synthesized parameter name `p<nparams-1>` from
   2767    * the enclosing function. */
   2768   const CGFuncDesc* fd = t->cur_fn;
   2769   SrcLoc loc = fd ? fd->loc : (SrcLoc){0, 0, 0};
   2770   if (!fd) compiler_panic(t->c, loc, "C target: va_start outside function");
   2771   const CgType* fty = cg_type_get(t->c, fd->fn_type);
   2772   if (!fty || fty->kind != KIT_CG_TYPE_FUNC || fty->func.nparams == 0) {
   2773     compiler_panic(t->c, loc,
   2774                    "C target: va_start in non-variadic function shape");
   2775   }
   2776   cbuf_puts(&t->body, "  __builtin_va_start(*(va_list*)(");
   2777   c_emit_operand(t, ap_addr);
   2778   cbuf_puts(&t->body, "), p");
   2779   cbuf_put_u64(&t->body, (u64)(fty->func.nparams - 1u));
   2780   cbuf_puts(&t->body, ");\n");
   2781 }
   2782 
   2783 void c_emit_va_end(CTarget* t, Operand ap_addr) {
   2784   cbuf_puts(&t->body, "  __builtin_va_end(*(va_list*)(");
   2785   c_emit_operand(t, ap_addr);
   2786   cbuf_puts(&t->body, "));\n");
   2787 }
   2788 
   2789 void c_emit_va_copy(CTarget* t, Operand dst_addr, Operand src_addr) {
   2790   cbuf_puts(&t->body, "  __builtin_va_copy(*(va_list*)(");
   2791   c_emit_operand(t, dst_addr);
   2792   cbuf_puts(&t->body, "), *(va_list*)(");
   2793   c_emit_operand(t, src_addr);
   2794   cbuf_puts(&t->body, "));\n");
   2795 }
   2796 
   2797 void c_emit_va_arg(CTarget* t, Operand dst, Operand ap_addr, KitCgTypeId ty) {
   2798   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
   2799   if (dst.kind != OPK_LOCAL) {
   2800     compiler_panic(t->c, loc, "C target: va_arg dst must be LOCAL");
   2801   }
   2802   c_ensure_local(t, dst.v.local, dst.type);
   2803   /* __builtin_va_arg yields a value of `ty`. */
   2804   c_emit_local_assign_open(t, dst.v.local, ty);
   2805   cbuf_puts(&t->body, "__builtin_va_arg(*(va_list*)(");
   2806   c_emit_operand(t, ap_addr);
   2807   cbuf_puts(&t->body, "), ");
   2808   c_emit_type(t, &t->body, ty);
   2809   cbuf_puts(&t->body, ")");
   2810   c_emit_local_assign_close(t);
   2811 }
   2812 
   2813 /* === copy_bytes / set_bytes === */
   2814 
   2815 void c_emit_copy_bytes(CTarget* t, Operand dst_addr, Operand src_addr,
   2816                        AggregateAccess m) {
   2817   c_assert_no_index(t, dst_addr, "copy_bytes dst");
   2818   c_assert_no_index(t, src_addr, "copy_bytes src");
   2819   /* dst/src may be plain integer regs from address arithmetic (declared
   2820    * int64_t); __builtin_memcpy takes void*, so cast to avoid
   2821    * -Wint-conversion. */
   2822   cbuf_puts(&t->body, "  __builtin_memcpy((void*)");
   2823   c_emit_copy_addr(t, dst_addr);
   2824   cbuf_puts(&t->body, ", (void*)");
   2825   c_emit_copy_addr(t, src_addr);
   2826   cbuf_puts(&t->body, ", ");
   2827   cbuf_put_u64(&t->body, (u64)m.size);
   2828   cbuf_puts(&t->body, ");\n");
   2829 }
   2830 
   2831 static void c_emit_copy_addr(CTarget* t, Operand addr) {
   2832   char buf[24];
   2833   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
   2834   switch (addr.kind) {
   2835     case OPK_LOCAL:
   2836       c_ensure_local(t, addr.v.local, addr.type);
   2837       if (c_operand_is_ptr_typed(t, addr)) {
   2838         c_emit_operand(t, addr);
   2839       } else {
   2840         cbuf_putc(&t->body, '&');
   2841         c_local_name(addr.v.local, buf, sizeof buf);
   2842         cbuf_puts(&t->body, buf);
   2843       }
   2844       return;
   2845     case OPK_GLOBAL: {
   2846       obj_sym_mark_referenced(t->obj, addr.v.global.sym);
   2847       cbuf_puts(&t->body, "((char*)&");
   2848       cbuf_puts(&t->body, c_sym_name(t, addr.v.global.sym));
   2849       if (addr.v.global.addend != 0) {
   2850         cbuf_puts(&t->body, " + ");
   2851         cbuf_put_i64(&t->body, addr.v.global.addend);
   2852       }
   2853       cbuf_putc(&t->body, ')');
   2854       return;
   2855     }
   2856     case OPK_INDIRECT:
   2857       c_emit_indirect_addr_expr(t, c_addr_mode(addr));
   2858       return;
   2859     default:
   2860       compiler_panic(t->c, loc,
   2861                      "C target: copy_bytes address operand kind %d not "
   2862                      "supported",
   2863                      (int)addr.kind);
   2864   }
   2865 }
   2866 
   2867 void c_emit_set_bytes(CTarget* t, Operand dst_addr, Operand byte_value,
   2868                       AggregateAccess m) {
   2869   c_assert_no_index(t, dst_addr, "set_bytes dst");
   2870   /* dst may be a plain integer local from address arithmetic (declared
   2871    * int64_t); __builtin_memset takes void*, so cast to avoid
   2872    * -Wint-conversion. */
   2873   cbuf_puts(&t->body, "  __builtin_memset((void*)");
   2874   c_emit_copy_addr(t, dst_addr);
   2875   cbuf_puts(&t->body, ", (int)");
   2876   c_emit_operand(t, byte_value);
   2877   cbuf_puts(&t->body, ", ");
   2878   cbuf_put_u64(&t->body, (u64)m.size);
   2879   cbuf_puts(&t->body, ");\n");
   2880 }
   2881 
   2882 /* === TLS ===
   2883  *
   2884  * Thread-local data is emitted as `_Thread_local _Alignas(A) uint8_t name[N];`
   2885  * during c_emit_data, and tls_addr_of spells `((char*)&name + addend)` with
   2886  * the requested pointer type. The host C compiler picks the TLS model. */
   2887 
   2888 void c_emit_tls_addr_of(CTarget* t, Operand dst, ObjSymId sym, i64 addend);
   2889 
   2890 void c_emit_tls_addr_of(CTarget* t, Operand dst, ObjSymId sym, i64 addend) {
   2891   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
   2892   if (dst.kind != OPK_LOCAL) {
   2893     compiler_panic(t->c, loc, "C target: tls_addr_of dst must be LOCAL");
   2894   }
   2895   c_ensure_local(t, dst.v.local, dst.type);
   2896   const char* nm = c_sym_name(t, sym);
   2897   /* RHS spells `(char*)&sym + addend` — pointer type that may not match
   2898    * dst.type; keep the bridge to cast through cleanly. */
   2899   c_emit_local_assign_open(t, dst.v.local, (KitCgTypeId)0);
   2900   cbuf_puts(&t->body, "((char*)&");
   2901   cbuf_puts(&t->body, nm);
   2902   if (addend != 0) {
   2903     cbuf_puts(&t->body, " + ");
   2904     cbuf_put_i64(&t->body, addend);
   2905   }
   2906   cbuf_puts(&t->body, ")");
   2907   c_emit_local_assign_close(t);
   2908 }
   2909 
   2910 /* === bitfields ===
   2911  *
   2912  * kit CG flattens bitfields to (storage_type, byte_offset, bit_offset,
   2913  * bit_width) at the access boundary, so the C target never sees a C-level
   2914  * bitfield declaration. We extract/insert via explicit mask+shift on the
   2915  * underlying storage unit (a fixed-width unsigned int loaded through the
   2916  * usual address-deref path), which sidesteps the C bitfield ABI ambiguity
   2917  * entirely. */
   2918 
   2919 void c_emit_bitfield_load(CTarget* t, Operand dst, Operand addr,
   2920                           BitFieldAccess bf);
   2921 void c_emit_bitfield_store(CTarget* t, Operand addr, Operand src,
   2922                            BitFieldAccess bf);
   2923 
   2924 /* Returns the unsigned C integer type matching the storage-unit byte size. */
   2925 static const char* c_bf_storage_type(u32 size) {
   2926   switch (size) {
   2927     case 1:
   2928       return "uint8_t";
   2929     case 2:
   2930       return "uint16_t";
   2931     case 4:
   2932       return "uint32_t";
   2933     case 8:
   2934       return "uint64_t";
   2935     default:
   2936       return NULL;
   2937   }
   2938 }
   2939 
   2940 /* Spell an address expression for a backend-addressable lvalue operand.
   2941  * Unlike c_emit_operand, this never reads the object value; it materializes
   2942  * the address of the local/global/indirect storage itself. */
   2943 static void c_emit_lvalue_addr_expr_raw(CTarget* t, Operand addr) {
   2944   char buf[24];
   2945   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
   2946   switch (addr.kind) {
   2947     case OPK_LOCAL:
   2948       cbuf_putc(&t->body, '&');
   2949       c_ensure_local(t, addr.v.local, addr.type);
   2950       c_local_name(addr.v.local, buf, sizeof buf);
   2951       cbuf_puts(&t->body, buf);
   2952       return;
   2953     case OPK_GLOBAL: {
   2954       obj_sym_mark_referenced(t->obj, addr.v.global.sym);
   2955       const char* nm = c_sym_name(t, addr.v.global.sym);
   2956       cbuf_puts(&t->body, "((char*)&");
   2957       cbuf_puts(&t->body, nm);
   2958       if (addr.v.global.addend != 0) {
   2959         cbuf_puts(&t->body, " + ");
   2960         cbuf_put_i64(&t->body, addr.v.global.addend);
   2961       }
   2962       cbuf_putc(&t->body, ')');
   2963       return;
   2964     }
   2965     case OPK_INDIRECT: {
   2966       CAddrMode m = c_addr_mode(addr);
   2967       if ((u32)m.base >= t->local_cap || !t->local_declared[m.base]) {
   2968         compiler_panic(t->c, loc,
   2969                        "C target: bitfield on undeclared base local v%u",
   2970                        (unsigned)m.base);
   2971       }
   2972       cbuf_putc(&t->body, '(');
   2973       c_emit_indirect_addr_expr(t, m);
   2974       cbuf_putc(&t->body, ')');
   2975       return;
   2976     }
   2977     default:
   2978       compiler_panic(t->c, loc,
   2979                      "C target: bitfield address on operand kind %d not "
   2980                      "supported",
   2981                      (int)addr.kind);
   2982   }
   2983 }
   2984 
   2985 /* Spell `*(uintN_t*)((char*)addr + bf.storage_offset)` into the body. */
   2986 static void c_bf_storage_lvalue(CTarget* t, Operand addr, BitFieldAccess bf,
   2987                                 const char* storage_ty) {
   2988   cbuf_puts(&t->body, "(*(");
   2989   cbuf_puts(&t->body, storage_ty);
   2990   cbuf_puts(&t->body, "*)((char*)");
   2991   c_emit_lvalue_addr_expr_raw(t, addr);
   2992   if (bf.storage_offset != 0) {
   2993     cbuf_puts(&t->body, " + ");
   2994     cbuf_put_u64(&t->body, (u64)bf.storage_offset);
   2995   }
   2996   cbuf_puts(&t->body, "))");
   2997 }
   2998 
   2999 void c_emit_bitfield_load(CTarget* t, Operand dst, Operand addr,
   3000                           BitFieldAccess bf) {
   3001   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
   3002   if (dst.kind != OPK_LOCAL) {
   3003     compiler_panic(t->c, loc, "C target: bitfield_load dst must be LOCAL");
   3004   }
   3005   c_assert_no_index(t, addr, "bitfield_load");
   3006   if (bf.bit_width == 0) {
   3007     /* Zero-width — layout barrier only; nothing to load. Emit a no-op
   3008      * assignment so the dst local still gets a defined value. */
   3009     c_ensure_local(t, dst.v.local, dst.type);
   3010     /* RHS is the literal 0 (int); narrowing to dst.type is fine. */
   3011     c_emit_local_assign_open(t, dst.v.local, dst.type);
   3012     cbuf_puts(&t->body, "0");
   3013     c_emit_local_assign_close(t);
   3014     return;
   3015   }
   3016   const char* sty = c_bf_storage_type(bf.storage.size);
   3017   if (!sty) {
   3018     compiler_panic(t->c, loc, "C target: bitfield storage size %u unsupported",
   3019                    (unsigned)bf.storage.size);
   3020   }
   3021   c_ensure_local(t, dst.v.local, dst.type);
   3022   /* RHS is the storage-width int from the mask/shift expression; bridge
   3023    * to dst.type so any signedness/width adjustment is explicit. */
   3024   c_emit_local_assign_open(t, dst.v.local, (KitCgTypeId)0);
   3025   /* For signed bitfields, sign-extend via the standard shift-up / arith-shift-
   3026    * down trick on a signed integer of the storage width. For unsigned, mask
   3027    * the extracted bits.
   3028    *
   3029    * Storage is little-endian-bit-indexed on every kit-supported target
   3030    * (LSB-first within a storage unit on x86_64/aarch64/rv64). */
   3031   u32 sw = bf.storage.size * 8u;
   3032   if (bf.signed_) {
   3033     /* (int_storage_t)((storage << shl) >> shr) where:
   3034      *   shl = sw - bit_width - bit_offset
   3035      *   shr = sw - bit_width
   3036      * Then cast to dst type. */
   3037     u32 shl = sw - (u32)bf.bit_width - (u32)bf.bit_offset;
   3038     u32 shr = sw - (u32)bf.bit_width;
   3039     cbuf_puts(&t->body, "(((int");
   3040     cbuf_put_u64(&t->body, (u64)sw);
   3041     cbuf_puts(&t->body, "_t)(");
   3042     c_bf_storage_lvalue(t, addr, bf, sty);
   3043     cbuf_puts(&t->body, " << ");
   3044     cbuf_put_u64(&t->body, (u64)shl);
   3045     cbuf_puts(&t->body, ")) >> ");
   3046     cbuf_put_u64(&t->body, (u64)shr);
   3047     cbuf_puts(&t->body, ")");
   3048   } else {
   3049     /* ((storage >> bit_offset) & ((1u << bit_width) - 1)) */
   3050     u64 mask = (bf.bit_width >= 64) ? ~(u64)0 : (((u64)1 << bf.bit_width) - 1u);
   3051     cbuf_puts(&t->body, "((");
   3052     c_bf_storage_lvalue(t, addr, bf, sty);
   3053     cbuf_puts(&t->body, " >> ");
   3054     cbuf_put_u64(&t->body, (u64)bf.bit_offset);
   3055     cbuf_puts(&t->body, ") & (");
   3056     cbuf_puts(&t->body, sty);
   3057     cbuf_puts(&t->body, ")0x");
   3058     static const char hex[] = "0123456789abcdef";
   3059     int started = 0;
   3060     for (int sh = 60; sh >= 0; sh -= 4) {
   3061       u32 nib = (u32)((mask >> sh) & 0xfu);
   3062       if (nib || started || sh == 0) {
   3063         cbuf_putc(&t->body, hex[nib]);
   3064         started = 1;
   3065       }
   3066     }
   3067     cbuf_puts(&t->body, ")");
   3068   }
   3069   c_emit_local_assign_close(t);
   3070 }
   3071 
   3072 void c_emit_bitfield_store(CTarget* t, Operand addr, Operand src,
   3073                            BitFieldAccess bf) {
   3074   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
   3075   c_assert_no_index(t, addr, "bitfield_store");
   3076   if (bf.bit_width == 0) return; /* zero-width: no-op */
   3077   const char* sty = c_bf_storage_type(bf.storage.size);
   3078   if (!sty) {
   3079     compiler_panic(t->c, loc, "C target: bitfield storage size %u unsupported",
   3080                    (unsigned)bf.storage.size);
   3081   }
   3082   u64 mask = (bf.bit_width >= 64) ? ~(u64)0 : (((u64)1 << bf.bit_width) - 1u);
   3083   /* *(uintN_t*)p = (*(uintN_t*)p & ~(mask << bit_offset)) |
   3084    *               (((uintN_t)src & mask) << bit_offset); */
   3085   cbuf_puts(&t->body, "  ");
   3086   c_bf_storage_lvalue(t, addr, bf, sty);
   3087   cbuf_puts(&t->body, " = (");
   3088   c_bf_storage_lvalue(t, addr, bf, sty);
   3089   cbuf_puts(&t->body, " & ~((");
   3090   cbuf_puts(&t->body, sty);
   3091   cbuf_puts(&t->body, ")0x");
   3092   static const char hex[] = "0123456789abcdef";
   3093   int started = 0;
   3094   for (int sh = 60; sh >= 0; sh -= 4) {
   3095     u32 nib = (u32)((mask >> sh) & 0xfu);
   3096     if (nib || started || sh == 0) {
   3097       cbuf_putc(&t->body, hex[nib]);
   3098       started = 1;
   3099     }
   3100   }
   3101   cbuf_puts(&t->body, " << ");
   3102   cbuf_put_u64(&t->body, (u64)bf.bit_offset);
   3103   cbuf_puts(&t->body, ")) | ((((");
   3104   cbuf_puts(&t->body, sty);
   3105   cbuf_puts(&t->body, ")");
   3106   c_emit_operand(t, src);
   3107   cbuf_puts(&t->body, ") & (");
   3108   cbuf_puts(&t->body, sty);
   3109   cbuf_puts(&t->body, ")0x");
   3110   started = 0;
   3111   for (int sh = 60; sh >= 0; sh -= 4) {
   3112     u32 nib = (u32)((mask >> sh) & 0xfu);
   3113     if (nib || started || sh == 0) {
   3114       cbuf_putc(&t->body, hex[nib]);
   3115       started = 1;
   3116     }
   3117   }
   3118   cbuf_puts(&t->body, ") << ");
   3119   cbuf_put_u64(&t->body, (u64)bf.bit_offset);
   3120   cbuf_puts(&t->body, ");\n");
   3121 }
   3122 
   3123 /* === inline asm ===
   3124  *
   3125  * Re-serialize kit's asm-block IR (template + constraint-bound operands +
   3126  * clobbers) as GCC extended asm. The kit CG already speaks GCC-style
   3127  * constraint strings ("r", "=r", "+m", "[name]constraint", matching "0"...),
   3128  * so we pass the template through and emit the constraint+operand pairs in
   3129  * order. */
   3130 
   3131 void c_emit_asm_block(CTarget* t, const char* tmpl, const AsmConstraint* outs,
   3132                       u32 no, Operand* oo, const AsmConstraint* ins, u32 ni,
   3133                       const Operand* io, const Sym* clobs, u32 nc);
   3134 
   3135 static void c_emit_c_string_literal(CBuf* b, const char* s) {
   3136   cbuf_putc(b, '"');
   3137   for (; *s; ++s) {
   3138     char ch = *s;
   3139     if (ch == '"' || ch == '\\') {
   3140       cbuf_putc(b, '\\');
   3141       cbuf_putc(b, ch);
   3142     } else if (ch == '\n') {
   3143       cbuf_puts(b, "\\n");
   3144     } else if (ch == '\r') {
   3145       cbuf_puts(b, "\\r");
   3146     } else if (ch == '\t') {
   3147       cbuf_puts(b, "\\t");
   3148     } else if ((unsigned char)ch < 0x20 || (unsigned char)ch >= 0x7f) {
   3149       static const char hex[] = "0123456789abcdef";
   3150       cbuf_puts(b, "\\x");
   3151       cbuf_putc(b, hex[((unsigned char)ch >> 4) & 0xfu]);
   3152       cbuf_putc(b, hex[(unsigned char)ch & 0xfu]);
   3153     } else {
   3154       cbuf_putc(b, ch);
   3155     }
   3156   }
   3157   cbuf_putc(b, '"');
   3158 }
   3159 
   3160 /* "__kit_ao<i>" / "__kit_ai<i>": a unique name for the register temporary that
   3161  * carries a hard-register-pinned output/input operand. */
   3162 static void c_asm_reg_temp_name(char* out, size_t cap, int is_out, u32 idx) {
   3163   const char* pfx = is_out ? "__kit_ao" : "__kit_ai";
   3164   size_t i = 0;
   3165   char tmp[16];
   3166   size_t n = 0;
   3167   u32 v = idx;
   3168   while (*pfx && i + 1 < cap) out[i++] = *pfx++;
   3169   if (!v) tmp[n++] = '0';
   3170   while (v) {
   3171     tmp[n++] = (char)('0' + v % 10);
   3172     v /= 10;
   3173   }
   3174   while (n && i + 1 < cap) out[i++] = tmp[--n];
   3175   out[i] = '\0';
   3176 }
   3177 
   3178 /* Emit an asm output operand's lvalue expression (a plain local, or a
   3179  * dereferenced address for OPK_INDIRECT). Usable as both lvalue and rvalue. */
   3180 static void c_emit_asm_out_lvalue(CTarget* t, Operand op) {
   3181   if (op.kind == OPK_LOCAL) {
   3182     char rb[24];
   3183     c_ensure_local(t, op.v.local, op.type);
   3184     c_local_name(op.v.local, rb, sizeof rb);
   3185     cbuf_puts(&t->body, rb);
   3186   } else {
   3187     c_emit_addr_deref(t, op, op.type);
   3188   }
   3189 }
   3190 
   3191 void c_emit_asm_block(CTarget* t, const char* tmpl, const AsmConstraint* outs,
   3192                       u32 no, Operand* oo, const AsmConstraint* ins, u32 ni,
   3193                       const Operand* io, const Sym* clobs, u32 nc) {
   3194   char nm[24];
   3195   for (u32 i = 0; i < no; ++i) c_assert_no_index(t, oo[i], "asm_block out");
   3196   for (u32 i = 0; i < ni; ++i) c_assert_no_index(t, io[i], "asm_block in");
   3197 
   3198   /* GNU local register variables (AsmConstraint.reg): a target backend resolves
   3199    * the pin to a physical register, but the portable C backend has no register
   3200    * names to bind — so re-emit each pinned operand as a faithful
   3201    * `register T v __asm__("reg")` temporary (scoped in a block) and let the
   3202    * host compiler honor the binding. Dormant unless a frontend marks an
   3203    * operand; only the C frontend does, for register variables. */
   3204   int any_pin = 0;
   3205   for (u32 i = 0; i < no; ++i)
   3206     if (outs[i].reg) any_pin = 1;
   3207   for (u32 i = 0; i < ni; ++i)
   3208     if (ins[i].reg) any_pin = 1;
   3209 
   3210   if (any_pin) {
   3211     cbuf_puts(&t->body, "  {\n");
   3212     for (u32 i = 0; i < ni; ++i) {
   3213       if (!ins[i].reg) continue;
   3214       c_asm_reg_temp_name(nm, sizeof nm, 0, i);
   3215       cbuf_puts(&t->body, "    register ");
   3216       c_emit_type(t, &t->body, io[i].type);
   3217       cbuf_puts(&t->body, " ");
   3218       cbuf_puts(&t->body, nm);
   3219       cbuf_puts(&t->body, " __asm__(");
   3220       c_emit_c_string_literal(&t->body, pool_slice(t->c->global, ins[i].reg).s);
   3221       cbuf_puts(&t->body, ") = ");
   3222       c_emit_operand(t, io[i]);
   3223       cbuf_puts(&t->body, ";\n");
   3224     }
   3225     for (u32 i = 0; i < no; ++i) {
   3226       if (!outs[i].reg) continue;
   3227       c_asm_reg_temp_name(nm, sizeof nm, 1, i);
   3228       cbuf_puts(&t->body, "    register ");
   3229       c_emit_type(t, &t->body, oo[i].type);
   3230       cbuf_puts(&t->body, " ");
   3231       cbuf_puts(&t->body, nm);
   3232       cbuf_puts(&t->body, " __asm__(");
   3233       c_emit_c_string_literal(&t->body,
   3234                               pool_slice(t->c->global, outs[i].reg).s);
   3235       cbuf_puts(&t->body, ")");
   3236       if (outs[i].dir == KIT_CG_ASM_INOUT) {
   3237         cbuf_puts(&t->body, " = ");
   3238         c_emit_asm_out_lvalue(t, oo[i]);
   3239       }
   3240       cbuf_puts(&t->body, ";\n");
   3241     }
   3242   }
   3243 
   3244   cbuf_puts(&t->body, any_pin ? "    __asm__ __volatile__ ("
   3245                               : "  __asm__ __volatile__ (");
   3246   c_emit_c_string_literal(&t->body, tmpl ? tmpl : "");
   3247   /* Outputs. */
   3248   cbuf_puts(&t->body, " : ");
   3249   for (u32 i = 0; i < no; ++i) {
   3250     if (i > 0) cbuf_puts(&t->body, ", ");
   3251     if (outs[i].name) {
   3252       cbuf_puts(&t->body, "[");
   3253       cbuf_puts(&t->body, pool_slice(t->c->global, outs[i].name).s);
   3254       cbuf_puts(&t->body, "] ");
   3255     }
   3256     c_emit_c_string_literal(&t->body, outs[i].str ? outs[i].str : "");
   3257     cbuf_puts(&t->body, "(");
   3258     /* Outputs must be an lvalue. OPK_LOCAL is a plain C local; this
   3259      * works directly. OPK_LOCAL / OPK_INDIRECT also produce lvalues. A pinned
   3260      * output names its register temporary instead. */
   3261     if (outs[i].reg) {
   3262       c_asm_reg_temp_name(nm, sizeof nm, 1, i);
   3263       cbuf_puts(&t->body, nm);
   3264     } else {
   3265       c_emit_asm_out_lvalue(t, oo[i]);
   3266     }
   3267     cbuf_puts(&t->body, ")");
   3268   }
   3269   /* Inputs. kit synthesizes a matching `"N"` input for every ASM_INOUT
   3270    * output (so its IR sees a fresh read), but gcc treats `+r` outputs as
   3271    * already serving the read role and rejects a redundant matching input.
   3272    * Drop those synthesized matches when the referenced output is `+`-tied. */
   3273   cbuf_puts(&t->body, " : ");
   3274   int emitted_any = 0;
   3275   for (u32 i = 0; i < ni; ++i) {
   3276     const char* cs = ins[i].str ? ins[i].str : "";
   3277     if (cs[0] >= '0' && cs[0] <= '9') {
   3278       u32 idx = (u32)(cs[0] - '0');
   3279       if (idx < no && outs[idx].str && outs[idx].str[0] == '+') continue;
   3280     }
   3281     if (emitted_any) cbuf_puts(&t->body, ", ");
   3282     emitted_any = 1;
   3283     if (ins[i].name) {
   3284       cbuf_puts(&t->body, "[");
   3285       cbuf_puts(&t->body, pool_slice(t->c->global, ins[i].name).s);
   3286       cbuf_puts(&t->body, "] ");
   3287     }
   3288     c_emit_c_string_literal(&t->body, cs);
   3289     cbuf_puts(&t->body, "(");
   3290     if (ins[i].reg) {
   3291       c_asm_reg_temp_name(nm, sizeof nm, 0, i);
   3292       cbuf_puts(&t->body, nm);
   3293     } else {
   3294       c_emit_operand(t, io[i]);
   3295     }
   3296     cbuf_puts(&t->body, ")");
   3297   }
   3298   /* Clobbers. */
   3299   cbuf_puts(&t->body, " : ");
   3300   for (u32 i = 0; i < nc; ++i) {
   3301     if (i > 0) cbuf_puts(&t->body, ", ");
   3302     c_emit_c_string_literal(&t->body, pool_slice(t->c->global, clobs[i]).s);
   3303   }
   3304   cbuf_puts(&t->body, ");\n");
   3305 
   3306   if (any_pin) {
   3307     for (u32 i = 0; i < no; ++i) {
   3308       if (!outs[i].reg) continue;
   3309       c_asm_reg_temp_name(nm, sizeof nm, 1, i);
   3310       cbuf_puts(&t->body, "    ");
   3311       c_emit_asm_out_lvalue(t, oo[i]);
   3312       cbuf_puts(&t->body, " = ");
   3313       cbuf_puts(&t->body, nm);
   3314       cbuf_puts(&t->body, ";\n");
   3315     }
   3316     cbuf_puts(&t->body, "  }\n");
   3317   }
   3318 }
   3319 
   3320 /* === load_const ===
   3321  *
   3322  * Used by CG for non-integer literal pushes (mainly floats —
   3323  * `kit_cg_push_float`). Bytes are the target's ABI encoding of the value; we
   3324  * copy them into the dst local via a static const byte array and
   3325  * __builtin_memcpy so any host C compiler sees the same bit pattern. */
   3326 
   3327 void c_emit_load_const(CTarget* t, Operand dst, ConstBytes cb);
   3328 
   3329 void c_emit_load_const(CTarget* t, Operand dst, ConstBytes cb) {
   3330   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
   3331   if (dst.kind != OPK_LOCAL) {
   3332     compiler_panic(t->c, loc, "C target: load_const dst must be LOCAL");
   3333   }
   3334   c_ensure_local(t, dst.v.local, dst.type);
   3335   char buf[24];
   3336   c_local_name(dst.v.local, buf, sizeof buf);
   3337   u32 id = ++t->next_tmp;
   3338   cbuf_puts(&t->body, "  { static const uint8_t __k");
   3339   cbuf_put_u64(&t->body, (u64)id);
   3340   cbuf_puts(&t->body, "[");
   3341   cbuf_put_u64(&t->body, (u64)cb.size);
   3342   cbuf_puts(&t->body, "] = {");
   3343   static const char hex[] = "0123456789abcdef";
   3344   for (u32 i = 0; i < cb.size; ++i) {
   3345     if (i > 0) cbuf_putc(&t->body, ',');
   3346     cbuf_puts(&t->body, "0x");
   3347     cbuf_putc(&t->body, hex[(cb.bytes[i] >> 4) & 0xfu]);
   3348     cbuf_putc(&t->body, hex[cb.bytes[i] & 0xfu]);
   3349   }
   3350   cbuf_puts(&t->body, "}; __builtin_memcpy(&");
   3351   cbuf_puts(&t->body, buf);
   3352   cbuf_puts(&t->body, ", __k");
   3353   cbuf_put_u64(&t->body, (u64)id);
   3354   cbuf_puts(&t->body, ", ");
   3355   cbuf_put_u64(&t->body, (u64)cb.size);
   3356   cbuf_puts(&t->body, "); }\n");
   3357 }
   3358 
   3359 /* === atomics ===
   3360  *
   3361  * Lowered to gcc/clang's `__atomic_*` generic builtins. The host compiler
   3362  * picks the inline sequence vs. libcall and applies the requested memory
   3363  * order. kit's KitCgMemOrder enum aligns 1-1 with the `__ATOMIC_*` constants.
   3364  */
   3365 
   3366 static const char* c_memorder_token(KitCgMemOrder o) {
   3367   switch (o) {
   3368     case KIT_CG_MO_RELAXED:
   3369       return "__ATOMIC_RELAXED";
   3370     case KIT_CG_MO_CONSUME:
   3371       return "__ATOMIC_CONSUME";
   3372     case KIT_CG_MO_ACQUIRE:
   3373       return "__ATOMIC_ACQUIRE";
   3374     case KIT_CG_MO_RELEASE:
   3375       return "__ATOMIC_RELEASE";
   3376     case KIT_CG_MO_ACQ_REL:
   3377       return "__ATOMIC_ACQ_REL";
   3378     case KIT_CG_MO_SEQ_CST:
   3379       return "__ATOMIC_SEQ_CST";
   3380   }
   3381   return "__ATOMIC_SEQ_CST";
   3382 }
   3383 
   3384 void c_emit_atomic_load(CTarget* t, Operand dst, Operand addr, MemAccess m,
   3385                         KitCgMemOrder o);
   3386 void c_emit_atomic_store(CTarget* t, Operand addr, Operand src, MemAccess m,
   3387                          KitCgMemOrder o);
   3388 void c_emit_atomic_rmw(CTarget* t, KitCgAtomicOp op, Operand dst, Operand addr,
   3389                        Operand val, MemAccess m, KitCgMemOrder o);
   3390 void c_emit_atomic_cas(CTarget* t, Operand prior, Operand ok, Operand addr,
   3391                        Operand expected, Operand desired, MemAccess m,
   3392                        KitCgMemOrder so, KitCgMemOrder fo);
   3393 void c_emit_fence(CTarget* t, KitCgMemOrder o);
   3394 
   3395 void c_emit_atomic_load(CTarget* t, Operand dst, Operand addr, MemAccess m,
   3396                         KitCgMemOrder o) {
   3397   (void)m;
   3398   c_assert_no_index(t, addr, "atomic_load");
   3399   c_ensure_local(t, dst.v.local, dst.type);
   3400   /* __atomic_load_n returns a value of the pointed-to type (dst.type). */
   3401   c_emit_local_assign_open(t, dst.v.local, dst.type);
   3402   cbuf_puts(&t->body, "__atomic_load_n((");
   3403   c_emit_type(t, &t->body, dst.type);
   3404   cbuf_puts(&t->body, "*)");
   3405   c_emit_operand(t, addr);
   3406   cbuf_puts(&t->body, ", ");
   3407   cbuf_puts(&t->body, c_memorder_token(o));
   3408   cbuf_puts(&t->body, ")");
   3409   c_emit_local_assign_close(t);
   3410 }
   3411 
   3412 void c_emit_atomic_store(CTarget* t, Operand addr, Operand src, MemAccess m,
   3413                          KitCgMemOrder o) {
   3414   (void)m;
   3415   c_assert_no_index(t, addr, "atomic_store");
   3416   cbuf_puts(&t->body, "  __atomic_store_n((");
   3417   c_emit_type(t, &t->body, src.type);
   3418   cbuf_puts(&t->body, "*)");
   3419   c_emit_operand(t, addr);
   3420   cbuf_puts(&t->body, ", ");
   3421   c_emit_operand_as(t, src, src.type);
   3422   cbuf_puts(&t->body, ", ");
   3423   cbuf_puts(&t->body, c_memorder_token(o));
   3424   cbuf_puts(&t->body, ");\n");
   3425 }
   3426 
   3427 static const char* c_atomic_op_builtin(KitCgAtomicOp op) {
   3428   switch (op) {
   3429     case KIT_CG_ATOMIC_XCHG:
   3430       return "__atomic_exchange_n";
   3431     case KIT_CG_ATOMIC_ADD:
   3432       return "__atomic_fetch_add";
   3433     case KIT_CG_ATOMIC_SUB:
   3434       return "__atomic_fetch_sub";
   3435     case KIT_CG_ATOMIC_AND:
   3436       return "__atomic_fetch_and";
   3437     case KIT_CG_ATOMIC_OR:
   3438       return "__atomic_fetch_or";
   3439     case KIT_CG_ATOMIC_XOR:
   3440       return "__atomic_fetch_xor";
   3441     case KIT_CG_ATOMIC_NAND:
   3442       return "__atomic_fetch_nand";
   3443   }
   3444   return NULL;
   3445 }
   3446 
   3447 void c_emit_atomic_rmw(CTarget* t, KitCgAtomicOp op, Operand dst, Operand addr,
   3448                        Operand val, MemAccess m, KitCgMemOrder o) {
   3449   (void)m;
   3450   SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
   3451   c_assert_no_index(t, addr, "atomic_rmw");
   3452   const char* fn = c_atomic_op_builtin(op);
   3453   if (!fn) {
   3454     compiler_panic(t->c, loc, "C target: unknown atomic op %d", (int)op);
   3455   }
   3456   c_ensure_local(t, dst.v.local, dst.type);
   3457   /* __atomic_fetch_* returns the prior value of the pointed-to type. */
   3458   c_emit_local_assign_open(t, dst.v.local, val.type);
   3459   cbuf_puts(&t->body, fn);
   3460   cbuf_puts(&t->body, "((");
   3461   c_emit_type(t, &t->body, val.type);
   3462   cbuf_puts(&t->body, "*)");
   3463   c_emit_operand(t, addr);
   3464   cbuf_puts(&t->body, ", ");
   3465   c_emit_operand_as(t, val, val.type);
   3466   cbuf_puts(&t->body, ", ");
   3467   cbuf_puts(&t->body, c_memorder_token(o));
   3468   cbuf_puts(&t->body, ")");
   3469   c_emit_local_assign_close(t);
   3470 }
   3471 
   3472 void c_emit_atomic_cas(CTarget* t, Operand prior, Operand ok, Operand addr,
   3473                        Operand expected, Operand desired, MemAccess m,
   3474                        KitCgMemOrder so, KitCgMemOrder fo) {
   3475   (void)m;
   3476   c_assert_no_index(t, addr, "atomic_cas");
   3477   /* gcc's __atomic_compare_exchange_n needs a real lvalue holding the
   3478    * expected value because it is updated on failure. Materialize a scratch
   3479    * local with the compare type, then copy it out to the prior result. */
   3480   c_ensure_local(t, prior.v.local, prior.type);
   3481   c_ensure_local(t, ok.v.local, ok.type);
   3482   u32 id = ++t->next_tmp;
   3483   cbuf_puts(&t->body, "  { ");
   3484   c_emit_type(t, &t->body, prior.type);
   3485   cbuf_puts(&t->body, " __cas");
   3486   cbuf_put_u64(&t->body, (u64)id);
   3487   cbuf_puts(&t->body, " = ");
   3488   c_emit_operand_as(t, expected, prior.type);
   3489   cbuf_puts(&t->body, "; ");
   3490   char ok_name[24], prior_name[24];
   3491   c_local_name(ok.v.local, ok_name, sizeof ok_name);
   3492   c_local_name(prior.v.local, prior_name, sizeof prior_name);
   3493   cbuf_puts(&t->body, ok_name);
   3494   cbuf_puts(&t->body, " = (");
   3495   c_emit_type(t, &t->body, ok.type);
   3496   cbuf_puts(&t->body, ")__atomic_compare_exchange_n((");
   3497   c_emit_type(t, &t->body, prior.type);
   3498   cbuf_puts(&t->body, "*)");
   3499   c_emit_operand(t, addr);
   3500   cbuf_puts(&t->body, ", &__cas");
   3501   cbuf_put_u64(&t->body, (u64)id);
   3502   cbuf_puts(&t->body, ", ");
   3503   c_emit_operand_as(t, desired, prior.type);
   3504   cbuf_puts(&t->body, ", 0, ");
   3505   cbuf_puts(&t->body, c_memorder_token(so));
   3506   cbuf_puts(&t->body, ", ");
   3507   cbuf_puts(&t->body, c_memorder_token(fo));
   3508   cbuf_puts(&t->body, "); ");
   3509   /* prior local = __cas; */
   3510   cbuf_puts(&t->body, prior_name);
   3511   cbuf_puts(&t->body, " = __cas");
   3512   cbuf_put_u64(&t->body, (u64)id);
   3513   cbuf_puts(&t->body, "; }\n");
   3514 }
   3515 
   3516 void c_emit_fence(CTarget* t, KitCgMemOrder o) {
   3517   cbuf_puts(&t->body, "  __atomic_thread_fence(");
   3518   cbuf_puts(&t->body, c_memorder_token(o));
   3519   cbuf_puts(&t->body, ");\n");
   3520 }
   3521 
   3522 /* === set_loc === */
   3523 
   3524 static void cbuf_put_line_filename(CBuf* b, KitSlice s) {
   3525   size_t i;
   3526   cbuf_putc(b, '"');
   3527   for (i = 0; i < s.len; ++i) {
   3528     {
   3529       unsigned char ch = (unsigned char)s.s[i];
   3530       switch (ch) {
   3531         case '\\':
   3532         case '"':
   3533           cbuf_putc(b, '\\');
   3534           cbuf_putc(b, (char)ch);
   3535           break;
   3536         case '\n':
   3537           cbuf_puts(b, "\\n");
   3538           break;
   3539         case '\r':
   3540           cbuf_puts(b, "\\r");
   3541           break;
   3542         case '\t':
   3543           cbuf_puts(b, "\\t");
   3544           break;
   3545         default:
   3546           cbuf_putc(b, (char)ch);
   3547           break;
   3548       }
   3549     }
   3550   }
   3551   cbuf_putc(b, '"');
   3552 }
   3553 
   3554 void c_emit_set_loc(CTarget* t, SrcLoc l) {
   3555   KitSlice file;
   3556 
   3557   if (!t->cur_fn || l.file_id == 0 || l.line == 0) return;
   3558   if (t->have_emitted_loc && t->emitted_loc.file_id == l.file_id &&
   3559       t->emitted_loc.line == l.line) {
   3560     return;
   3561   }
   3562 
   3563   file = kit_compiler_file_name(t->c, l.file_id);
   3564   if (!file.len) return;
   3565 
   3566   cbuf_puts(&t->body, "#line ");
   3567   cbuf_put_u64(&t->body, (u64)l.line);
   3568   cbuf_putc(&t->body, ' ');
   3569   cbuf_put_line_filename(&t->body, file);
   3570   cbuf_putc(&t->body, '\n');
   3571 
   3572   t->emitted_loc = l;
   3573   t->have_emitted_loc = 1;
   3574 }
   3575 
   3576 /* === data emission ===
   3577  *
   3578  * Walks the ObjBuilder's symbol table at finalize and emits a C declaration
   3579  * for every data object — defined or extern. Bytes are emitted verbatim as a
   3580  * `uint8_t name[N] = { 0x.., ... }` initializer. Relocations targeting bytes
   3581  * inside a defined symbol are spelled as runtime fixups in a constructor; this
   3582  * covers both same-TU and cross-TU references uniformly and avoids the C
   3583  * static-initializer restrictions on non-constant addresses.
   3584  *
   3585  * The host C compiler re-applies the Mach-O leading-underscore on link, so the
   3586  * C source uses the kit linker name minus the `_` prefix (matching
   3587  * c_sym_name elsewhere in this file). */
   3588 
   3589 static int c_is_data_section(const Section* sec) {
   3590   if (!sec) return 0;
   3591   switch (sec->kind) {
   3592     case SEC_DATA:
   3593     case SEC_RODATA:
   3594     case SEC_BSS:
   3595       return 1;
   3596     case SEC_OTHER:
   3597       /* User-named sections holding allocated data (e.g. `.text.hot` would
   3598        * be EXEC, but a custom data section is just SF_ALLOC). */
   3599       return (sec->flags & SF_ALLOC) && !(sec->flags & SF_EXEC);
   3600     default:
   3601       return 0;
   3602   }
   3603 }
   3604 
   3605 static void c_emit_link_attrs(CBuf* b, const ObjSym* os) {
   3606   if (os->bind == SB_WEAK) cbuf_puts(b, "__attribute__((weak)) ");
   3607   if (os->vis == SV_HIDDEN) {
   3608     cbuf_puts(b, "__attribute__((visibility(\"hidden\"))) ");
   3609   } else if (os->vis == SV_PROTECTED) {
   3610     cbuf_puts(b, "__attribute__((visibility(\"protected\"))) ");
   3611   }
   3612 }
   3613 
   3614 /* Reads `len` bytes starting at `ofs` from the section's byte buffer. The
   3615  * Section uses a chunked Buf; buf_read does the splice for us. */
   3616 static void c_read_section_bytes(const Section* sec, u32 ofs, u8* out,
   3617                                  size_t len) {
   3618   buf_read(&sec->bytes, ofs, out, len);
   3619 }
   3620 
   3621 static void c_emit_data_bytes(CBuf* b, const u8* bytes, size_t n) {
   3622   cbuf_puts(b, " = {");
   3623   for (size_t i = 0; i < n; ++i) {
   3624     if (i > 0) cbuf_putc(b, ',');
   3625     if ((i & 15u) == 0) cbuf_puts(b, "\n    ");
   3626     cbuf_puts(b, "0x");
   3627     static const char hex[] = "0123456789abcdef";
   3628     cbuf_putc(b, hex[(bytes[i] >> 4) & 0xfu]);
   3629     cbuf_putc(b, hex[bytes[i] & 0xfu]);
   3630   }
   3631   cbuf_puts(b, "\n  }");
   3632 }
   3633 
   3634 /* Mach-O TLS support: the user-visible SK_TLS symbol is a 24-byte TLV
   3635  * descriptor in __DATA,__thread_vars, and the actual initial bytes live in
   3636  * a synthesized `<name>$tlv$init` sym in __thread_data (or __thread_bss).
   3637  * The descriptor carries an R_ABS64 reloc at offset +16 pointing at that
   3638  * init sym. For C-source emission we don't care about the descriptor at all
   3639  * — we just emit `_Thread_local` with the init sym's bytes and let the host
   3640  * C compiler synthesize whatever TLV plumbing it needs. */
   3641 
   3642 /* Find the data init sym referenced by a Mach-O TLS descriptor at
   3643  * `desc_base` in section `desc_sec`. Looks for an R_ABS64 reloc at
   3644  * `desc_base + 16`. Returns OBJ_SYM_NONE if not found. */
   3645 static ObjSymId c_macho_tls_find_init(CTarget* t, ObjSecId desc_sec,
   3646                                       u32 desc_base) {
   3647   u32 total = obj_reloc_total(t->obj);
   3648   for (u32 i = 0; i < total; ++i) {
   3649     const Reloc* r = obj_reloc_at(t->obj, i);
   3650     if (r->section_id != desc_sec) continue;
   3651     if (r->offset != desc_base + 16u) continue;
   3652     return r->sym;
   3653   }
   3654   return OBJ_SYM_NONE;
   3655 }
   3656 
   3657 /* Returns 1 if the section is __DATA,__thread_vars (the descriptor section
   3658  * on Mach-O). Compared by interned Sym id. */
   3659 static int c_sec_name_is_macho_tvars(CTarget* t, const Section* sec) {
   3660   if (!sec) return 0;
   3661   Sym tvars =
   3662       pool_intern_slice(t->c->global, SLICE_LIT("__DATA,__thread_vars"));
   3663   return sec->name == tvars;
   3664 }
   3665 
   3666 /* Returns 1 if any relocation falls into the half-open range [base, base+size)
   3667  * of section `sec_id` (i.e. patches the bytes of this symbol). */
   3668 static int c_sym_has_relocs(CTarget* t, ObjSecId sec_id, u32 base, u32 size) {
   3669   u32 total = obj_reloc_total(t->obj);
   3670   for (u32 i = 0; i < total; ++i) {
   3671     const Reloc* r = obj_reloc_at(t->obj, i);
   3672     if (r->section_id != sec_id) continue;
   3673     if (r->offset >= base && r->offset < base + size) return 1;
   3674   }
   3675   return 0;
   3676 }
   3677 
   3678 /* Emit one data symbol: extern declaration if undef, otherwise the full
   3679  * definition with bytes. Function symbols are skipped — those go through the
   3680  * forwards path. */
   3681 static void c_emit_data_symbol(CTarget* t, ObjSymId id, const ObjSym* os) {
   3682   if (c_is_local_static_sym(t, id)) return;
   3683   if (os->kind == SK_FUNC || os->kind == SK_IFUNC) return;
   3684   if (os->kind == SK_SECTION || os->kind == SK_FILE) return;
   3685   /* On descriptor-model TLS targets (Mach-O), obj_tls.c synthesizes
   3686    * `__tlv_bootstrap` as an SK_UNDEF extern for the TLV descriptor's first
   3687    * field. The C target delegates all TLS lowering to the host compiler via
   3688    * `_Thread_local`, so this descriptor-time-only symbol has no place in the
   3689    * emitted source. */
   3690   if (os->kind == SK_UNDEF && obj_format_tls_via_descriptor(t->c)) {
   3691     const ObjBuilder* ob = t->obj;
   3692     if (id == obj_tlv_bootstrap_get(ob)) return;
   3693   }
   3694   const char* nm = c_sym_name(t, id);
   3695   CBuf* b = &t->data_defs;
   3696   /* SK_TLS user-visible syms need a _Thread_local prefix. On ELF the sym
   3697    * lives in .tdata/.tbss with the right bytes/size and our normal data
   3698    * path handles them once we set the qualifier. On Mach-O the user-visible
   3699    * sym is the 24-byte TLV descriptor — its bytes are not the user's data,
   3700    * so we can't faithfully reproduce it in C; bail to a SKIP. */
   3701   int is_tls = (os->kind == SK_TLS);
   3702 
   3703   /* Extern (undefined) data — only declare if referenced. We can't readily
   3704    * distinguish "referenced as data" from "referenced as func address" here,
   3705    * so declare it as `extern uint8_t name[];` only if it was actually
   3706    * referenced from somewhere; otherwise it'd produce unused warnings. The
   3707    * obj symbol's `referenced` bit is exactly the right signal. */
   3708   /* SK_TLS with no defining section = extern TLS — falls through to the
   3709    * undef branch below with the `_Thread_local` qualifier. */
   3710   /* Extern: SK_UNDEF, or any other defined-kind sym that the producer
   3711    * marked as having no defining section (the C frontend uses SK_OBJ +
   3712    * section=NONE for `extern T x __attribute__((weak));`). */
   3713   int is_extern = (os->kind == SK_UNDEF) ||
   3714                   (os->kind != SK_COMMON && os->section_id == OBJ_SEC_NONE);
   3715   if (is_extern) {
   3716     /* Always declare extern data syms in C source: the host cc tolerates
   3717      * unused externs, and the ObjSym::referenced bit isn't reliably set on
   3718      * syms the C target only addresses by writing the name into the source
   3719      * (no relocation gets emitted against them).
   3720      *
   3721      * Weak externs need different attributes per object format: on Mach-O
   3722      * the `weak` attribute requires a definition; the right spelling for an
   3723      * undefined weak ref is `__attribute__((weak_import))`. On ELF/PE the
   3724      * existing `weak` attribute works as expected. */
   3725     if (os->bind == SB_WEAK) {
   3726       const ObjFormatImpl* fmt = obj_format_lookup(t->c->target.obj);
   3727       const char* weak_attr =
   3728           (fmt && fmt->weak_undef_attr) ? fmt->weak_undef_attr : "weak";
   3729       cbuf_puts(b, "__attribute__((");
   3730       cbuf_puts(b, weak_attr);
   3731       cbuf_puts(b, ")) ");
   3732     }
   3733     if (os->vis == SV_HIDDEN) {
   3734       cbuf_puts(b, "__attribute__((visibility(\"hidden\"))) ");
   3735     } else if (os->vis == SV_PROTECTED) {
   3736       cbuf_puts(b, "__attribute__((visibility(\"protected\"))) ");
   3737     }
   3738     cbuf_puts(b, "extern ");
   3739     if (is_tls) cbuf_puts(b, "_Thread_local ");
   3740     cbuf_puts(b, "uint8_t ");
   3741     cbuf_puts(b, nm);
   3742     cbuf_puts(b, "[];\n");
   3743     return;
   3744   }
   3745   if (is_tls && obj_format_tls_via_descriptor(t->c)) {
   3746     /* Mach-O splits TLS across two object-file symbols (see obj_tls.c): the
   3747      * user-visible sym is a 24-byte TLV descriptor in
   3748      * __DATA,__thread_vars; the actual initial bytes live in a synthesized
   3749      * `<name>$tlv$init` sym in __DATA,__thread_data (or __thread_bss). For
   3750      * C source emission we don't need either of those — `_Thread_local`
   3751      * delegates to the host C compiler, which builds its own descriptor.
   3752      *
   3753      * We use the descriptor sym as the carrier (its name is what user code
   3754      * references) and pull the initial bytes/size/alignment from the init
   3755      * sym, found via the R_ABS64 reloc at descriptor offset +16. The init
   3756      * sym is skipped in its own iteration. */
   3757     const Section* desc_sec = obj_section_get(t->obj, os->section_id);
   3758     if (c_sec_name_is_macho_tvars(t, desc_sec)) {
   3759       ObjSymId init_id =
   3760           c_macho_tls_find_init(t, os->section_id, (u32)os->value);
   3761       if (init_id == OBJ_SYM_NONE) {
   3762         compiler_panic(t->c, (SrcLoc){0, 0, 0},
   3763                        "C target: Mach-O TLS descriptor missing init reloc");
   3764       }
   3765       const ObjSym* init_os = obj_symbol_get(t->obj, init_id);
   3766       if (!init_os || init_os->section_id == OBJ_SEC_NONE) {
   3767         compiler_panic(t->c, (SrcLoc){0, 0, 0},
   3768                        "C target: Mach-O TLS init sym not defined");
   3769       }
   3770       const Section* init_sec = obj_section_get(t->obj, init_os->section_id);
   3771       u32 init_base = (u32)init_os->value;
   3772       u32 init_size = (u32)init_os->size;
   3773       /* TLS data with relocations would need the constructor-fixup path
   3774        * (and we'd have to rewrite the reloc target's section/offset to
   3775        * the descriptor's name in the emitted C). No test currently
   3776        * exercises this; surface it as a clear panic-as-skip if we hit it. */
   3777       if (c_sym_has_relocs(t, init_os->section_id, init_base, init_size)) {
   3778         compiler_panic(t->c, (SrcLoc){0, 0, 0},
   3779                        "C target: Mach-O TLS with pointer init not yet "
   3780                        "supported");
   3781       }
   3782       if (os->bind == SB_LOCAL) cbuf_puts(b, "static ");
   3783       cbuf_puts(b, "_Thread_local ");
   3784       c_emit_link_attrs(b, os);
   3785       cbuf_puts(b, "__attribute__((unused)) ");
   3786       cbuf_puts(b, "_Alignas(");
   3787       cbuf_put_u64(b, init_sec->align ? init_sec->align : 1);
   3788       cbuf_puts(b, ") uint8_t ");
   3789       cbuf_puts(b, nm);
   3790       cbuf_puts(b, "[");
   3791       cbuf_put_u64(b, init_size ? init_size : 1);
   3792       cbuf_puts(b, "]");
   3793       if (init_sec->kind == SEC_BSS || init_sec->sem == SSEM_NOBITS ||
   3794           init_size == 0) {
   3795         cbuf_puts(b, ";\n");
   3796       } else {
   3797         Heap* h = t->c->ctx->heap;
   3798         u8* bytes = (u8*)h->alloc(h, init_size, 1);
   3799         if (!bytes) {
   3800           compiler_panic(t->c, (SrcLoc){0, 0, 0},
   3801                          "C target: oom on TLS init bytes");
   3802         }
   3803         c_read_section_bytes(init_sec, init_base, bytes, init_size);
   3804         c_emit_data_bytes(b, bytes, init_size);
   3805         h->free(h, bytes, init_size);
   3806         cbuf_puts(b, ";\n");
   3807       }
   3808       return;
   3809     }
   3810     /* Not the descriptor: this is the synthesized `<name>$tlv$init` data
   3811      * sym (or a __thread_ptrs entry). The descriptor case above already
   3812      * emitted the user-facing _Thread_local; nothing more to do. */
   3813     return;
   3814   }
   3815   if (os->kind == SK_COMMON) {
   3816     /* Common — uninitialized, with explicit alignment. Emit as
   3817      * tentative-definition (`uint8_t name[size];` at file scope), which C
   3818      * treats as a common-style definition under -fcommon. */
   3819     cbuf_puts(b, "__attribute__((unused)) _Alignas(");
   3820     cbuf_put_u64(b, os->common_align ? os->common_align : 1);
   3821     cbuf_puts(b, ") uint8_t ");
   3822     cbuf_puts(b, nm);
   3823     cbuf_puts(b, "[");
   3824     cbuf_put_u64(b, os->size);
   3825     cbuf_puts(b, "];\n");
   3826     return;
   3827   }
   3828   if (os->section_id == OBJ_SEC_NONE) return;
   3829   const Section* sec = obj_section_get(t->obj, os->section_id);
   3830   if (!c_is_data_section(sec)) return;
   3831   u32 base = (u32)os->value;
   3832   u32 size = (u32)os->size;
   3833   u32 nrelocs = 0;
   3834   u32 total_relocs = obj_reloc_total(t->obj);
   3835   for (u32 i = 0; i < total_relocs; ++i) {
   3836     const Reloc* r = obj_reloc_at(t->obj, i);
   3837     if (r->section_id == os->section_id && r->offset >= base &&
   3838         r->offset < base + size) {
   3839       nrelocs++;
   3840     }
   3841   }
   3842 
   3843   Heap* h = t->c->ctx->heap;
   3844   const Reloc** rs = NULL;
   3845   if (nrelocs) {
   3846     rs = (const Reloc**)h->alloc(h, nrelocs * sizeof(const Reloc*), 1);
   3847     u32 j = 0;
   3848     for (u32 i = 0; i < total_relocs; ++i) {
   3849       const Reloc* r = obj_reloc_at(t->obj, i);
   3850       if (r->section_id == os->section_id && r->offset >= base &&
   3851           r->offset < base + size) {
   3852         rs[j++] = r;
   3853       }
   3854     }
   3855     for (u32 i = 1; i < nrelocs; ++i) {
   3856       const Reloc* tmp = rs[i];
   3857       u32 k = i;
   3858       while (k > 0 && rs[k - 1]->offset > tmp->offset) {
   3859         rs[k] = rs[k - 1];
   3860         k--;
   3861       }
   3862       rs[k] = tmp;
   3863     }
   3864     /* The C backend can only render an absolute address (&sym as a pointer
   3865      * value) into a data slot. A PC-relative or section-difference reloc is
   3866      * not a C constant expression, and a sub-pointer-width address cannot be
   3867      * truncated into its slot in portable C — reject those rather than
   3868      * silently mis-size the slot (the width logic below assumes ABS32/ABS64).
   3869      */
   3870     for (u32 i = 0; i < nrelocs; ++i) {
   3871       if (rs[i]->kind != R_ABS32 && rs[i]->kind != R_ABS64)
   3872         compiler_panic(t->c, (SrcLoc){0, 0, 0},
   3873                        "C target: unsupported non-absolute data relocation "
   3874                        "(kind %u) targeting '%s'; the C backend emits only "
   3875                        "absolute (R_ABS32/R_ABS64) data relocations",
   3876                        (unsigned)rs[i]->kind, c_sym_name(t, rs[i]->sym));
   3877     }
   3878   }
   3879 
   3880   cbuf_puts(b, "struct ");
   3881   if (nrelocs > 0) cbuf_puts(b, "__attribute__((packed)) ");
   3882   cbuf_puts(b, "__kit_data_");
   3883   cbuf_puts(b, nm);
   3884   cbuf_puts(b, " {\n");
   3885 
   3886   if (nrelocs == 0) {
   3887     cbuf_puts(b, "  uint8_t raw[");
   3888     cbuf_put_u64(b, size ? size : 1);
   3889     cbuf_puts(b, "];\n");
   3890   } else {
   3891     u32 cur = base;
   3892     for (u32 i = 0; i < nrelocs; ++i) {
   3893       const Reloc* r = rs[i];
   3894       if (r->offset > cur) {
   3895         cbuf_puts(b, "  uint8_t chunk_");
   3896         cbuf_put_u64(b, i);
   3897         cbuf_puts(b, "[");
   3898         cbuf_put_u64(b, r->offset - cur);
   3899         cbuf_puts(b, "];\n");
   3900       }
   3901       u32 width = (r->kind == R_ABS32) ? 4 : 8;
   3902       const char* ty = (width == 4) ? "uint32_t" : "void*";
   3903       cbuf_puts(b, "  ");
   3904       cbuf_puts(b, ty);
   3905       cbuf_puts(b, " ptr_");
   3906       cbuf_put_u64(b, i);
   3907       cbuf_puts(b, ";\n");
   3908       cur = r->offset + width;
   3909     }
   3910     if (cur < base + size) {
   3911       cbuf_puts(b, "  uint8_t chunk_");
   3912       cbuf_put_u64(b, nrelocs);
   3913       cbuf_puts(b, "[");
   3914       cbuf_put_u64(b, base + size - cur);
   3915       cbuf_puts(b, "];\n");
   3916     }
   3917   }
   3918   cbuf_puts(b, "};\n");
   3919 
   3920   if (os->bind == SB_LOCAL) cbuf_puts(b, "static ");
   3921   if (is_tls) cbuf_puts(b, "_Thread_local ");
   3922   c_emit_link_attrs(b, os);
   3923   cbuf_puts(b, "__attribute__((unused)) ");
   3924 
   3925   int is_ro = (sec->kind == SEC_RODATA);
   3926   if (is_ro) cbuf_puts(b, "const ");
   3927 
   3928   cbuf_puts(b, "_Alignas(");
   3929   cbuf_put_u64(b, sec->align ? sec->align : 1);
   3930   cbuf_puts(b, ") struct __kit_data_");
   3931   cbuf_puts(b, nm);
   3932   cbuf_puts(b, " ");
   3933   cbuf_puts(b, nm);
   3934 
   3935   if (sec->kind == SEC_BSS || sec->sem == SSEM_NOBITS) {
   3936     cbuf_puts(b, ";\n");
   3937   } else if (size == 0) {
   3938     cbuf_puts(b, " = {{0}};\n");
   3939   } else {
   3940     cbuf_puts(b, " = {\n");
   3941     u8* bytes = (u8*)h->alloc(h, size, 1);
   3942     c_read_section_bytes(sec, base, bytes, size);
   3943 
   3944     if (nrelocs == 0) {
   3945       cbuf_puts(b, "  .raw = {");
   3946       for (u32 i = 0; i < size; ++i) {
   3947         if (i > 0) cbuf_puts(b, ", ");
   3948         cbuf_put_u64(b, bytes[i]);
   3949       }
   3950       cbuf_puts(b, "}\n");
   3951     } else {
   3952       u32 cur = base;
   3953       for (u32 i = 0; i < nrelocs; ++i) {
   3954         const Reloc* r = rs[i];
   3955         if (r->offset > cur) {
   3956           cbuf_puts(b, "  .chunk_");
   3957           cbuf_put_u64(b, i);
   3958           cbuf_puts(b, " = {");
   3959           for (u32 k = 0; k < r->offset - cur; ++k) {
   3960             if (k > 0) cbuf_puts(b, ", ");
   3961             cbuf_put_u64(b, bytes[cur - base + k]);
   3962           }
   3963           cbuf_puts(b, "},\n");
   3964         }
   3965 
   3966         u32 width = (r->kind == R_ABS32) ? 4 : 8;
   3967         c_ensure_forward_decl(t, r->sym, 0);
   3968         const char* tgt = c_sym_name(t, r->sym);
   3969         const char* cast = (width == 4) ? "(uint32_t)(uintptr_t)" : "(void*)";
   3970 
   3971         cbuf_puts(b, "  .ptr_");
   3972         cbuf_put_u64(b, i);
   3973         cbuf_puts(b, " = ");
   3974         cbuf_puts(b, cast);
   3975         cbuf_puts(b, "((char*)&");
   3976         cbuf_puts(b, tgt);
   3977         if (r->addend != 0) {
   3978           cbuf_puts(b, " + ");
   3979           cbuf_put_i64(b, r->addend);
   3980         }
   3981         cbuf_puts(b, "),\n");
   3982         cur = r->offset + width;
   3983       }
   3984       if (cur < base + size) {
   3985         cbuf_puts(b, "  .chunk_");
   3986         cbuf_put_u64(b, nrelocs);
   3987         cbuf_puts(b, " = {");
   3988         for (u32 k = 0; k < base + size - cur; ++k) {
   3989           if (k > 0) cbuf_puts(b, ", ");
   3990           cbuf_put_u64(b, bytes[cur - base + k]);
   3991         }
   3992         cbuf_puts(b, "}\n");
   3993       }
   3994     }
   3995     h->free(h, bytes, size);
   3996     cbuf_puts(b, "};\n");
   3997   }
   3998 
   3999   if (nrelocs) h->free(h, (void*)rs, nrelocs * sizeof(const Reloc*));
   4000 }
   4001 
   4002 /* Re-emit a file-scope `__asm__("...")` block at TU scope. The CG layer hands
   4003  * us the de-escaped assembly text (real newlines); re-quote it as a single C
   4004  * string literal so the host C compiler assembles it. Lands in data_defs, which
   4005  * finalize flushes at file scope before any function body. */
   4006 void c_emit_file_scope_asm(CTarget* t, const char* src, size_t len) {
   4007   CBuf* b = &t->data_defs;
   4008   cbuf_puts(b, "__asm__(\"");
   4009   for (size_t i = 0; i < len; ++i) {
   4010     char ch = src[i];
   4011     switch (ch) {
   4012       case '\\':
   4013         cbuf_puts(b, "\\\\");
   4014         break;
   4015       case '"':
   4016         cbuf_puts(b, "\\\"");
   4017         break;
   4018       case '\n':
   4019         cbuf_puts(b, "\\n");
   4020         break;
   4021       case '\t':
   4022         cbuf_puts(b, "\\t");
   4023         break;
   4024       case '\r':
   4025         cbuf_puts(b, "\\r");
   4026         break;
   4027       default:
   4028         cbuf_putc(b, ch);
   4029         break;
   4030     }
   4031   }
   4032   cbuf_puts(b, "\");\n");
   4033 }
   4034 
   4035 static void c_emit_data(CTarget* t) {
   4036   ObjSymIter* it = obj_symiter_new(t->obj);
   4037   if (!it) return;
   4038   ObjSymEntry e;
   4039   while (obj_symiter_next(it, &e)) {
   4040     if (!e.sym) continue;
   4041     c_emit_data_symbol(t, e.id, e.sym);
   4042   }
   4043   obj_symiter_free(it);
   4044 }
   4045 
   4046 /* === finalize / destroy === */
   4047 
   4048 void c_emit_finalize(CTarget* t) {
   4049   if (t->finalized) return;
   4050   t->finalized = 1;
   4051   c_emit_prologue(t);
   4052   if (t->need_stdarg) c_writer_puts(t, "#include <stdarg.h>\n");
   4053   if (t->need_setjmp) c_writer_puts(t, "#include <setjmp.h>\n");
   4054   if (t->need_stdarg || t->need_setjmp) c_writer_puts(t, "\n");
   4055   if (t->typedefs.len) {
   4056     c_writer_write(t, t->typedefs.data, t->typedefs.len);
   4057     c_writer_puts(t, "\n");
   4058   }
   4059   c_emit_data(t);
   4060   if (t->forwards.len) {
   4061     c_writer_write(t, t->forwards.data, t->forwards.len);
   4062     c_writer_puts(t, "\n");
   4063   }
   4064   if (t->data_defs.len) {
   4065     c_writer_write(t, t->data_defs.data, t->data_defs.len);
   4066     c_writer_puts(t, "\n");
   4067   }
   4068   if (t->body.len) c_writer_write(t, t->body.data, t->body.len);
   4069 }
   4070 
   4071 void c_emit_destroy(CTarget* t) {
   4072   Heap* h = t->c->ctx->heap;
   4073   cbuf_fini(&t->forwards);
   4074   cbuf_fini(&t->typedefs);
   4075   cbuf_fini(&t->data_defs);
   4076   cbuf_fini(&t->decls);
   4077   cbuf_fini(&t->body);
   4078   if (t->sym_forwarded) h->free(h, t->sym_forwarded, t->sym_forwarded_cap);
   4079   t->sym_forwarded = NULL;
   4080   t->sym_forwarded_cap = 0;
   4081   if (t->local_static_syms) {
   4082     h->free(h, t->local_static_syms,
   4083             t->local_static_syms_cap * sizeof(*t->local_static_syms));
   4084   }
   4085   if (t->local_static_entries) {
   4086     h->free(h, t->local_static_entries,
   4087             t->local_static_entries_cap * sizeof(*t->local_static_entries));
   4088   }
   4089   if (t->local_declared) h->free(h, t->local_declared, t->local_cap);
   4090   if (t->local_type)
   4091     h->free(h, t->local_type, t->local_cap * sizeof(KitCgTypeId));
   4092   if (t->scopes) h->free(h, t->scopes, t->scopes_cap * sizeof(CScopeInfo));
   4093   t->local_declared = NULL;
   4094   t->local_type = NULL;
   4095   t->scopes = NULL;
   4096   t->local_static_syms = NULL;
   4097   t->local_static_entries = NULL;
   4098   t->local_cap = 0;
   4099   t->scopes_cap = 0;
   4100   t->local_static_syms_cap = 0;
   4101   t->local_static_entries_cap = 0;
   4102   t->local_static_nsyms = 0;
   4103   t->local_static_nentries = 0;
   4104 }