kit

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

builtins.c (61761B)


      1 #include <string.h>
      2 
      3 #include "internal.h"
      4 
      5 static KitCgMemAccess toy_mem_access_align(ToyParser* p, KitCgTypeId type,
      6                                            uint32_t align) {
      7   KitCgMemAccess access = toy_mem_access(p, type);
      8   access.align = align;
      9   return access;
     10 }
     11 
     12 static int toy_parse_mem_flags_tail(ToyParser* p, KitCgMemAccess* access) {
     13   static const ToyConstRow rows[] = {{"volatile", KIT_CG_MEM_VOLATILE}};
     14   return toy_parse_flag_set(p, rows, sizeof rows / sizeof rows[0],
     15                             "memory access flag", 1, &access->flags);
     16 }
     17 
     18 static void toy_store_top_to_local(ToyParser* p, KitCgLocal local,
     19                                    KitCgTypeId ty) {
     20   kit_cg_push_local(p->cg, local);
     21   kit_cg_swap(p->cg);
     22   kit_cg_store(p->cg, toy_mem_access(p, ty));
     23 }
     24 
     25 static void toy_store_const_to_local(ToyParser* p, KitCgLocal local,
     26                                      KitCgTypeId ty, uint64_t value) {
     27   kit_cg_push_local(p->cg, local);
     28   kit_cg_push_int(p->cg, value, ty);
     29   kit_cg_store(p->cg, toy_mem_access(p, ty));
     30 }
     31 
     32 static void toy_push_loaded_local(ToyParser* p, KitCgLocal local,
     33                                   KitCgTypeId ty) {
     34   kit_cg_push_local(p->cg, local);
     35   kit_cg_load(p->cg, toy_mem_access(p, ty));
     36 }
     37 
     38 static void toy_emit_dynamic_memory_loop(ToyParser* p, KitCgLocal dst_local,
     39                                          KitCgLocal src_local,
     40                                          KitCgLocal size_local, int is_memset,
     41                                          uint8_t set_value) {
     42   KitCgTypeId u8_ty = toy_builtin_type(p, KIT_CG_BUILTIN_I8);
     43   KitCgTypeId u8_ptr_ty = kit_cg_type_ptr(p->c, u8_ty, 0);
     44   KitCgLocal index_local = kit_cg_local(p->cg, p->size_type, toy_slot_attrs(0));
     45   KitCgLabel loop_label = kit_cg_label_new(p->cg);
     46   KitCgLabel end_label = kit_cg_label_new(p->cg);
     47 
     48   toy_store_const_to_local(p, index_local, p->size_type, 0);
     49   kit_cg_label_place(p->cg, loop_label);
     50 
     51   toy_push_loaded_local(p, index_local, p->size_type);
     52   toy_push_loaded_local(p, size_local, p->size_type);
     53   kit_cg_int_cmp(p->cg, KIT_CG_INT_LT_U);
     54   kit_cg_branch_false(p->cg, end_label);
     55 
     56   toy_push_loaded_local(p, dst_local, u8_ptr_ty);
     57   toy_push_loaded_local(p, index_local, p->size_type);
     58   /* Build the destination element place from [dst_ptr, index]; the element
     59    * scale = sizeof(u8) = 1 is derived from the base type. The store then
     60    * consumes [dst_place, value]. */
     61   kit_cg_elem(p->cg, 0);
     62   if (is_memset) {
     63     kit_cg_push_int(p->cg, set_value, u8_ty);
     64   } else {
     65     toy_push_loaded_local(p, src_local, u8_ptr_ty);
     66     toy_push_loaded_local(p, index_local, p->size_type);
     67     kit_cg_elem(p->cg, 0);
     68     kit_cg_load(p->cg, toy_mem_access(p, u8_ty));
     69   }
     70   kit_cg_store(p->cg, toy_mem_access(p, u8_ty));
     71 
     72   kit_cg_push_local(p->cg, index_local);
     73   toy_push_loaded_local(p, index_local, p->size_type);
     74   kit_cg_push_int(p->cg, 1, p->size_type);
     75   kit_cg_int_binop(p->cg, KIT_CG_INT_ADD, 0);
     76   kit_cg_store(p->cg, toy_mem_access(p, p->size_type));
     77 
     78   kit_cg_jump(p->cg, loop_label);
     79   kit_cg_label_place(p->cg, end_label);
     80 }
     81 
     82 static int toy_parse_memory_align_operand(ToyParser* p, int64_t* align,
     83                                           int* dynamic) {
     84   *align = 0;
     85   *dynamic = 0;
     86   if (!toy_parser_match(p, TOK_COMMA)) return 1;
     87   if (p->cur.kind == TOK_NUMBER && !p->cur.is_float)
     88     return toy_parse_number_arg(p, align);
     89   {
     90     KitCgTypeId ty = toy_parse_expr(p);
     91     if (ty == KIT_CG_TYPE_NONE) return 0;
     92     if (!toy_type_can_implicitly_cast(p, ty, p->size_type)) {
     93       toy_error(p, p->cur.loc, "memory alignment must be usize");
     94       return 0;
     95     }
     96     if (!toy_emit_implicit_cast(p, ty, p->size_type)) return 0;
     97     kit_cg_drop(p->cg);
     98     *dynamic = 1;
     99   }
    100   return 1;
    101 }
    102 
    103 static int toy_parse_access_group(ToyParser* p, KitCgMemAccess* access) {
    104   KitSym access_name = kit_sym_intern(p->c, KIT_SLICE_LIT("access"));
    105   if (p->cur.kind != TOK_IDENT || toy_tok_sym(p, p->cur) != access_name)
    106     return 0;
    107   toy_parser_advance(p);
    108   if (!toy_parser_expect(p, TOK_LPAREN)) {
    109     toy_error(p, p->cur.loc, "expected '(' after access");
    110     return 0;
    111   }
    112   while (p->cur.kind != TOK_RPAREN && p->cur.kind != TOK_EOF) {
    113     KitSym name;
    114     int64_t int_arg;
    115     if (!toy_parse_attr_dot_name(p, &name)) return 0;
    116     if (toy_sym_is(p, name, "align")) {
    117       if (!toy_parse_attr_int_arg(p, &int_arg) || int_arg < 0) return 0;
    118       access->align = (uint32_t)int_arg;
    119     } else if (toy_sym_is(p, name, "addrspace")) {
    120       if (!toy_parse_attr_int_arg(p, &int_arg) || int_arg < 0) return 0;
    121       access->address_space = (uint32_t)int_arg;
    122     } else if (toy_sym_is(p, name, "volatile")) {
    123       access->flags |= KIT_CG_MEM_VOLATILE;
    124     } else {
    125       toy_error(p, p->cur.loc, "unknown access entry");
    126       return 0;
    127     }
    128     if (!toy_parser_match(p, TOK_COMMA)) break;
    129   }
    130   if (!toy_parser_expect(p, TOK_RPAREN)) {
    131     toy_error(p, p->cur.loc, "expected ')' after access group");
    132     return 0;
    133   }
    134   return 1;
    135 }
    136 
    137 static int toy_parse_optional_access_arg(ToyParser* p, KitCgMemAccess* access) {
    138   if (!toy_parser_match(p, TOK_COMMA)) return 1;
    139   if (!toy_parse_access_group(p, access)) {
    140     toy_error(p, p->cur.loc, "expected access group");
    141     return 0;
    142   }
    143   return 1;
    144 }
    145 
    146 static int toy_parse_mem_order(ToyParser* p, KitCgMemOrder* out) {
    147   static const ToyConstRow rows[] = {
    148       {"relaxed", KIT_CG_MO_RELAXED}, {"consume", KIT_CG_MO_CONSUME},
    149       {"acquire", KIT_CG_MO_ACQUIRE}, {"release", KIT_CG_MO_RELEASE},
    150       {"acq_rel", KIT_CG_MO_ACQ_REL}, {"seq_cst", KIT_CG_MO_SEQ_CST},
    151   };
    152   uint64_t v;
    153   if (!toy_parse_dot_const(p, rows, sizeof rows / sizeof rows[0], 1,
    154                            "memory order", "memory order", &v))
    155     return 0;
    156   *out = (KitCgMemOrder)v;
    157   return 1;
    158 }
    159 
    160 static int toy_parse_atomic_op(ToyParser* p, KitCgAtomicOp* out) {
    161   static const ToyConstRow rows[] = {
    162       {"xchg", KIT_CG_ATOMIC_XCHG}, {"add", KIT_CG_ATOMIC_ADD},
    163       {"sub", KIT_CG_ATOMIC_SUB},   {"and", KIT_CG_ATOMIC_AND},
    164       {"or", KIT_CG_ATOMIC_OR},     {"xor", KIT_CG_ATOMIC_XOR},
    165       {"nand", KIT_CG_ATOMIC_NAND},
    166   };
    167   uint64_t v;
    168   if (!toy_parse_dot_const(p, rows, sizeof rows / sizeof rows[0], 0, NULL,
    169                            "atomic op", &v))
    170     return 0;
    171   *out = (KitCgAtomicOp)v;
    172   return 1;
    173 }
    174 
    175 static int toy_parse_cmpxchg_strength(ToyParser* p, int* weak_out) {
    176   static const ToyConstRow rows[] = {
    177       {"strong", 0},
    178       {"weak", 1},
    179   };
    180   uint64_t v;
    181   if (!toy_parse_dot_const(p, rows, sizeof rows / sizeof rows[0], 1,
    182                            "compare-exchange strength",
    183                            "compare-exchange strength", &v))
    184     return 0;
    185   *weak_out = (int)v;
    186   return 1;
    187 }
    188 
    189 static int toy_parse_call_attr(ToyParser* p, KitCgCallAttrs* attrs) {
    190   KitSym name;
    191   if (!toy_parse_attr_dot_name(p, &name)) return 0;
    192   if (toy_sym_is(p, name, "cold")) {
    193     attrs->flags |= KIT_CG_CALL_COLD;
    194   } else if (toy_sym_is(p, name, "tail") ||
    195              toy_sym_is(p, name, "tail_allowed")) {
    196     attrs->tail = KIT_CG_TAIL_ALLOWED;
    197   } else if (toy_sym_is(p, name, "musttail")) {
    198     attrs->tail = KIT_CG_TAIL_MUST;
    199   } else if (toy_sym_is(p, name, "notail") ||
    200              toy_sym_is(p, name, "tail_never")) {
    201     attrs->tail = KIT_CG_TAIL_DEFAULT;
    202   } else if (toy_sym_is(p, name, "inline")) {
    203     attrs->inline_policy = KIT_CG_INLINE_HINT;
    204   } else if (toy_sym_is(p, name, "always_inline")) {
    205     attrs->inline_policy = KIT_CG_INLINE_ALWAYS;
    206   } else if (toy_sym_is(p, name, "noinline")) {
    207     attrs->inline_policy = KIT_CG_INLINE_NEVER;
    208   } else {
    209     toy_error(p, p->cur.loc, "unknown call attribute");
    210     return 0;
    211   }
    212   return 1;
    213 }
    214 
    215 static int toy_parse_call_attr_tail(ToyParser* p, KitCgCallAttrs* attrs) {
    216   while (toy_parser_match(p, TOK_COMMA)) {
    217     if (p->cur.kind != TOK_DOT) {
    218       toy_error(p, p->cur.loc, "expected call attribute");
    219       return 0;
    220     }
    221     if (!toy_parse_call_attr(p, attrs)) return 0;
    222   }
    223   return 1;
    224 }
    225 
    226 static int toy_reject_tail_call_operand(ToyParser* p) {
    227   if (!p->tail_call_expr) return 0;
    228   toy_error(p, p->cur.loc, "tail @call must be returned directly");
    229   return 1;
    230 }
    231 
    232 static int toy_parse_call_braced_args(ToyParser* p, ToyToken call_tok,
    233                                       KitCgTypeId fn_ty,
    234                                       const ToyTypeId* toy_params,
    235                                       size_t toy_nparams, size_t* out_nargs) {
    236   uint32_t nparams = kit_cg_type_func_nparams(p->c, fn_ty);
    237   int variadic = kit_cg_type_func_is_variadic(p->c, fn_ty);
    238   size_t nargs = 0;
    239   if (!toy_parser_expect(p, TOK_LBRACE)) {
    240     toy_error(p, p->cur.loc, "expected braced call arguments");
    241     return 0;
    242   }
    243   if (p->cur.kind != TOK_RBRACE) {
    244     for (;;) {
    245       KitCgTypeId arg_ty = toy_parse_expr(p);
    246       if (arg_ty == KIT_CG_TYPE_NONE) return 0;
    247       if (toy_reject_tail_call_operand(p)) return 0;
    248       if (nargs < nparams) {
    249         KitCgFuncParam param =
    250             kit_cg_type_func_param(p->c, fn_ty, (uint32_t)nargs);
    251         ToyTypeId expected = (toy_params && nargs < toy_nparams)
    252                                  ? toy_params[nargs]
    253                                  : TOY_TYPE_NONE;
    254         ToyTypeId actual = p->last_type;
    255         if (expected != TOY_TYPE_NONE &&
    256             !toy_type_accepts_type(p, expected, actual)) {
    257           toy_error(p, call_tok.loc, "function argument type mismatch");
    258           return 0;
    259         }
    260         if (expected == TOY_TYPE_NONE &&
    261             !toy_type_can_implicitly_cast(p, arg_ty, param.type)) {
    262           toy_error(p, call_tok.loc, "function argument type mismatch");
    263           return 0;
    264         }
    265         if (arg_ty != param.type) {
    266           if (expected != TOY_TYPE_NONE) {
    267             if (!toy_emit_checked_cast(p, arg_ty, param.type)) return 0;
    268           } else if (!toy_emit_implicit_cast(p, arg_ty, param.type)) {
    269             return 0;
    270           }
    271         }
    272       } else if (!variadic) {
    273         toy_error(p, call_tok.loc, "too many arguments");
    274         return 0;
    275       }
    276       ++nargs;
    277       if (!toy_parser_match(p, TOK_COMMA)) break;
    278       if (p->cur.kind == TOK_RBRACE) break;
    279     }
    280   }
    281   if (!toy_parser_expect(p, TOK_RBRACE)) {
    282     toy_error(p, p->cur.loc, "expected '}' after call arguments");
    283     return 0;
    284   }
    285   if (nargs < nparams) {
    286     toy_error(p, call_tok.loc, "too few arguments");
    287     return 0;
    288   }
    289   *out_nargs = nargs;
    290   return 1;
    291 }
    292 
    293 static KitCgTypeId toy_parse_call_builtin(ToyParser* p) {
    294   KitCgCallAttrs attrs;
    295   ToyFn* fn = NULL;
    296   KitCgTypeId fn_ty = KIT_CG_TYPE_NONE;
    297   KitCgTypeId ret_ty;
    298   const ToyType* source_fn_type = NULL;
    299   ToyTypeId ret_toy_type = TOY_TYPE_NONE;
    300   ToyToken call_tok;
    301   int direct = 0;
    302   size_t nargs = 0;
    303   int tail;
    304   memset(&attrs, 0, sizeof attrs);
    305   if (!toy_parser_expect(p, TOK_LPAREN)) return KIT_CG_TYPE_NONE;
    306   call_tok = p->cur;
    307   if (p->cur.kind == TOK_IDENT) {
    308     KitSym callee_name = toy_tok_sym(p, p->cur);
    309     fn = toy_find_fn(p, callee_name);
    310     if (fn) {
    311       direct = 1;
    312       fn_ty = fn->type;
    313       ret_toy_type = fn->toy_ret;
    314       toy_parser_advance(p);
    315     }
    316   }
    317   if (!direct) {
    318     KitCgTypeId callee_ty = toy_parse_expr(p);
    319     ToyTypeId callee_toy_type = p->last_type;
    320     if (callee_ty == KIT_CG_TYPE_NONE) return KIT_CG_TYPE_NONE;
    321     if (toy_reject_tail_call_operand(p)) return KIT_CG_TYPE_NONE;
    322     fn_ty = toy_ptr_pointee_func_type(p, callee_ty);
    323     if (fn_ty == KIT_CG_TYPE_NONE) {
    324       toy_error(p, call_tok.loc, "@call callee is not a function pointer");
    325       return KIT_CG_TYPE_NONE;
    326     }
    327     source_fn_type = toy_type_get(p, toy_type_pointee(p, callee_toy_type));
    328     if (source_fn_type && source_fn_type->kind != TOY_TYPE_FUNC)
    329       source_fn_type = NULL;
    330     if (source_fn_type) ret_toy_type = source_fn_type->ret;
    331   }
    332   if (!toy_expect_comma(p)) return KIT_CG_TYPE_NONE;
    333   if (!toy_parse_call_braced_args(
    334           p, call_tok, fn_ty,
    335           direct ? fn->toy_params
    336                  : (source_fn_type ? source_fn_type->params : NULL),
    337           direct ? fn->nparams : (source_fn_type ? source_fn_type->nparams : 0),
    338           &nargs)) {
    339     return KIT_CG_TYPE_NONE;
    340   }
    341   if (!toy_parse_call_attr_tail(p, &attrs)) return KIT_CG_TYPE_NONE;
    342   if (!toy_parser_expect(p, TOK_RPAREN)) return KIT_CG_TYPE_NONE;
    343 
    344   ret_ty = toy_cg_func_ret(p, fn_ty);
    345   tail = attrs.tail == KIT_CG_TAIL_ALLOWED || attrs.tail == KIT_CG_TAIL_MUST;
    346   if (tail && !p->allow_tail_call_expr) {
    347     toy_error(p, call_tok.loc, "tail @call requires return");
    348     return KIT_CG_TYPE_NONE;
    349   }
    350 
    351   if (!ret_toy_type) ret_toy_type = toy_type_from_cg(p, ret_ty);
    352   if (tail) {
    353     p->tail_call_expr = 1;
    354     p->tail_call_ret_toy = ret_toy_type;
    355     if (p->cur_fn_ret_toy != TOY_TYPE_NONE && ret_toy_type != TOY_TYPE_NONE &&
    356         !toy_type_accepts_type(p, p->cur_fn_ret_toy, ret_toy_type)) {
    357       p->last_type = ret_toy_type;
    358       return ret_ty;
    359     }
    360   }
    361 
    362   if (direct)
    363     kit_cg_call_symbol(p->cg, toy_fn_cur_sym(p, fn), (uint32_t)nargs, attrs);
    364   else
    365     kit_cg_call(p->cg, (uint32_t)nargs, fn_ty, attrs);
    366   p->last_type = ret_toy_type;
    367   return ret_ty;
    368 }
    369 
    370 static int toy_parse_barrier_scope(ToyParser* p, KitCgBarrierScope* out) {
    371   static const ToyConstRow rows[] = {
    372       {"full", KIT_CG_BARRIER_FULL},
    373       {"inner", KIT_CG_BARRIER_INNER},
    374       {"inner_store", KIT_CG_BARRIER_INNER_STORE},
    375       {"outer", KIT_CG_BARRIER_OUTER},
    376       {"outer_store", KIT_CG_BARRIER_OUTER_STORE},
    377       {"non_share", KIT_CG_BARRIER_NON_SHARE},
    378   };
    379   uint64_t v;
    380   if (!toy_parse_dot_const(p, rows, sizeof rows / sizeof rows[0], 0, NULL,
    381                            "barrier scope", &v))
    382     return 0;
    383   *out = (KitCgBarrierScope)v;
    384   return 1;
    385 }
    386 
    387 static KitCgTypeId toy_unsupported_intrinsic(ToyParser* p) {
    388   toy_error(p, p->cur.loc, "unsupported target intrinsic");
    389   return KIT_CG_TYPE_NONE;
    390 }
    391 
    392 KitCgTypeId toy_parse_builtin_call(ToyParser* p, KitSym name, int* recognized) {
    393   *recognized = 1;
    394 
    395   if (toy_sym_is(p, name, "call")) return toy_parse_call_builtin(p);
    396 
    397   if (toy_sym_is(p, name, "popcount") || toy_sym_is(p, name, "ctz") ||
    398       toy_sym_is(p, name, "clz") || toy_sym_is(p, name, "bswap")) {
    399     KitCgIntrinsic intrin = KIT_CG_INTRIN_POPCOUNT;
    400     KitCgTypeId ty;
    401     if (toy_sym_is(p, name, "ctz"))
    402       intrin = KIT_CG_INTRIN_CTZ;
    403     else if (toy_sym_is(p, name, "clz"))
    404       intrin = KIT_CG_INTRIN_CLZ;
    405     else if (toy_sym_is(p, name, "bswap"))
    406       intrin = KIT_CG_INTRIN_BSWAP;
    407     toy_parser_advance(p); /* ( */
    408     ty = toy_parse_expr(p);
    409     if (ty == KIT_CG_TYPE_NONE) return KIT_CG_TYPE_NONE;
    410     if (!toy_parser_expect(p, TOK_RPAREN)) {
    411       toy_error(p, p->cur.loc, "expected ')'");
    412       return KIT_CG_TYPE_NONE;
    413     }
    414     if (!toy_type_is_intlike(p, ty)) {
    415       toy_error(p, p->cur.loc, "integer intrinsic expects integer operand");
    416       return KIT_CG_TYPE_NONE;
    417     }
    418     kit_cg_intrinsic(p->cg, intrin, 1, ty);
    419     return ty;
    420   }
    421 
    422   if (toy_sym_is(p, name, "frame_address") ||
    423       toy_sym_is(p, name, "return_address")) {
    424     /* __builtin_frame_address / __builtin_return_address. The constant level
    425      * rides as a single immediate operand; the result is a void*. */
    426     KitCgIntrinsic intrin = toy_sym_is(p, name, "return_address")
    427                                 ? KIT_CG_INTRIN_RETURN_ADDRESS
    428                                 : KIT_CG_INTRIN_FRAME_ADDRESS;
    429     KitCgTypeId void_ptr =
    430         kit_cg_type_ptr(p->c, toy_builtin_type(p, KIT_CG_BUILTIN_VOID), 0);
    431     KitCgTypeId level_ty;
    432     if (!toy_parser_expect(p, TOK_LPAREN)) return KIT_CG_TYPE_NONE;
    433     level_ty = toy_parse_expr(p);
    434     if (level_ty == KIT_CG_TYPE_NONE) return KIT_CG_TYPE_NONE;
    435     if (!toy_type_is_intlike(p, level_ty)) {
    436       toy_error(p, p->cur.loc, "frame/return address level must be integer");
    437       return KIT_CG_TYPE_NONE;
    438     }
    439     if (!toy_parser_expect(p, TOK_RPAREN)) {
    440       toy_error(p, p->cur.loc, "expected ')'");
    441       return KIT_CG_TYPE_NONE;
    442     }
    443     if (!kit_cg_target_supports_intrinsic(p->c, intrin))
    444       return toy_unsupported_intrinsic(p);
    445     kit_cg_intrinsic(p->cg, intrin, 1, void_ptr);
    446     return void_ptr;
    447   }
    448 
    449   if (toy_sym_is(p, name, "expect")) {
    450     KitCgTypeId a, b;
    451     toy_parser_advance(p);
    452     a = toy_parse_expr(p);
    453     if (a == KIT_CG_TYPE_NONE || !toy_expect_comma(p)) return KIT_CG_TYPE_NONE;
    454     b = toy_parse_expr(p);
    455     if (b == KIT_CG_TYPE_NONE) return KIT_CG_TYPE_NONE;
    456     if (!toy_parser_expect(p, TOK_RPAREN)) return KIT_CG_TYPE_NONE;
    457     if (a != b) {
    458       toy_error(p, p->cur.loc, "expect operands must have matching type");
    459       return KIT_CG_TYPE_NONE;
    460     }
    461     kit_cg_intrinsic(p->cg, KIT_CG_INTRIN_EXPECT, 2, a);
    462     return a;
    463   }
    464 
    465   if (toy_sym_is(p, name, "trap")) {
    466     if (!toy_parser_expect(p, TOK_LPAREN) || !toy_parser_expect(p, TOK_RPAREN))
    467       return KIT_CG_TYPE_NONE;
    468     kit_cg_intrinsic(p->cg, KIT_CG_INTRIN_TRAP, 0,
    469                      toy_builtin_type(p, KIT_CG_BUILTIN_VOID));
    470     return toy_builtin_type(p, KIT_CG_BUILTIN_VOID);
    471   }
    472 
    473   if (toy_sym_is(p, name, "cpu_nop") || toy_sym_is(p, name, "cpu_yield") ||
    474       toy_sym_is(p, name, "irq_disable") || toy_sym_is(p, name, "irq_enable") ||
    475       toy_sym_is(p, name, "isb")) {
    476     KitCgIntrinsic intrin = KIT_CG_INTRIN_CPU_NOP;
    477     if (toy_sym_is(p, name, "cpu_yield"))
    478       intrin = KIT_CG_INTRIN_CPU_YIELD;
    479     else if (toy_sym_is(p, name, "irq_disable"))
    480       intrin = KIT_CG_INTRIN_IRQ_DISABLE;
    481     else if (toy_sym_is(p, name, "irq_enable"))
    482       intrin = KIT_CG_INTRIN_IRQ_ENABLE;
    483     else if (toy_sym_is(p, name, "isb"))
    484       intrin = KIT_CG_INTRIN_ISB;
    485     if (!toy_parser_expect(p, TOK_LPAREN) || !toy_parser_expect(p, TOK_RPAREN))
    486       return KIT_CG_TYPE_NONE;
    487     if (!kit_cg_target_supports_intrinsic(p->c, intrin))
    488       return toy_unsupported_intrinsic(p);
    489     kit_cg_intrinsic(p->cg, intrin, 0,
    490                      toy_builtin_type(p, KIT_CG_BUILTIN_VOID));
    491     return toy_builtin_type(p, KIT_CG_BUILTIN_VOID);
    492   }
    493 
    494   /* C99-style floating comparison builtins (Route A). Five map directly to an
    495    * ordered FP predicate (NaN -> false); @islessgreater is ordered-and-not-
    496    * equal (ONE), reachable only here (toy `!=` is the unordered UNE). The
    497    * unordered duals are reached separately via `!(a < b)` etc. (Route B).
    498    * @isunordered has no standalone predicate, so it is synthesized as
    499    * isnan(a) || isnan(b), where UNE(x, x) is isnan(x). */
    500   if (toy_sym_is(p, name, "isless") || toy_sym_is(p, name, "islessequal") ||
    501       toy_sym_is(p, name, "isgreater") ||
    502       toy_sym_is(p, name, "isgreaterequal") ||
    503       toy_sym_is(p, name, "islessgreater") ||
    504       toy_sym_is(p, name, "isunordered")) {
    505     KitCgTypeId a, b;
    506     KitCgFpCmpOp pred = KIT_CG_FP_OLT;
    507     int is_unordered = toy_sym_is(p, name, "isunordered");
    508     if (toy_sym_is(p, name, "islessequal"))
    509       pred = KIT_CG_FP_OLE;
    510     else if (toy_sym_is(p, name, "isgreater"))
    511       pred = KIT_CG_FP_OGT;
    512     else if (toy_sym_is(p, name, "isgreaterequal"))
    513       pred = KIT_CG_FP_OGE;
    514     else if (toy_sym_is(p, name, "islessgreater"))
    515       pred = KIT_CG_FP_ONE;
    516     if (!toy_parser_expect(p, TOK_LPAREN)) return KIT_CG_TYPE_NONE;
    517     a = toy_parse_expr(p);
    518     if (a == KIT_CG_TYPE_NONE || !toy_expect_comma(p)) return KIT_CG_TYPE_NONE;
    519     b = toy_parse_expr(p);
    520     if (b == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_RPAREN))
    521       return KIT_CG_TYPE_NONE;
    522     if (a != b || !toy_type_is_float(p, a)) {
    523       toy_error(p, p->cur.loc,
    524                 "floating comparison builtin expects two floating operands of "
    525                 "the same type");
    526       return KIT_CG_TYPE_NONE;
    527     }
    528     if (is_unordered) {
    529       /* stack: [a, b] -> isnan(b) then isnan(a), OR'd. */
    530       kit_cg_dup(p->cg);                   /* [a, b, b] */
    531       kit_cg_fp_cmp(p->cg, KIT_CG_FP_UNE); /* [a, isnan_b] */
    532       kit_cg_swap(p->cg);                  /* [isnan_b, a] */
    533       kit_cg_dup(p->cg);                   /* [isnan_b, a, a] */
    534       kit_cg_fp_cmp(p->cg, KIT_CG_FP_UNE); /* [isnan_b, isnan_a] */
    535       kit_cg_int_binop(p->cg, KIT_CG_INT_OR, 0);
    536     } else {
    537       kit_cg_fp_cmp(p->cg, pred);
    538     }
    539     /* Widen the i32 0/1 to the toy int type, matching the comparison operators
    540      * (toy_parse_expr_cmp zext's its result), so the builtin composes in
    541      * arithmetic the same way `a < b` does. */
    542     kit_cg_zext(p->cg, p->int_type);
    543     return p->int_type;
    544   }
    545 
    546   {
    547     int low_level_recognized = 0;
    548     KitCgTypeId low_level_ty =
    549         toy_parse_low_level_builtin_call(p, name, &low_level_recognized);
    550     if (low_level_recognized) return low_level_ty;
    551   }
    552 
    553   if (toy_sym_is(p, name, "compile_error")) {
    554     KitSym msg;
    555     size_t msg_len;
    556     KitSlice text;
    557     if (!toy_parser_expect(p, TOK_LPAREN) ||
    558         !toy_parse_string_sym(p, &msg, &msg_len) ||
    559         !toy_parser_expect(p, TOK_RPAREN))
    560       return KIT_CG_TYPE_NONE;
    561     text = kit_sym_str(p->c, msg);
    562     toy_error(p, p->cur.loc, "compile_error: %.*s", KIT_SLICE_ARG(text));
    563     kit_cg_push_int(p->cg, 0, p->int_type);
    564     return p->int_type;
    565   }
    566 
    567   if (toy_sym_is(p, name, "unreachable")) {
    568     if (!toy_parser_expect(p, TOK_LPAREN) || !toy_parser_expect(p, TOK_RPAREN))
    569       return KIT_CG_TYPE_NONE;
    570     kit_cg_unreachable(p->cg);
    571     return toy_builtin_type(p, KIT_CG_BUILTIN_VOID);
    572   }
    573 
    574   if (toy_sym_is(p, name, "bitget")) {
    575     KitCgTypeId ty;
    576     int64_t lo, width;
    577     toy_parser_advance(p);
    578     ty = toy_parse_expr(p);
    579     if (ty == KIT_CG_TYPE_NONE || !toy_expect_comma(p) ||
    580         !toy_parse_number_arg(p, &lo) || !toy_expect_comma(p) ||
    581         !toy_parse_number_arg(p, &width) || !toy_parser_expect(p, TOK_RPAREN))
    582       return KIT_CG_TYPE_NONE;
    583     if (!toy_validate_bit_range(p, ty, lo, width, 1)) {
    584       toy_error(p, p->cur.loc, "invalid bitget arguments");
    585       return KIT_CG_TYPE_NONE;
    586     }
    587     kit_cg_bitget(p->cg, ty, (uint32_t)lo, (uint32_t)width);
    588     return ty;
    589   }
    590 
    591   if (toy_sym_is(p, name, "bitset")) {
    592     KitCgTypeId dst_ty, src_ty;
    593     int64_t lo, width;
    594     KitCgLocal dst_slot, src_slot;
    595     uint64_t src_mask, field_mask, clear_mask;
    596     toy_parser_advance(p);
    597     dst_ty = toy_parse_expr(p);
    598     if (dst_ty == KIT_CG_TYPE_NONE || !toy_expect_comma(p))
    599       return KIT_CG_TYPE_NONE;
    600     src_ty = toy_parse_expr(p);
    601     if (src_ty == KIT_CG_TYPE_NONE || !toy_expect_comma(p) ||
    602         !toy_parse_number_arg(p, &lo) || !toy_expect_comma(p) ||
    603         !toy_parse_number_arg(p, &width) || !toy_parser_expect(p, TOK_RPAREN))
    604       return KIT_CG_TYPE_NONE;
    605     if (dst_ty != src_ty || !toy_validate_bit_range(p, dst_ty, lo, width, 0)) {
    606       toy_error(p, p->cur.loc, "invalid bitset arguments");
    607       return KIT_CG_TYPE_NONE;
    608     }
    609     src_mask = (1ULL << (uint32_t)width) - 1u;
    610     field_mask = src_mask << (uint32_t)lo;
    611     clear_mask = ~field_mask;
    612     src_slot = kit_cg_local(p->cg, src_ty, toy_slot_attrs(0));
    613     dst_slot = kit_cg_local(p->cg, dst_ty, toy_slot_attrs(0));
    614     kit_cg_push_local(p->cg, src_slot);
    615     kit_cg_swap(p->cg);
    616     kit_cg_store(p->cg, toy_mem_access(p, src_ty));
    617     kit_cg_push_local(p->cg, dst_slot);
    618     kit_cg_swap(p->cg);
    619     kit_cg_store(p->cg, toy_mem_access(p, dst_ty));
    620 
    621     kit_cg_push_local(p->cg, dst_slot);
    622     kit_cg_load(p->cg, toy_mem_access(p, dst_ty));
    623     kit_cg_push_int(p->cg, clear_mask, dst_ty);
    624     kit_cg_int_binop(p->cg, KIT_CG_INT_AND, 0);
    625     kit_cg_push_local(p->cg, src_slot);
    626     kit_cg_load(p->cg, toy_mem_access(p, src_ty));
    627     kit_cg_push_int(p->cg, src_mask, src_ty);
    628     kit_cg_int_binop(p->cg, KIT_CG_INT_AND, 0);
    629     if (lo > 0) {
    630       kit_cg_push_int(p->cg, (uint64_t)lo, src_ty);
    631       kit_cg_int_binop(p->cg, KIT_CG_INT_SHL, 0);
    632     }
    633     kit_cg_int_binop(p->cg, KIT_CG_INT_OR, 0);
    634     return dst_ty;
    635   }
    636 
    637   if (toy_sym_is(p, name, "fma")) {
    638     KitCgTypeId a, b, c;
    639     toy_parser_advance(p);
    640     a = toy_parse_expr(p);
    641     if (a == KIT_CG_TYPE_NONE || !toy_expect_comma(p)) return KIT_CG_TYPE_NONE;
    642     b = toy_parse_expr(p);
    643     if (b == KIT_CG_TYPE_NONE || !toy_expect_comma(p)) return KIT_CG_TYPE_NONE;
    644     c = toy_parse_expr(p);
    645     if (c == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_RPAREN))
    646       return KIT_CG_TYPE_NONE;
    647     if (a != b || a != c || !toy_type_is_float(p, a)) {
    648       toy_error(p, p->cur.loc, "fma expects matching float operands");
    649       return KIT_CG_TYPE_NONE;
    650     }
    651     kit_cg_rot3(p->cg); /* [b, c, a] */
    652     kit_cg_rot3(p->cg); /* [c, a, b] */
    653     kit_cg_fp_binop(p->cg, KIT_CG_FP_MUL, 0);
    654     kit_cg_swap(p->cg);
    655     kit_cg_fp_binop(p->cg, KIT_CG_FP_ADD, 0);
    656     return a;
    657   }
    658 
    659   if (toy_sym_is(p, name, "prefetch")) {
    660     KitCgTypeId ptr_ty;
    661     toy_parser_advance(p);
    662     ptr_ty = toy_parse_expr(p);
    663     if (ptr_ty == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_RPAREN))
    664       return KIT_CG_TYPE_NONE;
    665     if (!toy_type_is_ptr(p, ptr_ty)) {
    666       toy_error(p, p->cur.loc, "prefetch expects pointer");
    667       return KIT_CG_TYPE_NONE;
    668     }
    669     kit_cg_intrinsic(p->cg, KIT_CG_INTRIN_PREFETCH, 1,
    670                      toy_builtin_type(p, KIT_CG_BUILTIN_VOID));
    671     return toy_builtin_type(p, KIT_CG_BUILTIN_VOID);
    672   }
    673 
    674   {
    675     int memory_recognized = 0;
    676     KitCgTypeId memory_ty =
    677         toy_parse_memory_builtin_call(p, name, &memory_recognized);
    678     if (memory_recognized) return memory_ty;
    679   }
    680 
    681   if (toy_sym_is(p, name, "labeladdr")) {
    682     KitSym label_name;
    683     ToyLabel* label;
    684     KitCgTypeId ptr_ty =
    685         kit_cg_type_ptr(p->c, toy_builtin_type(p, KIT_CG_BUILTIN_VOID), 0);
    686     if (!toy_parser_expect(p, TOK_LPAREN) || p->cur.kind != TOK_IDENT) {
    687       toy_error(p, p->cur.loc, "expected label name");
    688       return KIT_CG_TYPE_NONE;
    689     }
    690     label_name = toy_tok_sym(p, p->cur);
    691     toy_parser_advance(p);
    692     if (!toy_parser_expect(p, TOK_RPAREN)) return KIT_CG_TYPE_NONE;
    693     label = toy_find_label(p, label_name);
    694     if (!label) {
    695       toy_error(p, p->cur.loc, "unknown label");
    696       return KIT_CG_TYPE_NONE;
    697     }
    698     kit_cg_push_label_addr(p->cg, label->label, ptr_ty);
    699     return ptr_ty;
    700   }
    701 
    702   if (toy_sym_is(p, name, "target_arch")) {
    703     if (!toy_parser_expect(p, TOK_LPAREN) || !toy_parser_expect(p, TOK_RPAREN))
    704       return KIT_CG_TYPE_NONE;
    705     kit_cg_push_int(p->cg, (uint64_t)toy_target_code(p), p->int_type);
    706     return p->int_type;
    707   }
    708 
    709   if (toy_sym_is(p, name, "supports_callconv")) {
    710     KitCgCallConv cc;
    711     if (!toy_parser_expect(p, TOK_LPAREN) ||
    712         !toy_parse_callconv_const(p, &cc) || !toy_parser_expect(p, TOK_RPAREN))
    713       return KIT_CG_TYPE_NONE;
    714     kit_cg_push_int(p->cg, kit_cg_target_supports_call_conv(p->c, cc) ? 1u : 0u,
    715                     toy_builtin_type(p, KIT_CG_BUILTIN_BOOL));
    716     return toy_builtin_type(p, KIT_CG_BUILTIN_BOOL);
    717   }
    718 
    719   if (toy_sym_is(p, name, "supports_symbol_feature")) {
    720     KitCgSymbolFeature feature;
    721     if (!toy_parser_expect(p, TOK_LPAREN) ||
    722         !toy_parse_symbol_feature_const(p, &feature) ||
    723         !toy_parser_expect(p, TOK_RPAREN))
    724       return KIT_CG_TYPE_NONE;
    725     kit_cg_push_int(
    726         p->cg, kit_cg_target_supports_symbol_feature(p->c, feature) ? 1u : 0u,
    727         toy_builtin_type(p, KIT_CG_BUILTIN_BOOL));
    728     return toy_builtin_type(p, KIT_CG_BUILTIN_BOOL);
    729   }
    730 
    731   if (toy_sym_is(p, name, "supports_intrinsic")) {
    732     KitCgIntrinsic intrin;
    733     if (!toy_parser_expect(p, TOK_LPAREN) ||
    734         !toy_parse_intrinsic_const(p, &intrin) ||
    735         !toy_parser_expect(p, TOK_RPAREN))
    736       return KIT_CG_TYPE_NONE;
    737     kit_cg_push_int(p->cg,
    738                     kit_cg_target_supports_intrinsic(p->c, intrin) ? 1u : 0u,
    739                     toy_builtin_type(p, KIT_CG_BUILTIN_BOOL));
    740     return toy_builtin_type(p, KIT_CG_BUILTIN_BOOL);
    741   }
    742 
    743   if (toy_sym_is(p, name, "has_backend_feature")) {
    744     uint64_t feature;
    745     if (!toy_parser_expect(p, TOK_LPAREN) ||
    746         !toy_parse_backend_feature_const(p, &feature) ||
    747         !toy_parser_expect(p, TOK_RPAREN))
    748       return KIT_CG_TYPE_NONE;
    749     kit_cg_push_int(p->cg,
    750                     (kit_cg_target_backend_features(p->c) & feature) ? 1u : 0u,
    751                     toy_builtin_type(p, KIT_CG_BUILTIN_BOOL));
    752     return toy_builtin_type(p, KIT_CG_BUILTIN_BOOL);
    753   }
    754 
    755   if (toy_sym_is(p, name, "va_start")) {
    756     if (!toy_parser_expect(p, TOK_LPAREN) || !toy_parse_va_list_addr_arg(p) ||
    757         !toy_parser_expect(p, TOK_RPAREN))
    758       return KIT_CG_TYPE_NONE;
    759     kit_cg_vararg_start(p->cg);
    760     kit_cg_push_int(p->cg, 0, p->int_type);
    761     return p->int_type;
    762   }
    763 
    764   if (toy_sym_is(p, name, "va_end")) {
    765     if (!toy_parser_expect(p, TOK_LPAREN) || !toy_parse_va_list_addr_arg(p) ||
    766         !toy_parser_expect(p, TOK_RPAREN))
    767       return KIT_CG_TYPE_NONE;
    768     kit_cg_vararg_end(p->cg);
    769     kit_cg_push_int(p->cg, 0, p->int_type);
    770     return p->int_type;
    771   }
    772 
    773   if (toy_sym_is(p, name, "va_copy")) {
    774     if (!toy_parser_expect(p, TOK_LPAREN) || !toy_parse_va_list_addr_arg(p) ||
    775         !toy_expect_comma(p) || !toy_parse_va_list_addr_arg(p) ||
    776         !toy_parser_expect(p, TOK_RPAREN))
    777       return KIT_CG_TYPE_NONE;
    778     kit_cg_vararg_copy(p->cg);
    779     kit_cg_push_int(p->cg, 0, p->int_type);
    780     return p->int_type;
    781   }
    782 
    783   *recognized = 0;
    784   return KIT_CG_TYPE_NONE;
    785 }
    786 
    787 KitCgTypeId toy_parse_generic_builtin(ToyParser* p, KitSym name,
    788                                       int* recognized) {
    789   KitCgTypeId ty;
    790   *recognized = 1;
    791 
    792   if (toy_sym_is(p, name, "alloca")) {
    793     KitCgTypeId count_ty;
    794     KitCgTypeId ptr_ty;
    795     ToyTypeId elem_toy_type;
    796     int64_t align;
    797     if (!toy_parser_expect(p, TOK_LT)) return KIT_CG_TYPE_NONE;
    798     ty = toy_parse_type(p);
    799     elem_toy_type = p->last_type;
    800     if (ty == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_GT) ||
    801         !toy_parser_expect(p, TOK_LPAREN))
    802       return KIT_CG_TYPE_NONE;
    803     count_ty = toy_parse_expr(p);
    804     if (count_ty == KIT_CG_TYPE_NONE || !toy_expect_comma(p) ||
    805         !toy_parse_number_arg(p, &align) || !toy_parser_expect(p, TOK_RPAREN))
    806       return KIT_CG_TYPE_NONE;
    807     if (!toy_type_can_implicitly_cast(p, count_ty, p->size_type) ||
    808         align <= 0) {
    809       toy_error(p, p->cur.loc, "invalid alloca arguments");
    810       return KIT_CG_TYPE_NONE;
    811     }
    812     if (!toy_emit_implicit_cast(p, count_ty, p->size_type))
    813       return KIT_CG_TYPE_NONE;
    814     kit_cg_push_int(p->cg, kit_cg_type_size(p->c, ty), p->size_type);
    815     kit_cg_int_binop(p->cg, KIT_CG_INT_MUL, 0);
    816     ptr_ty = kit_cg_type_ptr(p->c, ty, 0);
    817     kit_cg_alloca(p->cg, (uint32_t)align, ptr_ty);
    818     p->last_type = toy_type_register_ptr(p, ptr_ty, elem_toy_type, 0);
    819     return ptr_ty;
    820   }
    821 
    822   if (toy_sym_is(p, name, "asm")) {
    823     KitSym tmpl;
    824     size_t tmpl_len;
    825     ToyTypeId result_toy_type;
    826     if (!toy_parser_expect(p, TOK_LT)) return KIT_CG_TYPE_NONE;
    827     ty = toy_parse_type(p);
    828     result_toy_type = p->last_type;
    829     if (ty == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_GT) ||
    830         !toy_parser_expect(p, TOK_LPAREN) ||
    831         !toy_parse_arch_string(p, &tmpl, &tmpl_len)) {
    832       return KIT_CG_TYPE_NONE;
    833     }
    834     if (!toy_parse_typed_asm_tail(p, ty, tmpl, tmpl_len))
    835       return KIT_CG_TYPE_NONE;
    836     p->last_type = result_toy_type;
    837     return ty;
    838   }
    839 
    840   {
    841     int low_level_recognized = 0;
    842     KitCgTypeId low_level_ty =
    843         toy_parse_low_level_generic_builtin(p, name, &low_level_recognized);
    844     if (low_level_recognized) return low_level_ty;
    845   }
    846 
    847   if (toy_sym_is(p, name, "sext") || toy_sym_is(p, name, "zext") ||
    848       toy_sym_is(p, name, "trunc") || toy_sym_is(p, name, "ptr_to_int") ||
    849       toy_sym_is(p, name, "int_to_ptr") || toy_sym_is(p, name, "bitcast") ||
    850       toy_sym_is(p, name, "fpext") || toy_sym_is(p, name, "fptrunc")) {
    851     KitCgTypeId src_ty;
    852     ToyTypeId result_toy_type;
    853     if (!toy_parser_expect(p, TOK_LT)) return KIT_CG_TYPE_NONE;
    854     ty = toy_parse_type(p);
    855     result_toy_type = p->last_type;
    856     if (ty == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_GT) ||
    857         !toy_parser_expect(p, TOK_LPAREN))
    858       return KIT_CG_TYPE_NONE;
    859     src_ty = toy_parse_expr(p);
    860     if (src_ty == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_RPAREN))
    861       return KIT_CG_TYPE_NONE;
    862     (void)src_ty;
    863     if (toy_sym_is(p, name, "sext"))
    864       kit_cg_sext(p->cg, ty);
    865     else if (toy_sym_is(p, name, "zext"))
    866       kit_cg_zext(p->cg, ty);
    867     else if (toy_sym_is(p, name, "trunc"))
    868       kit_cg_trunc(p->cg, ty);
    869     else if (toy_sym_is(p, name, "ptr_to_int"))
    870       kit_cg_ptr_to_int(p->cg, ty);
    871     else if (toy_sym_is(p, name, "int_to_ptr"))
    872       kit_cg_int_to_ptr(p->cg, ty);
    873     else if (toy_sym_is(p, name, "bitcast"))
    874       kit_cg_bitcast(p->cg, ty);
    875     else if (toy_sym_is(p, name, "fpext"))
    876       kit_cg_fpext(p->cg, ty);
    877     else if (toy_sym_is(p, name, "fptrunc"))
    878       kit_cg_fptrunc(p->cg, ty);
    879     p->last_type = result_toy_type;
    880     return ty;
    881   }
    882 
    883   if (toy_sym_is(p, name, "sint_to_float") ||
    884       toy_sym_is(p, name, "uint_to_float") ||
    885       toy_sym_is(p, name, "float_to_sint") ||
    886       toy_sym_is(p, name, "float_to_uint")) {
    887     KitCgTypeId src_ty;
    888     KitCgRounding rounding;
    889     if (!toy_parser_expect(p, TOK_LT)) return KIT_CG_TYPE_NONE;
    890     ty = toy_parse_type(p);
    891     if (ty == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_GT) ||
    892         !toy_parser_expect(p, TOK_LPAREN))
    893       return KIT_CG_TYPE_NONE;
    894     src_ty = toy_parse_expr(p);
    895     if (src_ty == KIT_CG_TYPE_NONE || !toy_expect_comma(p) ||
    896         !toy_parse_rounding_const(p, &rounding) ||
    897         !toy_parser_expect(p, TOK_RPAREN)) {
    898       return KIT_CG_TYPE_NONE;
    899     }
    900     (void)src_ty;
    901     if (toy_sym_is(p, name, "sint_to_float"))
    902       kit_cg_sint_to_float(p->cg, ty, rounding);
    903     else if (toy_sym_is(p, name, "uint_to_float"))
    904       kit_cg_uint_to_float(p->cg, ty, rounding);
    905     else if (toy_sym_is(p, name, "float_to_sint"))
    906       kit_cg_float_to_sint(p->cg, ty, rounding);
    907     else
    908       kit_cg_float_to_uint(p->cg, ty, rounding);
    909     return ty;
    910   }
    911 
    912   if (toy_sym_is(p, name, "assume_aligned")) {
    913     KitCgTypeId ptr_ty;
    914     int64_t align;
    915     if (!toy_parser_expect(p, TOK_LT)) return KIT_CG_TYPE_NONE;
    916     ty = toy_parse_type(p);
    917     if (ty == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_GT) ||
    918         !toy_parser_expect(p, TOK_LPAREN))
    919       return KIT_CG_TYPE_NONE;
    920     ptr_ty = toy_parse_expr(p);
    921     if (ptr_ty == KIT_CG_TYPE_NONE || !toy_expect_comma(p) ||
    922         !toy_parse_number_arg(p, &align) || !toy_parser_expect(p, TOK_RPAREN))
    923       return KIT_CG_TYPE_NONE;
    924     if (ptr_ty != ty || !toy_type_is_ptr(p, ty) || align <= 0) {
    925       toy_error(p, p->cur.loc, "invalid assume_aligned arguments");
    926       return KIT_CG_TYPE_NONE;
    927     }
    928     kit_cg_push_int(p->cg, (uint64_t)align, p->size_type);
    929     kit_cg_intrinsic(p->cg, KIT_CG_INTRIN_ASSUME_ALIGNED, 2, ty);
    930     return ty;
    931   }
    932 
    933   if (toy_sym_is(p, name, "sizeof") || toy_sym_is(p, name, "alignof")) {
    934     int is_align = toy_sym_is(p, name, "alignof");
    935     if (!toy_parser_expect(p, TOK_LT)) return KIT_CG_TYPE_NONE;
    936     ty = toy_parse_type(p);
    937     if (ty == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_GT) ||
    938         !toy_parser_expect(p, TOK_LPAREN) ||
    939         !toy_parser_expect(p, TOK_RPAREN)) {
    940       toy_error(p, p->cur.loc, "expected type query form");
    941       return KIT_CG_TYPE_NONE;
    942     }
    943     kit_cg_push_int(
    944         p->cg,
    945         is_align ? kit_cg_type_align(p->c, ty) : kit_cg_type_size(p->c, ty),
    946         p->size_type);
    947     return p->size_type;
    948   }
    949 
    950   if (toy_sym_is(p, name, "offsetof")) {
    951     uint32_t i, nfields;
    952     uint64_t off = 0;
    953     int found = 0;
    954     KitSym field_name = 0;
    955     int64_t tuple_index = -1;
    956     if (!toy_parser_expect(p, TOK_LT)) return KIT_CG_TYPE_NONE;
    957     ty = toy_parse_type(p);
    958     if (ty == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_GT) ||
    959         !toy_parser_expect(p, TOK_LPAREN) ||
    960         (p->cur.kind != TOK_IDENT &&
    961          !(p->cur.kind == TOK_NUMBER && !p->cur.is_float))) {
    962       toy_error(p, p->cur.loc, "expected offsetof<T>(field)");
    963       return KIT_CG_TYPE_NONE;
    964     }
    965     if (p->cur.kind == TOK_NUMBER)
    966       tuple_index = p->cur.int_value;
    967     else
    968       field_name = toy_tok_sym(p, p->cur);
    969     toy_parser_advance(p);
    970     if (!toy_parser_expect(p, TOK_RPAREN)) return KIT_CG_TYPE_NONE;
    971     if (kit_cg_type_kind(p->c, ty) != KIT_CG_TYPE_RECORD) {
    972       toy_error(p, p->cur.loc, "offsetof expects a record type");
    973       return KIT_CG_TYPE_NONE;
    974     }
    975     nfields = kit_cg_type_record_nfields(p->c, ty);
    976     if (tuple_index >= 0) {
    977       if (tuple_index >= (int64_t)nfields ||
    978           kit_cg_type_record_field(p->c, ty, (uint32_t)tuple_index, NULL,
    979                                    &off) != 0) {
    980         toy_error(p, p->cur.loc, "invalid tuple field");
    981         return KIT_CG_TYPE_NONE;
    982       }
    983       found = 1;
    984     } else {
    985       for (i = 0; i < nfields; ++i) {
    986         KitCgField field;
    987         uint64_t field_off = 0;
    988         if (kit_cg_type_record_field(p->c, ty, i, &field, &field_off) == 0 &&
    989             field.name == field_name) {
    990           off = field_off;
    991           found = 1;
    992           break;
    993         }
    994       }
    995       if (!found) {
    996         toy_error(p, p->cur.loc, "unknown record field");
    997         return KIT_CG_TYPE_NONE;
    998       }
    999     }
   1000     kit_cg_push_int(p->cg, off, p->size_type);
   1001     return p->size_type;
   1002   }
   1003 
   1004   if (toy_sym_is(p, name, "va_arg")) {
   1005     if (!toy_parser_expect(p, TOK_LT)) return KIT_CG_TYPE_NONE;
   1006     ty = toy_parse_type(p);
   1007     if (ty == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_GT) ||
   1008         !toy_parser_expect(p, TOK_LPAREN) || !toy_parse_va_list_addr_arg(p) ||
   1009         !toy_parser_expect(p, TOK_RPAREN))
   1010       return KIT_CG_TYPE_NONE;
   1011     kit_cg_vararg_next(p->cg, ty);
   1012     return ty;
   1013   }
   1014 
   1015   {
   1016     int atomic_recognized = 0;
   1017     KitCgTypeId atomic_ty =
   1018         toy_parse_atomic_generic_builtin(p, name, &atomic_recognized);
   1019     if (atomic_recognized) return atomic_ty;
   1020   }
   1021 
   1022   if (toy_sym_is(p, name, "add_overflow") ||
   1023       toy_sym_is(p, name, "sub_overflow") ||
   1024       toy_sym_is(p, name, "mul_overflow")) {
   1025     KitCgTypeId lhs_ty, rhs_ty, rec_ty;
   1026     KitCgField fields[2];
   1027     KitCgLocal rec_slot;
   1028     KitCgIntrinsic intrin = KIT_CG_INTRIN_SADD_OVERFLOW;
   1029     if (toy_sym_is(p, name, "sub_overflow"))
   1030       intrin = KIT_CG_INTRIN_SSUB_OVERFLOW;
   1031     else if (toy_sym_is(p, name, "mul_overflow"))
   1032       intrin = KIT_CG_INTRIN_SMUL_OVERFLOW;
   1033     if (!toy_parser_expect(p, TOK_LT)) return KIT_CG_TYPE_NONE;
   1034     ty = toy_parse_type(p);
   1035     if (ty == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_GT) ||
   1036         !toy_parser_expect(p, TOK_LPAREN))
   1037       return KIT_CG_TYPE_NONE;
   1038     lhs_ty = toy_parse_expr(p);
   1039     if (lhs_ty == KIT_CG_TYPE_NONE || !toy_expect_comma(p))
   1040       return KIT_CG_TYPE_NONE;
   1041     rhs_ty = toy_parse_expr(p);
   1042     if (rhs_ty == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_RPAREN))
   1043       return KIT_CG_TYPE_NONE;
   1044     if (!toy_type_is_intlike(p, ty) ||
   1045         !toy_type_can_implicitly_cast(p, lhs_ty, ty) ||
   1046         !toy_type_can_implicitly_cast(p, rhs_ty, ty)) {
   1047       toy_error(p, p->cur.loc, "overflow operand type mismatch");
   1048       return KIT_CG_TYPE_NONE;
   1049     }
   1050     if (lhs_ty != ty) {
   1051       kit_cg_swap(p->cg);
   1052       if (!toy_emit_implicit_cast(p, lhs_ty, ty)) return KIT_CG_TYPE_NONE;
   1053       kit_cg_swap(p->cg);
   1054     }
   1055     if (rhs_ty != ty && !toy_emit_implicit_cast(p, rhs_ty, ty))
   1056       return KIT_CG_TYPE_NONE;
   1057     kit_cg_intrinsic(p->cg, intrin, 2, ty);
   1058     memset(fields, 0, sizeof fields);
   1059     fields[0].name = kit_sym_intern(p->c, KIT_SLICE_LIT("value"));
   1060     fields[0].type = ty;
   1061     fields[1].name = kit_sym_intern(p->c, KIT_SLICE_LIT("overflow"));
   1062     fields[1].type = toy_builtin_type(p, KIT_CG_BUILTIN_BOOL);
   1063     rec_ty = kit_cg_type_record(p->c, 0, fields, 2);
   1064     rec_slot = kit_cg_local(p->cg, rec_ty, toy_slot_attrs(0));
   1065     {
   1066       uint64_t f0_off = 0, f1_off = 0;
   1067       kit_cg_type_record_field(p->c, rec_ty, 0, NULL, &f0_off);
   1068       kit_cg_type_record_field(p->c, rec_ty, 1, NULL, &f1_off);
   1069       kit_cg_push_local(p->cg, rec_slot);
   1070       kit_cg_addr(p->cg);
   1071       kit_cg_deref(p->cg, (int64_t)f1_off);
   1072       kit_cg_swap(p->cg);
   1073       kit_cg_store(p->cg, toy_mem_access(p, fields[1].type));
   1074       kit_cg_push_local(p->cg, rec_slot);
   1075       kit_cg_addr(p->cg);
   1076       kit_cg_deref(p->cg, (int64_t)f0_off);
   1077       kit_cg_swap(p->cg);
   1078       kit_cg_store(p->cg, toy_mem_access(p, fields[0].type));
   1079     }
   1080     /* Record result lives as a pointer VALUE (its address) on the stack, the
   1081      * shape record field-access / record-copy consume downstream. */
   1082     kit_cg_push_local_addr(p->cg, rec_slot);
   1083     return rec_ty;
   1084   }
   1085 
   1086   *recognized = 0;
   1087   return KIT_CG_TYPE_NONE;
   1088 }
   1089 
   1090 /* memcpy and memmove are identical except for the constant-size emission:
   1091  * memcpy lowers to kit_cg_memcpy, memmove to kit_cg_memmove. The dynamic path
   1092  * is byte-for-byte shared (both emit a forward element loop). */
   1093 static KitCgTypeId toy_parse_mem_copy_move(ToyParser* p, int is_move) {
   1094   KitCgTypeId dst, src;
   1095   int64_t size = 0, align = 0;
   1096   int dynamic_size = 0, dynamic_align = 0;
   1097   KitCgLocal dst_local = KIT_CG_LOCAL_NONE;
   1098   KitCgLocal src_local = KIT_CG_LOCAL_NONE;
   1099   KitCgLocal size_local = KIT_CG_LOCAL_NONE;
   1100   KitCgMemAccess access;
   1101   KitCgTypeId u8_ty = toy_builtin_type(p, KIT_CG_BUILTIN_I8);
   1102   KitCgTypeId u8_ptr_ty = kit_cg_type_ptr(p->c, u8_ty, 0);
   1103   toy_parser_advance(p);
   1104   dst = toy_parse_expr(p);
   1105   if (dst == KIT_CG_TYPE_NONE || !toy_expect_comma(p)) return KIT_CG_TYPE_NONE;
   1106   src = toy_parse_expr(p);
   1107   if (src == KIT_CG_TYPE_NONE || !toy_expect_comma(p)) return KIT_CG_TYPE_NONE;
   1108   if (p->cur.kind == TOK_NUMBER && !p->cur.is_float) {
   1109     if (!toy_parse_number_arg(p, &size)) return KIT_CG_TYPE_NONE;
   1110   } else {
   1111     KitCgTypeId size_ty = toy_parse_expr(p);
   1112     if (size_ty == KIT_CG_TYPE_NONE) return KIT_CG_TYPE_NONE;
   1113     if (!toy_type_can_implicitly_cast(p, size_ty, p->size_type)) {
   1114       toy_error(p, p->cur.loc, "memory size must be usize");
   1115       return KIT_CG_TYPE_NONE;
   1116     }
   1117     if (!toy_emit_implicit_cast(p, size_ty, p->size_type))
   1118       return KIT_CG_TYPE_NONE;
   1119     dynamic_size = 1;
   1120     size_local = kit_cg_local(p->cg, p->size_type, toy_slot_attrs(0));
   1121     toy_store_top_to_local(p, size_local, p->size_type);
   1122   }
   1123   if (!toy_parse_memory_align_operand(p, &align, &dynamic_align))
   1124     return KIT_CG_TYPE_NONE;
   1125   access = toy_mem_access_align(p, p->int_type, (uint32_t)align);
   1126   if (!toy_parse_mem_flags_tail(p, &access)) return KIT_CG_TYPE_NONE;
   1127   if (!toy_parser_expect(p, TOK_RPAREN)) return KIT_CG_TYPE_NONE;
   1128   if (dynamic_size || dynamic_align) {
   1129     if (!dynamic_size) {
   1130       size_local = kit_cg_local(p->cg, p->size_type, toy_slot_attrs(0));
   1131       toy_store_const_to_local(p, size_local, p->size_type, (uint64_t)size);
   1132     }
   1133     src_local = kit_cg_local(p->cg, u8_ptr_ty, toy_slot_attrs(0));
   1134     kit_cg_bitcast(p->cg, u8_ptr_ty);
   1135     toy_store_top_to_local(p, src_local, u8_ptr_ty);
   1136     dst_local = kit_cg_local(p->cg, u8_ptr_ty, toy_slot_attrs(0));
   1137     kit_cg_bitcast(p->cg, u8_ptr_ty);
   1138     toy_store_top_to_local(p, dst_local, u8_ptr_ty);
   1139     toy_emit_dynamic_memory_loop(p, dst_local, src_local, size_local, 0, 0);
   1140     return toy_builtin_type(p, KIT_CG_BUILTIN_VOID);
   1141   }
   1142   if (is_move)
   1143     kit_cg_memmove(p->cg, (uint64_t)size, access, access);
   1144   else
   1145     kit_cg_memcpy(p->cg, (uint64_t)size, access, access);
   1146   return toy_builtin_type(p, KIT_CG_BUILTIN_VOID);
   1147 }
   1148 
   1149 KitCgTypeId toy_parse_memory_builtin_call(ToyParser* p, KitSym name,
   1150                                           int* recognized) {
   1151   *recognized = 1;
   1152 
   1153   if (toy_sym_is(p, name, "memset")) {
   1154     KitCgTypeId dst;
   1155     int64_t val, size = 0, align = 0;
   1156     int dynamic_size = 0, dynamic_align = 0;
   1157     KitCgLocal dst_local = KIT_CG_LOCAL_NONE;
   1158     KitCgLocal size_local = KIT_CG_LOCAL_NONE;
   1159     KitCgMemAccess access;
   1160     KitCgTypeId u8_ty = toy_builtin_type(p, KIT_CG_BUILTIN_I8);
   1161     KitCgTypeId u8_ptr_ty = kit_cg_type_ptr(p->c, u8_ty, 0);
   1162     toy_parser_advance(p);
   1163     dst = toy_parse_expr(p);
   1164     if (dst == KIT_CG_TYPE_NONE || !toy_expect_comma(p) ||
   1165         !toy_parse_number_arg(p, &val) || !toy_expect_comma(p))
   1166       return KIT_CG_TYPE_NONE;
   1167     if (p->cur.kind == TOK_NUMBER && !p->cur.is_float) {
   1168       if (!toy_parse_number_arg(p, &size)) return KIT_CG_TYPE_NONE;
   1169     } else {
   1170       KitCgTypeId size_ty = toy_parse_expr(p);
   1171       if (size_ty == KIT_CG_TYPE_NONE) return KIT_CG_TYPE_NONE;
   1172       if (!toy_type_can_implicitly_cast(p, size_ty, p->size_type)) {
   1173         toy_error(p, p->cur.loc, "memory size must be usize");
   1174         return KIT_CG_TYPE_NONE;
   1175       }
   1176       if (!toy_emit_implicit_cast(p, size_ty, p->size_type))
   1177         return KIT_CG_TYPE_NONE;
   1178       dynamic_size = 1;
   1179       size_local = kit_cg_local(p->cg, p->size_type, toy_slot_attrs(0));
   1180       toy_store_top_to_local(p, size_local, p->size_type);
   1181     }
   1182     if (!toy_parse_memory_align_operand(p, &align, &dynamic_align))
   1183       return KIT_CG_TYPE_NONE;
   1184     access = toy_mem_access_align(p, p->int_type, (uint32_t)align);
   1185     if (!toy_parse_mem_flags_tail(p, &access)) return KIT_CG_TYPE_NONE;
   1186     if (!toy_parser_expect(p, TOK_RPAREN)) return KIT_CG_TYPE_NONE;
   1187     if (dynamic_size || dynamic_align) {
   1188       if (val < 0 || val > 255) {
   1189         toy_error(p, p->cur.loc, "memset value out of range");
   1190         return KIT_CG_TYPE_NONE;
   1191       }
   1192       if (!dynamic_size) {
   1193         size_local = kit_cg_local(p->cg, p->size_type, toy_slot_attrs(0));
   1194         toy_store_const_to_local(p, size_local, p->size_type, (uint64_t)size);
   1195       }
   1196       dst_local = kit_cg_local(p->cg, u8_ptr_ty, toy_slot_attrs(0));
   1197       kit_cg_bitcast(p->cg, u8_ptr_ty);
   1198       toy_store_top_to_local(p, dst_local, u8_ptr_ty);
   1199       toy_emit_dynamic_memory_loop(p, dst_local, KIT_CG_LOCAL_NONE, size_local,
   1200                                    1, (uint8_t)val);
   1201       return toy_builtin_type(p, KIT_CG_BUILTIN_VOID);
   1202     }
   1203     kit_cg_memset(p->cg, (uint8_t)val, (uint64_t)size, access);
   1204     return toy_builtin_type(p, KIT_CG_BUILTIN_VOID);
   1205   }
   1206 
   1207   if (toy_sym_is(p, name, "memcpy")) return toy_parse_mem_copy_move(p, 0);
   1208 
   1209   if (toy_sym_is(p, name, "memmove")) return toy_parse_mem_copy_move(p, 1);
   1210 
   1211   if (toy_sym_is(p, name, "atomic_fence")) {
   1212     KitCgMemOrder order;
   1213     if (!toy_parser_expect(p, TOK_LPAREN) || !toy_parse_mem_order(p, &order) ||
   1214         !toy_parser_expect(p, TOK_RPAREN))
   1215       return KIT_CG_TYPE_NONE;
   1216     kit_cg_atomic_fence(p->cg, order);
   1217     return toy_builtin_type(p, KIT_CG_BUILTIN_VOID);
   1218   }
   1219 
   1220   *recognized = 0;
   1221   return KIT_CG_TYPE_NONE;
   1222 }
   1223 
   1224 KitCgTypeId toy_parse_atomic_generic_builtin(ToyParser* p, KitSym name,
   1225                                              int* recognized) {
   1226   KitCgTypeId ty;
   1227   *recognized = 1;
   1228 
   1229   if (toy_sym_is(p, name, "atomic_load")) {
   1230     KitCgTypeId ptr_ty;
   1231     KitCgMemOrder order;
   1232     KitCgMemAccess access;
   1233     if (!toy_parser_expect(p, TOK_LT)) return KIT_CG_TYPE_NONE;
   1234     ty = toy_parse_type(p);
   1235     if (ty == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_GT) ||
   1236         !toy_parser_expect(p, TOK_LPAREN))
   1237       return KIT_CG_TYPE_NONE;
   1238     ptr_ty = toy_parse_expr(p);
   1239     if (ptr_ty == KIT_CG_TYPE_NONE || !toy_expect_comma(p) ||
   1240         !toy_parse_mem_order(p, &order))
   1241       return KIT_CG_TYPE_NONE;
   1242     access = toy_mem_access(p, ty);
   1243     if (!toy_parse_optional_access_arg(p, &access) ||
   1244         !toy_parser_expect(p, TOK_RPAREN))
   1245       return KIT_CG_TYPE_NONE;
   1246     if (!toy_type_is_ptr(p, ptr_ty) ||
   1247         kit_cg_type_ptr_pointee(p->c, ptr_ty) != ty) {
   1248       toy_error(p, p->cur.loc, "atomic_load pointer type mismatch");
   1249       return KIT_CG_TYPE_NONE;
   1250     }
   1251     kit_cg_atomic_load(p->cg, access, order);
   1252     return ty;
   1253   }
   1254 
   1255   if (toy_sym_is(p, name, "atomic_store")) {
   1256     KitCgTypeId ptr_ty, val_ty;
   1257     KitCgMemOrder order;
   1258     KitCgMemAccess access;
   1259     if (!toy_parser_expect(p, TOK_LT)) return KIT_CG_TYPE_NONE;
   1260     ty = toy_parse_type(p);
   1261     if (ty == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_GT) ||
   1262         !toy_parser_expect(p, TOK_LPAREN))
   1263       return KIT_CG_TYPE_NONE;
   1264     ptr_ty = toy_parse_expr(p);
   1265     if (ptr_ty == KIT_CG_TYPE_NONE || !toy_expect_comma(p))
   1266       return KIT_CG_TYPE_NONE;
   1267     val_ty = toy_parse_expr(p);
   1268     if (val_ty == KIT_CG_TYPE_NONE || !toy_expect_comma(p) ||
   1269         !toy_parse_mem_order(p, &order))
   1270       return KIT_CG_TYPE_NONE;
   1271     access = toy_mem_access(p, ty);
   1272     if (!toy_parse_optional_access_arg(p, &access) ||
   1273         !toy_parser_expect(p, TOK_RPAREN))
   1274       return KIT_CG_TYPE_NONE;
   1275     if (!toy_type_is_ptr(p, ptr_ty) ||
   1276         kit_cg_type_ptr_pointee(p->c, ptr_ty) != ty ||
   1277         !toy_type_can_implicitly_cast(p, val_ty, ty)) {
   1278       toy_error(p, p->cur.loc, "atomic_store type mismatch");
   1279       return KIT_CG_TYPE_NONE;
   1280     }
   1281     if (val_ty != ty && !toy_emit_implicit_cast(p, val_ty, ty))
   1282       return KIT_CG_TYPE_NONE;
   1283     kit_cg_atomic_store(p->cg, access, order);
   1284     return toy_builtin_type(p, KIT_CG_BUILTIN_VOID);
   1285   }
   1286 
   1287   if (toy_sym_is(p, name, "atomic_rmw")) {
   1288     KitCgAtomicOp op;
   1289     KitCgTypeId ptr_ty, val_ty;
   1290     KitCgMemOrder order;
   1291     KitCgMemAccess access;
   1292     if (!toy_parser_expect(p, TOK_LT)) return KIT_CG_TYPE_NONE;
   1293     ty = toy_parse_type(p);
   1294     if (ty == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_GT) ||
   1295         !toy_parser_expect(p, TOK_LPAREN) || !toy_parse_atomic_op(p, &op) ||
   1296         !toy_expect_comma(p))
   1297       return KIT_CG_TYPE_NONE;
   1298     ptr_ty = toy_parse_expr(p);
   1299     if (ptr_ty == KIT_CG_TYPE_NONE || !toy_expect_comma(p))
   1300       return KIT_CG_TYPE_NONE;
   1301     val_ty = toy_parse_expr(p);
   1302     if (val_ty == KIT_CG_TYPE_NONE || !toy_expect_comma(p) ||
   1303         !toy_parse_mem_order(p, &order))
   1304       return KIT_CG_TYPE_NONE;
   1305     access = toy_mem_access(p, ty);
   1306     if (!toy_parse_optional_access_arg(p, &access) ||
   1307         !toy_parser_expect(p, TOK_RPAREN))
   1308       return KIT_CG_TYPE_NONE;
   1309     if (!toy_type_is_ptr(p, ptr_ty) ||
   1310         kit_cg_type_ptr_pointee(p->c, ptr_ty) != ty ||
   1311         !toy_type_can_implicitly_cast(p, val_ty, ty)) {
   1312       toy_error(p, p->cur.loc, "atomic_rmw type mismatch");
   1313       return KIT_CG_TYPE_NONE;
   1314     }
   1315     if (val_ty != ty && !toy_emit_implicit_cast(p, val_ty, ty))
   1316       return KIT_CG_TYPE_NONE;
   1317     kit_cg_atomic_rmw(p->cg, access, op, order);
   1318     return ty;
   1319   }
   1320 
   1321   if (toy_sym_is(p, name, "atomic_cmpxchg")) {
   1322     KitCgTypeId ptr_ty, expected_ty, desired_ty, rec_ty;
   1323     KitCgMemOrder success_order, failure_order;
   1324     int weak;
   1325     KitCgMemAccess access;
   1326     KitCgField fields[2];
   1327     KitCgLocal rec_slot;
   1328     if (!toy_parser_expect(p, TOK_LT)) return KIT_CG_TYPE_NONE;
   1329     ty = toy_parse_type(p);
   1330     if (ty == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_GT) ||
   1331         !toy_parser_expect(p, TOK_LPAREN))
   1332       return KIT_CG_TYPE_NONE;
   1333     ptr_ty = toy_parse_expr(p);
   1334     if (ptr_ty == KIT_CG_TYPE_NONE || !toy_expect_comma(p))
   1335       return KIT_CG_TYPE_NONE;
   1336     expected_ty = toy_parse_expr(p);
   1337     if (expected_ty == KIT_CG_TYPE_NONE)
   1338       return KIT_CG_TYPE_NONE;
   1339     if (!toy_type_can_implicitly_cast(p, expected_ty, ty)) {
   1340       toy_error(p, p->cur.loc, "atomic_cmpxchg type mismatch");
   1341       return KIT_CG_TYPE_NONE;
   1342     }
   1343     if (expected_ty != ty && !toy_emit_implicit_cast(p, expected_ty, ty))
   1344       return KIT_CG_TYPE_NONE;
   1345     expected_ty = ty;
   1346     if (!toy_expect_comma(p)) return KIT_CG_TYPE_NONE;
   1347     desired_ty = toy_parse_expr(p);
   1348     if (desired_ty == KIT_CG_TYPE_NONE || !toy_expect_comma(p) ||
   1349         !toy_parse_mem_order(p, &success_order) || !toy_expect_comma(p) ||
   1350         !toy_parse_mem_order(p, &failure_order) || !toy_expect_comma(p) ||
   1351         !toy_parse_cmpxchg_strength(p, &weak))
   1352       return KIT_CG_TYPE_NONE;
   1353     access = toy_mem_access(p, ty);
   1354     if (!toy_parse_optional_access_arg(p, &access) ||
   1355         !toy_parser_expect(p, TOK_RPAREN))
   1356       return KIT_CG_TYPE_NONE;
   1357     if (!toy_type_is_ptr(p, ptr_ty) ||
   1358         kit_cg_type_ptr_pointee(p->c, ptr_ty) != ty || expected_ty != ty ||
   1359         !toy_type_can_implicitly_cast(p, desired_ty, ty)) {
   1360       toy_error(p, p->cur.loc, "atomic_cmpxchg type mismatch");
   1361       return KIT_CG_TYPE_NONE;
   1362     }
   1363     if (desired_ty != ty && !toy_emit_implicit_cast(p, desired_ty, ty))
   1364       return KIT_CG_TYPE_NONE;
   1365     kit_cg_atomic_cmpxchg(p->cg, access, success_order, failure_order, weak);
   1366     memset(fields, 0, sizeof fields);
   1367     fields[0].name = kit_sym_intern(p->c, KIT_SLICE_LIT("prior"));
   1368     fields[0].type = ty;
   1369     fields[1].name = kit_sym_intern(p->c, KIT_SLICE_LIT("ok"));
   1370     fields[1].type = toy_builtin_type(p, KIT_CG_BUILTIN_BOOL);
   1371     rec_ty = kit_cg_type_record(p->c, 0, fields, 2);
   1372     rec_slot = kit_cg_local(p->cg, rec_ty, toy_slot_attrs(0));
   1373     {
   1374       uint64_t f0_off = 0, f1_off = 0;
   1375       kit_cg_type_record_field(p->c, rec_ty, 0, NULL, &f0_off);
   1376       kit_cg_type_record_field(p->c, rec_ty, 1, NULL, &f1_off);
   1377       kit_cg_push_local(p->cg, rec_slot);
   1378       kit_cg_addr(p->cg);
   1379       kit_cg_deref(p->cg, (int64_t)f1_off);
   1380       kit_cg_swap(p->cg);
   1381       kit_cg_store(p->cg, toy_mem_access(p, fields[1].type));
   1382       kit_cg_push_local(p->cg, rec_slot);
   1383       kit_cg_addr(p->cg);
   1384       kit_cg_deref(p->cg, (int64_t)f0_off);
   1385       kit_cg_swap(p->cg);
   1386       kit_cg_store(p->cg, toy_mem_access(p, fields[0].type));
   1387     }
   1388     /* Record result lives as a pointer VALUE (its address) on the stack. */
   1389     kit_cg_push_local_addr(p->cg, rec_slot);
   1390     return rec_ty;
   1391   }
   1392 
   1393   if (toy_sym_is(p, name, "atomic_is_legal")) {
   1394     KitCgMemOrder order;
   1395     KitCgMemAccess access;
   1396     if (!toy_parser_expect(p, TOK_LT)) return KIT_CG_TYPE_NONE;
   1397     ty = toy_parse_type(p);
   1398     if (ty == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_GT) ||
   1399         !toy_parser_expect(p, TOK_LPAREN) || !toy_parse_mem_order(p, &order))
   1400       return KIT_CG_TYPE_NONE;
   1401     access = toy_mem_access(p, ty);
   1402     if (!toy_parse_optional_access_arg(p, &access) ||
   1403         !toy_parser_expect(p, TOK_RPAREN))
   1404       return KIT_CG_TYPE_NONE;
   1405     kit_cg_push_int(p->cg,
   1406                     kit_cg_atomic_is_legal(p->c, access, order) ? 1u : 0u,
   1407                     toy_builtin_type(p, KIT_CG_BUILTIN_BOOL));
   1408     return toy_builtin_type(p, KIT_CG_BUILTIN_BOOL);
   1409   }
   1410 
   1411   if (toy_sym_is(p, name, "atomic_is_lock_free")) {
   1412     KitCgMemAccess access;
   1413     if (!toy_parser_expect(p, TOK_LT)) return KIT_CG_TYPE_NONE;
   1414     ty = toy_parse_type(p);
   1415     if (ty == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_GT) ||
   1416         !toy_parser_expect(p, TOK_LPAREN))
   1417       return KIT_CG_TYPE_NONE;
   1418     access = toy_mem_access(p, ty);
   1419     if (p->cur.kind != TOK_RPAREN && !toy_parse_access_group(p, &access))
   1420       return KIT_CG_TYPE_NONE;
   1421     if (!toy_parser_expect(p, TOK_RPAREN)) return KIT_CG_TYPE_NONE;
   1422     kit_cg_push_int(p->cg, kit_cg_atomic_is_lock_free(p->c, access) ? 1u : 0u,
   1423                     toy_builtin_type(p, KIT_CG_BUILTIN_BOOL));
   1424     return toy_builtin_type(p, KIT_CG_BUILTIN_BOOL);
   1425   }
   1426 
   1427   *recognized = 0;
   1428   return KIT_CG_TYPE_NONE;
   1429 }
   1430 
   1431 KitCgTypeId toy_parse_low_level_builtin_call(ToyParser* p, KitSym name,
   1432                                              int* recognized) {
   1433   *recognized = 1;
   1434 
   1435   if (toy_sym_is(p, name, "syscall")) {
   1436     uint32_t nargs = 0;
   1437     KitCgTypeId long_ty = p->int_type;
   1438     if (!toy_parser_expect(p, TOK_LPAREN)) return KIT_CG_TYPE_NONE;
   1439     if (!toy_parser_match(p, TOK_RPAREN)) {
   1440       for (;;) {
   1441         KitCgTypeId arg_ty = toy_parse_expr(p);
   1442         if (arg_ty == KIT_CG_TYPE_NONE) return KIT_CG_TYPE_NONE;
   1443         if (!toy_type_is_intlike(p, arg_ty) && !toy_type_is_ptr(p, arg_ty)) {
   1444           toy_error(p, p->cur.loc,
   1445                     "syscall arguments must be integer or pointer");
   1446           return KIT_CG_TYPE_NONE;
   1447         }
   1448         if (arg_ty != long_ty && !toy_emit_cast(p, arg_ty, long_ty))
   1449           return KIT_CG_TYPE_NONE;
   1450         nargs++;
   1451         if (nargs > 7u) {
   1452           toy_error(p, p->cur.loc, "too many syscall arguments");
   1453           return KIT_CG_TYPE_NONE;
   1454         }
   1455         if (!toy_parser_match(p, TOK_COMMA)) break;
   1456       }
   1457       if (!toy_parser_expect(p, TOK_RPAREN)) return KIT_CG_TYPE_NONE;
   1458     }
   1459     if (nargs == 0) {
   1460       toy_error(p, p->cur.loc, "syscall expects a syscall number");
   1461       return KIT_CG_TYPE_NONE;
   1462     }
   1463     if (!kit_cg_target_supports_intrinsic(p->c, KIT_CG_INTRIN_SYSCALL))
   1464       return toy_unsupported_intrinsic(p);
   1465     kit_cg_intrinsic(p->cg, KIT_CG_INTRIN_SYSCALL, nargs, long_ty);
   1466     return long_ty;
   1467   }
   1468 
   1469   if (toy_sym_is(p, name, "setjmp")) {
   1470     KitCgTypeId buf_ty;
   1471     if (!toy_parser_expect(p, TOK_LPAREN)) return KIT_CG_TYPE_NONE;
   1472     buf_ty = toy_parse_expr(p);
   1473     if (buf_ty == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_RPAREN))
   1474       return KIT_CG_TYPE_NONE;
   1475     if (!toy_type_is_ptr(p, buf_ty)) {
   1476       toy_error(p, p->cur.loc, "setjmp expects pointer buffer");
   1477       return KIT_CG_TYPE_NONE;
   1478     }
   1479     return toy_unsupported_intrinsic(p);
   1480   }
   1481 
   1482   if (toy_sym_is(p, name, "longjmp")) {
   1483     KitCgTypeId buf_ty, value_ty;
   1484     if (!toy_parser_expect(p, TOK_LPAREN)) return KIT_CG_TYPE_NONE;
   1485     buf_ty = toy_parse_expr(p);
   1486     if (buf_ty == KIT_CG_TYPE_NONE || !toy_expect_comma(p))
   1487       return KIT_CG_TYPE_NONE;
   1488     value_ty = toy_parse_expr(p);
   1489     if (value_ty == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_RPAREN))
   1490       return KIT_CG_TYPE_NONE;
   1491     if (!toy_type_is_ptr(p, buf_ty) || !toy_type_is_intlike(p, value_ty)) {
   1492       toy_error(p, p->cur.loc,
   1493                 "longjmp expects pointer buffer and integer value");
   1494       return KIT_CG_TYPE_NONE;
   1495     }
   1496     return toy_unsupported_intrinsic(p);
   1497   }
   1498 
   1499   if (toy_sym_is(p, name, "wfi") || toy_sym_is(p, name, "wfe") ||
   1500       toy_sym_is(p, name, "sev")) {
   1501     KitCgIntrinsic intrin = KIT_CG_INTRIN_WFI;
   1502     if (toy_sym_is(p, name, "wfe"))
   1503       intrin = KIT_CG_INTRIN_WFE;
   1504     else if (toy_sym_is(p, name, "sev"))
   1505       intrin = KIT_CG_INTRIN_SEV;
   1506     if (!toy_parser_expect(p, TOK_LPAREN) || !toy_parser_expect(p, TOK_RPAREN))
   1507       return KIT_CG_TYPE_NONE;
   1508     if (!kit_cg_target_supports_intrinsic(p->c, intrin))
   1509       return toy_unsupported_intrinsic(p);
   1510     kit_cg_intrinsic(p->cg, intrin, 0,
   1511                      toy_builtin_type(p, KIT_CG_BUILTIN_VOID));
   1512     return toy_builtin_type(p, KIT_CG_BUILTIN_VOID);
   1513   }
   1514 
   1515   if (toy_sym_is(p, name, "irq_save")) {
   1516     if (!toy_parser_expect(p, TOK_LPAREN) || !toy_parser_expect(p, TOK_RPAREN))
   1517       return KIT_CG_TYPE_NONE;
   1518     if (!kit_cg_target_supports_intrinsic(p->c, KIT_CG_INTRIN_IRQ_SAVE))
   1519       return toy_unsupported_intrinsic(p);
   1520     kit_cg_intrinsic(p->cg, KIT_CG_INTRIN_IRQ_SAVE, 0, p->size_type);
   1521     return p->size_type;
   1522   }
   1523 
   1524   if (toy_sym_is(p, name, "irq_restore")) {
   1525     KitCgTypeId prev_ty;
   1526     if (!toy_parser_expect(p, TOK_LPAREN)) return KIT_CG_TYPE_NONE;
   1527     prev_ty = toy_parse_expr(p);
   1528     if (prev_ty == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_RPAREN))
   1529       return KIT_CG_TYPE_NONE;
   1530     if (!toy_type_can_implicitly_cast(p, prev_ty, p->size_type)) {
   1531       toy_error(p, p->cur.loc, "irq_restore expects usize state");
   1532       return KIT_CG_TYPE_NONE;
   1533     }
   1534     if (!toy_emit_implicit_cast(p, prev_ty, p->size_type))
   1535       return KIT_CG_TYPE_NONE;
   1536     if (!kit_cg_target_supports_intrinsic(p->c, KIT_CG_INTRIN_IRQ_RESTORE))
   1537       return toy_unsupported_intrinsic(p);
   1538     kit_cg_intrinsic(p->cg, KIT_CG_INTRIN_IRQ_RESTORE, 1,
   1539                      toy_builtin_type(p, KIT_CG_BUILTIN_VOID));
   1540     return toy_builtin_type(p, KIT_CG_BUILTIN_VOID);
   1541   }
   1542 
   1543   if (toy_sym_is(p, name, "dmb") || toy_sym_is(p, name, "dsb")) {
   1544     KitCgIntrinsic intrin =
   1545         toy_sym_is(p, name, "dsb") ? KIT_CG_INTRIN_DSB : KIT_CG_INTRIN_DMB;
   1546     KitCgBarrierScope scope;
   1547     if (!toy_parser_expect(p, TOK_LPAREN) ||
   1548         !toy_parse_barrier_scope(p, &scope) ||
   1549         !toy_parser_expect(p, TOK_RPAREN))
   1550       return KIT_CG_TYPE_NONE;
   1551     if (!kit_cg_target_supports_intrinsic(p->c, intrin))
   1552       return toy_unsupported_intrinsic(p);
   1553     /* The barrier domain rides as an immediate operand the backend maps onto
   1554      * the arch's barrier-option field. */
   1555     kit_cg_push_int(p->cg, (uint64_t)scope, p->int_type);
   1556     kit_cg_intrinsic(p->cg, intrin, 1,
   1557                      toy_builtin_type(p, KIT_CG_BUILTIN_VOID));
   1558     return toy_builtin_type(p, KIT_CG_BUILTIN_VOID);
   1559   }
   1560 
   1561   if (toy_sym_is(p, name, "dcache_clean") ||
   1562       toy_sym_is(p, name, "dcache_invalidate") ||
   1563       toy_sym_is(p, name, "dcache_clean_invalidate") ||
   1564       toy_sym_is(p, name, "icache_invalidate")) {
   1565     KitCgTypeId ptr_ty, size_ty;
   1566     if (!toy_parser_expect(p, TOK_LPAREN)) return KIT_CG_TYPE_NONE;
   1567     ptr_ty = toy_parse_expr(p);
   1568     if (ptr_ty == KIT_CG_TYPE_NONE || !toy_expect_comma(p))
   1569       return KIT_CG_TYPE_NONE;
   1570     size_ty = toy_parse_expr(p);
   1571     if (size_ty == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_RPAREN))
   1572       return KIT_CG_TYPE_NONE;
   1573     if (!toy_type_is_ptr(p, ptr_ty) || !toy_type_is_intlike(p, size_ty)) {
   1574       toy_error(p, p->cur.loc,
   1575                 "cache intrinsic expects pointer and integer size");
   1576       return KIT_CG_TYPE_NONE;
   1577     }
   1578     return toy_unsupported_intrinsic(p);
   1579   }
   1580 
   1581   *recognized = 0;
   1582   return KIT_CG_TYPE_NONE;
   1583 }
   1584 
   1585 KitCgTypeId toy_parse_low_level_generic_builtin(ToyParser* p, KitSym name,
   1586                                                 int* recognized) {
   1587   KitCgTypeId ty;
   1588   *recognized = 1;
   1589 
   1590   if (toy_sym_is(p, name, "coro_switch")) {
   1591     KitCgTypeId from_ty, to_ty, value_ty;
   1592     if (!toy_parser_expect(p, TOK_LT)) return KIT_CG_TYPE_NONE;
   1593     ty = toy_parse_type(p);
   1594     if (ty == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_GT) ||
   1595         !toy_parser_expect(p, TOK_LPAREN))
   1596       return KIT_CG_TYPE_NONE;
   1597     from_ty = toy_parse_expr(p);
   1598     if (from_ty == KIT_CG_TYPE_NONE || !toy_expect_comma(p))
   1599       return KIT_CG_TYPE_NONE;
   1600     to_ty = toy_parse_expr(p);
   1601     if (to_ty == KIT_CG_TYPE_NONE || !toy_expect_comma(p))
   1602       return KIT_CG_TYPE_NONE;
   1603     value_ty = toy_parse_expr(p);
   1604     if (value_ty == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_RPAREN))
   1605       return KIT_CG_TYPE_NONE;
   1606     if (!toy_type_is_ptr(p, from_ty) || !toy_type_is_ptr(p, to_ty) ||
   1607         value_ty != ty) {
   1608       toy_error(p, p->cur.loc,
   1609                 "coro_switch expects pointer contexts and matching value type");
   1610       return KIT_CG_TYPE_NONE;
   1611     }
   1612     return toy_unsupported_intrinsic(p);
   1613   }
   1614 
   1615   *recognized = 0;
   1616   return KIT_CG_TYPE_NONE;
   1617 }