/* { dg-do compile { target x86_64-*-* } } */ #include #include "libgccjit++.h" #include "harness.h" /********************************************************************** Support fns for creating code. **********************************************************************/ /* Make a "void FUNC_NAME (void)" function with a single block, returning that block. */ static gccjit::block make_single_block_func (gccjit::context ctxt, const char *func_name) { gccjit::type void_type = ctxt.get_type (GCC_JIT_TYPE_VOID); std::vector params; gccjit::function func = ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED, void_type, func_name, params, 0); return func.new_block ("initial"); } /********************************************************************** Support fns for verifying code. **********************************************************************/ typedef void (*void_void_fn) (void); static void_void_fn get_test_fn (gcc_jit_result *result, const char *func_name) { return (void_void_fn)gcc_jit_result_get_code (result, func_name); } /********************************************************************** test_i386_basic_asm_1: simple example of asm **********************************************************************/ /* Create the equivalent of: int src; int dst; void test_i386_basic_asm_1 (void) { // Quote from here in docs/cp/topics/asm.rst: example 1: C asm ("mov %1, %0\n\t" "add $1, %0" : "=r" (dst) : "r" (src)); // Quote up to here in docs/cp/topics/asm.rst: example 1: C } i.e. copy src to dst and add 1 to dst. */ static void create_test_i386_basic_asm_1 (gcc_jit_context *c_ctxt) { gccjit::context ctxt (c_ctxt); gccjit::type int_type = ctxt.get_type (GCC_JIT_TYPE_INT); gccjit::lvalue dst = ctxt.new_global (GCC_JIT_GLOBAL_EXPORTED, int_type, "dst"); gccjit::lvalue src = ctxt.new_global (GCC_JIT_GLOBAL_EXPORTED, int_type, "src"); gccjit::block block = make_single_block_func (ctxt, "test_i386_basic_asm_1"); gccjit::extended_asm ext_asm = /* Quote from here in docs/cp/topics/asm.rst: example 1: jit. */ block.add_extended_asm ("mov %1, %0\n\t" "add $1, %0") .add_output_operand ("=r", dst) .add_input_operand ("r", src); /* Quote up to here in docs/cp/topics/asm.rst: example 1: jit. */ std::string desc = ext_asm.get_debug_string (); CHECK_STRING_VALUE (desc.c_str (), "asm (\"mov %1, %0\\n\\tadd $1, %0\" : \"=r\" (dst) : \"r\" (src) : )"); block.end_with_return (); } static void verify_code_1 (gcc_jit_context *ctxt, gcc_jit_result *result) { void_void_fn test_i386_basic_asm_1 = get_test_fn (result, "test_i386_basic_asm_1"); CHECK_NON_NULL (test_i386_basic_asm_1); int *dst_ptr = (int *)gcc_jit_result_get_global (result, "dst"); CHECK_NON_NULL (dst_ptr); int *src_ptr = (int *)gcc_jit_result_get_global (result, "src"); CHECK_NON_NULL (src_ptr); *src_ptr = 42; *dst_ptr = 0; test_i386_basic_asm_1 (); CHECK_VALUE (*src_ptr, 42); CHECK_VALUE (*dst_ptr, 43); } /********************************************************************** test_i386_basic_asm_2: test of symbolic names and clobbers **********************************************************************/ /* Create the equivalent of: uint32_t test_i386_basic_asm_2 (uint32_t Mask) { uint32_t Index; // Quote from here in docs/cp/topics/asm.rst: example 2: C asm ("bsfl %[aMask], %[aIndex]" : [aIndex] "=r" (Index) : [aMask] "r" (Mask) : "cc"); // Quote up to here in docs/cp/topics/asm.rst: example 2: C return Index; } i.e. return the first bit set in "Mask" This exercises symbolic names and clobbers. */ static void create_test_i386_basic_asm_2 (gcc_jit_context *c_ctxt) { gccjit::context ctxt (c_ctxt); gccjit::type uint32_type = ctxt.get_int_type (4, 0); gccjit::param mask = ctxt.new_param (uint32_type, "Mask"); std::vector params {mask}; gccjit::function func = ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED, uint32_type, "test_i386_basic_asm_2", params, 0); gccjit::lvalue index = func.new_local (uint32_type, "Index"); gccjit::block block = func.new_block ("initial"); gccjit::extended_asm ext_asm = /* Quote from here in docs/cp/topics/asm.rst: example 2: jit. */ block.add_extended_asm ("bsfl %[aMask], %[aIndex]") .add_output_operand ("aIndex", "=r", index) .add_input_operand ("aMask", "r", mask) .add_clobber ("cc"); /* Quote up to here in docs/cp/topics/asm.rst: example 2: jit. */ std::string desc = ext_asm.get_debug_string (); CHECK_STRING_VALUE (desc.c_str (), "asm (\"bsfl %[aMask], %[aIndex]\"" " : [aIndex] \"=r\" (Index) : [aMask] \"r\" (Mask) : \"cc\")"); block.end_with_return (index); } static void verify_code_2 (gcc_jit_context *ctxt, gcc_jit_result *result) { typedef uint32_t (*fntype) (uint32_t); fntype test_i386_basic_asm_2 = (fntype)gcc_jit_result_get_code (result, "test_i386_basic_asm_2"); CHECK_NON_NULL (test_i386_basic_asm_2); CHECK_VALUE (test_i386_basic_asm_2 (1), 0); CHECK_VALUE (test_i386_basic_asm_2 (2), 1); CHECK_VALUE (test_i386_basic_asm_2 (4), 2); CHECK_VALUE (test_i386_basic_asm_2 (8), 3); } /********************************************************************** test_i386_basic_asm_3a/b: test of control flow: "asm goto" **********************************************************************/ /* Create the equivalent of: int test_i386_basic_asm_3a (int p1, int p2) { asm goto ("btl %1, %0\n\t" "jc %l2" : // No outputs : "r" (p1), "r" (p2) : "cc" : carry); return 0; carry: return 1; } or (the "_3b" variant) using a name rather than a number for the goto label: // Quote from here in docs/cp/topics/asm.rst: example 3b: C asm goto ("btl %1, %0\n\t" "jc %l[carry]" : // No outputs : "r" (p1), "r" (p2) : "cc" : carry); // Quote up to here in docs/cp/topics/asm.rst: example 3b: C This exercises control flow with an asm. */ static void create_test_i386_basic_asm_3 (gcc_jit_context *c_ctxt, const char *funcname, int use_name) { gccjit::context ctxt (c_ctxt); gccjit::type int_type = ctxt.get_type (GCC_JIT_TYPE_INT); gccjit::param p1 = ctxt.new_param (int_type, "p1"); gccjit::param p2 = ctxt.new_param (int_type, "p2"); std::vector params ({p1, p2}); gccjit::function func = ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED, int_type, funcname, params, 0); gccjit::block b_start = func.new_block ("start"); gccjit::block b_fallthru = func.new_block ("fallthru"); gccjit::block b_carry = func.new_block ("carry"); gccjit::rvalue zero = ctxt.new_rvalue (int_type, 0); gccjit::rvalue one = ctxt.new_rvalue (int_type, 1); /* Quote from here in docs/cp/topics/asm.rst: example 3: jit. */ const char *asm_template = (use_name ? /* Label referred to by name: "%l[carry]". */ ("btl %1, %0\n\t" "jc %l[carry]") : /* Label referred to numerically: "%l2". */ ("btl %1, %0\n\t" "jc %l2")); std::vector goto_blocks ({b_carry}); gccjit::extended_asm ext_asm = (b_start.end_with_extended_asm_goto (asm_template, goto_blocks, &b_fallthru) .add_input_operand ("r", p1) .add_input_operand ("r", p2) .add_clobber ("cc")); /* Quote up to here in docs/cp/topics/asm.rst: example 3: jit. */ std::string desc = ext_asm.get_debug_string (); CHECK_STRING_VALUE (desc.c_str (), (use_name ? ("asm goto (\"btl %1, %0\\n\\tjc %l[carry]\" " ": : \"r\" (p1), \"r\" (p2) : \"cc\" " ": carry [fallthrough: fallthru])") : ("asm goto (\"btl %1, %0\\n\\tjc %l2\" " ": : \"r\" (p1), \"r\" (p2) : \"cc\" " ": carry [fallthrough: fallthru])"))); b_fallthru.end_with_return (zero); b_carry.end_with_return (one); } static void verify_code_3 (gcc_jit_context *ctxt, gcc_jit_result *result, const char *funcname) { typedef int (*test_i386_basic_asm_3_type) (int, int); test_i386_basic_asm_3_type test_i386_basic_asm_3 = (test_i386_basic_asm_3_type) gcc_jit_result_get_code (result, funcname); CHECK_NON_NULL (test_i386_basic_asm_3); /* The fn should test bits, returning 0 or 1. */ /* Bit 0. */ CHECK_VALUE (test_i386_basic_asm_3 (0x0000, 0), 0); CHECK_VALUE (test_i386_basic_asm_3 (0x0001, 0), 1); CHECK_VALUE (test_i386_basic_asm_3 (0x0002, 0), 0); CHECK_VALUE (test_i386_basic_asm_3 (0x0003, 0), 1); CHECK_VALUE (test_i386_basic_asm_3 (0x0004, 0), 0); /* Bit 1. */ CHECK_VALUE (test_i386_basic_asm_3 (0x0000, 1), 0); CHECK_VALUE (test_i386_basic_asm_3 (0x0001, 1), 0); CHECK_VALUE (test_i386_basic_asm_3 (0x0002, 1), 1); CHECK_VALUE (test_i386_basic_asm_3 (0x0003, 1), 1); CHECK_VALUE (test_i386_basic_asm_3 (0x0004, 1), 0); for (int i = 0; i < 15; i++) { CHECK_VALUE (test_i386_basic_asm_3 (0x0000, i), 0); CHECK_VALUE (test_i386_basic_asm_3 (0xffff, i), 1); } } /********************************************************************** test_i386_basic_asm_4: test of "volatile" **********************************************************************/ /* Create the equivalent of: uint64_t test_i386_basic_asm_4 (void) { uint64_t start_time, end_time; // Get start time asm volatile ("rdtsc\n\t" // Returns the time in EDX:EAX. "shl $32, %%rdx\n\t" // Shift the upper bits left. "or %%rdx, %0" // 'Or' in the lower bits. : "=a" (start_time) : : "rdx"); // could do other work here // Get end time asm volatile ("rdtsc\n\t" // Returns the time in EDX:EAX. "shl $32, %%rdx\n\t" // Shift the upper bits left. "or %%rdx, %0" // 'Or' in the lower bits. : "=a" (start_time) : : "rdx"); // Get elapsed time return end_time - start_time; } This exercises "volatile"; without it, the optimizer can assume that both asm generate the same value and thus the time difference is zero. */ static void add_rdtsc (gccjit::block block, gccjit::lvalue msr) { /* Quote from here in docs/cp/topics/asm.rst: example 4: jit. */ gccjit::extended_asm ext_asm = block.add_extended_asm ("rdtsc\n\t" /* Returns the time in EDX:EAX. */ "shl $32, %%rdx\n\t" /* Shift the upper bits left. */ "or %%rdx, %0") /* 'Or' in the lower bits. */ .set_volatile_flag (true) .add_output_operand ("=a", msr) .add_clobber ("rdx"); /* Quote up to here in docs/cp/topics/asm.rst: example 4: jit. */ std::string desc = ext_asm.get_debug_string (); CHECK_STRING_STARTS_WITH (desc.c_str (), "asm volatile ("); } static void create_test_i386_basic_asm_4 (gcc_jit_context *c_ctxt) { gccjit::context ctxt (c_ctxt); gccjit::type uint64_type = ctxt.get_int_type (8, 0); std::vector params; gccjit::function func = ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED, uint64_type, "test_i386_basic_asm_4", params, 0); gccjit::block block = func.new_block (); gccjit::lvalue start_time = func.new_local (uint64_type, "start_time"); add_rdtsc (block, start_time); block.add_comment ("other work here"); gccjit::lvalue end_time = func.new_local (uint64_type, "end_time"); add_rdtsc (block, end_time); block.end_with_return (end_time - start_time); } static void verify_code_4 (gcc_jit_context *ctxt, gcc_jit_result *result) { typedef uint64_t (*fntype) (void); fntype test_i386_basic_asm_4 = (fntype)gcc_jit_result_get_code (result, "test_i386_basic_asm_4"); CHECK_NON_NULL (test_i386_basic_asm_4); test_i386_basic_asm_4 (); } /********************************************************************** test_i386_basic_asm_5: test of top-level asm **********************************************************************/ /* Create the equivalent of: // Quote from here in docs/cp/topics/asm.rst: example 5: C asm ("\t.pushsection .text\n" "\t.globl add_asm\n" "\t.type add_asm, @function\n" "add_asm:\n" "\tmovq %rdi, %rax\n" "\tadd %rsi, %rax\n" "\tret\n" "\t.popsection\n"); // Quote up to here in docs/cp/topics/asm.rst: example 5: C to add a simple function ("add_asm") directly in assembly language. */ static void create_test_i386_basic_asm_5 (gcc_jit_context *c_ctxt) { gccjit::context ctxt (c_ctxt); #if __APPLE__ /* Darwin's assemblers do not support push/pop section, do not use .type and external symbols should use __USER_LABEL_PREFIX__. */ ctxt.add_top_level_asm ("\t.text\n" "\t.globl _add_asm\n" "_add_asm:\n" "\tmovq %rdi, %rax\n" "\tadd %rsi, %rax\n" "\tret\n" "\t# some asm here\n"); #else /* Quote from here in docs/cp/topics/asm.rst: example 5: jit. */ ctxt.add_top_level_asm ("\t.pushsection .text\n" "\t.globl add_asm\n" "\t.type add_asm, @function\n" "add_asm:\n" "\tmovq %rdi, %rax\n" "\tadd %rsi, %rax\n" "\tret\n" "\t# some asm here\n" "\t.popsection\n"); /* Quote up to here in docs/cp/topics/asm.rst: example 5: jit. */ #endif } static void verify_code_5 (gcc_jit_context *ctxt, gcc_jit_result *result) { typedef int (*test_i386_basic_asm_5_type) (int, int); test_i386_basic_asm_5_type test_i386_basic_asm_5 = (test_i386_basic_asm_5_type) gcc_jit_result_get_code (result, "add_asm"); CHECK_NON_NULL (test_i386_basic_asm_5); CHECK_VALUE (test_i386_basic_asm_5 (2, 2), 4); CHECK_VALUE (test_i386_basic_asm_5 (20, 7), 27); } /********************************************************************** Code for harness **********************************************************************/ void create_code (gcc_jit_context *ctxt, void *user_data) { create_test_i386_basic_asm_1 (ctxt); create_test_i386_basic_asm_2 (ctxt); create_test_i386_basic_asm_3 (ctxt, "test_i386_basic_asm_3a", 0); create_test_i386_basic_asm_3 (ctxt, "test_i386_basic_asm_3b", 1); create_test_i386_basic_asm_4 (ctxt); create_test_i386_basic_asm_5 (ctxt); } void verify_code (gcc_jit_context *ctxt, gcc_jit_result *result) { CHECK_NON_NULL (result); verify_code_1 (ctxt, result); verify_code_2 (ctxt, result); verify_code_3 (ctxt, result, "test_i386_basic_asm_3a"); verify_code_3 (ctxt, result, "test_i386_basic_asm_3b"); verify_code_4 (ctxt, result); verify_code_5 (ctxt, result); }