kit

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

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 }