kit

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

x64_inline_test.c (3129B)


      1 /* Public-API unit test for the x86_64 inline-asm backend. */
      2 
      3 #include <stdint.h>
      4 
      5 #include "inline_public_test.h"
      6 
      7 static void x64_body(KitCompiler* c, KitCg* cg, KitCgTypeId i64_ty) {
      8   KitCgAsmOperand imm;
      9   (void)i64_ty;
     10 
     11   it_inline_asm(c, cg, "nop ; nop", NULL, 0, NULL, 0, NULL, 0);
     12   it_inline_asm(c, cg, "movq %%rcx, %%rax", NULL, 0, NULL, 0, NULL, 0);
     13 
     14   kit_cg_push_int(cg, 7, i64_ty);
     15   imm = it_asm_op(c, "i", "imm", i64_ty, KIT_CG_ASM_IN);
     16   it_inline_asm(c, cg, "addq %[imm], %%rax", NULL, 0, &imm, 1, NULL, 0);
     17 }
     18 
     19 static void x64_bad_operand(KitCompiler* c, KitCg* cg, KitCgTypeId i64_ty) {
     20   (void)i64_ty;
     21   it_inline_asm(c, cg, "movq %9, %%rax", NULL, 0, NULL, 0, NULL, 0);
     22 }
     23 
     24 /* A GNU local register variable pinned to %rax — the Linux syscall idiom
     25  * (syscall number in rax). rax is reserved (return / div-mul), but no longer
     26  * an emit-internal scratch register, so an asm operand may pin it while the
     27  * allocator still leaves it alone. Clobbers rcx/r11 as `syscall` does. */
     28 static void x64_rax_pin(KitCompiler* c, KitCg* cg, KitCgTypeId i64_ty) {
     29   KitCgAsmOperand in;
     30   const char* clob[] = {"rcx", "r11", "memory"};
     31   in = it_asm_op(c, "r", "n", i64_ty, KIT_CG_ASM_IN);
     32   in.reg = kit_sym_intern(c, kit_slice_cstr("rax"));
     33   kit_cg_push_int(cg, 60, i64_ty); /* SYS_exit */
     34   it_inline_asm(c, cg, "syscall", NULL, 0, &in, 1, clob, 3);
     35 }
     36 
     37 int main(void) {
     38   static const uint8_t nops[] = {0x90u, 0x90u};
     39   static const uint8_t movq_rcx_rax[] = {0x48u, 0x89u, 0xc8u};
     40   static const uint8_t addq_7_rax[] = {0x48u, 0x83u, 0xc0u, 0x07u};
     41   InlineTestEnv env;
     42   InlineText text;
     43 
     44   it_env_init(&env);
     45   IT_EXPECT(
     46       &env,
     47       it_emit_text(&env, KIT_ARCH_X86_64, "x64_inline_public", x64_body, &text),
     48       "failed to emit x64 inline-asm object");
     49   if (text.data) {
     50     IT_EXPECT(&env, it_contains(text.data, text.len, nops, sizeof nops),
     51               "missing two-nop inline asm encoding");
     52     IT_EXPECT(
     53         &env,
     54         it_contains(text.data, text.len, movq_rcx_rax, sizeof movq_rcx_rax),
     55         "missing movq %%rcx, %%rax literal-escape encoding");
     56     IT_EXPECT(&env,
     57               it_contains(text.data, text.len, addq_7_rax, sizeof addq_7_rax),
     58               "missing addq $7, %%rax immediate-substitution encoding");
     59   }
     60   it_text_close(&text);
     61 
     62   IT_EXPECT(&env,
     63             it_expect_panic(&env, KIT_ARCH_X86_64, "x64_bad_operand",
     64                             x64_bad_operand, "operand index"),
     65             "expected out-of-range x64 asm operand to panic");
     66 
     67   {
     68     static const uint8_t syscall_bytes[] = {0x0fu, 0x05u};
     69     InlineText sc;
     70     IT_EXPECT(&env,
     71               it_emit_text(&env, KIT_ARCH_X86_64, "x64_rax_pin", x64_rax_pin,
     72                            &sc),
     73               "failed to emit rax-pinned syscall inline asm");
     74     if (sc.data)
     75       IT_EXPECT(
     76           &env, it_contains(sc.data, sc.len, syscall_bytes, sizeof syscall_bytes),
     77           "missing syscall encoding for rax-pinned operand");
     78     it_text_close(&sc);
     79   }
     80 
     81   if (env.fails) {
     82     fprintf(stderr, "%d failure(s)\n", env.fails);
     83     return 1;
     84   }
     85   printf("x64_inline_test: ok\n");
     86   return 0;
     87 }