kit

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

cg_adapter.c (42618B)


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