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