kit

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

value.c (19497B)


      1 #include "cg/internal.h"
      2 
      3 int api_type_is_float(Compiler* c, KitCgTypeId ty) {
      4   const CgType* cg;
      5   ty = api_unalias_type(c, ty);
      6   cg = cg_type_get(c, ty);
      7   return cg && cg->kind == KIT_CG_TYPE_FLOAT;
      8 }
      9 
     10 int api_is_f128_type(Compiler* c, KitCgTypeId ty) {
     11   const CgType* cg;
     12   ty = api_unalias_type(c, ty);
     13   cg = cg_type_get(c, ty);
     14   return cg && cg->kind == KIT_CG_TYPE_FLOAT && cg->fp.width == 128;
     15 }
     16 
     17 int api_is_i128_type(Compiler* c, KitCgTypeId ty) {
     18   const CgType* cg;
     19   ty = api_unalias_type(c, ty);
     20   cg = cg_type_get(c, ty);
     21   return cg && cg->kind == KIT_CG_TYPE_INT && cg->integer.width == 128;
     22 }
     23 
     24 int api_is_wide16_scalar_type(Compiler* c, KitCgTypeId ty) {
     25   return api_is_f128_type(c, ty) || api_is_i128_type(c, ty);
     26 }
     27 
     28 /* 8-byte scalar split into two 4-byte lanes by the selected ABI. This covers
     29  * 32-bit native ABIs whose generic CG path cannot keep the value in a single
     30  * scalar register/value. Such values are forced memory-resident so operations
     31  * can be legalized as lane sequences or runtime calls. */
     32 int api_is_wide8_scalar_type(Compiler* c, KitCgTypeId ty) {
     33   ABITypeInfo ti;
     34   if (!c || !c->abi || !ty) return 0;
     35   if (abi_cg_scalar_split_lane_size(c->abi, ty) != 4u) return 0;
     36   ti = abi_cg_type_info(c->abi, ty);
     37   return ti.size == 8u &&
     38          (ti.scalar_kind == ABI_SC_INT || ti.scalar_kind == ABI_SC_FLOAT);
     39 }
     40 
     41 Operand api_op_imm(i64 v, KitCgTypeId ty) {
     42   Operand o;
     43   memset(&o, 0, sizeof o);
     44   o.kind = OPK_IMM;
     45   o.type = ty;
     46   o.v.imm = v;
     47   return o;
     48 }
     49 
     50 Operand api_op_local(CGLocal local, KitCgTypeId ty) {
     51   Operand o;
     52   memset(&o, 0, sizeof o);
     53   o.kind = OPK_LOCAL;
     54   o.type = ty;
     55   o.v.local = local;
     56   return o;
     57 }
     58 
     59 Operand api_op_global(ObjSymId sym, i64 addend, KitCgTypeId ty) {
     60   Operand o;
     61   memset(&o, 0, sizeof o);
     62   o.kind = OPK_GLOBAL;
     63   o.type = ty;
     64   o.v.global.sym = sym;
     65   o.v.global.addend = addend;
     66   return o;
     67 }
     68 
     69 Operand api_op_indirect(CGLocal base, i32 ofs, KitCgTypeId ty) {
     70   Operand o;
     71   memset(&o, 0, sizeof o);
     72   o.kind = OPK_INDIRECT;
     73   o.type = ty;
     74   o.v.ind.base = base;
     75   o.v.ind.index = CG_LOCAL_NONE;
     76   o.v.ind.log2_scale = 0;
     77   o.v.ind.ofs = ofs;
     78   return o;
     79 }
     80 
     81 Operand api_op_indirect_indexed(CGLocal base, CGLocal index, u8 log2_scale,
     82                                 i32 ofs, KitCgTypeId ty) {
     83   Operand o;
     84   memset(&o, 0, sizeof o);
     85   o.kind = OPK_INDIRECT;
     86   o.type = ty;
     87   o.v.ind.base = base;
     88   o.v.ind.index = index;
     89   o.v.ind.log2_scale = log2_scale;
     90   o.v.ind.ofs = ofs;
     91   return o;
     92 }
     93 
     94 u8 api_residency_for(const Operand* o) {
     95   if (o->kind == OPK_LOCAL || o->kind == OPK_INDIRECT) return RES_LOCAL;
     96   return RES_INHERENT;
     97 }
     98 
     99 ApiSValue api_make_sv(Operand op, KitCgTypeId ty) {
    100   ApiSValue sv;
    101   memset(&sv, 0, sizeof sv);
    102   sv.kind = SV_OPERAND;
    103   sv.op = op;
    104   sv.type = ty;
    105   sv.res = api_residency_for(&op);
    106   sv.source_local = KIT_CG_LOCAL_NONE;
    107   return sv;
    108 }
    109 
    110 ApiSValue api_make_lv(Operand op, KitCgTypeId ty) {
    111   ApiSValue sv = api_make_sv(op, ty);
    112   sv.lvalue = 1;
    113   return sv;
    114 }
    115 
    116 ApiSValue api_make_sv_with_local_ownership(Operand op, KitCgTypeId ty,
    117                                            int owned) {
    118   ApiSValue sv = api_make_sv(op, ty);
    119   if (op.kind == OPK_LOCAL && !owned) sv.res = RES_FIXED_LOCAL;
    120   return sv;
    121 }
    122 
    123 KitCgTypeId api_sv_type(const ApiSValue* sv) {
    124   return sv->type ? sv->type : sv->op.type;
    125 }
    126 
    127 int api_operand_can_address(const Operand* o) {
    128   return o->kind == OPK_LOCAL || o->kind == OPK_GLOBAL ||
    129          o->kind == OPK_INDIRECT;
    130 }
    131 
    132 int api_sv_op_is(const ApiSValue* sv, OpKind kind) {
    133   return sv->kind == SV_OPERAND && sv->op.kind == kind;
    134 }
    135 
    136 int api_sv_op_is_local_or_imm(const ApiSValue* sv) {
    137   return sv->kind == SV_OPERAND &&
    138          (sv->op.kind == OPK_IMM || sv->op.kind == OPK_LOCAL);
    139 }
    140 
    141 /* A PLACE (lvalue) is exactly an SV_OPERAND whose operand addresses storage:
    142  * op.kind in { OPK_LOCAL, OPK_GLOBAL, OPK_INDIRECT } (api_operand_can_address).
    143  * This is a kind-based predicate, not a heuristic: the `lvalue` flag marks the
    144  * value as a place, SV_OPERAND guarantees `op` is meaningful (SV_CMP/SV_ARITH
    145  * never carry lvalue=1 — see fold.c, which clears it on materialization), and
    146  * the operand kind decides addressability. The former heuristic OR'd in
    147  * `bitfield_lvalue` and a (source_local && OPK_LOCAL) term, both subsumed here:
    148  * a bit-field place is an ordinary addressable place (it addresses the storage
    149  * unit) that additionally carries a bit descriptor — see api_sv_is_bitfield and
    150  * kit_cg_field — and source_local only co-occurs with OPK_LOCAL, so
    151  * api_operand_can_address already covers both. */
    152 int api_is_lvalue_sv(const ApiSValue* sv) {
    153   return sv->lvalue && sv->kind == SV_OPERAND &&
    154          api_operand_can_address(&sv->op);
    155 }
    156 
    157 /* A bit-field PLACE is a PLACE that additionally carries bit geometry (set by
    158  * kit_cg_field when it projects to a bit-field). The plain load/store detect
    159  * this and perform the extract/insert; there is no separate bit-field memop. */
    160 int api_sv_is_bitfield(const ApiSValue* sv) {
    161   return sv->bitfield.bit_width != 0;
    162 }
    163 
    164 /* Rebuild the full BitFieldAccess the extract/insert helper consumes from the
    165  * geometry the place carries plus the storage operand it addresses. The storage
    166  * MemAccess is derived from the operand + field type, the way the old
    167  * bf_from_access did, so the place need only carry the bit geometry. */
    168 /* Build the bit-field MemAccess that rides the generic load/store: the storage
    169  * unit ({type,size}) plus the bf_* bit geometry from the place's descriptor.
    170  * The CgTarget impl reconstructs a BitFieldAccess via bf_from_mem. */
    171 MemAccess api_mem_for_bitfield(KitCg* g, const ApiSValue* sv,
    172                                const Operand* storage, KitCgTypeId field_ty) {
    173   MemAccess m = api_mem_for_lvalue(g, storage, field_ty);
    174   if (sv->bitfield.bit_storage_size) m.size = sv->bitfield.bit_storage_size;
    175   m.bf_offset = sv->bitfield.bit_offset;
    176   m.bf_width = sv->bitfield.bit_width;
    177   m.bf_signed = sv->bitfield.bit_signed ? 1 : 0;
    178   return m;
    179 }
    180 
    181 void api_stack_grow(KitCg* g, u32 want) {
    182   Heap* h = g->c->ctx->heap;
    183   u32 cap = g->cap;
    184   ApiSValue* nb;
    185   if (cap >= want) return;
    186   while (cap < want) cap = cap ? cap * 2u : API_CG_STACK_INITIAL;
    187   nb = (ApiSValue*)h->alloc(h, sizeof(ApiSValue) * cap, _Alignof(ApiSValue));
    188   if (g->stack) {
    189     memcpy(nb, g->stack, sizeof(ApiSValue) * g->sp);
    190     h->free(h, g->stack, sizeof(ApiSValue) * g->cap);
    191   }
    192   g->stack = nb;
    193   g->cap = cap;
    194 }
    195 
    196 void api_push(KitCg* g, ApiSValue v) {
    197   /* An aggregate (record) can only ever be a PLACE: it is addressed, loaded,
    198    * and passed by SRET/BYVAL/BYREF, never materialized as a scalar VALUE. Catch
    199    * any aggregate VALUE at the point it would enter the stack. i128/f128 are
    200    * scalars (cg_type_is_aggregate is false for them), so they remain valid
    201    * VALUEs and are unaffected. */
    202   if (cg_type_is_aggregate(g->c, api_sv_type(&v)) && !api_is_lvalue_sv(&v)) {
    203     compiler_panic(g->c, g->cur_loc,
    204                    "KitCg: aggregate must be a place, not a value; load the "
    205                    "place or pass it by reference");
    206   }
    207   api_stack_grow(g, g->sp + 1);
    208   g->stack[g->sp++] = v;
    209 }
    210 
    211 ApiSValue api_pop(KitCg* g) {
    212   if (g->sp == 0) {
    213     compiler_panic(g->c, g->cur_loc, "KitCg: stack underflow");
    214   }
    215   return g->stack[--g->sp];
    216 }
    217 
    218 /* ---- local helpers ---- */
    219 
    220 CGLocal api_local_of_sv(const ApiSValue* sv) {
    221   if (sv->kind == SV_ARITH || sv->kind == SV_CMP) return (CGLocal)CG_LOCAL_NONE;
    222   if (sv->op.kind == OPK_LOCAL) return sv->op.v.local;
    223   if (sv->op.kind == OPK_INDIRECT) return sv->op.v.ind.base;
    224   return (CGLocal)CG_LOCAL_NONE;
    225 }
    226 
    227 void api_set_owned_local(ApiSValue* sv, CGLocal r) {
    228   if (sv->op.kind == OPK_LOCAL)
    229     sv->op.v.local = r;
    230   else if (sv->op.kind == OPK_INDIRECT)
    231     sv->op.v.ind.base = r;
    232 }
    233 
    234 KitCgTypeId api_owned_local_type(KitCg* g, const ApiSValue* sv) {
    235   if (sv->op.kind == OPK_INDIRECT) {
    236     KitCgTypeId base = sv->type ? sv->type : builtin_id(KIT_CG_BUILTIN_VOID);
    237     return cg_type_ptr_to(g->c, base);
    238   }
    239   return api_sv_type(sv);
    240 }
    241 
    242 /* ---- temporary local allocation ---- */
    243 
    244 void api_temp_locals_begin(KitCg* g) { (void)g; }
    245 
    246 void api_temp_locals_finish(KitCg* g) { (void)g; }
    247 
    248 CGLocal api_alloc_temp_local(KitCg* g, KitCgTypeId ty) {
    249   CGLocalDesc d;
    250   CGLocal local;
    251   memset(&d, 0, sizeof d);
    252   d.type = ty;
    253   if (ty) {
    254     d.size = abi_cg_sizeof(g->c->abi, ty);
    255     d.align = abi_cg_alignof(g->c->abi, ty);
    256   }
    257   /* A split-lane 8-byte scalar temp must live in memory so its two words are
    258    * addressable for lane ops and the multi-part ABI path; the allocator gives
    259    * an unflagged scalar one value slot, which would truncate it. (wide16 temps
    260    * are already forced via the size>word auto-home in cg_ir_lower.) */
    261   if (ty && api_is_wide8_scalar_type(g->c, ty))
    262     d.flags |= CG_LOCAL_MEMORY_REQUIRED;
    263   local = g->target->local(g->target, &d);
    264   if (local == CG_LOCAL_NONE) {
    265     compiler_panic(g->c, g->cur_loc,
    266                    "KitCg: target failed to allocate temporary local");
    267   }
    268   return local;
    269 }
    270 
    271 void api_release_temp_local(KitCg* g, CGLocal r) {
    272   (void)g;
    273   (void)r;
    274 }
    275 
    276 MemAccess api_mem_for_lvalue(KitCg* g, const Operand* lv, KitCgTypeId ty) {
    277   MemAccess m;
    278   memset(&m, 0, sizeof m);
    279   m.type = ty;
    280   m.size = ty ? abi_cg_sizeof(g->c->abi, ty) : 0;
    281   m.align = ty ? abi_cg_alignof(g->c->abi, ty) : 0;
    282   m.flags = MF_NONE;
    283   if (lv->kind == OPK_LOCAL) {
    284     m.alias.kind = (u8)ALIAS_LOCAL;
    285     m.alias.v.local_id = (i32)lv->v.local;
    286   } else if (lv->kind == OPK_GLOBAL) {
    287     m.alias.kind = (u8)ALIAS_GLOBAL;
    288   } else {
    289     m.alias.kind = (u8)ALIAS_UNKNOWN;
    290   }
    291   return m;
    292 }
    293 
    294 MemAccess api_mem_from_access(KitCg* g, const Operand* lv,
    295                               KitCgMemAccess access) {
    296   KitCgTypeId ty = resolve_type(g->c, access.type);
    297   MemAccess m = api_mem_for_lvalue(g, lv, ty);
    298   if (access.align) m.align = access.align;
    299   m.addr_space = (u16)access.address_space;
    300   if (access.flags & KIT_CG_MEM_VOLATILE) m.flags |= MF_VOLATILE;
    301   if (!access.align || (ty && access.align < abi_cg_alignof(g->c->abi, ty))) {
    302     m.flags |= MF_UNALIGNED;
    303   }
    304   return m;
    305 }
    306 
    307 KitCgTypeId api_mem_access_type(KitCg* g, KitCgMemAccess access,
    308                                 KitCgTypeId fallback, const char* who) {
    309   KitCgTypeId ty = resolve_type(g->c, access.type);
    310   if (!ty) ty = resolve_type(g->c, fallback);
    311   if (!ty) {
    312     compiler_panic(g->c, g->cur_loc, "KitCg: %.*s has no value type",
    313                    SLICE_ARG(slice_from_cstr(who)));
    314   }
    315   return ty;
    316 }
    317 
    318 u32 api_mem_type_size(KitCg* g, KitCgTypeId ty, const char* who) {
    319   ty = resolve_type(g->c, ty);
    320   if (!ty) {
    321     compiler_panic(g->c, g->cur_loc, "KitCg: %.*s has invalid type",
    322                    SLICE_ARG(slice_from_cstr(who)));
    323   }
    324   if (cg_type_is_void(g->c, ty)) {
    325     compiler_panic(g->c, g->cur_loc, "KitCg: %.*s uses void type",
    326                    SLICE_ARG(slice_from_cstr(who)));
    327   }
    328   return abi_cg_sizeof(g->c->abi, ty);
    329 }
    330 
    331 void api_require_scalar_mem_type(KitCg* g, const char* who, KitCgTypeId ty) {
    332   if (cg_type_is_aggregate(g->c, ty)) {
    333     compiler_panic(g->c, g->cur_loc,
    334                    "KitCg: %.*s cannot use aggregate value type (size %u); "
    335                    "copy fields or use byte memory operations",
    336                    SLICE_ARG(slice_from_cstr(who)),
    337                    (unsigned)api_mem_type_size(g, ty, who));
    338   }
    339   (void)api_mem_type_size(g, ty, who);
    340 }
    341 
    342 void api_require_pointer_value(KitCg* g, const char* who, KitCgTypeId ty) {
    343   if (!cg_type_pointee(g->c, ty)) {
    344     compiler_panic(g->c, g->cur_loc, "KitCg: %.*s operand must be a pointer",
    345                    SLICE_ARG(slice_from_cstr(who)));
    346   }
    347 }
    348 
    349 void api_validate_memory_value(KitCg* g, const char* who, KitCgTypeId access_ty,
    350                                KitCgTypeId value_ty) {
    351   u32 access_size;
    352   u32 value_size;
    353   access_ty = resolve_type(g->c, access_ty);
    354   value_ty = resolve_type(g->c, value_ty);
    355   api_require_scalar_mem_type(g, who, access_ty);
    356   if (!value_ty) {
    357     compiler_panic(g->c, g->cur_loc, "KitCg: %.*s value has no type",
    358                    SLICE_ARG(slice_from_cstr(who)));
    359   }
    360   if (cg_type_is_aggregate(g->c, value_ty)) {
    361     compiler_panic(g->c, g->cur_loc,
    362                    "KitCg: %.*s value is aggregate (size %u); copy fields or "
    363                    "use byte memory operations",
    364                    SLICE_ARG(slice_from_cstr(who)),
    365                    (unsigned)api_mem_type_size(g, value_ty, who));
    366   }
    367   access_size = api_mem_type_size(g, access_ty, who);
    368   value_size = api_mem_type_size(g, value_ty, who);
    369   if (access_size != value_size ||
    370       api_type_is_float(g->c, access_ty) != api_type_is_float(g->c, value_ty)) {
    371     compiler_panic(g->c, g->cur_loc,
    372                    "KitCg: %.*s value type/size mismatch: access size %u, "
    373                    "value size %u",
    374                    SLICE_ARG(slice_from_cstr(who)), (unsigned)access_size,
    375                    (unsigned)value_size);
    376   }
    377 }
    378 
    379 void api_release_operand_local(KitCg* g, Operand op) {
    380   if (op.kind == OPK_LOCAL) api_release_temp_local(g, op.v.local);
    381 }
    382 
    383 int api_sv_owns_operand_local(const ApiSValue* sv, const Operand* op) {
    384   return sv->res == RES_LOCAL && op->kind == OPK_LOCAL &&
    385          sv->op.kind == OPK_LOCAL && sv->op.v.local == op->v.local;
    386 }
    387 
    388 void api_ensure_local(KitCg* g, ApiSValue* sv) {
    389   if (sv->kind == SV_CMP) {
    390     KitCgTypeId ty = api_sv_type(sv);
    391     Operand dst;
    392     if (sv->delayed.cmp.a_owned && sv->delayed.cmp.a.kind == OPK_LOCAL &&
    393         api_unalias_type(g->c, sv->delayed.cmp.a.type) ==
    394             api_unalias_type(g->c, ty)) {
    395       dst = api_op_local(sv->delayed.cmp.a.v.local, ty);
    396     } else if (sv->delayed.cmp.b_owned && sv->delayed.cmp.b.kind == OPK_LOCAL &&
    397                api_unalias_type(g->c, sv->delayed.cmp.b.type) ==
    398                    api_unalias_type(g->c, ty)) {
    399       dst = api_op_local(sv->delayed.cmp.b.v.local, ty);
    400     } else {
    401       CGLocal r =
    402           api_alloc_temp_local(g, ty ? ty : builtin_id(KIT_CG_BUILTIN_I32));
    403       dst = api_op_local(r, ty);
    404     }
    405     api_materialize_cmp_to(g, sv, dst);
    406     return;
    407   }
    408   if (sv->kind == SV_ARITH) {
    409     KitCgTypeId ty = api_sv_type(sv);
    410     Operand dst;
    411     if (sv->delayed.arith.a_owned && sv->delayed.arith.a.kind == OPK_LOCAL &&
    412         api_unalias_type(g->c, sv->delayed.arith.a.type) ==
    413             api_unalias_type(g->c, ty)) {
    414       dst = api_op_local(sv->delayed.arith.a.v.local, ty);
    415     } else if (api_arith_rhs_reusable(sv) && sv->delayed.arith.b_owned &&
    416                sv->delayed.arith.b.kind == OPK_LOCAL &&
    417                api_unalias_type(g->c, sv->delayed.arith.b.type) ==
    418                    api_unalias_type(g->c, ty)) {
    419       dst = api_op_local(sv->delayed.arith.b.v.local, ty);
    420     } else {
    421       CGLocal r =
    422           api_alloc_temp_local(g, ty ? ty : builtin_id(KIT_CG_BUILTIN_I32));
    423       dst = api_op_local(r, ty);
    424     }
    425     api_materialize_arith_to(g, sv, dst);
    426     return;
    427   }
    428   return;
    429 }
    430 
    431 Operand api_force_local(KitCg* g, ApiSValue* v, KitCgTypeId ty) {
    432   CgTarget* T = g->target;
    433   ty = api_unalias_type(g->c, ty);
    434   api_ensure_local(g, v);
    435   if (v->op.kind == OPK_LOCAL && !api_is_lvalue_sv(v)) {
    436     return v->op;
    437   }
    438   CGLocal r = api_alloc_temp_local(g, ty);
    439   Operand dst = api_op_local(r, ty);
    440   if (v->op.kind == OPK_IMM) {
    441     T->load_imm(T, dst, v->op.v.imm);
    442   } else if (api_is_lvalue_sv(v)) {
    443     T->load(T, dst, v->op, api_mem_for_lvalue(g, &v->op, ty));
    444     if (v->op.kind == OPK_INDIRECT) {
    445       api_release_temp_local(g, v->op.v.ind.base);
    446     }
    447   } else if (v->op.kind == OPK_GLOBAL) {
    448     T->addr_of(T, dst, v->op);
    449   } else {
    450     compiler_panic(g->c, g->cur_loc, "KitCg: cannot force operand to local");
    451   }
    452   v->op = dst;
    453   v->res = RES_LOCAL;
    454   return dst;
    455 }
    456 
    457 Operand api_force_local_unless_imm(KitCg* g, ApiSValue* v, KitCgTypeId ty) {
    458   if (api_sv_op_is(v, OPK_IMM)) return v->op;
    459   return api_force_local(g, v, ty);
    460 }
    461 
    462 void api_release(KitCg* g, ApiSValue* sv) {
    463   if (sv->kind == SV_CMP) {
    464     api_release_cmp(g, sv);
    465   } else if (sv->kind == SV_ARITH) {
    466     api_release_arith(g, sv);
    467   } else if (sv->res == RES_LOCAL) {
    468     api_release_temp_local(g, (CGLocal)api_local_of_sv(sv));
    469   }
    470   sv->res = RES_INHERENT;
    471 }
    472 
    473 /* ---- BinOp / UnOp / CmpOp mapping ---- */
    474 
    475 BinOp api_map_int_binop(KitCgIntBinOp op) {
    476   switch (op) {
    477     case KIT_CG_INT_ADD:
    478       return BO_IADD;
    479     case KIT_CG_INT_SUB:
    480       return BO_ISUB;
    481     case KIT_CG_INT_MUL:
    482       return BO_IMUL;
    483     case KIT_CG_INT_SDIV:
    484       return BO_SDIV;
    485     case KIT_CG_INT_UDIV:
    486       return BO_UDIV;
    487     case KIT_CG_INT_SREM:
    488       return BO_SREM;
    489     case KIT_CG_INT_UREM:
    490       return BO_UREM;
    491     case KIT_CG_INT_AND:
    492       return BO_AND;
    493     case KIT_CG_INT_OR:
    494       return BO_OR;
    495     case KIT_CG_INT_XOR:
    496       return BO_XOR;
    497     case KIT_CG_INT_SHL:
    498       return BO_SHL;
    499     case KIT_CG_INT_LSHR:
    500       return BO_SHR_U;
    501     case KIT_CG_INT_ASHR:
    502       return BO_SHR_S;
    503   }
    504   return BO_IADD;
    505 }
    506 
    507 BinOp api_map_fp_binop(KitCgFpBinOp op) {
    508   switch (op) {
    509     case KIT_CG_FP_ADD:
    510       return BO_FADD;
    511     case KIT_CG_FP_SUB:
    512       return BO_FSUB;
    513     case KIT_CG_FP_MUL:
    514       return BO_FMUL;
    515     case KIT_CG_FP_DIV:
    516       return BO_FDIV;
    517   }
    518   return BO_FADD;
    519 }
    520 
    521 UnOp api_map_int_unop(KitCgIntUnOp op) {
    522   switch (op) {
    523     case KIT_CG_INT_NEG:
    524       return UO_NEG;
    525     case KIT_CG_INT_NOT:
    526       return UO_NOT;
    527     case KIT_CG_INT_BNOT:
    528       return UO_BNOT;
    529   }
    530   return UO_NEG;
    531 }
    532 
    533 CmpOp api_map_int_cmp(KitCgIntCmpOp op) {
    534   switch (op) {
    535     case KIT_CG_INT_EQ:
    536       return CMP_EQ;
    537     case KIT_CG_INT_NE:
    538       return CMP_NE;
    539     case KIT_CG_INT_LT_S:
    540       return CMP_LT_S;
    541     case KIT_CG_INT_LE_S:
    542       return CMP_LE_S;
    543     case KIT_CG_INT_GT_S:
    544       return CMP_GT_S;
    545     case KIT_CG_INT_GE_S:
    546       return CMP_GE_S;
    547     case KIT_CG_INT_LT_U:
    548       return CMP_LT_U;
    549     case KIT_CG_INT_LE_U:
    550       return CMP_LE_U;
    551     case KIT_CG_INT_GT_U:
    552       return CMP_GT_U;
    553     case KIT_CG_INT_GE_U:
    554       return CMP_GE_U;
    555   }
    556   return CMP_EQ;
    557 }
    558 
    559 /* 1:1: the internal FP block mirrors KitCgFpCmpOp's order, so the public
    560  * ordered/unordered distinction is preserved all the way to the backends. */
    561 CmpOp api_map_fp_cmp(KitCgFpCmpOp op) {
    562   switch (op) {
    563     case KIT_CG_FP_OEQ:
    564       return CMP_OEQ_F;
    565     case KIT_CG_FP_ONE:
    566       return CMP_ONE_F;
    567     case KIT_CG_FP_OLT:
    568       return CMP_OLT_F;
    569     case KIT_CG_FP_OLE:
    570       return CMP_OLE_F;
    571     case KIT_CG_FP_OGT:
    572       return CMP_OGT_F;
    573     case KIT_CG_FP_OGE:
    574       return CMP_OGE_F;
    575     case KIT_CG_FP_UEQ:
    576       return CMP_UEQ_F;
    577     case KIT_CG_FP_UNE:
    578       return CMP_UNE_F;
    579     case KIT_CG_FP_ULT:
    580       return CMP_ULT_F;
    581     case KIT_CG_FP_ULE:
    582       return CMP_ULE_F;
    583     case KIT_CG_FP_UGT:
    584       return CMP_UGT_F;
    585     case KIT_CG_FP_UGE:
    586       return CMP_UGE_F;
    587   }
    588   return CMP_OEQ_F;
    589 }
    590 
    591 Operand api_lvalue_addr(KitCg* g, ApiSValue* v, KitCgTypeId pty) {
    592   CgTarget* T;
    593   ApiSourceLocal* rec;
    594   CGLocal r;
    595   Operand dst;
    596   api_local_const_address_taken(g, v->source_local);
    597   api_ensure_local(g, v);
    598   if (!api_is_lvalue_sv(v)) {
    599     compiler_panic(g->c, g->cur_loc, "KitCg: addr operand is not an lvalue");
    600   }
    601   T = g->target;
    602   r = api_alloc_temp_local(g, pty);
    603   dst = api_op_local(r, pty);
    604   rec = v->source_local != KIT_CG_LOCAL_NONE
    605             ? api_local_from_handle(g, v->source_local)
    606             : NULL;
    607   if (rec && rec->storage != CG_LOCAL_NONE && T->local_addr)
    608     T->local_addr(T, dst, &rec->desc, rec->storage);
    609   else
    610     T->addr_of(T, dst, v->op);
    611   return dst;
    612 }
    613 
    614 /* ---- C-symbol mangling ---- */