asm.c (16423B)
1 #include <string.h> 2 3 #include "internal.h" 4 5 typedef struct ToyAsmOperandList { 6 KitCgAsmOperand* items; 7 uint32_t count; 8 size_t cap; 9 } ToyAsmOperandList; 10 11 typedef struct ToyAsmClobberList { 12 KitSym* items; 13 uint32_t count; 14 size_t cap; 15 } ToyAsmClobberList; 16 17 static int toy_expect_ident(ToyParser* p, const char* name) { 18 if (p->cur.kind != TOK_IDENT || !toy_sym_is(p, toy_tok_sym(p, p->cur), name)) 19 return 0; 20 toy_parser_advance(p); 21 return 1; 22 } 23 24 static int toy_asm_append_operand(ToyParser* p, ToyAsmOperandList* list, 25 const KitCgAsmOperand* operand) { 26 if (!toy_parser_reserve(p, (void**)&list->items, &list->cap, 27 (size_t)list->count + 1u, sizeof *list->items, 28 "asm operands")) { 29 return 0; 30 } 31 list->items[list->count++] = *operand; 32 return 1; 33 } 34 35 static int toy_asm_append_clobber(ToyParser* p, ToyAsmClobberList* list, 36 KitSym clobber) { 37 if (!toy_parser_reserve(p, (void**)&list->items, &list->cap, 38 (size_t)list->count + 1u, sizeof *list->items, 39 "asm clobbers")) { 40 return 0; 41 } 42 list->items[list->count++] = clobber; 43 return 1; 44 } 45 46 static const char* toy_asm_constraint_body(const char* s) { 47 if (!s) return ""; 48 if (s[0] == '=' && s[1] == '&') return s + 2; 49 if (s[0] == '=' || s[0] == '+' || s[0] == '&') return s + 1; 50 return s; 51 } 52 53 static int toy_asm_is_decimal_constraint(const char* s) { 54 if (!s || s[0] < '0' || s[0] > '9') return 0; 55 while (*s) { 56 if (*s < '0' || *s > '9') return 0; 57 ++s; 58 } 59 return 1; 60 } 61 62 static int toy_validate_asm_output_constraint(ToyParser* p, 63 const KitCgAsmOperand* op) { 64 const char* s = kit_sym_str(p->c, op->constraint).s; 65 const char* body = toy_asm_constraint_body(s); 66 if (op->dir == KIT_CG_ASM_OUT) { 67 if (!s || s[0] != '=' || body[0] != 'r' || body[1] != '\0') { 68 toy_error(p, p->cur.loc, "unsupported asm output constraint"); 69 return 0; 70 } 71 } else if (op->dir == KIT_CG_ASM_INOUT) { 72 if (!s || s[0] != '+' || body[0] != 'r' || body[1] != '\0') { 73 toy_error(p, p->cur.loc, "unsupported asm output constraint"); 74 return 0; 75 } 76 } 77 return 1; 78 } 79 80 static int toy_validate_asm_input_constraint(ToyParser* p, 81 const KitCgAsmOperand* op) { 82 const char* s = kit_sym_str(p->c, op->constraint).s; 83 if ((s && s[0] && !s[1] && (s[0] == 'r' || s[0] == 'i' || s[0] == 'm')) || 84 toy_asm_is_decimal_constraint(s)) { 85 return 1; 86 } 87 toy_error(p, p->cur.loc, "unsupported asm input constraint"); 88 return 0; 89 } 90 91 static int toy_parse_asm_output_operand(ToyParser* p, 92 KitCgAsmOperand* operand) { 93 KitSym op_name; 94 memset(operand, 0, sizeof *operand); 95 if (p->cur.kind != TOK_IDENT) { 96 toy_error(p, p->cur.loc, "expected asm output operand"); 97 return 0; 98 } 99 op_name = toy_tok_sym(p, p->cur); 100 if (!toy_sym_is(p, op_name, "out") && !toy_sym_is(p, op_name, "inout")) { 101 operand->name = op_name; 102 toy_parser_advance(p); 103 if (!toy_parser_expect(p, TOK_EQ)) { 104 toy_error(p, p->cur.loc, "expected '=' after asm output name"); 105 return 0; 106 } 107 if (p->cur.kind != TOK_IDENT) { 108 toy_error(p, p->cur.loc, "expected asm output operand"); 109 return 0; 110 } 111 op_name = toy_tok_sym(p, p->cur); 112 if (!toy_sym_is(p, op_name, "out") && !toy_sym_is(p, op_name, "inout")) { 113 toy_error(p, p->cur.loc, "expected asm output operand"); 114 return 0; 115 } 116 } 117 toy_parser_advance(p); 118 if (!toy_parser_expect(p, TOK_LPAREN) || 119 !toy_parse_string_sym(p, &operand->constraint, NULL) || 120 !toy_expect_comma(p)) { 121 return 0; 122 } 123 if (toy_sym_is(p, op_name, "inout")) { 124 operand->type = toy_parse_expr(p); 125 if (operand->type == KIT_CG_TYPE_NONE || 126 !toy_parser_expect(p, TOK_RPAREN)) { 127 return 0; 128 } 129 operand->dir = KIT_CG_ASM_INOUT; 130 return 1; 131 } 132 if (p->cur.kind != TOK_IDENT) { 133 toy_error(p, p->cur.loc, "expected asm output name"); 134 return 0; 135 } 136 { 137 KitSym inner_name = toy_tok_sym(p, p->cur); 138 if (operand->name && operand->name != inner_name) { 139 toy_error(p, p->cur.loc, "asm output name mismatch"); 140 return 0; 141 } 142 operand->name = inner_name; 143 } 144 toy_parser_advance(p); 145 if (!toy_parser_expect(p, TOK_COLON)) { 146 toy_error(p, p->cur.loc, "expected ':' in asm output"); 147 return 0; 148 } 149 operand->type = toy_parse_type(p); 150 if (operand->type == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_RPAREN)) { 151 return 0; 152 } 153 operand->dir = KIT_CG_ASM_OUT; 154 return 1; 155 } 156 157 static int toy_parse_asm_input_operand(ToyParser* p, KitCgAsmOperand* operand) { 158 KitSym mem_constraint; 159 memset(operand, 0, sizeof *operand); 160 if (p->cur.kind == TOK_IDENT && 161 !toy_sym_is(p, toy_tok_sym(p, p->cur), "in")) { 162 if (toy_lexer_peek(&p->lex).kind != TOK_EQ) { 163 toy_error(p, p->cur.loc, "expected asm input operand"); 164 return 0; 165 } 166 operand->name = toy_tok_sym(p, p->cur); 167 toy_parser_advance(p); 168 if (!toy_parser_expect(p, TOK_EQ)) { 169 toy_error(p, p->cur.loc, "expected '=' after asm input name"); 170 return 0; 171 } 172 } 173 if (!toy_expect_ident(p, "in") || !toy_parser_expect(p, TOK_LPAREN) || 174 !toy_parse_string_sym(p, &operand->constraint, NULL) || 175 !toy_expect_comma(p)) { 176 toy_error(p, p->cur.loc, "expected asm input operand"); 177 return 0; 178 } 179 mem_constraint = kit_sym_intern(p->c, KIT_SLICE_LIT("m")); 180 if (operand->constraint == mem_constraint && p->cur.kind == TOK_IDENT) { 181 KitSym name = toy_tok_sym(p, p->cur); 182 toy_parser_advance(p); 183 operand->type = toy_emit_var_lvalue(p, name); 184 if (operand->type == KIT_CG_TYPE_NONE) { 185 toy_error(p, p->cur.loc, "unknown asm memory operand"); 186 return 0; 187 } 188 /* The "m" operand wants a memory PLACE; toy_emit_var_lvalue leaves the 189 * variable's address as a pointer VALUE, so deref it back to a place. */ 190 kit_cg_deref(p->cg, 0); 191 } else { 192 operand->type = toy_parse_expr(p); 193 } 194 if (operand->type == KIT_CG_TYPE_NONE || !toy_parser_expect(p, TOK_RPAREN)) { 195 return 0; 196 } 197 operand->dir = KIT_CG_ASM_IN; 198 return 1; 199 } 200 201 static int toy_parse_asm_outputs(ToyParser* p, ToyAsmOperandList* outputs) { 202 if (!toy_expect_ident(p, "outputs") || !toy_parser_expect(p, TOK_LPAREN)) { 203 toy_error(p, p->cur.loc, "expected outputs(...)"); 204 return 0; 205 } 206 while (p->cur.kind != TOK_RPAREN && p->cur.kind != TOK_EOF) { 207 KitCgAsmOperand operand; 208 if (!toy_parse_asm_output_operand(p, &operand) || 209 !toy_validate_asm_output_constraint(p, &operand) || 210 !toy_asm_append_operand(p, outputs, &operand)) { 211 return 0; 212 } 213 if (!toy_parser_match(p, TOK_COMMA)) break; 214 } 215 if (!toy_parser_expect(p, TOK_RPAREN)) { 216 toy_error(p, p->cur.loc, "expected ')' after asm outputs"); 217 return 0; 218 } 219 return 1; 220 } 221 222 static int toy_parse_asm_inputs(ToyParser* p, ToyAsmOperandList* inputs) { 223 if (!toy_expect_ident(p, "inputs") || !toy_parser_expect(p, TOK_LPAREN)) { 224 toy_error(p, p->cur.loc, "expected inputs(...)"); 225 return 0; 226 } 227 while (p->cur.kind != TOK_RPAREN && p->cur.kind != TOK_EOF) { 228 KitCgAsmOperand operand; 229 if (!toy_parse_asm_input_operand(p, &operand) || 230 !toy_validate_asm_input_constraint(p, &operand) || 231 !toy_asm_append_operand(p, inputs, &operand)) { 232 return 0; 233 } 234 if (!toy_parser_match(p, TOK_COMMA)) break; 235 } 236 if (!toy_parser_expect(p, TOK_RPAREN)) { 237 toy_error(p, p->cur.loc, "expected ')' after asm inputs"); 238 return 0; 239 } 240 return 1; 241 } 242 243 static int toy_parse_asm_clobbers(ToyParser* p, ToyAsmClobberList* clobbers) { 244 if (!toy_expect_ident(p, "clobbers") || !toy_parser_expect(p, TOK_LPAREN)) { 245 toy_error(p, p->cur.loc, "expected clobbers(...)"); 246 return 0; 247 } 248 while (p->cur.kind != TOK_RPAREN && p->cur.kind != TOK_EOF) { 249 KitSym clobber; 250 size_t len; 251 if (!toy_parse_arch_string(p, &clobber, &len) || 252 !toy_asm_append_clobber(p, clobbers, clobber)) { 253 return 0; 254 } 255 (void)len; 256 if (!toy_parser_match(p, TOK_COMMA)) break; 257 } 258 if (!toy_parser_expect(p, TOK_RPAREN)) { 259 toy_error(p, p->cur.loc, "expected ')' after asm clobbers"); 260 return 0; 261 } 262 return 1; 263 } 264 265 static int toy_asm_record_field_by_name(ToyParser* p, KitCgTypeId record_ty, 266 KitSym name, uint32_t* index_out, 267 KitCgField* field_out) { 268 uint32_t i, nfields = kit_cg_type_record_nfields(p->c, record_ty); 269 for (i = 0; i < nfields; ++i) { 270 KitCgField field; 271 if (kit_cg_type_record_field(p->c, record_ty, i, &field, NULL) == 0 && 272 field.name == name) { 273 if (index_out) *index_out = i; 274 if (field_out) *field_out = field; 275 return 1; 276 } 277 } 278 return 0; 279 } 280 281 static int toy_parse_asm_flags(ToyParser* p, uint32_t* flags) { 282 static const ToyConstRow rows[] = { 283 {"volatile", KIT_CG_ASM_VOLATILE}, 284 {"pure", KIT_CG_ASM_PURE}, 285 {"nomem", KIT_CG_ASM_NOMEM}, 286 {"readonly", KIT_CG_ASM_READONLY}, 287 {"preserves_flags", KIT_CG_ASM_PRESERVES_FLAGS}, 288 {"nostack", KIT_CG_ASM_NOSTACK}, 289 {"noreturn", KIT_CG_ASM_NORETURN}, 290 }; 291 if (!toy_expect_ident(p, "flags") || !toy_parser_expect(p, TOK_LPAREN)) { 292 toy_error(p, p->cur.loc, "expected flags(...)"); 293 return 0; 294 } 295 if (!toy_parse_flag_set(p, rows, sizeof rows / sizeof rows[0], "asm flag", 0, 296 flags)) 297 return 0; 298 if (!toy_parser_expect(p, TOK_RPAREN)) { 299 toy_error(p, p->cur.loc, "expected ')' after asm flags"); 300 return 0; 301 } 302 return 1; 303 } 304 305 static int toy_parse_asm_clobber_abi(ToyParser* p, uint32_t* clobber_abi_sets) { 306 static const ToyConstRow rows[] = { 307 {"caller_saved", KIT_CG_ASM_CLOBBER_ABI_CALLER_SAVED}, 308 {"callee_saved", KIT_CG_ASM_CLOBBER_ABI_CALLEE_SAVED}, 309 }; 310 if (!toy_expect_ident(p, "clobber_abi") || 311 !toy_parser_expect(p, TOK_LPAREN)) { 312 toy_error(p, p->cur.loc, "expected clobber_abi(...)"); 313 return 0; 314 } 315 if (!toy_parse_flag_set(p, rows, sizeof rows / sizeof rows[0], 316 "asm clobber ABI", 0, clobber_abi_sets)) 317 return 0; 318 if (!toy_parser_expect(p, TOK_RPAREN)) { 319 toy_error(p, p->cur.loc, "expected ')' after asm clobber ABI"); 320 return 0; 321 } 322 return 1; 323 } 324 325 int toy_parse_typed_asm_tail(ToyParser* p, KitCgTypeId result_ty, KitSym tmpl, 326 size_t tmpl_len) { 327 ToyAsmOperandList outputs = {0}; 328 ToyAsmOperandList inputs = {0}; 329 ToyAsmClobberList clobbers = {0}; 330 uint32_t* record_field_indexes = NULL; 331 uint32_t record_field_count = 0; 332 uint32_t flags = 0; 333 uint32_t clobber_abi_sets = 0; 334 int have_outputs = 0; 335 int have_inputs = 0; 336 int have_clobbers = 0; 337 int have_flags = 0; 338 int have_clobber_abi = 0; 339 int ok = 0; 340 341 while (toy_parser_match(p, TOK_COMMA)) { 342 if (p->cur.kind != TOK_IDENT) { 343 toy_error(p, p->cur.loc, "expected asm operand group"); 344 goto done; 345 } 346 if (toy_sym_is(p, toy_tok_sym(p, p->cur), "outputs")) { 347 if (have_outputs) { 348 toy_error(p, p->cur.loc, "duplicate asm outputs group"); 349 goto done; 350 } 351 if (!toy_parse_asm_outputs(p, &outputs)) goto done; 352 have_outputs = 1; 353 } else if (toy_sym_is(p, toy_tok_sym(p, p->cur), "inputs")) { 354 if (have_inputs) { 355 toy_error(p, p->cur.loc, "duplicate asm inputs group"); 356 goto done; 357 } 358 if (!toy_parse_asm_inputs(p, &inputs)) goto done; 359 have_inputs = 1; 360 } else if (toy_sym_is(p, toy_tok_sym(p, p->cur), "clobbers")) { 361 if (have_clobbers) { 362 toy_error(p, p->cur.loc, "duplicate asm clobbers group"); 363 goto done; 364 } 365 if (!toy_parse_asm_clobbers(p, &clobbers)) goto done; 366 have_clobbers = 1; 367 } else if (toy_sym_is(p, toy_tok_sym(p, p->cur), "flags")) { 368 if (have_flags) { 369 toy_error(p, p->cur.loc, "duplicate asm flags group"); 370 goto done; 371 } 372 if (!toy_parse_asm_flags(p, &flags)) goto done; 373 have_flags = 1; 374 } else if (toy_sym_is(p, toy_tok_sym(p, p->cur), "clobber_abi")) { 375 if (have_clobber_abi) { 376 toy_error(p, p->cur.loc, "duplicate asm clobber_abi group"); 377 goto done; 378 } 379 if (!toy_parse_asm_clobber_abi(p, &clobber_abi_sets)) goto done; 380 have_clobber_abi = 1; 381 } else { 382 toy_error(p, p->cur.loc, "unknown asm operand group"); 383 goto done; 384 } 385 } 386 if (!toy_parser_expect(p, TOK_RPAREN)) { 387 toy_error(p, p->cur.loc, "expected ')' after asm"); 388 goto done; 389 } 390 if (!have_outputs) { 391 toy_error(p, p->cur.loc, "asm outputs group is required"); 392 goto done; 393 } 394 if (result_ty == toy_builtin_type(p, KIT_CG_BUILTIN_VOID)) { 395 if (outputs.count != 0) { 396 toy_error(p, p->cur.loc, "void asm cannot have outputs"); 397 goto done; 398 } 399 } else if (kit_cg_type_kind(p->c, result_ty) == KIT_CG_TYPE_RECORD) { 400 uint32_t i, nfields = kit_cg_type_record_nfields(p->c, result_ty); 401 uint8_t* seen; 402 record_field_count = nfields; 403 if (outputs.count != nfields) { 404 toy_error(p, p->cur.loc, "asm record result output count mismatch"); 405 goto done; 406 } 407 record_field_indexes = (uint32_t*)toy_parser_zalloc( 408 p, nfields, sizeof *record_field_indexes, "asm record outputs"); 409 seen = (uint8_t*)toy_parser_zalloc(p, nfields, sizeof *seen, 410 "asm record outputs"); 411 if (!record_field_indexes || !seen) { 412 toy_parser_free_mem(p, seen, nfields * sizeof *seen); 413 toy_error(p, p->cur.loc, "out of memory growing asm record outputs"); 414 goto done; 415 } 416 for (i = 0; i < nfields; ++i) { 417 KitCgField field; 418 uint32_t field_index = i; 419 if (outputs.items[i].name) { 420 if (!toy_asm_record_field_by_name(p, result_ty, outputs.items[i].name, 421 &field_index, &field)) { 422 toy_parser_free_mem(p, seen, nfields * sizeof *seen); 423 toy_error(p, p->cur.loc, "asm record result output mismatch"); 424 goto done; 425 } 426 } else if (kit_cg_type_record_field(p->c, result_ty, i, &field, NULL) != 427 0) { 428 toy_parser_free_mem(p, seen, nfields * sizeof *seen); 429 goto done; 430 } 431 if (outputs.items[i].type != field.type || seen[field_index]) { 432 toy_parser_free_mem(p, seen, nfields * sizeof *seen); 433 toy_error(p, p->cur.loc, "asm record result output mismatch"); 434 goto done; 435 } 436 seen[field_index] = 1u; 437 record_field_indexes[i] = field_index; 438 } 439 toy_parser_free_mem(p, seen, nfields * sizeof *seen); 440 } else { 441 if (outputs.count != 1 || outputs.items[0].type != result_ty) { 442 toy_error(p, p->cur.loc, "asm result type must match single output"); 443 goto done; 444 } 445 } 446 if (tmpl_len || outputs.count || inputs.count || clobbers.count || 447 clobber_abi_sets) { 448 toy_inline_asm(p, tmpl, outputs.items, outputs.count, inputs.items, 449 inputs.count, clobbers.items, clobbers.count, flags, 450 clobber_abi_sets); 451 } 452 if (kit_cg_type_kind(p->c, result_ty) == KIT_CG_TYPE_RECORD && 453 outputs.count != 0) { 454 KitCgLocal rec_slot = kit_cg_local(p->cg, result_ty, toy_slot_attrs(0)); 455 uint32_t i = outputs.count; 456 while (i > 0) { 457 KitCgField field; 458 uint32_t field_index; 459 uint64_t foff = 0; 460 --i; 461 field_index = record_field_indexes ? record_field_indexes[i] : i; 462 if (kit_cg_type_record_field(p->c, result_ty, field_index, &field, 463 &foff) != 0) 464 goto done; 465 kit_cg_push_local(p->cg, rec_slot); 466 kit_cg_addr(p->cg); 467 kit_cg_deref(p->cg, (int64_t)foff); 468 kit_cg_swap(p->cg); 469 kit_cg_store(p->cg, toy_mem_access(p, field.type)); 470 } 471 /* Record result lives as a pointer VALUE (its address) on the stack. */ 472 kit_cg_push_local_addr(p->cg, rec_slot); 473 } 474 ok = 1; 475 476 done: 477 toy_parser_free_mem(p, record_field_indexes, 478 record_field_count * sizeof *record_field_indexes); 479 toy_parser_free_mem(p, outputs.items, outputs.cap * sizeof *outputs.items); 480 toy_parser_free_mem(p, inputs.items, inputs.cap * sizeof *inputs.items); 481 toy_parser_free_mem(p, clobbers.items, clobbers.cap * sizeof *clobbers.items); 482 return ok; 483 }