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 }