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