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 }