kit

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

parse_stmt.c (28463B)


      1 /* parse_stmt.c — statement parsers.
      2  *
      3  * Covers §6.8: if, while, for, do-while, return, break, continue, goto,
      4  * labeled, case, default, switch, _Static_assert, asm, compound,
      5  * and the top-level parse_stmt dispatcher.
      6  */
      7 
      8 #include "parse/parse_priv.h"
      9 
     10 /* ============================================================
     11  * File-local helpers
     12  * ============================================================ */
     13 
     14 static CKw ident_kw_stmt(const Parser* p, Sym name) {
     15   return ident_kw_inline(p, name);
     16 }
     17 
     18 static SrcLoc tok_loc_stmt(const Tok* t) { return t->loc; }
     19 
     20 static int accept_kw_stmt(Parser* p, CKw k) {
     21   if (!is_kw(p, &p->cur, k)) return 0;
     22   advance(p);
     23   return 1;
     24 }
     25 
     26 static void parse_stmt_suppressed(Parser* p) {
     27   pcg_codegen_suppress_push(p);
     28   parse_stmt(p);
     29   pcg_codegen_suppress_pop(p);
     30 }
     31 
     32 /* ============================================================
     33  * Statement parsers
     34  * ============================================================ */
     35 
     36 static void parse_if_stmt(Parser* p) {
     37   i64 cond = 0;
     38   int cond_known;
     39   KitCgIf it;
     40   expect_punct(p, '(', "'('");
     41   parse_expr(p);
     42   to_rvalue(p);
     43   if (!c_type_is_scalar(pcg_top_type(p))) {
     44     perr(p, "if condition requires scalar type");
     45   }
     46   cond_known = kit_cg_top_const_int(p->cg, &cond);
     47   expect_punct(p, ')', "')'");
     48   if (cond_known) {
     49     pcg_drop(p);
     50     if (cond) {
     51       parse_stmt(p);
     52       if (accept_kw_stmt(p, KW_ELSE)) parse_stmt_suppressed(p);
     53     } else {
     54       parse_stmt_suppressed(p);
     55       if (accept_kw_stmt(p, KW_ELSE)) parse_stmt(p);
     56     }
     57     return;
     58   }
     59   /* Structured `if`: nested SCOPE_BLOCKs with break. Lets every backend that
     60    * already lowers SCOPE_BLOCK natively (native arches, Wasm) emit
     61    * `if`/`if-else` without falling back to label/jump primitives.
     62    *
     63    * Under codegen suppression no cond is on the SValue stack, so emit
     64    * nothing and just walk the body for the diagnostics-only pass. */
     65   if (!pcg_emit_enabled(p)) {
     66     parse_stmt(p);
     67     if (accept_kw_stmt(p, KW_ELSE)) parse_stmt(p);
     68     return;
     69   }
     70   it = kit_cg_if_begin(p->cg);
     71   parse_stmt(p);
     72   kit_cg_if_else(p->cg, it);
     73   if (accept_kw_stmt(p, KW_ELSE)) parse_stmt(p);
     74   kit_cg_if_end(p->cg, it);
     75 }
     76 
     77 static void parse_while_stmt(Parser* p) {
     78   /* Drive the structured-CF API so the C-source target can lower this to
     79    * `for (;;) { … break; … continue; }` instead of goto soup. The labels
     80    * the scope mints are reused as cur_break/cur_continue, so parse_break
     81    * /parse_continue/etc. keep using their existing raw `pcg_jump` calls —
     82    * the C target recognizes the labels as the innermost scope's
     83    * boundaries and emits the structured keywords on its own. */
     84   CGLabel saved_break = p->cur_break;
     85   CGLabel saved_continue = p->cur_continue;
     86   KitCgScope scope;
     87   CGLabel L_top;
     88   CGLabel L_end;
     89   int emit = pcg_emit_enabled(p);
     90   if (emit) {
     91     scope = kit_cg_scope_begin(p->cg, KIT_CG_TYPE_NONE);
     92     L_top = kit_cg_scope_continue_label(p->cg, scope);
     93     L_end = kit_cg_scope_break_label(p->cg, scope);
     94   } else {
     95     scope = 0;
     96     L_top = (CGLabel)1;
     97     L_end = (CGLabel)1;
     98   }
     99   expect_punct(p, '(', "'('");
    100   parse_expr(p);
    101   to_rvalue(p);
    102   if (!c_type_is_scalar(pcg_top_type(p))) {
    103     perr(p, "while condition requires scalar type");
    104   }
    105   expect_punct(p, ')', "')'");
    106   if (!emit) {
    107     pcg_drop(p);
    108     p->cur_break = L_end;
    109     p->cur_continue = L_top;
    110     parse_stmt(p);
    111     p->cur_break = saved_break;
    112     p->cur_continue = saved_continue;
    113     return;
    114   }
    115   pcg_branch_false(p, L_end);
    116   p->cur_break = L_end;
    117   p->cur_continue = L_top;
    118   parse_stmt(p);
    119   p->cur_break = saved_break;
    120   p->cur_continue = saved_continue;
    121   pcg_jump(p, L_top);
    122   kit_cg_scope_end(p->cg, scope);
    123 }
    124 
    125 static void parse_for_stmt(Parser* p) {
    126   CGLabel L_top = pcg_label_new(p);
    127   CGLabel L_step = pcg_label_new(p);
    128   CGLabel L_end = pcg_label_new(p);
    129   CGLabel saved_break = p->cur_break;
    130   CGLabel saved_continue = p->cur_continue;
    131 
    132   scope_push(p);
    133   expect_punct(p, '(', "'('");
    134 
    135   /* init: declaration | expr | ; */
    136   if (!accept_punct(p, ';')) {
    137     DeclSpecs specs;
    138     if (parse_decl_specs(p, &specs)) {
    139       parse_local_decl(p, &specs);
    140     } else {
    141       parse_expr(p);
    142       pcg_drop(p);
    143       expect_punct(p, ';', "';'");
    144     }
    145   }
    146 
    147   pcg_label_place(p, L_top);
    148   if (!is_punct(&p->cur, ';')) {
    149     parse_expr(p);
    150     to_rvalue(p);
    151     if (!c_type_is_scalar(pcg_top_type(p))) {
    152       perr(p, "for condition requires scalar type");
    153     }
    154     pcg_branch_false(p, L_end);
    155   }
    156   expect_punct(p, ';', "';'");
    157 
    158   {
    159     CGLabel L_body = pcg_label_new(p);
    160     pcg_jump(p, L_body);
    161     pcg_label_place(p, L_step);
    162     if (!is_punct(&p->cur, ')')) {
    163       parse_expr(p);
    164       pcg_drop(p);
    165     }
    166     pcg_jump(p, L_top);
    167     expect_punct(p, ')', "')'");
    168     pcg_label_place(p, L_body);
    169 
    170     p->cur_break = L_end;
    171     p->cur_continue = L_step;
    172     parse_stmt(p);
    173     p->cur_break = saved_break;
    174     p->cur_continue = saved_continue;
    175 
    176     pcg_jump(p, L_step);
    177     pcg_label_place(p, L_end);
    178   }
    179   scope_pop(p);
    180 }
    181 
    182 static void parse_return_stmt(Parser* p) {
    183   if (accept_punct(p, ';')) {
    184     if (p->cur_func_ret && p->cur_func_ret->kind != TY_VOID) {
    185       perr(p, "return with no value in non-void function");
    186     }
    187     pcg_ret(p, 0);
    188     return;
    189   }
    190   if (p->cur_func_ret && p->cur_func_ret->kind == TY_VOID) {
    191     perr(p, "return with a value in void function");
    192   }
    193   parse_expr(p);
    194   to_rvalue(p);
    195   {
    196     const Type* rhs = pcg_top_type(p);
    197     CSemCheck chk = c_sem_check_assignment(p->pool, p->cur_func_ret, rhs,
    198                                            C_SEM_ASSIGN_RETURN);
    199     if (!chk.ok) perr(p, "%.*s", KIT_SLICE_ARG(kit_slice_cstr(chk.message)));
    200   }
    201   /* Convert the value to the function return type, as `return` performs the
    202    * equivalent of assignment to an object of the return type (§6.8.6.4).
    203    * pcg_ret expects the value already in the return type; without this a
    204    * narrower value (e.g. a _Bool call result returned from an int function)
    205    * would be reloaded at the wider return width and read adjacent bytes. */
    206   coerce_top_to_type(p, p->cur_func_ret);
    207   expect_punct(p, ';', "';' after return value");
    208   pcg_ret(p, 1);
    209 }
    210 
    211 static void parse_break_stmt(Parser* p) {
    212   if (p->cur_break == 0) perr(p, "'break' outside of loop or switch");
    213   pcg_jump(p, p->cur_break);
    214   expect_punct(p, ';', "';' after break");
    215 }
    216 
    217 static void parse_continue_stmt(Parser* p) {
    218   if (p->cur_continue == 0) perr(p, "'continue' outside of loop");
    219   pcg_jump(p, p->cur_continue);
    220   expect_punct(p, ';', "';' after continue");
    221 }
    222 
    223 static void parse_do_stmt(Parser* p) {
    224   CGLabel L_top = pcg_label_new(p);
    225   CGLabel L_cond = pcg_label_new(p);
    226   CGLabel L_end = pcg_label_new(p);
    227   CGLabel saved_break = p->cur_break;
    228   CGLabel saved_continue = p->cur_continue;
    229   pcg_label_place(p, L_top);
    230   p->cur_break = L_end;
    231   p->cur_continue = L_cond;
    232   parse_stmt(p);
    233   p->cur_break = saved_break;
    234   p->cur_continue = saved_continue;
    235   pcg_label_place(p, L_cond);
    236   if (!is_kw(p, &p->cur, KW_WHILE)) perr(p, "expected 'while' after do-body");
    237   advance(p); /* while */
    238   expect_punct(p, '(', "'('");
    239   parse_expr(p);
    240   to_rvalue(p);
    241   if (!c_type_is_scalar(pcg_top_type(p))) {
    242     perr(p, "do-while condition requires scalar type");
    243   }
    244   expect_punct(p, ')', "')' after do-while condition");
    245   expect_punct(p, ';', "';' after do-while");
    246   pcg_branch_true(p, L_top);
    247   pcg_label_place(p, L_end);
    248 }
    249 
    250 GotoLabel* label_get_or_create(Parser* p, Sym name, SrcLoc loc) {
    251   GotoLabel* gl;
    252   for (gl = p->goto_labels; gl; gl = gl->next) {
    253     if (gl->name == name) return gl;
    254   }
    255   gl = arena_new(p->pool->arena, GotoLabel);
    256   if (!gl) perr(p, "out of memory in label_get_or_create");
    257   memset(gl, 0, sizeof *gl);
    258   gl->name = name;
    259   gl->label = pcg_label_new(p);
    260   gl->placed = 0;
    261   gl->first_use = loc;
    262   gl->min_forward_vla_mark = p->vla_mark;
    263   gl->label_vla_mark = 0;
    264   gl->next = p->goto_labels;
    265   p->goto_labels = gl;
    266   return gl;
    267 }
    268 
    269 CGLabel take_label_addr(Parser* p, Sym name, SrcLoc loc) {
    270   GotoLabel* gl;
    271   if (!p->cur_func_name) {
    272     perr(p, "label address ('&&label') is only valid inside a function");
    273   }
    274   gl = label_get_or_create(p, name, loc);
    275   if (p->computed_goto_emitted && !gl->addr_taken) {
    276     perr(p,
    277          "label address taken after a computed 'goto *'; take all label "
    278          "addresses before the first computed goto in a function");
    279   }
    280   gl->addr_taken = 1;
    281   return gl->label;
    282 }
    283 
    284 /* Computed goto: `goto *expr;` (GNU labels-as-values). The branch may target
    285  * any label whose address has been taken in this function with `&&label`. */
    286 static void parse_computed_goto(Parser* p) {
    287   GotoLabel* gl;
    288   CGLabel* targets;
    289   u32 ntargets = 0;
    290   u32 i = 0;
    291   advance(p); /* '*' */
    292   parse_expr(p);
    293   to_rvalue(p);
    294   if (!type_is_ptr(pcg_top_type(p))) {
    295     perr(p, "computed goto requires a pointer operand");
    296   }
    297   expect_punct(p, ';', "';' after computed goto");
    298   for (gl = p->goto_labels; gl; gl = gl->next) {
    299     if (gl->addr_taken) ++ntargets;
    300   }
    301   if (ntargets == 0) {
    302     perr(p,
    303          "computed 'goto *' requires at least one label whose address is "
    304          "taken with '&&label'");
    305   }
    306   targets = arena_array(p->pool->arena, CGLabel, ntargets);
    307   if (!targets) perr(p, "out of memory for computed goto targets");
    308   for (gl = p->goto_labels; gl; gl = gl->next) {
    309     if (gl->addr_taken) targets[i++] = gl->label;
    310   }
    311   p->computed_goto_emitted = 1;
    312   pcg_computed_goto(p, targets, ntargets);
    313 }
    314 
    315 static void parse_goto_stmt(Parser* p) {
    316   Sym name;
    317   SrcLoc loc;
    318   GotoLabel* gl;
    319   if (is_punct(&p->cur, '*')) {
    320     parse_computed_goto(p);
    321     return;
    322   }
    323   if (p->cur.kind != TOK_IDENT || ident_kw_stmt(p, p->cur.v.ident) != KW_NONE) {
    324     perr(p, "expected label name after 'goto'");
    325   }
    326   name = p->cur.v.ident;
    327   loc = tok_loc_stmt(&p->cur);
    328   advance(p);
    329   expect_punct(p, ';', "';' after goto");
    330   gl = label_get_or_create(p, name, loc);
    331   if (gl->placed) {
    332     if (p->vla_mark < gl->label_vla_mark) {
    333       perr(p, "goto into scope of variably modified object");
    334     }
    335   } else if (p->vla_mark < gl->min_forward_vla_mark) {
    336     gl->min_forward_vla_mark = p->vla_mark;
    337   }
    338   pcg_jump(p, gl->label);
    339 }
    340 
    341 static void parse_label_stmt(Parser* p) {
    342   Sym name = p->cur.v.ident;
    343   SrcLoc loc = tok_loc_stmt(&p->cur);
    344   GotoLabel* gl;
    345   advance(p); /* IDENT */
    346   advance(p); /* ':' */
    347   gl = label_get_or_create(p, name, loc);
    348   if (gl->placed) perr(p, "duplicate label");
    349   if (gl->min_forward_vla_mark < p->vla_mark) {
    350     perr(p, "goto into scope of variably modified object");
    351   }
    352   gl->placed = 1;
    353   gl->label_vla_mark = p->vla_mark;
    354   pcg_label_place(p, gl->label);
    355   parse_stmt(p);
    356 }
    357 
    358 static void parse_case_stmt(Parser* p) {
    359   i64 v;
    360   CGLabel L;
    361   CaseEntry* ce;
    362   SrcLoc loc = tok_loc_stmt(&p->cur);
    363   if (!p->cur_switch) perr(p, "'case' label not in switch statement");
    364   v = eval_const_int(p, loc);
    365   for (ce = p->cur_switch->cases; ce; ce = ce->next) {
    366     if (ce->value == v) perr(p, "duplicate case value");
    367   }
    368   expect_punct(p, ':', "':' after case constant");
    369   L = pcg_label_new(p);
    370   pcg_label_place(p, L);
    371   ce = arena_new(p->pool->arena, CaseEntry);
    372   if (!ce) perr(p, "out of memory in parse_case_stmt");
    373   ce->value = v;
    374   ce->label = L;
    375   ce->next = p->cur_switch->cases;
    376   p->cur_switch->cases = ce;
    377   parse_stmt(p);
    378 }
    379 
    380 static void parse_default_stmt(Parser* p) {
    381   CGLabel L;
    382   if (!p->cur_switch) perr(p, "'default' label not in switch statement");
    383   expect_punct(p, ':', "':' after default");
    384   if (p->cur_switch->default_label != 0) perr(p, "duplicate 'default' label");
    385   L = pcg_label_new(p);
    386   pcg_label_place(p, L);
    387   p->cur_switch->default_label = L;
    388   parse_stmt(p);
    389 }
    390 
    391 static void parse_switch_stmt(Parser* p) {
    392   /* Wrap the whole switch in a structured scope so the C-source target
    393    * renders `break;` as the keyword. Continue isn't applicable to
    394    * switch (C `continue` skips switches and targets the enclosing loop),
    395    * so cur_continue is left alone. The dispatch itself goes through
    396    * `kit_cg_switch_value`, which native arches lower to a cmp_branch
    397    * chain (unchanged behaviour) and which the C target overrides to
    398    * emit a real `switch (sel) { case V: goto L_V; …; default: goto
    399    * L_def; }`. */
    400   CGLabel saved_break = p->cur_break;
    401   SwitchCtx ctx;
    402   SwitchCtx* saved_switch = p->cur_switch;
    403   KitCgScope scope;
    404   CGLabel L_dispatch;
    405   CGLabel L_end;
    406   int emit = pcg_emit_enabled(p);
    407   FrameSlotDesc fsd;
    408   const Type* vty;
    409   CaseEntry* it;
    410   CaseEntry* prev;
    411   CaseEntry* head;
    412 
    413   if (emit) {
    414     scope = kit_cg_scope_begin(p->cg, KIT_CG_TYPE_NONE);
    415     L_dispatch = pcg_label_new(p);
    416     L_end = kit_cg_scope_break_label(p->cg, scope);
    417   } else {
    418     scope = 0;
    419     L_dispatch = (CGLabel)1;
    420     L_end = (CGLabel)1;
    421   }
    422 
    423   expect_punct(p, '(', "'('");
    424   parse_expr(p);
    425   to_rvalue(p);
    426   vty = pcg_top_type(p);
    427   if (!vty) vty = type_prim(p->pool, TY_INT);
    428   if (!type_is_int(vty)) perr(p, "switch expression requires integer type");
    429   /* C99 6.8.4.2: the integer promotions are performed on the controlling
    430    * expression. Without this, a `signed char` selector is stored to a
    431    * byte-sized slot and reloaded with an unsigned-byte load, dropping the
    432    * sign bit and miscomparing against negative case constants. */
    433   {
    434     const Type* prom = type_unqual(p->pool, vty);
    435     if (prom && prom->kind == TY_ENUM)
    436       prom = type_prim(p->pool, TY_INT);
    437     else
    438       prom = type_promoted(p->pool, prom);
    439     if (prom && prom != vty) {
    440       pcg_convert(p, prom);
    441       vty = prom;
    442     }
    443   }
    444   expect_punct(p, ')', "')' after switch expression");
    445 
    446   if (!emit) {
    447     pcg_drop(p);
    448     memset(&ctx, 0, sizeof ctx);
    449     ctx.parent = saved_switch;
    450     p->cur_switch = &ctx;
    451     p->cur_break = L_end;
    452     parse_stmt(p);
    453     p->cur_break = saved_break;
    454     p->cur_switch = saved_switch;
    455     return;
    456   }
    457 
    458   memset(&ctx, 0, sizeof ctx);
    459   memset(&fsd, 0, sizeof fsd);
    460   fsd.type = vty;
    461   fsd.size = c_abi_sizeof(p->abi, vty);
    462   fsd.align = c_abi_alignof(p->abi, vty);
    463   fsd.kind = FS_LOCAL;
    464   ctx.value_slot = pcg_local(p, &fsd);
    465   ctx.value_type = vty;
    466   ctx.parent = saved_switch;
    467 
    468   pcg_push_local_typed(p, ctx.value_slot, vty);
    469   pcg_swap(p);
    470   pcg_store(p);
    471   pcg_drop(p);
    472 
    473   pcg_jump(p, L_dispatch);
    474 
    475   p->cur_switch = &ctx;
    476   p->cur_break = L_end;
    477   parse_stmt(p);
    478   p->cur_break = saved_break;
    479   p->cur_switch = saved_switch;
    480 
    481   pcg_jump(p, L_end);
    482 
    483   pcg_label_place(p, L_dispatch);
    484   /* Reverse cases into source order; CaseEntry list grows at the head
    485    * during parsing so iteration here is LIFO without the flip. */
    486   prev = NULL;
    487   head = ctx.cases;
    488   while (head) {
    489     CaseEntry* nxt = head->next;
    490     head->next = prev;
    491     prev = head;
    492     head = nxt;
    493   }
    494   /* Count and pack into the value/label arrays the public API expects. */
    495   {
    496     u32 ncases = 0;
    497     for (it = prev; it; it = it->next) ncases++;
    498     if (ncases == 0 && ctx.default_label == 0) {
    499       /* `switch (x) {}` — no cases, no default. Nothing to dispatch.
    500        * Fall through to scope_end which terminates the loop. */
    501     } else {
    502       KitCgSwitchCase* cases =
    503           ncases ? arena_array(p->pool->arena, KitCgSwitchCase, ncases) : NULL;
    504       if (ncases && !cases) perr(p, "out of memory in parse_switch_stmt");
    505       {
    506         u32 i = 0;
    507         for (it = prev; it; it = it->next) {
    508           cases[i].value = (uint64_t)it->value;
    509           cases[i].label = (KitCgLabel)it->label;
    510           i++;
    511         }
    512       }
    513       pcg_push_local_typed(p, ctx.value_slot, vty);
    514       pcg_load(p);
    515       {
    516         KitCgSwitch sw;
    517         memset(&sw, 0, sizeof sw);
    518         sw.selector_type = pcg_tid(p, vty);
    519         sw.default_label = ctx.default_label ? (KitCgLabel)ctx.default_label
    520                                              : (KitCgLabel)L_end;
    521         sw.cases = cases;
    522         sw.ncases = ncases;
    523         sw.hint = KIT_CG_SWITCH_TARGET_DEFAULT;
    524         kit_cg_switch(p->cg, sw);
    525       }
    526     }
    527   }
    528   kit_cg_scope_end(p->cg, scope);
    529 }
    530 
    531 void parse_static_assert(Parser* p) {
    532   SrcLoc loc = tok_loc_stmt(&p->cur);
    533   i64 v;
    534   if (!accept_kw_stmt(p, KW_STATIC_ASSERT)) {
    535     perr(p, "expected _Static_assert");
    536   }
    537   expect_punct(p, '(', "'(' after _Static_assert");
    538   v = eval_const_int(p, tok_loc_stmt(&p->cur));
    539   expect_punct(p, ',', "',' separating _Static_assert args");
    540   if (p->cur.kind != TOK_STR) {
    541     perr(p, "expected string literal as _Static_assert message");
    542   }
    543   {
    544     Tok msg = p->cur;
    545     advance(p);
    546     expect_punct(p, ')', "')' after _Static_assert");
    547     expect_punct(p, ';', "';' after _Static_assert");
    548     if (!v) {
    549       KitSlice msg_sl = kit_sym_str(p->pool->c, msg.spelling);
    550       size_t mlen = msg_sl.len;
    551       const char* mstr = msg_sl.s;
    552       compiler_panic(p->c, loc, "static assertion failed: %.*s", (int)mlen,
    553                      mstr ? mstr : "");
    554     }
    555   }
    556 }
    557 
    558 /* GNU inline-asm statement. The leading 'asm'/'__asm__' keyword has
    559  * already been consumed by parse_stmt. */
    560 typedef struct AsmOutLValue {
    561   FrameSlot addr_slot;
    562   FrameSlot value_slot;
    563   const Type* ptr_ty;
    564   const Type* val_ty;
    565   u8 direct_local;
    566   u8 pad[3];
    567 } AsmOutLValue;
    568 
    569 static void asm_out_lvalue_push(Parser* p, const AsmOutLValue* lv) {
    570   if (lv->direct_local) {
    571     pcg_push_local_typed(p, lv->value_slot, lv->val_ty);
    572     return;
    573   }
    574   pcg_push_local_typed(p, lv->addr_slot, lv->ptr_ty);
    575   pcg_load(p);
    576   pcg_deref(p, lv->val_ty);
    577 }
    578 
    579 static void asm_out_value_push(Parser* p, const AsmOutLValue* lv) {
    580   asm_out_lvalue_push(p, lv);
    581   pcg_load(p);
    582 }
    583 
    584 static Sym parse_asm_operand_name(Parser* p) {
    585   Sym name = 0;
    586   if (!is_punct(&p->cur, '[')) return 0;
    587   advance(p);
    588   if (p->cur.kind != TOK_IDENT) {
    589     perr(p, "expected identifier inside '[name]' on asm operand");
    590   }
    591   name = p->cur.v.ident;
    592   advance(p);
    593   expect_punct(p, ']', "']' after asm operand name");
    594   return name;
    595 }
    596 
    597 static const char* parse_asm_str(Parser* p, const char* what) {
    598   u8* bytes;
    599   size_t nlen = 0;
    600   Sym s;
    601   Tok t;
    602   if (p->cur.kind != TOK_STR) {
    603     perr(p, "expected string literal in %.*s",
    604          KIT_SLICE_ARG(kit_slice_cstr(what)));
    605   }
    606   t = p->cur;
    607   advance(p);
    608   bytes = decode_string_literal(p, &t, &nlen);
    609   if (nlen > 0) nlen -= 1;
    610   s = kit_sym_intern(p->pool->c,
    611                      (KitSlice){.s = (const char*)bytes, .len = nlen});
    612   kit_compiler_context(p->c)->heap->free(kit_compiler_context(p->c)->heap,
    613                                          bytes, 0);
    614   return kit_sym_str(p->pool->c, s).s;
    615 }
    616 
    617 /* GNU local register variables: when an asm operand is exactly a bare reference
    618  * to a `register T x __asm__("reg")` local, return that register name (else 0).
    619  * Called with p->cur positioned at the first token of the operand expression,
    620  * so it only peeks — it must not consume. The operand has to be a lone
    621  * identifier (the canonical idiom); anything more complex is not a
    622  * hard-register operand under GCC's rules either. The name is carried opaquely
    623  * on the constraint's `reg` field; CG/native code validates that the constraint
    624  * is a target register constraint and only the target resolves it to a
    625  * register. */
    626 static Sym asm_operand_pinned_reg(Parser* p, FrameSlot* slot_out) {
    627   Tok nxt;
    628   SymEntry* e;
    629   if (p->cur.kind != TOK_IDENT) return 0;
    630   nxt = peek1(p);
    631   if (!is_punct(&nxt, ')')) return 0;
    632   e = scope_lookup(p, p->cur.v.ident);
    633   if (!e || e->kind != SEK_LOCAL) return 0;
    634   if (e->reg_asm_name && slot_out) *slot_out = e->v.slot;
    635   return e->reg_asm_name;
    636 }
    637 
    638 static void parse_asm_stmt(Parser* p) {
    639   const char* tmpl;
    640   AsmConstraint* outs = NULL;
    641   AsmConstraint* ins = NULL;
    642   Sym* clobbers = NULL;
    643   AsmOutLValue* out_lvs = NULL;
    644   u32 nout = 0, nin = 0, nclob = 0;
    645   u32 cap_out = 0, cap_in = 0, cap_clob = 0;
    646   int saw_goto = 0;
    647   SrcLoc loc = tok_loc_stmt(&p->cur);
    648 
    649   for (;;) {
    650     if (accept_kw_stmt(p, KW_VOLATILE)) continue;
    651     if (p->cur.kind == TOK_IDENT && p->cur.v.ident == p->sym_volatile_alias) {
    652       advance(p);
    653       continue;
    654     }
    655     break;
    656   }
    657   if (accept_kw_stmt(p, KW_GOTO)) saw_goto = 1;
    658 
    659   expect_punct(p, '(', "'(' after asm");
    660   tmpl = parse_asm_str(p, "asm template");
    661 
    662   if (accept_punct(p, ':')) {
    663     if (!is_punct(&p->cur, ':') && !is_punct(&p->cur, ')')) {
    664       cap_out = 4;
    665       outs =
    666           (AsmConstraint*)arena_array(p->pool->arena, AsmConstraint, cap_out);
    667       out_lvs =
    668           (AsmOutLValue*)arena_array(p->pool->arena, AsmOutLValue, cap_out);
    669       for (;;) {
    670         AsmConstraint c;
    671         AsmOutLValue lv;
    672         const Type* val_ty;
    673         const Type* ptr_ty;
    674         FrameSlotDesc fsd;
    675         FrameSlot slot;
    676         FrameSlot pinned_slot;
    677         memset(&c, 0, sizeof c);
    678         memset(&lv, 0, sizeof lv);
    679         pinned_slot = FRAME_SLOT_NONE;
    680         c.name = parse_asm_operand_name(p);
    681         c.str = parse_asm_str(p, "asm output constraint");
    682         if (c.str && c.str[0] == '+')
    683           c.dir = ASM_INOUT;
    684         else
    685           c.dir = ASM_OUT;
    686         expect_punct(p, '(', "'(' before asm output lvalue");
    687         c.reg = asm_operand_pinned_reg(p, &pinned_slot);
    688         parse_assign_expr(p);
    689         val_ty = pcg_top_type(p);
    690         if (!val_ty) perr(p, "asm output: cannot determine lvalue type");
    691         c.type = val_ty;
    692         if (c.reg && pinned_slot != FRAME_SLOT_NONE) {
    693           pcg_drop(p);
    694           lv.direct_local = 1;
    695           lv.value_slot = pinned_slot;
    696         } else {
    697           pcg_addr(p);
    698           ptr_ty = pcg_top_type(p);
    699           if (!ptr_ty) perr(p, "asm output: cannot take address");
    700           memset(&fsd, 0, sizeof fsd);
    701           fsd.type = ptr_ty;
    702           fsd.size = 8;
    703           fsd.align = 8;
    704           fsd.kind = FS_LOCAL;
    705           slot = pcg_local(p, &fsd);
    706           pcg_push_local_typed(p, slot, ptr_ty);
    707           pcg_swap(p);
    708           pcg_store(p);
    709           pcg_drop(p);
    710           lv.addr_slot = slot;
    711           lv.ptr_ty = ptr_ty;
    712         }
    713         lv.val_ty = val_ty;
    714         expect_punct(p, ')', "')' after asm output lvalue");
    715         if (nout == cap_out) {
    716           u32 nc = cap_out * 2;
    717           AsmConstraint* nb =
    718               (AsmConstraint*)arena_array(p->pool->arena, AsmConstraint, nc);
    719           AsmOutLValue* nlv =
    720               (AsmOutLValue*)arena_array(p->pool->arena, AsmOutLValue, nc);
    721           memcpy(nb, outs, sizeof(AsmConstraint) * nout);
    722           memcpy(nlv, out_lvs, sizeof(AsmOutLValue) * nout);
    723           outs = nb;
    724           out_lvs = nlv;
    725           cap_out = nc;
    726         }
    727         outs[nout] = c;
    728         out_lvs[nout] = lv;
    729         nout++;
    730         if (!accept_punct(p, ',')) break;
    731       }
    732     }
    733 
    734     if (accept_punct(p, ':')) {
    735       if (!is_punct(&p->cur, ':') && !is_punct(&p->cur, ')')) {
    736         cap_in = 4;
    737         ins =
    738             (AsmConstraint*)arena_array(p->pool->arena, AsmConstraint, cap_in);
    739         for (;;) {
    740           AsmConstraint c;
    741           memset(&c, 0, sizeof c);
    742           c.name = parse_asm_operand_name(p);
    743           c.str = parse_asm_str(p, "asm input constraint");
    744           c.dir = ASM_IN;
    745           expect_punct(p, '(', "'(' before asm input expression");
    746           c.reg = asm_operand_pinned_reg(p, NULL);
    747           parse_assign_expr(p);
    748           to_rvalue(p);
    749           c.type = pcg_top_type(p);
    750           expect_punct(p, ')', "')' after asm input expression");
    751           if (nin == cap_in) {
    752             u32 nc = cap_in * 2;
    753             AsmConstraint* nb =
    754                 (AsmConstraint*)arena_array(p->pool->arena, AsmConstraint, nc);
    755             memcpy(nb, ins, sizeof(AsmConstraint) * nin);
    756             ins = nb;
    757             cap_in = nc;
    758           }
    759           ins[nin++] = c;
    760           if (!accept_punct(p, ',')) break;
    761         }
    762       }
    763 
    764       if (accept_punct(p, ':')) {
    765         if (!is_punct(&p->cur, ':') && !is_punct(&p->cur, ')')) {
    766           cap_clob = 4;
    767           clobbers = (Sym*)arena_array(p->pool->arena, Sym, cap_clob);
    768           for (;;) {
    769             const char* cstr;
    770             Sym cs;
    771             cstr = parse_asm_str(p, "asm clobber");
    772             cs = kit_sym_intern(p->pool->c, kit_slice_cstr(cstr));
    773             if (nclob == cap_clob) {
    774               u32 nc = cap_clob * 2;
    775               Sym* nb = (Sym*)arena_array(p->pool->arena, Sym, nc);
    776               memcpy(nb, clobbers, sizeof(Sym) * nclob);
    777               clobbers = nb;
    778               cap_clob = nc;
    779             }
    780             clobbers[nclob++] = cs;
    781             if (!accept_punct(p, ',')) break;
    782           }
    783         }
    784 
    785         if (accept_punct(p, ':')) {
    786           if (!is_punct(&p->cur, ')')) {
    787             for (;;) {
    788               if (p->cur.kind != TOK_IDENT) {
    789                 perr(p, "expected label identifier in asm-goto label list");
    790               }
    791               advance(p);
    792               if (!accept_punct(p, ',')) break;
    793             }
    794           }
    795         }
    796       }
    797     }
    798   }
    799 
    800   expect_punct(p, ')', "')' to close asm");
    801   expect_punct(p, ';', "';' after asm statement");
    802 
    803   (void)saw_goto;
    804 
    805   u32 ninout = 0;
    806   for (u32 i = 0; i < nout; ++i) {
    807     if (outs[i].dir == ASM_INOUT) ninout++;
    808   }
    809   if (ninout > 0) {
    810     static const char* const k_match_strs[10] = {"0", "1", "2", "3", "4",
    811                                                  "5", "6", "7", "8", "9"};
    812     u32 need = nin + ninout;
    813     if (need > cap_in) {
    814       u32 nc = cap_in ? cap_in : 4;
    815       while (nc < need) nc *= 2;
    816       AsmConstraint* nb =
    817           (AsmConstraint*)arena_array(p->pool->arena, AsmConstraint, nc);
    818       if (nin) memcpy(nb, ins, sizeof(AsmConstraint) * nin);
    819       ins = nb;
    820       cap_in = nc;
    821     }
    822     for (u32 i = 0; i < nout; ++i) {
    823       if (outs[i].dir != ASM_INOUT) continue;
    824       if (i >= 10) {
    825         perr(p,
    826              "asm: '+r' constraint at output index >9 exceeds "
    827              "matching-digit syntax");
    828       }
    829       AsmOutLValue* lv = &out_lvs[i];
    830       asm_out_value_push(p, lv);
    831       AsmConstraint mc;
    832       memset(&mc, 0, sizeof mc);
    833       mc.str = k_match_strs[i];
    834       mc.dir = ASM_IN;
    835       mc.type = lv->val_ty;
    836       ins[nin++] = mc;
    837     }
    838   }
    839 
    840   pcg_set_loc(p, loc);
    841   pcg_inline_asm(p, tmpl, outs, nout, ins, nin, clobbers, nclob);
    842 
    843   if (nout > 0) {
    844     u32 i;
    845     for (i = nout; i-- > 0;) {
    846       AsmOutLValue* lv = &out_lvs[i];
    847       asm_out_lvalue_push(p, lv);
    848       pcg_swap(p);
    849       pcg_store(p);
    850       pcg_drop(p);
    851     }
    852   }
    853 }
    854 
    855 void parse_compound_stmt(Parser* p) {
    856   expect_punct(p, '{', "'{'");
    857   scope_push(p);
    858   while (!is_punct(&p->cur, '}') && p->cur.kind != TOK_EOF) {
    859     if (p->cur.kind == TOK_NEWLINE || is_pp_hash(&p->cur)) {
    860       advance(p);
    861       continue;
    862     }
    863     if (is_kw(p, &p->cur, KW_STATIC_ASSERT)) {
    864       parse_static_assert(p);
    865       continue;
    866     }
    867     {
    868       DeclSpecs specs;
    869       Tok save_tok = p->cur;
    870       (void)save_tok;
    871       if (parse_decl_specs(p, &specs)) {
    872         parse_local_decl(p, &specs);
    873       } else {
    874         parse_stmt(p);
    875       }
    876     }
    877   }
    878   expect_punct(p, '}', "'}'");
    879   scope_pop(p);
    880 }
    881 
    882 void parse_stmt(Parser* p) {
    883   pcg_set_loc(p, tok_loc_stmt(&p->cur));
    884   if (p->cur.kind == TOK_IDENT && ident_kw_stmt(p, p->cur.v.ident) == KW_NONE) {
    885     Tok n = peek1(p);
    886     if (is_punct(&n, ':')) {
    887       parse_label_stmt(p);
    888       return;
    889     }
    890   }
    891   if (is_punct(&p->cur, '{')) {
    892     parse_compound_stmt(p);
    893     return;
    894   }
    895   if (is_punct(&p->cur, ';')) {
    896     advance(p);
    897     return;
    898   }
    899   if (is_kw(p, &p->cur, KW_IF)) {
    900     advance(p);
    901     parse_if_stmt(p);
    902     return;
    903   }
    904   if (is_kw(p, &p->cur, KW_WHILE)) {
    905     advance(p);
    906     parse_while_stmt(p);
    907     return;
    908   }
    909   if (is_kw(p, &p->cur, KW_FOR)) {
    910     advance(p);
    911     parse_for_stmt(p);
    912     return;
    913   }
    914   if (is_kw(p, &p->cur, KW_DO)) {
    915     advance(p);
    916     parse_do_stmt(p);
    917     return;
    918   }
    919   if (is_kw(p, &p->cur, KW_RETURN)) {
    920     advance(p);
    921     parse_return_stmt(p);
    922     return;
    923   }
    924   if (is_kw(p, &p->cur, KW_BREAK)) {
    925     advance(p);
    926     parse_break_stmt(p);
    927     return;
    928   }
    929   if (is_kw(p, &p->cur, KW_CONTINUE)) {
    930     advance(p);
    931     parse_continue_stmt(p);
    932     return;
    933   }
    934   if (is_kw(p, &p->cur, KW_GOTO)) {
    935     advance(p);
    936     parse_goto_stmt(p);
    937     return;
    938   }
    939   if (is_kw(p, &p->cur, KW_SWITCH)) {
    940     advance(p);
    941     parse_switch_stmt(p);
    942     return;
    943   }
    944   if (is_kw(p, &p->cur, KW_CASE)) {
    945     advance(p);
    946     parse_case_stmt(p);
    947     return;
    948   }
    949   if (is_kw(p, &p->cur, KW_DEFAULT)) {
    950     advance(p);
    951     parse_default_stmt(p);
    952     return;
    953   }
    954   if (is_kw(p, &p->cur, KW_ASM) || is_kw(p, &p->cur, KW_BUILTIN_ASM)) {
    955     advance(p);
    956     parse_asm_stmt(p);
    957     return;
    958   }
    959   /* Expression statement. */
    960   parse_expr(p);
    961   pcg_drop(p);
    962   expect_punct(p, ';', "';' after expression");
    963 }