kit

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

cg_adapter.c (49982B)


      1 #include <string.h>
      2 
      3 #include "parse/parse_priv.h"
      4 
      5 KitCgTypeId pcg_tid(Parser* p, const Type* ty) {
      6   return type_cg_id_in_pool(p->c, p->pool, ty);
      7 }
      8 
      9 /* Set both a slot's C type and its lowered CG id together so the two never
     10  * drift. Every site that writes slot->type must go through this (or copy a
     11  * whole slot, which carries cg_id along). The id is lowered lazily on first
     12  * read (pcg_slot_cg_id), not here, so an incomplete record stamped at push time
     13  * still lowers to its real layout once completed -- identical to a direct
     14  * type_cg_id_in_pool call. */
     15 static void pcg_slot_set_type(PcgSlot* s, const Type* ty) {
     16   s->type = ty;
     17   s->cg_id = KIT_CG_TYPE_NONE; /* lazily lowered on first read */
     18 }
     19 
     20 /* The slot's lowered CG id, lowering+caching on first read. A NULL type yields
     21  * KIT_CG_TYPE_NONE every time (the sentinel also means "unfilled", but
     22  * type_cg_id_in_pool(NULL) is constant so re-lowering is free). For a real type
     23  * the first read crosses the bridge once; later reads are a load. */
     24 static KitCgTypeId pcg_slot_cg_id(Parser* p, PcgSlot* s) {
     25   if (s->cg_id == KIT_CG_TYPE_NONE && s->type)
     26     s->cg_id = type_cg_id_in_pool(p->c, p->pool, s->type);
     27   return s->cg_id;
     28 }
     29 
     30 /* The cached CG id of the TOS slot's type, lowering lazily on first read.
     31  * Empty-stack-safe (returns NONE), matching pcg_tid(p, pcg_top_type(p)). */
     32 static KitCgTypeId pcg_top_cg_id(Parser* p) {
     33   return p->cg_type_sp
     34              ? pcg_slot_cg_id(p, &p->cg_slot_stack[p->cg_type_sp - 1u])
     35              : KIT_CG_TYPE_NONE;
     36 }
     37 
     38 /* The cached CG id of the depth-2 slot's type (matching pcg_top2_type). */
     39 static KitCgTypeId pcg_top2_cg_id(Parser* p) {
     40   return p->cg_type_sp >= 2
     41              ? pcg_slot_cg_id(p, &p->cg_slot_stack[p->cg_type_sp - 2u])
     42              : KIT_CG_TYPE_NONE;
     43 }
     44 
     45 #define PCG_VALUE_LVALUE 1u
     46 #define PCG_VALUE_MODIFIABLE 2u
     47 #define PCG_VALUE_BITFIELD 4u
     48 #define PCG_VALUE_NULL_PTR_CONST 8u
     49 #define PCG_VALUE_REGISTER 16u
     50 
     51 static u8 pcg_lvalue_flags_for_type(const Type* ty) {
     52   u8 flags = PCG_VALUE_LVALUE;
     53   if (ty && !(ty->qual & Q_CONST) && ty->kind != TY_ARRAY &&
     54       ty->kind != TY_FUNC && ty->kind != TY_VOID) {
     55     flags |= PCG_VALUE_MODIFIABLE;
     56   }
     57   return flags;
     58 }
     59 
     60 /* Build a MemAccess from an already-lowered CG id plus the C type (for its
     61  * qualifiers). Lets a caller that already has the slot's cached cg_id skip the
     62  * type_cg_id_in_pool re-crossing pcg_mem(ty) does twice (lower + align). */
     63 static KitCgMemAccess pcg_mem_id(Parser* p, KitCgTypeId id, const Type* ty) {
     64   KitCgMemAccess m;
     65   memset(&m, 0, sizeof m);
     66   m.type = id;
     67   m.align = (u32)kit_cg_type_align(p->c, id);
     68   if (ty && (ty->qual & Q_VOLATILE)) m.flags |= KIT_CG_MEM_VOLATILE;
     69   if (type_is_int(ty) && pcg_type_is_signed(ty))
     70     m.flags |= KIT_CG_MEM_SOURCE_SIGNED;
     71   return m;
     72 }
     73 
     74 KitCgMemAccess pcg_mem(Parser* p, const Type* ty) {
     75   return pcg_mem_id(p, pcg_tid(p, ty), ty);
     76 }
     77 
     78 static void pcg_aux_clear(PcgLvAux* a) {
     79   a->offset = 0;
     80   a->scale = 0;
     81   a->bit_offset = 0;
     82   a->bit_width = 0;
     83   a->storage_size = 0;
     84   a->bit_signed = 0;
     85   a->base_kind = PCG_LV_BASE_LOCAL;
     86   a->is_subobject = 0;
     87   a->pad[0] = a->pad[1] = a->pad[2] = a->pad[3] = a->pad[4] = 0;
     88 }
     89 
     90 static void pcg_stack_grow(Parser* p, u32 want) {
     91   PcgSlot* ns;
     92   u32 nc;
     93   if (p->cg_type_cap >= want) return;
     94   nc = p->cg_type_cap ? p->cg_type_cap * 2u : 64u;
     95   while (nc < want) nc *= 2u;
     96   ns = arena_zarray(p->pool->arena, PcgSlot, nc);
     97   if (!ns) perr(p, "out of memory in CG shadow stack");
     98   if (p->cg_slot_stack && p->cg_type_sp)
     99     memcpy(ns, p->cg_slot_stack, sizeof(*ns) * p->cg_type_sp);
    100   p->cg_slot_stack = ns;
    101   p->cg_type_cap = nc;
    102 }
    103 
    104 void pcg_push_type(Parser* p, const Type* ty) {
    105   PcgSlot* s;
    106   pcg_stack_grow(p, p->cg_type_sp + 1u);
    107   s = &p->cg_slot_stack[p->cg_type_sp];
    108   pcg_slot_set_type(s, ty);
    109   s->flags = 0;
    110   pcg_aux_clear(&s->aux);
    111   ++p->cg_type_sp;
    112 }
    113 
    114 void pcg_drop_type(Parser* p) {
    115   if (p->cg_type_sp) --p->cg_type_sp;
    116 }
    117 
    118 void pcg_dup_type(Parser* p) {
    119   PcgSlot top;
    120   if (p->cg_type_sp) {
    121     top = p->cg_slot_stack[p->cg_type_sp - 1u];
    122   } else {
    123     pcg_slot_set_type(&top, NULL);
    124     top.flags = 0;
    125     pcg_aux_clear(&top.aux);
    126   }
    127   /* Copy the slot out before push: pcg_stack_grow may reallocate. The whole
    128    * slot (cg_id included) is copied back at the end, so dup preserves the id.
    129    */
    130   pcg_push_type(p, top.type);
    131   if (p->cg_type_sp) p->cg_slot_stack[p->cg_type_sp - 1u] = top;
    132 }
    133 
    134 void pcg_swap_type(Parser* p) {
    135   if (p->cg_type_sp >= 2) {
    136     PcgSlot tmp = p->cg_slot_stack[p->cg_type_sp - 1u];
    137     p->cg_slot_stack[p->cg_type_sp - 1u] = p->cg_slot_stack[p->cg_type_sp - 2u];
    138     p->cg_slot_stack[p->cg_type_sp - 2u] = tmp;
    139   }
    140 }
    141 
    142 void pcg_rot3_type(Parser* p) {
    143   if (p->cg_type_sp >= 3) {
    144     PcgSlot a = p->cg_slot_stack[p->cg_type_sp - 3u];
    145     p->cg_slot_stack[p->cg_type_sp - 3u] = p->cg_slot_stack[p->cg_type_sp - 2u];
    146     p->cg_slot_stack[p->cg_type_sp - 2u] = p->cg_slot_stack[p->cg_type_sp - 1u];
    147     p->cg_slot_stack[p->cg_type_sp - 1u] = a;
    148   }
    149 }
    150 
    151 /* Structural CG ops that must keep the parser's typed shadow stack in lockstep
    152  * with the CG stack: each emits the CG op (when codegen is enabled) and mirrors
    153  * the same structural effect onto the type stack. */
    154 void pcg_dup(Parser* p) {
    155   if (pcg_emit_enabled(p)) kit_cg_dup(p->cg);
    156   pcg_dup_type(p);
    157 }
    158 
    159 void pcg_swap(Parser* p) {
    160   if (pcg_emit_enabled(p)) kit_cg_swap(p->cg);
    161   pcg_swap_type(p);
    162 }
    163 
    164 void pcg_drop(Parser* p) {
    165   if (pcg_emit_enabled(p)) kit_cg_drop(p->cg);
    166   pcg_drop_type(p);
    167 }
    168 
    169 /* Reclaim dead compiler-temp slots at a statement boundary. The authoritative
    170  * "no value is live" witness is the CG value stack depth, which
    171  * kit_cg_reclaim_temps checks (g->sp == 0): a stray non-empty call is a safe
    172  * no-op rather than a miscompile. (The parser's typed shadow stack cg_type_sp
    173  * is NOT a reliable witness — it is not drained in lockstep with the value
    174  * stack.) */
    175 void pcg_reclaim_temps(Parser* p) {
    176   if (!pcg_emit_enabled(p)) return;
    177   kit_cg_reclaim_temps(p->cg);
    178 }
    179 
    180 PcgLvAux* pcg_top_lv_aux(Parser* p) {
    181   return p->cg_type_sp ? &p->cg_slot_stack[p->cg_type_sp - 1u].aux : NULL;
    182 }
    183 
    184 PcgLvAux* pcg_lv_aux_at(Parser* p, u32 depth) {
    185   return (p->cg_type_sp > depth)
    186              ? &p->cg_slot_stack[p->cg_type_sp - 1u - depth].aux
    187              : NULL;
    188 }
    189 
    190 const Type* pcg_top_type(Parser* p) {
    191   return p->cg_type_sp ? p->cg_slot_stack[p->cg_type_sp - 1u].type : NULL;
    192 }
    193 
    194 const Type* pcg_top2_type(Parser* p) {
    195   return p->cg_type_sp >= 2 ? p->cg_slot_stack[p->cg_type_sp - 2u].type : NULL;
    196 }
    197 
    198 void pcg_retag_top(Parser* p, const Type* ty) {
    199   if (p->cg_type_sp) {
    200     pcg_slot_set_type(&p->cg_slot_stack[p->cg_type_sp - 1u], ty);
    201     p->cg_slot_stack[p->cg_type_sp - 1u].flags = 0;
    202     pcg_aux_clear(&p->cg_slot_stack[p->cg_type_sp - 1u].aux);
    203   }
    204 }
    205 
    206 /* Replace the type of the slot `depth` below the top (depth 0 == top) while
    207  * preserving its value flags and lvalue aux. Unlike pcg_retag_top this keeps
    208  * the slot's lvalue-ness/bitfield/aux intact — used where only the C type
    209  * changes (e.g. struct unqualification, narrowing a compound-assign LHS). */
    210 void pcg_retag_keep_flags(Parser* p, u32 depth, const Type* ty) {
    211   if (p->cg_type_sp > depth)
    212     pcg_slot_set_type(&p->cg_slot_stack[p->cg_type_sp - 1u - depth], ty);
    213 }
    214 
    215 int pcg_top_is_bitfield(Parser* p) {
    216   return p->cg_type_sp &&
    217          (p->cg_slot_stack[p->cg_type_sp - 1u].flags & PCG_VALUE_BITFIELD) != 0;
    218 }
    219 
    220 void pcg_set_top_bitfield(Parser* p) {
    221   if (p->cg_type_sp)
    222     p->cg_slot_stack[p->cg_type_sp - 1u].flags |= PCG_VALUE_BITFIELD;
    223 }
    224 
    225 int pcg_top_is_register(Parser* p) {
    226   return p->cg_type_sp &&
    227          (p->cg_slot_stack[p->cg_type_sp - 1u].flags & PCG_VALUE_REGISTER) != 0;
    228 }
    229 
    230 void pcg_set_top_register(Parser* p) {
    231   if (p->cg_type_sp)
    232     p->cg_slot_stack[p->cg_type_sp - 1u].flags |= PCG_VALUE_REGISTER;
    233 }
    234 
    235 int pcg_top_is_lvalue(Parser* p) {
    236   return p->cg_type_sp &&
    237          (p->cg_slot_stack[p->cg_type_sp - 1u].flags & PCG_VALUE_LVALUE) != 0;
    238 }
    239 
    240 int pcg_top_is_modifiable_lvalue(Parser* p) {
    241   return p->cg_type_sp && (p->cg_slot_stack[p->cg_type_sp - 1u].flags &
    242                            (PCG_VALUE_LVALUE | PCG_VALUE_MODIFIABLE)) ==
    243                               (PCG_VALUE_LVALUE | PCG_VALUE_MODIFIABLE);
    244 }
    245 
    246 int pcg_top_is_null_ptr_const(Parser* p) {
    247   return p->cg_type_sp && (p->cg_slot_stack[p->cg_type_sp - 1u].flags &
    248                            PCG_VALUE_NULL_PTR_CONST) != 0;
    249 }
    250 
    251 void pcg_set_top_lvalue(Parser* p) {
    252   const Type* ty = pcg_top_type(p);
    253   if (p->cg_type_sp)
    254     p->cg_slot_stack[p->cg_type_sp - 1u].flags = pcg_lvalue_flags_for_type(ty);
    255 }
    256 
    257 int pcg_emit_enabled(Parser* p) { return p && p->suppress_codegen == 0; }
    258 
    259 void pcg_codegen_suppress_push(Parser* p) {
    260   if (p) ++p->suppress_codegen;
    261 }
    262 
    263 void pcg_codegen_suppress_pop(Parser* p) {
    264   if (!p) return;
    265   if (!p->suppress_codegen)
    266     perr(p, "internal parser codegen suppression underflow");
    267   --p->suppress_codegen;
    268 }
    269 
    270 int pcg_type_is_fp(const Type* ty) {
    271   return ty && type_kind_is_fp((TypeKind)ty->kind);
    272 }
    273 
    274 int pcg_type_is_signed(const Type* ty) { return type_is_signed_integer(ty); }
    275 
    276 KitCgIntBinOp pcg_int_binop(BinOp op) {
    277   switch (op) {
    278     case BO_IADD:
    279       return KIT_CG_INT_ADD;
    280     case BO_ISUB:
    281       return KIT_CG_INT_SUB;
    282     case BO_IMUL:
    283       return KIT_CG_INT_MUL;
    284     case BO_SDIV:
    285       return KIT_CG_INT_SDIV;
    286     case BO_UDIV:
    287       return KIT_CG_INT_UDIV;
    288     case BO_SREM:
    289       return KIT_CG_INT_SREM;
    290     case BO_UREM:
    291       return KIT_CG_INT_UREM;
    292     case BO_AND:
    293       return KIT_CG_INT_AND;
    294     case BO_OR:
    295       return KIT_CG_INT_OR;
    296     case BO_XOR:
    297       return KIT_CG_INT_XOR;
    298     case BO_SHL:
    299       return KIT_CG_INT_SHL;
    300     case BO_SHR_S:
    301       return KIT_CG_INT_ASHR;
    302     case BO_SHR_U:
    303       return KIT_CG_INT_LSHR;
    304     default:
    305       return KIT_CG_INT_ADD;
    306   }
    307 }
    308 
    309 KitCgFpBinOp pcg_fp_binop(BinOp op) {
    310   switch (op) {
    311     case BO_FADD:
    312       return KIT_CG_FP_ADD;
    313     case BO_FSUB:
    314       return KIT_CG_FP_SUB;
    315     case BO_FMUL:
    316       return KIT_CG_FP_MUL;
    317     case BO_FDIV:
    318       return KIT_CG_FP_DIV;
    319     default:
    320       return KIT_CG_FP_ADD;
    321   }
    322 }
    323 
    324 KitCgIntCmpOp pcg_int_cmp(CmpOp op) {
    325   switch (op) {
    326     case CMP_EQ:
    327       return KIT_CG_INT_EQ;
    328     case CMP_NE:
    329       return KIT_CG_INT_NE;
    330     case CMP_LT_S:
    331       return KIT_CG_INT_LT_S;
    332     case CMP_LE_S:
    333       return KIT_CG_INT_LE_S;
    334     case CMP_GT_S:
    335       return KIT_CG_INT_GT_S;
    336     case CMP_GE_S:
    337       return KIT_CG_INT_GE_S;
    338     case CMP_LT_U:
    339       return KIT_CG_INT_LT_U;
    340     case CMP_LE_U:
    341       return KIT_CG_INT_LE_U;
    342     case CMP_GT_U:
    343       return KIT_CG_INT_GT_U;
    344     case CMP_GE_U:
    345       return KIT_CG_INT_GE_U;
    346     default:
    347       return KIT_CG_INT_EQ;
    348   }
    349 }
    350 
    351 KitCgFpCmpOp pcg_fp_cmp(CmpOp op) {
    352   switch (op) {
    353     case CMP_EQ:
    354       return KIT_CG_FP_OEQ;
    355     case CMP_NE:
    356       /* C `!=` on floats is *unordered* not-equal: `NaN != x` is true (and
    357        * `__builtin_isnan(x)` lowers to `x != x`). Map to UNE, not ONE. */
    358       return KIT_CG_FP_UNE;
    359     case CMP_LT_F:
    360     case CMP_OLT_F:
    361       return KIT_CG_FP_OLT;
    362     case CMP_LE_F:
    363     case CMP_OLE_F:
    364       return KIT_CG_FP_OLE;
    365     case CMP_GT_F:
    366     case CMP_OGT_F:
    367       return KIT_CG_FP_OGT;
    368     case CMP_GE_F:
    369     case CMP_OGE_F:
    370       return KIT_CG_FP_OGE;
    371     case CMP_OEQ_F:
    372       return KIT_CG_FP_OEQ;
    373     case CMP_ONE_F:
    374       return KIT_CG_FP_ONE;
    375     case CMP_UEQ_F:
    376       return KIT_CG_FP_UEQ;
    377     case CMP_UNE_F:
    378       return KIT_CG_FP_UNE;
    379     case CMP_ULT_F:
    380       return KIT_CG_FP_ULT;
    381     case CMP_ULE_F:
    382       return KIT_CG_FP_ULE;
    383     case CMP_UGT_F:
    384       return KIT_CG_FP_UGT;
    385     case CMP_UGE_F:
    386       return KIT_CG_FP_UGE;
    387     default:
    388       return KIT_CG_FP_OEQ;
    389   }
    390 }
    391 
    392 KitCgAtomicOp pcg_atomic_op(AtomicOp op) {
    393   switch (op) {
    394     case AO_XCHG:
    395       return KIT_CG_ATOMIC_XCHG;
    396     case AO_ADD:
    397       return KIT_CG_ATOMIC_ADD;
    398     case AO_SUB:
    399       return KIT_CG_ATOMIC_SUB;
    400     case AO_AND:
    401       return KIT_CG_ATOMIC_AND;
    402     case AO_OR:
    403       return KIT_CG_ATOMIC_OR;
    404     case AO_XOR:
    405       return KIT_CG_ATOMIC_XOR;
    406     case AO_NAND:
    407       return KIT_CG_ATOMIC_NAND;
    408   }
    409   return KIT_CG_ATOMIC_XCHG;
    410 }
    411 
    412 KitCgMemOrder pcg_mem_order(MemOrder ord) { return (KitCgMemOrder)ord; }
    413 
    414 static int pcg_slot_is_volatile(const FrameSlotDesc* fsd) {
    415   return fsd && ((fsd->flags & FSF_VOLATILE) ||
    416                  (fsd->type && (fsd->type->qual & Q_VOLATILE)));
    417 }
    418 
    419 FrameSlot pcg_local(Parser* p, const FrameSlotDesc* fsd) {
    420   KitCgLocalAttrs attrs;
    421   memset(&attrs, 0, sizeof attrs);
    422   if (!pcg_emit_enabled(p)) return FRAME_SLOT_NONE;
    423   attrs.name = fsd->name;
    424   attrs.align = fsd->align;
    425   if (pcg_slot_is_volatile(fsd)) attrs.flags |= KIT_CG_LOCAL_MEMORY_REQUIRED;
    426   /* FSF_ADDR_TAKEN is no longer propagated to CG: there is no
    427    * KIT_CG_LOCAL_ADDRESS_TAKEN attribute. The C-side flag stays for any
    428    * parser-internal uses; opt's opt_promote_scalar_locals (Stream I) decides
    429    * register-promotion from observed access patterns, not from the flag. */
    430   return kit_cg_local(p->cg, pcg_tid(p, fsd->type), attrs);
    431 }
    432 
    433 FrameSlot pcg_param_slot(Parser* p, u32 index, const FrameSlotDesc* fsd) {
    434   KitCgLocalAttrs attrs;
    435   if (!pcg_emit_enabled(p)) return FRAME_SLOT_NONE;
    436   memset(&attrs, 0, sizeof attrs);
    437   attrs.name = fsd->name;
    438   attrs.align = fsd->align;
    439   if (pcg_slot_is_volatile(fsd)) attrs.flags |= KIT_CG_LOCAL_MEMORY_REQUIRED;
    440   return kit_cg_param(p->cg, index, pcg_tid(p, fsd->type), attrs);
    441 }
    442 
    443 void pcg_param(Parser* p, const CGParamDesc* pd) {
    444   (void)p;
    445   (void)pd;
    446 }
    447 
    448 void pcg_func_begin(Parser* p, const CGFuncDesc* fd) {
    449   if (pcg_emit_enabled(p)) {
    450     KitCgFuncAttrs attrs;
    451     memset(&attrs, 0, sizeof attrs);
    452     attrs.inline_policy = fd->inline_policy;
    453     if (fd->flags & CGFD_NORETURN) attrs.flags |= KIT_CG_FUNC_NORETURN;
    454     kit_cg_func_begin_attrs(p->cg, fd->sym, attrs);
    455   }
    456 }
    457 
    458 void pcg_func_end(Parser* p) {
    459   if (pcg_emit_enabled(p)) kit_cg_func_end(p->cg);
    460 }
    461 
    462 void pcg_set_loc(Parser* p, SrcLoc loc) {
    463   if (pcg_emit_enabled(p)) kit_cg_set_loc(p->cg, loc);
    464 }
    465 
    466 void pcg_push_int(Parser* p, i64 v, const Type* ty) {
    467   if (pcg_emit_enabled(p)) {
    468     kit_cg_push_int(p->cg, (uint64_t)v, pcg_tid(p, ty));
    469   }
    470   pcg_push_type(p, ty);
    471   if (v == 0 && p->cg_type_sp) {
    472     p->cg_slot_stack[p->cg_type_sp - 1u].flags |= PCG_VALUE_NULL_PTR_CONST;
    473   }
    474 }
    475 
    476 void pcg_push_float(Parser* p, double v, const Type* ty) {
    477   if (pcg_emit_enabled(p)) kit_cg_push_float(p->cg, v, pcg_tid(p, ty));
    478   pcg_push_type(p, ty);
    479 }
    480 
    481 /* Tag the TOS place as a bit-field place when `lv` carries bit-field geometry.
    482  * The frontend builds the storage-unit place itself (materialize + deref), so
    483  * it attaches the descriptor here; a plain load/store then extracts/inserts the
    484  * field. Must be called on the place each time a fresh one is built (each deref
    485  * yields a plain place). No-op for non-bit-field lvalues. */
    486 static void pcg_apply_bitfield(Parser* p, const PcgLvAux* lv) {
    487   if (lv && lv->bit_width) {
    488     kit_cg_field_bits(p->cg, lv->bit_offset, lv->bit_width, lv->storage_size,
    489                       lv->bit_signed);
    490   }
    491 }
    492 
    493 /* A trivially-addressable lvalue: a bare local slot with no pending field
    494  * offset, array scale, or bit-field. Its PLACE is already on the CG stack
    495  * (push_local), so a load/store can consume it directly without first
    496  * materializing an address. */
    497 static int pcg_lv_is_trivial_local(const PcgLvAux* lv) {
    498   return lv && lv->base_kind == PCG_LV_BASE_LOCAL && lv->offset == 0 &&
    499          lv->scale == 0 && lv->bit_width == 0;
    500 }
    501 
    502 /* Materialize the TOS lvalue (folding its pending field offset / array scale
    503  * into the address) as a single pointer rvalue. Defined below; the memops use
    504  * it to build an explicit place before load/store. */
    505 static void pcg_materialize_lv_to_ptr(Parser* p, const Type* result_ptr_ty);
    506 static void pcg_lv_to_memop_place(Parser* p, const Type* field_ty);
    507 
    508 void pcg_push_local_typed(Parser* p, FrameSlot s, const Type* ty) {
    509   if (pcg_emit_enabled(p)) kit_cg_push_local(p->cg, s);
    510   pcg_push_type(p, ty);
    511   if (p->cg_type_sp) {
    512     p->cg_slot_stack[p->cg_type_sp - 1u].flags = pcg_lvalue_flags_for_type(ty);
    513     p->cg_slot_stack[p->cg_type_sp - 1u].aux.base_kind = PCG_LV_BASE_LOCAL;
    514   }
    515 }
    516 
    517 void pcg_push_global(Parser* p, ObjSymId sym, const Type* ty) {
    518   /* push_symbol_addr produces a pointer rvalue; the parser tags the slot as
    519    * a C-language lvalue with PCG_LV_BASE_POINTER_RV so subsequent
    520    * load/store/addr know the base is already a pointer. The cg layer accepts
    521    * pointer-rvalue bases for memops uniformly (Stream A). */
    522   if (pcg_emit_enabled(p)) kit_cg_push_symbol_addr(p->cg, sym, 0);
    523   pcg_push_type(p, ty);
    524   if (p->cg_type_sp) {
    525     p->cg_slot_stack[p->cg_type_sp - 1u].flags = pcg_lvalue_flags_for_type(ty);
    526     p->cg_slot_stack[p->cg_type_sp - 1u].aux.base_kind = PCG_LV_BASE_POINTER_RV;
    527   }
    528 }
    529 
    530 void pcg_load(Parser* p) {
    531   const Type* ty = pcg_top_type(p);
    532   int was_lvalue = pcg_top_is_lvalue(p);
    533   if (pcg_emit_enabled(p)) {
    534     PcgLvAux* lv = pcg_top_lv_aux(p);
    535     KitCgTypeId cg_id = pcg_top_cg_id(p); /* cached TOS-slot id, lowered once */
    536     KitCgMemAccess access = pcg_mem_id(p, cg_id, ty);
    537     /* Snapshot bit-field geometry before materialize clears the aux. */
    538     PcgLvAux bf = lv ? *lv : (PcgLvAux){0};
    539     /* Build the PLACE the strict load requires. A trivial local already has its
    540      * PLACE on the CG stack (push_local) and loads directly; anything else is
    541      * reduced to a single pointer (materialize, or a pointer-rvalue base) and
    542      * then deref'd to a place. A bit-field place is then tagged with its bit
    543      * geometry so the load extracts the field. */
    544     if (!was_lvalue || !pcg_lv_is_trivial_local(lv)) {
    545       if (was_lvalue)
    546         pcg_lv_to_memop_place(p, ty);
    547       else
    548         kit_cg_deref(p->cg, 0);
    549     }
    550     pcg_apply_bitfield(p, &bf);
    551     kit_cg_load(p->cg, access);
    552   }
    553   if (was_lvalue && p->cg_type_sp) {
    554     /* The emit block above can rewrite the TOS slot's type (materialize /
    555      * deref push/drop), so restore it to the loaded value's type `ty` here --
    556      * stamping cg_id alongside it. */
    557     pcg_slot_set_type(&p->cg_slot_stack[p->cg_type_sp - 1u], ty);
    558     p->cg_slot_stack[p->cg_type_sp - 1u].flags = 0;
    559     pcg_aux_clear(&p->cg_slot_stack[p->cg_type_sp - 1u].aux);
    560   }
    561 }
    562 
    563 /* Materialize the pending EA on the TOS lvalue as a pointer rvalue.
    564  * Postcondition: TOS is a pointer rvalue of type result_ptr_ty (which the
    565  * caller has computed as type_ptr(pool, current_lv_type)) and the CG stack
    566  * holds that single pointer where the lvalue's [base] or [base, index] used
    567  * to be. Aux is cleared.
    568  *
    569  * The materialization sequence depends on the aux:
    570  *   base_kind == LOCAL:
    571  *     - scale == 0, offset == 0:        addr
    572  *     - scale == 0, offset != 0:        addr ; ptr_to_int ; +offset ;
    573  * int_to_ptr
    574  *     - scale != 0:                     addr ; ptr_to_int ; idx*scale + ofs ;
    575  * int_to_ptr base_kind == POINTER_RV:
    576  *     - scale == 0, offset == 0:        no-op
    577  *     - scale == 0, offset != 0:        ptr_to_int ; +offset ; int_to_ptr
    578  *     - scale != 0:                     ptr_to_int ; idx*scale + ofs ;
    579  * int_to_ptr */
    580 static void pcg_materialize_lv_to_ptr(Parser* p, const Type* result_ptr_ty) {
    581   PcgLvAux* lv = pcg_top_lv_aux(p);
    582   int emit = pcg_emit_enabled(p);
    583   PcgLvBaseKind base_kind =
    584       lv ? (PcgLvBaseKind)lv->base_kind : PCG_LV_BASE_LOCAL;
    585   i64 ofs = lv ? lv->offset : 0;
    586   u32 scale = lv ? lv->scale : 0u;
    587   const Type* idx_ty = c_abi_ptrdiff_type(p->abi, p->pool);
    588   KitCgTypeId idx_tid = pcg_tid(p, idx_ty);
    589   KitCgTypeId ptr_tid = pcg_tid(p, result_ptr_ty);
    590   if (scale == 0 && ofs == 0) {
    591     if (base_kind == PCG_LV_BASE_LOCAL) {
    592       if (emit) kit_cg_addr(p->cg);
    593     }
    594     /* Already a pointer with no pending modifiers. */
    595   } else if (scale == 0) {
    596     if (emit) {
    597       if (base_kind == PCG_LV_BASE_LOCAL) kit_cg_addr(p->cg);
    598       kit_cg_ptr_to_int(p->cg, idx_tid);
    599       kit_cg_push_int(p->cg, (uint64_t)ofs, idx_tid);
    600       kit_cg_int_binop(p->cg, KIT_CG_INT_ADD, KIT_CG_INTOP_NONE);
    601       kit_cg_int_to_ptr(p->cg, ptr_tid);
    602     }
    603   } else {
    604     /* CG stack on entry: [base_ptr_now, index]. Compute
    605      * base_ptr_now + index*scale + ofs. */
    606     if (emit) {
    607       if (base_kind == PCG_LV_BASE_LOCAL) {
    608         kit_cg_swap(p->cg); /* [index, base_lv] */
    609         kit_cg_addr(p->cg); /* [index, base_ptr] */
    610         kit_cg_swap(p->cg); /* [base_ptr, index] */
    611       }
    612       kit_cg_swap(p->cg); /* [index, base_ptr] */
    613       kit_cg_ptr_to_int(p->cg, idx_tid);
    614       kit_cg_swap(p->cg); /* [base_int, index] */
    615       kit_cg_push_int(p->cg, (uint64_t)scale, idx_tid);
    616       kit_cg_int_binop(p->cg, KIT_CG_INT_MUL, KIT_CG_INTOP_NONE);
    617       kit_cg_int_binop(p->cg, KIT_CG_INT_ADD, KIT_CG_INTOP_NONE);
    618       if (ofs != 0) {
    619         kit_cg_push_int(p->cg, (uint64_t)ofs, idx_tid);
    620         kit_cg_int_binop(p->cg, KIT_CG_INT_ADD, KIT_CG_INTOP_NONE);
    621       }
    622       kit_cg_int_to_ptr(p->cg, ptr_tid);
    623     }
    624   }
    625   pcg_retag_top(p, result_ptr_ty);
    626   {
    627     PcgLvAux* out = pcg_top_lv_aux(p);
    628     if (out) out->base_kind = PCG_LV_BASE_POINTER_RV;
    629   }
    630 }
    631 
    632 /* Build the PLACE a load/store reads, from the TOS lvalue, keeping a constant
    633  * field/element offset as the deref displacement (so the backend folds it into
    634  * the load/store: `ldr w, [base, #ofs]`) rather than baking it into an explicit
    635  * `base + ofs` pointer the way pcg_materialize_lv_to_ptr + kit_cg_deref(0)
    636  * does. Mirrors that pair's postcondition (CG TOS is the place; parser slot is
    637  * the field pointer type with aux cleared). The pointer is reinterpreted to the
    638  * field type with a bitcast — free on the native backend (a no-op same-width
    639  * cast), an explicit cast on the C-source backend. Indexed (scale != 0) and
    640  * out-of-displacement-range offsets fall back to the explicit-pointer fold.
    641  * Caller guarantees emit is enabled. */
    642 static void pcg_lv_to_memop_place(Parser* p, const Type* field_ty) {
    643   PcgLvAux* lv = pcg_top_lv_aux(p);
    644   PcgLvBaseKind base_kind =
    645       lv ? (PcgLvBaseKind)lv->base_kind : PCG_LV_BASE_LOCAL;
    646   i64 ofs = lv ? lv->offset : 0;
    647   u32 scale = lv ? lv->scale : 0u;
    648   const Type* fptr = type_ptr(p->pool, field_ty);
    649   if (ofs < INT32_MIN || ofs > INT32_MAX) {
    650     /* Out-of-int32 displacement can't ride a deref/EA offset; materialize an
    651      * explicit base+ofs pointer. */
    652     pcg_materialize_lv_to_ptr(p, fptr);
    653     kit_cg_deref(p->cg, 0);
    654     return;
    655   }
    656   if (scale != 0u) {
    657     /* Scaled subscript: build one fused [base + index*scale + ofs] place via
    658      * kit_cg_elem_scaled rather than materializing a standalone
    659      * base+index*scale+ofs pointer (an explicit mul/add + a copy into the deref
    660      * base). CG stack on entry is [base, index]; decay the base to a field_ty*
    661      * pointer value, then fuse. The index stride is the containing array's
    662      * element size (scale) while the access type is field_ty, so `a[i].f`
    663      * strides by sizeof(elem) but reads `f`. */
    664     kit_cg_swap(p->cg); /* [index, base] */
    665     if (base_kind == PCG_LV_BASE_LOCAL)
    666       kit_cg_addr(p->cg);                    /* [index, base_ptr] */
    667     kit_cg_bitcast(p->cg, pcg_tid(p, fptr)); /* [index, (T*)base] */
    668     kit_cg_swap(p->cg);                      /* [base, index] */
    669     kit_cg_elem_scaled(p->cg, scale, ofs);   /* [place] */
    670   } else {
    671     if (base_kind == PCG_LV_BASE_LOCAL) kit_cg_addr(p->cg);
    672     kit_cg_bitcast(p->cg, pcg_tid(p, fptr));
    673     kit_cg_deref(p->cg, ofs);
    674   }
    675   pcg_retag_top(p, fptr);
    676   {
    677     PcgLvAux* out = pcg_top_lv_aux(p);
    678     if (out) out->base_kind = PCG_LV_BASE_POINTER_RV;
    679   }
    680 }
    681 
    682 void pcg_addr(Parser* p) {
    683   const Type* ty = pcg_top_type(p);
    684   pcg_materialize_lv_to_ptr(p, type_ptr(p->pool, ty));
    685 }
    686 
    687 /* Pushes the address of a label as a `void*` rvalue (GNU `&&label`). */
    688 void pcg_push_label_addr(Parser* p, CGLabel label) {
    689   const Type* vp = type_ptr(p->pool, type_void(p->pool));
    690   if (pcg_emit_enabled(p)) kit_cg_push_label_addr(p->cg, label, pcg_tid(p, vp));
    691   pcg_push_type(p, vp);
    692 }
    693 
    694 /* Pops the target pointer and emits a computed `goto *expr;`. */
    695 void pcg_computed_goto(Parser* p, const CGLabel* targets, u32 ntargets) {
    696   if (pcg_emit_enabled(p)) kit_cg_computed_goto(p->cg, targets, ntargets);
    697   pcg_drop_type(p);
    698 }
    699 
    700 /* ---- Control flow ----
    701  *
    702  * Label placement / jump / branch ops gate on emit; the conditional branches
    703  * also pop the tested value off the type stack to mirror the CG-side consume.
    704  * Suppressed parses, such as C99 `extern inline` bodies, do not open a CG
    705  * function, so they use a nonzero dummy label only for semantic bookkeeping
    706  * around break/continue/case validation. */
    707 CGLabel pcg_label_new(Parser* p) {
    708   if (!pcg_emit_enabled(p)) return (CGLabel)1;
    709   return kit_cg_label_new(p->cg);
    710 }
    711 
    712 void pcg_label_place(Parser* p, CGLabel l) {
    713   if (pcg_emit_enabled(p)) kit_cg_label_place(p->cg, l);
    714 }
    715 
    716 void pcg_jump(Parser* p, CGLabel l) {
    717   if (pcg_emit_enabled(p)) kit_cg_jump(p->cg, l);
    718 }
    719 
    720 void pcg_branch_true(Parser* p, CGLabel l) {
    721   if (pcg_emit_enabled(p)) kit_cg_branch_true(p->cg, l);
    722   pcg_drop_type(p);
    723 }
    724 
    725 void pcg_branch_false(Parser* p, CGLabel l) {
    726   if (pcg_emit_enabled(p)) kit_cg_branch_false(p->cg, l);
    727   pcg_drop_type(p);
    728 }
    729 
    730 /* Store [lv, rv] -> [rv]. The expression-value of an assignment is the
    731  * assigned rvalue, so the store sequence must leave a copy of rv on TOS. */
    732 /* keep_result == 0: consume [place, value] and leave nothing — the assignment's
    733  * value is discarded (the dominant `expr;` / initializer case). Skipping the
    734  * dup lets a still-delayed RHS flow straight into the destination local
    735  * (kit_cg_store's scalar-local fast path), killing the temp->local routing mov.
    736  * keep_result == 1: leave the assignment's value on the stack for an enclosing
    737  * expression (`a = b = c`, `if ((x = f()))`). */
    738 static void pcg_store_impl(Parser* p, int keep_result) {
    739   const Type* lv_ty = pcg_top2_type(p);
    740   const Type* rv_ty = pcg_top_type(p);
    741   const Type* mem_ty = lv_ty;
    742   /* Cached CG ids of the two slots (lowered once, not re-crossed by pcg_mem).
    743    */
    744   KitCgTypeId rv_cg = pcg_top_cg_id(p);
    745   KitCgTypeId mem_cg = pcg_top2_cg_id(p); /* tracks mem_ty (= lv_ty) */
    746   int emit = pcg_emit_enabled(p);
    747   /* The aux to consume lives on the lvalue slot at parser depth 1. */
    748   PcgLvAux* lv = pcg_lv_aux_at(p, 1);
    749   /* Snapshot bit-field geometry before materialize clears the aux. */
    750   PcgLvAux bf = lv ? *lv : (PcgLvAux){0};
    751   KitCgMemAccess access;
    752   if (rv_ty && type_is_ptr(rv_ty) && (!lv_ty || !type_is_ptr(lv_ty))) {
    753     mem_ty = rv_ty;
    754     mem_cg = rv_cg;
    755   }
    756   access = pcg_mem_id(p, mem_ty ? mem_cg : rv_cg, mem_ty ? mem_ty : rv_ty);
    757   if (emit) {
    758     int wide =
    759         rv_ty && (rv_ty->kind == TY_INT128 || rv_ty->kind == TY_UINT128 ||
    760                   rv_ty->kind == TY_LDOUBLE);
    761     if (pcg_lv_is_trivial_local(lv) && !wide) {
    762       /* The destination place is already on the CG stack: [place, value]. When
    763        * the result is kept, dup rv first so a copy survives the store; when it
    764        * is discarded, store straight from [place, value] so the RHS (possibly a
    765        * still-delayed arith/cmp) lands directly in the local. */
    766       if (keep_result) {
    767         kit_cg_dup(p->cg);
    768         kit_cg_rot3(p->cg);
    769         kit_cg_swap(p->cg);
    770       }
    771       kit_cg_store(p->cg, access);
    772     } else {
    773       /* Stash rv into a temp so the destination place can be built from the
    774        * base — folding any field offset / array scale into an explicit pointer
    775        * — then reload rv to store it and to leave it as the expression value.
    776        */
    777       FrameSlotDesc fsd;
    778       FrameSlot tmp;
    779       KitCgMemAccess rv_access = pcg_mem_id(p, rv_cg, rv_ty);
    780       int trivial = pcg_lv_is_trivial_local(lv);
    781       memset(&fsd, 0, sizeof fsd);
    782       fsd.type = rv_ty;
    783       fsd.size = c_abi_sizeof(p->abi, p->pool, rv_ty);
    784       fsd.align = c_abi_alignof(p->abi, p->pool, rv_ty);
    785       fsd.kind = FS_LOCAL;
    786       tmp = pcg_local(p, &fsd);
    787       kit_cg_push_local(p->cg, tmp);  /* [base.., value, &tmp] */
    788       kit_cg_swap(p->cg);             /* [base.., &tmp, value] */
    789       kit_cg_store(p->cg, rv_access); /* [base..] (stash rv) */
    790       if (!trivial) {
    791         /* Materialize operates on the parser-TOS lvalue; drop the rv type slot
    792          * so the lvalue is on top and matches the CG [base..]. The pointer is
    793          * deref'd to the PLACE the strict store requires, then tagged with the
    794          * bit-field geometry so the store inserts the field. */
    795         pcg_drop_type(p);
    796         pcg_lv_to_memop_place(p, lv_ty); /* [dst_place] */
    797         pcg_apply_bitfield(p, &bf);
    798         pcg_push_type(p, rv_ty);
    799       }
    800       kit_cg_push_local(p->cg, tmp);
    801       kit_cg_load(p->cg, rv_access); /* [dst, value] */
    802       kit_cg_store(p->cg, access);   /* [] */
    803       if (keep_result) {
    804         kit_cg_push_local(p->cg, tmp);
    805         kit_cg_load(p->cg, rv_access); /* [value] */
    806       }
    807     }
    808   }
    809   pcg_drop_type(p);
    810   pcg_drop_type(p);
    811   if (keep_result) pcg_push_type(p, rv_ty);
    812 }
    813 
    814 void pcg_store(Parser* p) { pcg_store_impl(p, 1); }
    815 
    816 /* Store whose assignment value is discarded (the `expr;` / initializer case).
    817  * Replaces the `pcg_store(p); pcg_drop(p)` idiom; avoids materializing the RHS
    818  * into a temp just to drop it. */
    819 void pcg_store_void(Parser* p) { pcg_store_impl(p, 0); }
    820 
    821 void pcg_deref(Parser* p, const Type* pointee) {
    822   const Type* ptr_ty = pcg_top_type(p);
    823   if (pointee && pointee->kind == TY_FUNC) {
    824     /* Function lvalues collapse to function pointers in C; no CG-level
    825      * dereference is needed (functions aren't first-class data). */
    826     pcg_retag_top(p, pointee);
    827     return;
    828   }
    829   if (ptr_ty && ptr_ty->kind == TY_PTR && ptr_ty->ptr.pointee != pointee) {
    830     const Type* want_ptr_ty = type_ptr(p->pool, pointee);
    831     if (pcg_emit_enabled(p)) kit_cg_bitcast(p->cg, pcg_tid(p, want_ptr_ty));
    832     pcg_retag_top(p, want_ptr_ty);
    833   }
    834   /* No kit_cg_indirect: the cg load/store accept pointer-rvalue bases
    835    * directly. Mark the slot as a C-language lvalue with POINTER_RV base; the
    836    * pointer stays on the CG stack untouched. */
    837   pcg_retag_top(p, pointee);
    838   if (p->cg_type_sp) {
    839     p->cg_slot_stack[p->cg_type_sp - 1u].flags =
    840         pcg_lvalue_flags_for_type(pointee);
    841     p->cg_slot_stack[p->cg_type_sp - 1u].aux.base_kind = PCG_LV_BASE_POINTER_RV;
    842   }
    843 }
    844 
    845 /* ---- Lvalue chain helpers ---- */
    846 
    847 void pcg_lv_member(Parser* p, i64 byte_offset, const Type* field_ty,
    848                    u16 bf_offset, u16 bf_width, u32 bf_storage_size) {
    849   PcgLvAux* lv = pcg_top_lv_aux(p);
    850   int was_lvalue = pcg_top_is_lvalue(p);
    851   const Type* base_ty = pcg_top_type(p);
    852   /* A member of an rvalue aggregate (e.g. `mk().field` for a struct-returning
    853    * call) lives in a CG-side temporary that is memory-backed, so the member is
    854    * readable as an lvalue — but per C it is not a *modifiable* lvalue. */
    855   int base_is_rvalue_agg =
    856       !was_lvalue && base_ty &&
    857       (base_ty->kind == TY_STRUCT || base_ty->kind == TY_UNION);
    858   i64 saved_offset = lv ? lv->offset + byte_offset : byte_offset;
    859   u32 saved_scale = lv ? lv->scale : 0u;
    860   u8 saved_base_kind = lv ? lv->base_kind : PCG_LV_BASE_LOCAL;
    861   /* Bumping the offset preserves the base kind and any earlier offset/scale
    862    * accumulated on the chain (`a[i].f.g` keeps `scale = sizeof(elem)` and
    863    * adds the field offsets). */
    864   pcg_retag_top(p, field_ty);
    865   if (was_lvalue) {
    866     pcg_set_top_lvalue(p);
    867   } else if (base_is_rvalue_agg && p->cg_type_sp) {
    868     p->cg_slot_stack[p->cg_type_sp - 1u].flags = PCG_VALUE_LVALUE;
    869   }
    870   /* pcg_retag_top cleared aux; re-apply the bumped offset and base kind. */
    871   {
    872     PcgLvAux* lv_after = pcg_top_lv_aux(p);
    873     if (lv_after) {
    874       lv_after->offset = saved_offset;
    875       lv_after->scale = saved_scale;
    876       lv_after->base_kind = saved_base_kind;
    877       lv_after->bit_offset = bf_offset;
    878       lv_after->bit_width = bf_width;
    879       lv_after->storage_size = bf_storage_size;
    880       lv_after->bit_signed = pcg_type_is_signed(field_ty) ? 1u : 0u;
    881       /* A member access narrows to a sub-object of a larger CG-tracked
    882        * object; record it so aggregate reads materialize a pointer to the
    883        * exact sub-object (the offset alone can be 0 for a first member). */
    884       lv_after->is_subobject = 1u;
    885     }
    886     if (bf_width && p->cg_type_sp)
    887       p->cg_slot_stack[p->cg_type_sp - 1u].flags |= PCG_VALUE_BITFIELD;
    888   }
    889 }
    890 
    891 void pcg_lv_subscript(Parser* p, u32 elem_size, const Type* elem_ty) {
    892   /* Stack on entry (parser side): [base_lv, index_rv].
    893    * Stack on entry (CG side):     [base, index].
    894    * After this call (parser):     [elem_lv] with aux.scale = elem_size.
    895    * After this call (CG):         [base, index] — unchanged; the eventual
    896    * load/store consumes both via the EA. */
    897   PcgLvAux* base_lv = pcg_lv_aux_at(p, 1);
    898   i64 saved_offset = base_lv ? base_lv->offset : 0;
    899   u8 base_is_lvalue =
    900       (p->cg_type_sp >= 2u &&
    901        (p->cg_slot_stack[p->cg_type_sp - 2u].flags & PCG_VALUE_LVALUE) != 0);
    902   u8 saved_base_kind = !base_is_lvalue
    903                            ? PCG_LV_BASE_POINTER_RV
    904                            : (base_lv ? base_lv->base_kind : PCG_LV_BASE_LOCAL);
    905   if (base_lv && base_lv->scale != 0) {
    906     perr(p, "internal: nested subscript without materialization");
    907   }
    908   pcg_drop_type(p);          /* drop index parser slot */
    909   pcg_retag_top(p, elem_ty); /* retag base parser slot as element */
    910   pcg_set_top_lvalue(p);
    911   {
    912     PcgLvAux* lv = pcg_top_lv_aux(p);
    913     if (lv) {
    914       lv->offset = saved_offset;
    915       lv->scale = elem_size;
    916       lv->base_kind = saved_base_kind;
    917     }
    918   }
    919 }
    920 
    921 void pcg_decay_array(Parser* p, const Type* arr_ty) {
    922   const Type* ptr_ty = type_ptr(p->pool, arr_ty->arr.elem);
    923   pcg_materialize_lv_to_ptr(p, ptr_ty);
    924 }
    925 
    926 void pcg_binop(Parser* p, BinOp op) {
    927   const Type* result = pcg_top2_type(p);
    928   if (op == BO_FADD || op == BO_FSUB || op == BO_FMUL || op == BO_FDIV) {
    929     if (pcg_emit_enabled(p)) {
    930       kit_cg_fp_binop(p->cg, pcg_fp_binop(op), KIT_CG_FP_NONE);
    931     }
    932   } else {
    933     if (pcg_emit_enabled(p)) {
    934       kit_cg_int_binop(p->cg, pcg_int_binop(op), KIT_CG_INTOP_NONE);
    935     }
    936   }
    937   pcg_drop_type(p);
    938   pcg_retag_top(p, result);
    939 }
    940 
    941 void pcg_unop(Parser* p, UnOp op) {
    942   if (op == UO_NEG && pcg_type_is_fp(pcg_top_type(p))) {
    943     if (pcg_emit_enabled(p))
    944       kit_cg_fp_unop(p->cg, KIT_CG_FP_NEG, KIT_CG_FP_NONE);
    945   } else {
    946     KitCgIntUnOp iop = op == UO_NOT    ? KIT_CG_INT_NOT
    947                        : op == UO_BNOT ? KIT_CG_INT_BNOT
    948                                        : KIT_CG_INT_NEG;
    949     if (pcg_emit_enabled(p)) kit_cg_int_unop(p->cg, iop, KIT_CG_INTOP_NONE);
    950   }
    951 }
    952 
    953 void pcg_cmp(Parser* p, CmpOp op) {
    954   /* The FP block starts at CMP_LT_F (relational operator markers) and runs
    955    * through the ordered/unordered predicate members; everything below is
    956    * integer. CMP_EQ/CMP_NE additionally route to FP when the operand type is
    957    * floating (C `==`/`!=` on floats is ordered-equal / unordered-not-equal). */
    958   if (op >= CMP_LT_F ||
    959       ((op == CMP_EQ || op == CMP_NE) && pcg_type_is_fp(pcg_top_type(p)))) {
    960     if (pcg_emit_enabled(p)) kit_cg_fp_cmp(p->cg, pcg_fp_cmp(op));
    961   } else {
    962     if (pcg_emit_enabled(p)) kit_cg_int_cmp(p->cg, pcg_int_cmp(op));
    963   }
    964   pcg_drop_type(p);
    965   pcg_retag_top(p, type_prim(p->pool, TY_INT));
    966 }
    967 
    968 void pcg_convert(Parser* p, const Type* dst) {
    969   const Type* src = pcg_top_type(p);
    970   /* src is the TOS slot: read its cached id; dst is lowered once and reused for
    971    * both its size and the convert id. */
    972   KitCgTypeId src_id = pcg_top_cg_id(p);
    973   KitCgTypeId id = pcg_tid(p, dst);
    974   u32 ss = (u32)kit_cg_type_size(p->c, src_id);
    975   u32 ds = (u32)kit_cg_type_size(p->c, id);
    976   int si = type_is_int(src) || type_is_ptr(src);
    977   int di = type_is_int(dst) || type_is_ptr(dst);
    978   int sf = pcg_type_is_fp(src);
    979   int df = pcg_type_is_fp(dst);
    980   int emit = pcg_emit_enabled(p);
    981   if (src == dst) return;
    982   /* C has no struct/union conversion: a pcg_convert reaching here with an
    983    * aggregate on the stack is only reconciling a qualifier difference between
    984    * two compatible types — e.g. the arms of a `?:` where one is a `const`
    985    * struct lvalue (`*top`, top a `const T*`) and the other is not. The
    986    * representation is identical, so retag the place to dst and emit nothing.
    987    * Falling through would hand the aggregate to the scalar bitcast path, which
    988    * pushes it as a value — illegal for an aggregate (api_push rejects it). The
    989    * flag-preserving retag keeps it a place so the caller can copy it. */
    990   if (src->kind == TY_STRUCT || src->kind == TY_UNION) {
    991     pcg_retag_keep_flags(p, 0, dst);
    992     return;
    993   }
    994   /* Conversion to _Bool is "value != 0", not a truncation: a value whose set
    995    * bits all lie above the bool storage width (e.g. 256, or the sign bit of a
    996    * 128-bit operand) must still become 1. Emit an explicit compare-against-zero
    997    * for any non-bool scalar source. */
    998   if (dst->kind == TY_BOOL && src->kind != TY_BOOL) {
    999     if (emit) {
   1000       KitCgTypeId sid = src_id; /* cached TOS-slot id */
   1001       if (sf) {
   1002         kit_cg_push_float(p->cg, 0.0, sid);
   1003         kit_cg_fp_cmp(p->cg, KIT_CG_FP_UNE);
   1004       } else {
   1005         kit_cg_push_int(p->cg, 0, sid);
   1006         kit_cg_int_cmp(p->cg, KIT_CG_INT_NE);
   1007       }
   1008       /* The compare yields a 0/1 int; narrow it to the bool storage width. */
   1009       kit_cg_trunc(p->cg, id);
   1010     }
   1011     pcg_retag_top(p, dst);
   1012     return;
   1013   }
   1014   if (type_is_ptr(src) && type_is_ptr(dst)) {
   1015     if (emit) kit_cg_bitcast(p->cg, id);
   1016     pcg_retag_top(p, dst);
   1017     return;
   1018   }
   1019   if (si && di) {
   1020     if (ds < ss) {
   1021       if (emit) kit_cg_trunc(p->cg, id);
   1022     } else if (ds > ss && type_is_int(src) && pcg_type_is_signed(src)) {
   1023       if (emit) kit_cg_sext(p->cg, id);
   1024     } else if (ds > ss) {
   1025       if (emit) kit_cg_zext(p->cg, id);
   1026     } else if (type_is_ptr(src) != type_is_ptr(dst)) {
   1027       if (emit) kit_cg_bitcast(p->cg, id);
   1028     }
   1029   } else if (type_is_int(src) && df) {
   1030     if (pcg_type_is_signed(src)) {
   1031       if (emit) kit_cg_sint_to_float(p->cg, id, KIT_CG_ROUND_DEFAULT);
   1032     } else {
   1033       if (emit) kit_cg_uint_to_float(p->cg, id, KIT_CG_ROUND_DEFAULT);
   1034     }
   1035   } else if (sf && type_is_int(dst)) {
   1036     if (pcg_type_is_signed(dst)) {
   1037       if (emit) kit_cg_float_to_sint(p->cg, id, KIT_CG_ROUND_DEFAULT);
   1038     } else {
   1039       if (emit) kit_cg_float_to_uint(p->cg, id, KIT_CG_ROUND_DEFAULT);
   1040     }
   1041   } else if (sf && df) {
   1042     if (ds > ss) {
   1043       if (emit) kit_cg_fpext(p->cg, id);
   1044     } else if (ds < ss) {
   1045       if (emit) kit_cg_fptrunc(p->cg, id);
   1046     }
   1047   } else {
   1048     if (emit) kit_cg_bitcast(p->cg, id);
   1049   }
   1050   pcg_retag_top(p, dst);
   1051 }
   1052 
   1053 /* Emit "value <op> step" for an inc/dec, picking the float or integer binop
   1054  * based on the operand type. Floating operands step by 1.0 via an FP add/sub;
   1055  * pointers step by the pointee size and everything else by 1. */
   1056 static void pcg_emit_inc_step(Parser* p, const Type* ty, BinOp op,
   1057                               KitCgIntBinOp cg_op, const Type* step_ty,
   1058                               u32 step) {
   1059   if (pcg_type_is_fp(ty)) {
   1060     BinOp fop = (op == BO_ISUB) ? BO_FSUB : BO_FADD;
   1061     kit_cg_push_float(p->cg, 1.0, pcg_tid(p, ty));
   1062     kit_cg_fp_binop(p->cg, pcg_fp_binop(fop), KIT_CG_FP_NONE);
   1063   } else {
   1064     i64 amount = (ty && ty->kind == TY_PTR) ? (i64)step : 1;
   1065     kit_cg_push_int(p->cg, amount, pcg_tid(p, step_ty));
   1066     kit_cg_int_binop(p->cg, cg_op, 0);
   1067   }
   1068 }
   1069 
   1070 void pcg_inc_dec(Parser* p, BinOp op, int post) {
   1071   const Type* ty = pcg_top_type(p);
   1072   /* Cache the TOS-slot id NOW: pcg_materialize_lv_to_ptr below retypes the slot
   1073    * to a pointer, so a later pcg_top_cg_id would no longer be `ty`'s id. */
   1074   KitCgTypeId ty_cg = pcg_top_cg_id(p);
   1075   if (!pcg_emit_enabled(p)) {
   1076     /* Drop the lvalue parser slot and push the rvalue result type. */
   1077     pcg_drop_type(p);
   1078     pcg_push_type(p, ty);
   1079     return;
   1080   }
   1081   {
   1082     KitCgIntBinOp cg_op = pcg_int_binop(op);
   1083     PcgLvAux* lv = pcg_top_lv_aux(p);
   1084     /* Snapshot bit-field geometry before materialize clears the aux. */
   1085     PcgLvAux bf = lv ? *lv : (PcgLvAux){0};
   1086     KitCgMemAccess access = pcg_mem_id(p, ty_cg, ty);
   1087     const Type* step_ty = ty;
   1088     u32 step = 1;
   1089     if (ty && ty->kind == TY_PTR) {
   1090       const Type* pointee = ty->ptr.pointee;
   1091       if (pointee && pointee->kind == TY_VOID)
   1092         perr(p, "pointer arithmetic on void pointer");
   1093       step = c_abi_sizeof(p->abi, p->pool, pointee);
   1094       step_ty = c_abi_ptrdiff_type(p->abi, p->pool);
   1095     }
   1096     /* Materialize the lvalue to a single destination pointer so its address can
   1097      * be duplicated for the read-modify-write. */
   1098     pcg_materialize_lv_to_ptr(p, type_ptr(p->pool, ty));
   1099     {
   1100       FrameSlotDesc fsd;
   1101       FrameSlot tmp;
   1102       const Type* result_ty = ty;
   1103       KitCgMemAccess r_access = pcg_mem_id(p, ty_cg, result_ty);
   1104       memset(&fsd, 0, sizeof fsd);
   1105       fsd.type = result_ty;
   1106       fsd.size = c_abi_sizeof(p->abi, p->pool, result_ty);
   1107       fsd.align = c_abi_alignof(p->abi, p->pool, result_ty);
   1108       fsd.kind = FS_LOCAL;
   1109       tmp = pcg_local(p, &fsd);
   1110       kit_cg_dup(p->cg);      /* [ptr, ptr] */
   1111       kit_cg_deref(p->cg, 0); /* [ptr, place] */
   1112       pcg_apply_bitfield(p, &bf);
   1113       kit_cg_load(p->cg, access); /* [ptr, old] */
   1114       if (post) {
   1115         /* Stash old, compute new, store, then re-load old as result. */
   1116         kit_cg_dup(p->cg); /* [ptr, old, old] */
   1117         kit_cg_push_local(p->cg, tmp);
   1118         kit_cg_swap(p->cg);
   1119         kit_cg_store(p->cg, r_access);                      /* [ptr, old] */
   1120         pcg_emit_inc_step(p, ty, op, cg_op, step_ty, step); /* [ptr, new] */
   1121         kit_cg_swap(p->cg);                                 /* [new, ptr] */
   1122         kit_cg_deref(p->cg, 0);                             /* [new, place] */
   1123         pcg_apply_bitfield(p, &bf);
   1124         kit_cg_swap(p->cg);          /* [place, new] */
   1125         kit_cg_store(p->cg, access); /* [] */
   1126         kit_cg_push_local(p->cg, tmp);
   1127         kit_cg_load(p->cg, r_access); /* [old] */
   1128       } else {
   1129         /* Compute new, stash new, store, then re-load new as result. */
   1130         pcg_emit_inc_step(p, ty, op, cg_op, step_ty, step); /* [ptr, new] */
   1131         kit_cg_dup(p->cg); /* [ptr, new, new] */
   1132         kit_cg_push_local(p->cg, tmp);
   1133         kit_cg_swap(p->cg);
   1134         kit_cg_store(p->cg, r_access); /* [ptr, new] */
   1135         kit_cg_swap(p->cg);            /* [new, ptr] */
   1136         kit_cg_deref(p->cg, 0);        /* [new, place] */
   1137         pcg_apply_bitfield(p, &bf);
   1138         kit_cg_swap(p->cg);          /* [place, new] */
   1139         kit_cg_store(p->cg, access); /* [] */
   1140         kit_cg_push_local(p->cg, tmp);
   1141         kit_cg_load(p->cg, r_access); /* [new] */
   1142       }
   1143       (void)step;
   1144     }
   1145   }
   1146   /* Parser stack: drop the lvalue slot, push the result rvalue type. */
   1147   pcg_drop_type(p);
   1148   pcg_push_type(p, ty);
   1149 }
   1150 
   1151 void pcg_call(Parser* p, u32 nargs, const Type* fn_type) {
   1152   if (pcg_emit_enabled(p)) {
   1153     kit_cg_call_default(p->cg, nargs, pcg_tid(p, fn_type));
   1154   }
   1155   for (u32 i = 0; i < nargs + 1u; ++i) pcg_drop_type(p);
   1156   if (fn_type && fn_type->kind == TY_FUNC && fn_type->fn.ret->kind != TY_VOID) {
   1157     pcg_push_type(p, fn_type->fn.ret);
   1158   }
   1159 }
   1160 
   1161 void pcg_call_symbol(Parser* p, KitCgSym sym, u32 nargs, const Type* fn_type) {
   1162   if (pcg_emit_enabled(p)) {
   1163     KitCgCallAttrs attrs;
   1164     memset(&attrs, 0, sizeof attrs);
   1165     kit_cg_call_symbol(p->cg, sym, nargs, attrs);
   1166   }
   1167   for (u32 i = 0; i < nargs; ++i) pcg_drop_type(p);
   1168   if (fn_type && fn_type->kind == TY_FUNC && fn_type->fn.ret->kind != TY_VOID) {
   1169     pcg_push_type(p, fn_type->fn.ret);
   1170   }
   1171 }
   1172 
   1173 void pcg_ret(Parser* p, int has_value) {
   1174   if (has_value) {
   1175     if (pcg_emit_enabled(p)) kit_cg_ret(p->cg);
   1176     pcg_drop_type(p);
   1177   } else if (pcg_emit_enabled(p)) {
   1178     /* No value supplied. For a void function this is the normal 0-result
   1179      * return. For a non-void function it is a UB fall-off-the-end or an
   1180      * already-diagnosed bare `return;` — terminate with unreachable rather
   1181      * than asking kit_cg_ret to pop a result that was never pushed. */
   1182     if (p->cur_func_ret && p->cur_func_ret->kind != TY_VOID)
   1183       kit_cg_unreachable(p->cg);
   1184     else
   1185       kit_cg_ret(p->cg);
   1186   }
   1187 }
   1188 
   1189 void pcg_alloca(Parser* p) {
   1190   if (pcg_emit_enabled(p)) {
   1191     kit_cg_alloca(p->cg, 16, pcg_tid(p, type_ptr(p->pool, type_void(p->pool))));
   1192   }
   1193   pcg_drop_type(p);
   1194   pcg_push_type(p, type_ptr(p->pool, type_void(p->pool)));
   1195 }
   1196 
   1197 void pcg_va_arg(Parser* p, const Type* ty) {
   1198   if (pcg_emit_enabled(p)) kit_cg_vararg_next(p->cg, pcg_tid(p, ty));
   1199   pcg_drop_type(p);
   1200   pcg_push_type(p, ty);
   1201 }
   1202 
   1203 void pcg_va_start(Parser* p) {
   1204   if (pcg_emit_enabled(p)) kit_cg_vararg_start(p->cg);
   1205 }
   1206 
   1207 void pcg_va_end(Parser* p) {
   1208   if (pcg_emit_enabled(p)) kit_cg_vararg_end(p->cg);
   1209 }
   1210 
   1211 void pcg_va_copy(Parser* p) {
   1212   if (pcg_emit_enabled(p)) kit_cg_vararg_copy(p->cg);
   1213 }
   1214 
   1215 void pcg_atomic_load(Parser* p, MemOrder ord) {
   1216   const Type* pty = pcg_top_type(p);
   1217   const Type* ty = (pty && pty->kind == TY_PTR) ? pty->ptr.pointee : pty;
   1218   if (pcg_emit_enabled(p)) {
   1219     kit_cg_atomic_load(p->cg, pcg_mem(p, ty), pcg_mem_order(ord));
   1220   }
   1221   pcg_retag_top(p, ty);
   1222 }
   1223 
   1224 void pcg_atomic_store(Parser* p, MemOrder ord) {
   1225   const Type* pty = pcg_top2_type(p);
   1226   const Type* ty = (pty && pty->kind == TY_PTR) ? pty->ptr.pointee : pty;
   1227   if (pcg_emit_enabled(p)) {
   1228     kit_cg_atomic_store(p->cg, pcg_mem(p, ty), pcg_mem_order(ord));
   1229   }
   1230   pcg_drop_type(p);
   1231   pcg_drop_type(p);
   1232 }
   1233 
   1234 void pcg_atomic_rmw(Parser* p, AtomicOp op, MemOrder ord) {
   1235   const Type* pty = pcg_top2_type(p);
   1236   const Type* ty = (pty && pty->kind == TY_PTR) ? pty->ptr.pointee : pty;
   1237   if (pcg_emit_enabled(p)) {
   1238     kit_cg_atomic_rmw(p->cg, pcg_mem(p, ty), pcg_atomic_op(op),
   1239                       pcg_mem_order(ord));
   1240   }
   1241   pcg_drop_type(p);
   1242   pcg_retag_top(p, ty);
   1243 }
   1244 
   1245 void pcg_atomic_cas(Parser* p, MemOrder succ, MemOrder fail) {
   1246   const Type* ty = pcg_top2_type(p);
   1247   if (pcg_emit_enabled(p)) {
   1248     kit_cg_atomic_cmpxchg(p->cg, pcg_mem(p, ty), pcg_mem_order(succ),
   1249                           pcg_mem_order(fail), 0);
   1250   }
   1251   pcg_drop_type(p); /* desired */
   1252   pcg_drop_type(p); /* expected */
   1253   pcg_drop_type(p); /* pointer */
   1254   pcg_push_type(p, ty);
   1255   pcg_push_type(p, type_prim(p->pool, TY_BOOL));
   1256 }
   1257 
   1258 void pcg_fence(Parser* p, MemOrder ord) {
   1259   if (pcg_emit_enabled(p)) kit_cg_atomic_fence(p->cg, pcg_mem_order(ord));
   1260 }
   1261 
   1262 void pcg_intrinsic_unary_to_int(Parser* p, IntrinKind k) {
   1263   KitCgIntrinsic ck = k == INTRIN_CLZ   ? KIT_CG_INTRIN_CLZ
   1264                       : k == INTRIN_CTZ ? KIT_CG_INTRIN_CTZ
   1265                                         : KIT_CG_INTRIN_POPCOUNT;
   1266   const Type* ity = type_prim(p->pool, TY_INT);
   1267   if (pcg_emit_enabled(p)) {
   1268     kit_cg_intrinsic(p->cg, ck, 1, pcg_tid(p, ity));
   1269   }
   1270   pcg_retag_top(p, ity);
   1271 }
   1272 
   1273 void pcg_intrinsic_void(Parser* p, IntrinKind k) {
   1274   if (k == INTRIN_UNREACHABLE) {
   1275     if (pcg_emit_enabled(p)) kit_cg_unreachable(p->cg);
   1276   } else {
   1277     if (pcg_emit_enabled(p)) {
   1278       kit_cg_intrinsic(p->cg, KIT_CG_INTRIN_TRAP, 0,
   1279                        kit_cg_type_builtin(p->c, KIT_CG_BUILTIN_VOID));
   1280     }
   1281   }
   1282 }
   1283 
   1284 void pcg_syscall(Parser* p, u32 nargs, const Type* long_ty) {
   1285   if (pcg_emit_enabled(p))
   1286     kit_cg_intrinsic(p->cg, KIT_CG_INTRIN_SYSCALL, nargs, pcg_tid(p, long_ty));
   1287   for (u32 i = 0; i < nargs; ++i) pcg_drop_type(p);
   1288   pcg_push_type(p, long_ty);
   1289 }
   1290 
   1291 /* __builtin_readcyclecounter(): read the target cycle/timestamp counter. No
   1292  * operands; pushes an unsigned long long result. */
   1293 void pcg_readcyclecounter(Parser* p) {
   1294   const Type* ull = type_prim(p->pool, TY_ULLONG);
   1295   if (pcg_emit_enabled(p))
   1296     kit_cg_intrinsic(p->cg, KIT_CG_INTRIN_READCYCLECOUNTER, 0, pcg_tid(p, ull));
   1297   pcg_push_type(p, ull);
   1298 }
   1299 
   1300 /* __builtin_return_address(level) / __builtin_frame_address(level): emit the
   1301  * frame-pointer-chain intrinsic. The constant level rides as a single immediate
   1302  * operand (kept as OPK_IMM by kit_cg_intrinsic); the result is void*. */
   1303 void pcg_frame_or_return_address(Parser* p, int is_return, u32 level) {
   1304   const Type* void_ptr = type_ptr(p->pool, type_void(p->pool));
   1305   KitCgIntrinsic intrin =
   1306       is_return ? KIT_CG_INTRIN_RETURN_ADDRESS : KIT_CG_INTRIN_FRAME_ADDRESS;
   1307   pcg_push_int(p, (i64)level, type_prim(p->pool, TY_INT));
   1308   if (pcg_emit_enabled(p))
   1309     kit_cg_intrinsic(p->cg, intrin, 1, pcg_tid(p, void_ptr));
   1310   /* kit_cg_intrinsic popped the level operand and pushed the void* result;
   1311    * retag the slot pcg_push_int added (also clears the level's value flags,
   1312    * e.g. the null-pointer-constant tag set for level 0). */
   1313   pcg_retag_top(p, void_ptr);
   1314 }
   1315 
   1316 void pcg_inline_asm(Parser* p, const char* tmpl, const AsmConstraint* outs,
   1317                     u32 nout, const AsmConstraint* ins, u32 nin,
   1318                     const Sym* clobbers, u32 nclob) {
   1319   KitCgInlineAsm a;
   1320   KitCgAsmOperand* o = NULL;
   1321   KitCgAsmOperand* in = NULL;
   1322   KitSym* cl = NULL;
   1323   memset(&a, 0, sizeof a);
   1324   a.tmpl = kit_sym_intern(p->c, kit_slice_cstr(tmpl ? tmpl : ""));
   1325   if (nout) {
   1326     o = arena_zarray(p->pool->arena, KitCgAsmOperand, nout);
   1327     for (u32 i = 0; i < nout; ++i) {
   1328       o[i].constraint =
   1329           kit_sym_intern(p->c, kit_slice_cstr(outs[i].str ? outs[i].str : ""));
   1330       o[i].name = outs[i].name;
   1331       o[i].type = pcg_tid(p, outs[i].type);
   1332       o[i].reg = outs[i].reg;
   1333       o[i].dir = KIT_CG_ASM_OUT;
   1334     }
   1335   }
   1336   if (nin) {
   1337     in = arena_zarray(p->pool->arena, KitCgAsmOperand, nin);
   1338     for (u32 i = 0; i < nin; ++i) {
   1339       in[i].constraint =
   1340           kit_sym_intern(p->c, kit_slice_cstr(ins[i].str ? ins[i].str : ""));
   1341       in[i].name = ins[i].name;
   1342       in[i].type = pcg_tid(p, ins[i].type);
   1343       in[i].reg = ins[i].reg;
   1344       in[i].dir = (ins[i].dir == ASM_INOUT) ? KIT_CG_ASM_INOUT : KIT_CG_ASM_IN;
   1345     }
   1346   }
   1347   if (nclob) {
   1348     cl = arena_array(p->pool->arena, KitSym, nclob);
   1349     for (u32 i = 0; i < nclob; ++i) cl[i] = clobbers[i];
   1350   }
   1351   a.outputs = o;
   1352   a.noutputs = nout;
   1353   a.inputs = in;
   1354   a.ninputs = nin;
   1355   a.clobbers = cl;
   1356   a.nclobbers = nclob;
   1357   if (pcg_emit_enabled(p)) kit_cg_inline_asm(p->cg, a);
   1358   /* Mirror kit_cg_inline_asm's stack effect on the parser's typed shadow stack.
   1359    */
   1360   for (u32 i = 0; i < nin; ++i) pcg_drop_type(p);
   1361   for (u32 i = 0; i < nout; ++i) pcg_push_type(p, outs[i].type);
   1362 }