kit

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

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 }