kit

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

arm32_inline_test.c (12807B)


      1 /* Public-API unit test for the ARM32 Thumb-2 inline-asm backend. */
      2 
      3 #include <stdint.h>
      4 #include <string.h>
      5 
      6 #include <kit/disasm.h>
      7 
      8 #include "inline_public_test.h"
      9 
     10 static void arm32_body(KitCompiler* c, KitCg* cg, KitCgTypeId i64_ty) {
     11   (void)i64_ty;
     12 
     13   it_inline_asm(c, cg, "nop ; nop", NULL, 0, NULL, 0, NULL, 0);
     14   it_inline_asm(c, cg, "add.w r0, r0, #7", NULL, 0, NULL, 0, NULL, 0);
     15   it_inline_asm(c, cg, "movw r1, #0x1234", NULL, 0, NULL, 0, NULL, 0);
     16   it_inline_asm(c, cg, "bkpt #0xab", NULL, 0, NULL, 0, NULL, 0);
     17   /* ARMv7E-M DSP saturating (no dsp-feature gate; assembler is permissive). */
     18   it_inline_asm(c, cg, "ssat r0, #16, r1", NULL, 0, NULL, 0, NULL, 0);
     19   it_inline_asm(c, cg, "qadd r3, r1, r2", NULL, 0, NULL, 0, NULL, 0);
     20 }
     21 
     22 static void arm32_bad_operand(KitCompiler* c, KitCg* cg, KitCgTypeId i64_ty) {
     23   (void)i64_ty;
     24   it_inline_asm(c, cg, "mov %9, r0", NULL, 0, NULL, 0, NULL, 0);
     25 }
     26 
     27 static void arm32_named_r4_clobber(KitCompiler* c, KitCg* cg,
     28                                   KitCgTypeId i64_ty) {
     29   static const char* const clobbers[] = {"r4"};
     30   (void)i64_ty;
     31   it_inline_asm(c, cg, "movs r4, #99", NULL, 0, NULL, 0, clobbers, 1);
     32 }
     33 
     34 static void arm32_bad_frame_clobber(KitCompiler* c, KitCg* cg,
     35                                     KitCgTypeId i64_ty) {
     36   static const char* const clobbers[] = {"r7"};
     37   (void)i64_ty;
     38   it_inline_asm(c, cg, "", NULL, 0, NULL, 0, clobbers, 1);
     39 }
     40 
     41 /* r0-r3 are the direct binder's caller-saved operand pool. Five simultaneous
     42  * generic inputs therefore put the fifth value in callee-saved r4. */
     43 static void arm32_generic_callee_saved_operand(KitCompiler* c, KitCg* cg,
     44                                                KitCgTypeId i64_ty) {
     45   KitCgTypeId i32_ty = kit_cg_type_builtin(c, KIT_CG_BUILTIN_I32);
     46   KitCgAsmOperand ins[5];
     47   KitCgLocal locals[5];
     48   KitCgLocalAttrs attrs;
     49   KitCgMemAccess mem;
     50   uint32_t i;
     51   (void)i64_ty;
     52 
     53   memset(&attrs, 0, sizeof attrs);
     54   memset(&mem, 0, sizeof mem);
     55   mem.type = i32_ty;
     56   mem.align = kit_cg_type_align(c, i32_ty);
     57   for (i = 0; i < 5u; ++i) {
     58     locals[i] = kit_cg_local(cg, i32_ty, attrs);
     59     kit_cg_push_int(cg, i + 1u, i32_ty);
     60     kit_cg_local_write(cg, locals[i], mem);
     61   }
     62   for (i = 0; i < 5u; ++i) {
     63     kit_cg_local_read(cg, locals[i], mem);
     64     ins[i] = it_asm_op(c, "r", NULL, i32_ty, KIT_CG_ASM_IN);
     65   }
     66   it_inline_asm(c, cg, "", NULL, 0, ins, 5, NULL, 0);
     67 }
     68 
     69 static int arm32_is_r4_mem(const KitInsn* insn, const char* mnemonic) {
     70   size_t n = strlen(mnemonic);
     71   return insn->mnemonic.s && insn->mnemonic.len >= n &&
     72          memcmp(insn->mnemonic.s, mnemonic, n) == 0 && insn->operands.s &&
     73          insn->operands.len >= 2u && insn->operands.s[0] == 'r' &&
     74          insn->operands.s[1] == '4';
     75 }
     76 
     77 static int arm32_is_r4_reglist(const KitInsn* insn, const char* mnemonic) {
     78   size_t n = strlen(mnemonic);
     79   if (!insn->mnemonic.s || insn->mnemonic.len < n ||
     80       memcmp(insn->mnemonic.s, mnemonic, n) != 0 || !insn->operands.s)
     81     return 0;
     82   for (size_t i = 0; i + 1u < insn->operands.len; ++i)
     83     if (insn->operands.s[i] == 'r' && insn->operands.s[i + 1u] == '4')
     84       return 1;
     85   return 0;
     86 }
     87 
     88 static int arm32_has_known_frame_r4_preservation(InlineTestEnv* env,
     89                                                   const InlineText* text) {
     90   KitTargetOptions opts;
     91   KitTarget* target = NULL;
     92   KitDisasmContext dc;
     93   KitDisasmIter* it = NULL;
     94   KitInsn insn;
     95   int saw_push = 0;
     96   int saw_pop = 0;
     97 
     98   memset(&opts, 0, sizeof opts);
     99   opts.spec = it_target(KIT_ARCH_ARM_32);
    100   if (kit_target_new(&env->ctx, &opts, &target) != KIT_OK || !target)
    101     goto done;
    102   memset(&dc, 0, sizeof dc);
    103   dc.target = target;
    104   dc.context = env->ctx;
    105   if (kit_disasm_iter_new(&dc, text->data, text->len, 0, text->file, &it) !=
    106           KIT_OK ||
    107       !it)
    108     goto done;
    109   while (kit_disasm_iter_next(it, &insn) == KIT_ITER_ITEM) {
    110     if (arm32_is_r4_reglist(&insn, "push")) saw_push = 1;
    111     if (arm32_is_r4_reglist(&insn, "pop")) saw_pop = 1;
    112   }
    113 
    114 done:
    115   if (it) kit_disasm_iter_free(it);
    116   kit_target_free(target);
    117   return saw_push && saw_pop;
    118 }
    119 
    120 static int arm32_has_generic_r4_preservation(InlineTestEnv* env,
    121                                              const InlineText* text) {
    122   KitTargetOptions opts;
    123   KitTarget* target = NULL;
    124   KitDisasmContext dc;
    125   KitDisasmIter* it = NULL;
    126   KitInsn insn;
    127   uint32_t loads_after_save = 0;
    128   int saw_save = 0;
    129   int ok = 0;
    130 
    131   memset(&opts, 0, sizeof opts);
    132   opts.spec = it_target(KIT_ARCH_ARM_32);
    133   if (kit_target_new(&env->ctx, &opts, &target) != KIT_OK || !target)
    134     goto done;
    135   memset(&dc, 0, sizeof dc);
    136   dc.target = target;
    137   dc.context = env->ctx;
    138   if (kit_disasm_iter_new(&dc, text->data, text->len, 0, text->file, &it) !=
    139           KIT_OK ||
    140       !it)
    141     goto done;
    142   while (kit_disasm_iter_next(it, &insn) == KIT_ITER_ITEM) {
    143     if (!saw_save && arm32_is_r4_mem(&insn, "str")) {
    144       saw_save = 1;
    145     } else if (saw_save && arm32_is_r4_mem(&insn, "ldr")) {
    146       loads_after_save++;
    147     }
    148   }
    149   /* The first load stages operand five; the second restores caller r4. */
    150   ok = saw_save && loads_after_save >= 2u;
    151 
    152 done:
    153   if (it) kit_disasm_iter_free(it);
    154   kit_target_free(target);
    155   return ok;
    156 }
    157 
    158 static int arm32_emit_file_asm(InlineTestEnv* env, const char* src,
    159                                InlineText* text) {
    160   KitCompiler* c = NULL;
    161   KitTarget* target = NULL;
    162   KitTargetOptions target_opts;
    163   KitObjBuilder* ob = NULL;
    164   KitCg* cg = NULL;
    165   KitCodeOptions opts;
    166   KitSlice bytes;
    167   size_t len = 0;
    168   int ok = 0;
    169 
    170   memset(text, 0, sizeof *text);
    171   memset(&target_opts, 0, sizeof target_opts);
    172   target_opts.spec = it_target(KIT_ARCH_ARM_32);
    173   if (kit_target_new(&env->ctx, &target_opts, &target) != KIT_OK || !target) {
    174     snprintf(env->last_diag, sizeof env->last_diag, "kit_target_new failed");
    175     return 0;
    176   }
    177   if (kit_compiler_new(target, &env->ctx, &c) != KIT_OK || !c) {
    178     snprintf(env->last_diag, sizeof env->last_diag, "kit_compiler_new failed");
    179     goto done;
    180   }
    181   if (kit_obj_builder_new(c, &ob) != KIT_OK || !ob) {
    182     snprintf(env->last_diag, sizeof env->last_diag,
    183              "kit_obj_builder_new failed");
    184     goto done;
    185   }
    186   if (kit_cg_new(c, &cg) != KIT_OK || !cg) {
    187     snprintf(env->last_diag, sizeof env->last_diag, "kit_cg_new failed");
    188     goto done;
    189   }
    190   memset(&opts, 0, sizeof opts);
    191   if (kit_cg_begin(cg, ob, &opts) != KIT_OK) {
    192     snprintf(env->last_diag, sizeof env->last_diag, "kit_cg_begin failed");
    193     goto done;
    194   }
    195   kit_cg_file_scope_asm(cg, kit_slice_cstr(src));
    196   if (kit_cg_finish(cg, NULL) != KIT_OK) {
    197     if (!env->last_diag[0])
    198       snprintf(env->last_diag, sizeof env->last_diag, "kit_cg_finish failed");
    199     goto done;
    200   }
    201   if (kit_cg_detach(cg) != KIT_OK) {
    202     snprintf(env->last_diag, sizeof env->last_diag, "kit_cg_detach failed");
    203     goto done;
    204   }
    205   kit_cg_free(cg);
    206   cg = NULL;
    207 
    208   if (kit_writer_mem(&env->heap, &text->writer) != KIT_OK || !text->writer) {
    209     snprintf(env->last_diag, sizeof env->last_diag, "kit_writer_mem failed");
    210     goto done;
    211   }
    212   if (kit_obj_builder_emit(ob, text->writer) != KIT_OK) {
    213     if (!env->last_diag[0])
    214       snprintf(env->last_diag, sizeof env->last_diag,
    215                "kit_obj_builder_emit failed");
    216     goto done;
    217   }
    218   bytes.data = kit_writer_mem_bytes(text->writer, &len);
    219   bytes.len = len;
    220   if (kit_obj_open(&env->ctx, KIT_SLICE_LIT("<arm32-file-asm>"), &bytes,
    221                    &text->file) != KIT_OK) {
    222     if (!env->last_diag[0])
    223       snprintf(env->last_diag, sizeof env->last_diag, "kit_obj_open failed");
    224     goto done;
    225   }
    226   ok = 1;
    227 
    228 done:
    229   if (cg) kit_cg_free(cg);
    230   if (ob) kit_obj_builder_free(ob);
    231   kit_compiler_free(c);
    232   kit_target_free(target);
    233   if (!ok) {
    234     if (text->file) kit_obj_free(text->file);
    235     if (text->writer) kit_writer_close(text->writer);
    236     memset(text, 0, sizeof *text);
    237   }
    238   return ok;
    239 }
    240 
    241 static int arm32_has_abs32_to(KitObjFile* file, KitSlice name) {
    242   KitObjRelocIter* it = NULL;
    243   KitObjReloc r;
    244   int found = 0;
    245   if (kit_obj_reliter_new(file, &it) != KIT_OK || !it) return 0;
    246   while (kit_obj_reliter_next(it, &r) == KIT_ITER_ITEM) {
    247     if (r.kind.arch == KIT_ARCH_ARM_32 && r.kind.code == KIT_RELOC_ABS32 &&
    248         r.sym_name.len == name.len &&
    249         memcmp(r.sym_name.s, name.s, name.len) == 0) {
    250       found = 1;
    251       break;
    252     }
    253   }
    254   kit_obj_reliter_free(it);
    255   return found;
    256 }
    257 
    258 static int arm32_check_thumb_func(InlineTestEnv* env) {
    259   static const char src[] =
    260       ".syntax unified\n"
    261       ".thumb\n"
    262       ".section .text.thumb_fn, \"ax\"\n"
    263       ".globl thumb_target\n"
    264       ".thumb_func\n"
    265       ".type thumb_target, %function\n"
    266       "thumb_target:\n"
    267       "  bx lr\n"
    268       ".size thumb_target, .-thumb_target\n"
    269       ".section .rodata.thumb_fn, \"a\"\n"
    270       ".globl thumb_target_ptr\n"
    271       "thumb_target_ptr:\n"
    272       "  .word thumb_target\n";
    273   InlineText obj;
    274   KitObjSymInfo sym;
    275   KitSlice target_name = KIT_SLICE_LIT("thumb_target");
    276   int ok;
    277 
    278   ok = arm32_emit_file_asm(env, src, &obj);
    279   if (!ok) return 0;
    280   ok = kit_obj_symbol_by_name(obj.file, target_name, &sym) == KIT_OK &&
    281        sym.kind == KIT_SK_FUNC && (sym.value & 1u) != 0 && sym.size == 2u &&
    282        arm32_has_abs32_to(obj.file, target_name);
    283   if (!ok && !env->last_diag[0])
    284     snprintf(env->last_diag, sizeof env->last_diag,
    285              ".thumb_func did not produce odd FUNC symbol plus ABS32 reloc");
    286   it_text_close(&obj);
    287   return ok;
    288 }
    289 
    290 int main(void) {
    291   static const uint8_t nops[] = {0x00u, 0xbfu, 0x00u, 0xbfu};
    292   static const uint8_t add_w_r0_7[] = {0x00u, 0xf1u, 0x07u, 0x00u};
    293   static const uint8_t movw_r1_1234[] = {0x41u, 0xf2u, 0x34u, 0x21u};
    294   static const uint8_t bkpt_ab[] = {0xabu, 0xbeu};
    295   static const uint8_t ssat_r0_16_r1[] = {0x01u, 0xf3u, 0x0fu, 0x00u};
    296   static const uint8_t qadd_r3_r1_r2[] = {0x82u, 0xfau, 0x81u, 0xf3u};
    297   InlineTestEnv env;
    298   InlineText text;
    299 
    300   it_env_init(&env);
    301   IT_EXPECT(&env,
    302             it_emit_text(&env, KIT_ARCH_ARM_32, "arm32_inline_public",
    303                          arm32_body, &text),
    304             "failed to emit arm32 inline-asm object: %s", env.last_diag);
    305   if (text.data) {
    306     IT_EXPECT(&env, it_contains(text.data, text.len, nops, sizeof nops),
    307               "missing two-nop inline asm encoding");
    308     IT_EXPECT(&env,
    309               it_contains(text.data, text.len, add_w_r0_7, sizeof add_w_r0_7),
    310               "missing add.w r0, r0, #7 encoding");
    311     IT_EXPECT(&env,
    312               it_contains(text.data, text.len, movw_r1_1234,
    313                           sizeof movw_r1_1234),
    314               "missing movw r1, #0x1234 encoding");
    315     IT_EXPECT(&env, it_contains(text.data, text.len, bkpt_ab, sizeof bkpt_ab),
    316               "missing bkpt #0xab encoding");
    317     IT_EXPECT(&env,
    318               it_contains(text.data, text.len, ssat_r0_16_r1,
    319                           sizeof ssat_r0_16_r1),
    320               "missing ssat r0, #16, r1 encoding");
    321     IT_EXPECT(&env,
    322               it_contains(text.data, text.len, qadd_r3_r1_r2,
    323                           sizeof qadd_r3_r1_r2),
    324               "missing qadd r3, r1, r2 encoding");
    325   }
    326   it_text_close(&text);
    327 
    328   IT_EXPECT(&env,
    329             it_expect_panic(&env, KIT_ARCH_ARM_32, "arm32_bad_operand",
    330                             arm32_bad_operand, "operand index"),
    331             "expected out-of-range arm32 asm operand to panic");
    332   IT_EXPECT(&env,
    333             it_expect_panic(&env, KIT_ARCH_ARM_32,
    334                             "arm32_bad_frame_clobber",
    335                             arm32_bad_frame_clobber,
    336                             "structural register cannot be clobbered"),
    337             "expected arm32 r7 clobber to panic");
    338   IT_EXPECT(&env, arm32_check_thumb_func(&env),
    339             "arm32 .thumb_func symbol regression failed: %s", env.last_diag);
    340 
    341   IT_EXPECT(&env,
    342             it_emit_text(&env, KIT_ARCH_ARM_32,
    343                          "arm32_generic_callee_saved_operand",
    344                          arm32_generic_callee_saved_operand, &text),
    345             "failed to emit generic callee-saved asm operand: %s",
    346             env.last_diag);
    347   if (text.data)
    348     IT_EXPECT(&env, arm32_has_generic_r4_preservation(&env, &text),
    349               "generic r4 asm operand was not saved and restored");
    350   it_text_close(&text);
    351 
    352   IT_EXPECT(&env,
    353             it_emit_text_opt(&env, KIT_ARCH_ARM_32,
    354                              "arm32_named_r4_clobber_o1",
    355                              arm32_named_r4_clobber, 1, &text),
    356             "failed to emit O1 named r4 clobber: %s", env.last_diag);
    357   if (text.data)
    358     IT_EXPECT(&env, arm32_has_known_frame_r4_preservation(&env, &text),
    359               "O1 known frame did not preserve named r4 clobber");
    360   it_text_close(&text);
    361 
    362   if (env.fails) {
    363     fprintf(stderr, "%d failure(s)\n", env.fails);
    364     return 1;
    365   }
    366   printf("arm32_inline_test: ok\n");
    367   return 0;
    368 }