diff --git a/goalc/emitter/CodeTester.h b/goalc/emitter/CodeTester.h index a4cfa8497d..3dd17d3ae1 100644 --- a/goalc/emitter/CodeTester.h +++ b/goalc/emitter/CodeTester.h @@ -21,6 +21,15 @@ class CodeTester { void emit(const Instruction& instr); u64 execute(); u64 execute(u64 in0, u64 in1, u64 in2, u64 in3); + + template + T execute_ret(u64 in0, u64 in1, u64 in2, u64 in3) { + u64 result_u64 = ((u64(*)(u64, u64, u64, u64))code_buffer)(in0, in1, in2, in3); + T result_T; + memcpy(&result_T, &result_u64, sizeof(T)); + return result_T; + } + void clear(); ~CodeTester(); diff --git a/goalc/emitter/IGen.h b/goalc/emitter/IGen.h index 869ff9f913..bcd695e3a4 100644 --- a/goalc/emitter/IGen.h +++ b/goalc/emitter/IGen.h @@ -120,7 +120,7 @@ class IGen { // todo - XMM128 - XMM128 //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - // GOAL Loads + // GOAL Loads and Stores //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; /*! @@ -469,6 +469,20 @@ class IGen { return instr; } + static Instruction store32_gpr64_gpr64_plus_gpr64(Register addr1, + Register addr2, + Register value) { + assert(value.is_gpr()); + assert(addr1.is_gpr()); + assert(addr2.is_gpr()); + assert(addr1 != addr2); + assert(addr1 != RSP); + assert(addr2 != RSP); + Instruction instr(0x89); + instr.set_modrm_and_rex_for_reg_plus_reg_addr(value.hw_id(), addr1.hw_id(), addr2.hw_id()); + return instr; + } + static Instruction load32s_gpr64_gpr64_plus_gpr64_plus_s8(Register dst, Register addr1, Register addr2, @@ -486,6 +500,23 @@ class IGen { return instr; } + static Instruction store32_gpr64_gpr64_plus_gpr64_plus_s8(Register addr1, + Register addr2, + Register value, + s64 offset) { + assert(value.is_gpr()); + assert(addr1.is_gpr()); + assert(addr2.is_gpr()); + assert(addr1 != addr2); + assert(addr1 != RSP); + assert(addr2 != RSP); + assert(offset >= INT8_MIN && offset <= INT8_MAX); + Instruction instr(0x89); + instr.set_modrm_and_rex_for_reg_plus_reg_plus_s8(value.hw_id(), addr1.hw_id(), addr2.hw_id(), + offset, false); + return instr; + } + static Instruction load32s_gpr64_gpr64_plus_gpr64_plus_s32(Register dst, Register addr1, Register addr2, @@ -503,6 +534,23 @@ class IGen { return instr; } + static Instruction store32_gpr64_gpr64_plus_gpr64_plus_s32(Register addr1, + Register addr2, + Register value, + s64 offset) { + assert(value.is_gpr()); + assert(addr1.is_gpr()); + assert(addr2.is_gpr()); + assert(addr1 != addr2); + assert(addr1 != RSP); + assert(addr2 != RSP); + assert(offset >= INT32_MIN && offset <= INT32_MAX); + Instruction instr(0x89); + instr.set_modrm_and_rex_for_reg_plus_reg_plus_s32(value.hw_id(), addr1.hw_id(), addr2.hw_id(), + offset, false); + return instr; + } + /*! * movzxd dst, DWORD PTR [addr1 + addr2] * addr1 and addr2 have to be different registers. @@ -571,6 +619,21 @@ class IGen { return instr; } + static Instruction store64_gpr64_gpr64_plus_gpr64(Register addr1, + Register addr2, + Register value) { + assert(value.is_gpr()); + assert(addr1.is_gpr()); + assert(addr2.is_gpr()); + assert(addr1 != addr2); + assert(addr1 != RSP); + assert(addr2 != RSP); + Instruction instr(0x89); + instr.set_modrm_and_rex_for_reg_plus_reg_addr(value.hw_id(), addr1.hw_id(), addr2.hw_id(), + true); + return instr; + } + static Instruction load64_gpr64_gpr64_plus_gpr64_plus_s8(Register dst, Register addr1, Register addr2, @@ -588,6 +651,23 @@ class IGen { return instr; } + static Instruction store64_gpr64_gpr64_plus_gpr64_plus_s8(Register addr1, + Register addr2, + Register value, + s64 offset) { + assert(value.is_gpr()); + assert(addr1.is_gpr()); + assert(addr2.is_gpr()); + assert(addr1 != addr2); + assert(addr1 != RSP); + assert(addr2 != RSP); + assert(offset >= INT8_MIN && offset <= INT8_MAX); + Instruction instr(0x89); + instr.set_modrm_and_rex_for_reg_plus_reg_plus_s8(value.hw_id(), addr1.hw_id(), addr2.hw_id(), + offset, true); + return instr; + } + static Instruction load64_gpr64_gpr64_plus_gpr64_plus_s32(Register dst, Register addr1, Register addr2, @@ -605,6 +685,23 @@ class IGen { return instr; } + static Instruction store64_gpr64_gpr64_plus_gpr64_plus_s32(Register addr1, + Register addr2, + Register value, + s64 offset) { + assert(value.is_gpr()); + assert(addr1.is_gpr()); + assert(addr2.is_gpr()); + assert(addr1 != addr2); + assert(addr1 != RSP); + assert(addr2 != RSP); + assert(offset >= INT32_MIN && offset <= INT32_MAX); + Instruction instr(0x89); + instr.set_modrm_and_rex_for_reg_plus_reg_plus_s32(value.hw_id(), addr1.hw_id(), addr2.hw_id(), + offset, true); + return instr; + } + static Instruction store_goal_gpr(Register addr, Register value, Register off, @@ -631,6 +728,26 @@ class IGen { } else { assert(false); } + case 4: + if (offset == 0) { + return store32_gpr64_gpr64_plus_gpr64(addr, off, value); + } else if (offset >= INT8_MIN && offset <= INT8_MAX) { + return store32_gpr64_gpr64_plus_gpr64_plus_s8(addr, off, value, offset); + } else if (offset >= INT32_MIN && offset <= INT32_MAX) { + return store32_gpr64_gpr64_plus_gpr64_plus_s32(addr, off, value, offset); + } else { + assert(false); + } + case 8: + if (offset == 0) { + return store64_gpr64_gpr64_plus_gpr64(addr, off, value); + } else if (offset >= INT8_MIN && offset <= INT8_MAX) { + return store64_gpr64_gpr64_plus_gpr64_plus_s8(addr, off, value, offset); + } else if (offset >= INT32_MIN && offset <= INT32_MAX) { + return store64_gpr64_gpr64_plus_gpr64_plus_s32(addr, off, value, offset); + } else { + assert(false); + } default: assert(false); } @@ -719,6 +836,117 @@ class IGen { } } + //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + // LOADS n' STORES - XMM32 + //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + static Instruction store32_xmm32_gpr64_plus_gpr64(Register addr1, + Register addr2, + Register xmm_value) { + assert(xmm_value.is_xmm()); + assert(addr1.is_gpr()); + assert(addr2.is_gpr()); + + Instruction instr(0xf3); + instr.set_op2(0x0f); + instr.set_op3(0x11); + instr.set_modrm_and_rex_for_reg_plus_reg_addr(xmm_value.hw_id(), addr1.hw_id(), addr2.hw_id()); + + instr.swap_op0_rex(); + return instr; + } + + static Instruction load32_xmm32_gpr64_plus_gpr64(Register xmm_dest, + Register addr1, + Register addr2) { + assert(xmm_dest.is_xmm()); + assert(addr1.is_gpr()); + assert(addr2.is_gpr()); + + Instruction instr(0xf3); + instr.set_op2(0x0f); + instr.set_op3(0x10); + instr.set_modrm_and_rex_for_reg_plus_reg_addr(xmm_dest.hw_id(), addr1.hw_id(), addr2.hw_id()); + + instr.swap_op0_rex(); + return instr; + } + + static Instruction store32_xmm32_gpr64_plus_gpr64_plus_s8(Register addr1, + Register addr2, + Register xmm_value, + s64 offset) { + assert(xmm_value.is_xmm()); + assert(addr1.is_gpr()); + assert(addr2.is_gpr()); + assert(offset >= INT8_MIN && offset <= INT8_MAX); + + Instruction instr(0xf3); + instr.set_op2(0x0f); + instr.set_op3(0x11); + instr.set_modrm_and_rex_for_reg_plus_reg_plus_s8(xmm_value.hw_id(), addr1.hw_id(), + addr2.hw_id(), offset, false); + + instr.swap_op0_rex(); + return instr; + } + + static Instruction load32_xmm32_gpr64_plus_gpr64_plus_s8(Register xmm_dest, + Register addr1, + Register addr2, + s64 offset) { + assert(xmm_dest.is_xmm()); + assert(addr1.is_gpr()); + assert(addr2.is_gpr()); + assert(offset >= INT8_MIN && offset <= INT8_MAX); + + Instruction instr(0xf3); + instr.set_op2(0x0f); + instr.set_op3(0x10); + instr.set_modrm_and_rex_for_reg_plus_reg_plus_s8(xmm_dest.hw_id(), addr1.hw_id(), addr2.hw_id(), + offset, false); + + instr.swap_op0_rex(); + return instr; + } + + static Instruction store32_xmm32_gpr64_plus_gpr64_plus_s32(Register addr1, + Register addr2, + Register xmm_value, + s64 offset) { + assert(xmm_value.is_xmm()); + assert(addr1.is_gpr()); + assert(addr2.is_gpr()); + assert(offset >= INT32_MIN && offset <= INT32_MAX); + + Instruction instr(0xf3); + instr.set_op2(0x0f); + instr.set_op3(0x11); + instr.set_modrm_and_rex_for_reg_plus_reg_plus_s32(xmm_value.hw_id(), addr1.hw_id(), + addr2.hw_id(), offset, false); + + instr.swap_op0_rex(); + return instr; + } + + static Instruction load32_xmm32_gpr64_plus_gpr64_plus_s32(Register xmm_dest, + Register addr1, + Register addr2, + s64 offset) { + assert(xmm_dest.is_xmm()); + assert(addr1.is_gpr()); + assert(addr2.is_gpr()); + assert(offset >= INT32_MIN && offset <= INT32_MAX); + + Instruction instr(0xf3); + instr.set_op2(0x0f); + instr.set_op3(0x10); + instr.set_modrm_and_rex_for_reg_plus_reg_plus_s32(xmm_dest.hw_id(), addr1.hw_id(), + addr2.hw_id(), offset, false); + + instr.swap_op0_rex(); + return instr; + } + //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; // LOADS n' STORES - XMM128 //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -748,6 +976,12 @@ class IGen { return instr; } + //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + // RIP + //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + // TODO + //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; // FUNCTION STUFF //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -805,6 +1039,10 @@ class IGen { // BIT STUFF //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + // SHIFTS + //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; // CONTROL FLOW //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/goalc/emitter/Instruction.h b/goalc/emitter/Instruction.h index 0ec970e939..c82f52753c 100644 --- a/goalc/emitter/Instruction.h +++ b/goalc/emitter/Instruction.h @@ -230,7 +230,7 @@ struct Instruction { void set_modrm_and_rex_for_reg_plus_reg_plus_s32(uint8_t reg, uint8_t addr1, uint8_t addr2, - s8 offset, + s32 offset, bool rex_w) { bool rex_b = false, rex_r = false, rex_x = false; bool addr1_ext = false; diff --git a/goalc/old_emitter/IGen.h b/goalc/old_emitter/IGen.h index eea12519e1..7f297993bf 100644 --- a/goalc/old_emitter/IGen.h +++ b/goalc/old_emitter/IGen.h @@ -8,272 +8,10 @@ namespace goal { class IGen { public: - //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - // MOVES - //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - /*! - * mov gpr, gpr, 64 bits - */ - static Instruction mov_gpr64_gpr64(uint8_t dst, uint8_t src) { - assert(is_gpr(dst)); - assert(is_gpr(src)); - Instruction instr(0x89); - instr.set_modrm_and_rex(src, dst, 3, true); - return instr; - } - - /*! - * Move a 64-bit constant into a register. - */ - static Instruction mov_gpr64_u64(uint8_t dst, uint64_t val) { - assert(is_gpr(dst)); - bool rex_b = false; - if (dst >= 8) { - dst -= 8; - rex_b = true; - } - Instruction instr(0xb8 + dst); - instr.set(REX(true, false, false, rex_b)); - instr.set(Imm(8, val)); - return instr; - } - - /*! - * Move a 32-bit constant into a register. - */ - static Instruction mov_gpr64_u32(uint8_t dst, uint64_t val) { - assert(val <= UINT32_MAX); - assert(is_gpr(dst)); - bool rex_b = false; - if (dst >= 8) { - dst -= 8; - rex_b = true; - } - - Instruction instr(0xb8 + dst); - if (rex_b) { - instr.set(REX(false, false, false, rex_b)); - } - instr.set(Imm(4, val)); - return instr; - } - - /*! - * Move a signed 32-bit constant into a register. - * When possible prefer mov_gpr64_u32. (use this only for negative values...) - * This is always bigger than mov_gpr64_u32, but smaller than a mov_gpr_u64. - */ - static Instruction mov_gpr64_s32(uint8_t dst, int64_t val) { - assert(val >= INT32_MIN && val <= INT32_MAX); - assert(is_gpr(dst)); - Instruction instr(0xc7); - instr.set_modrm_and_rex(0, dst, 3, true); - instr.set(Imm(4, val)); - return instr; - } - - /*! - * Move 32-bits of xmm to 32 bits of gpr (no sign extension). - */ - static Instruction movd_gpr32_xmm32(uint8_t dst, uint8_t src) { - assert(is_gpr(dst)); - assert(is_xmm(src)); - Instruction instr(0x66); - instr.set_op2(0x0f); - instr.set_op3(0x7e); - instr.set_modrm_and_rex(xmm_to_id(src), dst, 3, false); - instr.swap_op0_rex(); - return instr; - } - - /*! - * Move 32-bits of gpr to 32-bits of xmm (no sign extenion) - */ - static Instruction movd_xmm32_gpr32(uint8_t dst, uint8_t src) { - assert(is_xmm(dst)); - assert(is_gpr(src)); - Instruction instr(0x66); - instr.set_op2(0x0f); - instr.set_op3(0x6e); - instr.set_modrm_and_rex(dst, xmm_to_id(src), 3, false); - instr.swap_op0_rex(); - return instr; - } - - /*! - * Move 32-bits between xmm's - */ - static Instruction mov_xmm32_xmm32(uint8_t dst, uint8_t src) { - assert(is_xmm(dst)); - assert(is_xmm(src)); - Instruction instr(0xf3); - instr.set_op2(0x0f); - instr.set_op3(0x10); - instr.set_modrm_and_rex(xmm_to_id(dst), xmm_to_id(src), 3, false); - return instr; - } - // - // //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - // // LOADS n' STORES - // //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - // - // /*! - // * Store 8-bits from register into a memory location that is the sum of a 64-bit register - // * and signed 32-bit offset. - // */ - // static Instruction store8_r64off32s_gpr8(uint8_t dst_reg, int32_t offset, uint8_t src_reg) { - // Instruction instr(0x88); - // instr.set_modrm_and_rex_for_addr(src_reg, dst_reg, 2, false); - // instr.set_disp(Imm(4, offset)); - // if (src_reg > int(X86R::RBX)) { - // instr.add_rex(); - // } - // return instr; - // } - // - // /*! - // * Store 16-bits from register into a memory location that is the sum of a 64-bit register - // * and signed 32-bit offset. - // */ - // static Instruction store16_r64off32s_gpr16(uint8_t dst_reg, int32_t offset, uint8_t src_reg) { - // Instruction instr(0x66); - // instr.set_op2(0x89); - // instr.set_modrm_and_rex_for_addr(src_reg, dst_reg, 2, false); - // instr.set_disp(Imm(4, offset)); - // return instr; - // } - // - // /*! - // * Store 32-bits from register into a memory location that is the sum of a 64-bit register - // * and signed 32-bit offset. - // */ - // static Instruction store32_r64off32s_gpr32(uint8_t dst_reg, int32_t offset, uint8_t src_reg) { - // Instruction instr(0x89); - // instr.set_modrm_and_rex_for_addr(src_reg, dst_reg, 2, false); - // instr.set_disp(Imm(4, offset)); - // return instr; - // } - // - // /*! - // * Store 64-bits from gpr into memory located at 64-bit reg + 32-bit signed offset. - // */ - // static Instruction store64_r64off32s_gpr64(uint8_t dst_reg, int32_t offset, uint8_t src_reg) { - // Instruction instr(0x89); - // instr.set_modrm_rex_sib_for_reg_reg_disp32(src_reg, 2, dst_reg, true); - // instr.set_disp(Imm(4, offset)); - // return instr; - // } - // - // /*! - // * Load 8-bits from memory (at address of 64-bit reg + 32-bit signed offset) into gpr (zero - // * extended) - // */ - // static Instruction load16_gpr8z_r64off32s(uint8_t dst, uint8_t src, int32_t offset) { - // Instruction instr(0x0f); - // instr.set_op2(0xb6); - // instr.set_modrm_rex_sib_for_reg_reg_disp32(dst, 2, src, true); - // instr.set_disp(Imm(4, offset)); - // return instr; - // } - // - // /*! - // * Load 16-bits from memory (at address of 64-bit reg + 32-bit signed offset) into gpr (zero - // * extended) - // */ - // static Instruction load16_gpr16z_r64off32s(uint8_t dst, uint8_t src, int32_t offset) { - // Instruction instr(0x0f); - // instr.set_op2(0xb7); - // instr.set_modrm_rex_sib_for_reg_reg_disp32(dst, 2, src, true); - // instr.set_disp(Imm(4, offset)); - // return instr; - // } - // - // /*! - // * Load 16-bits from memory (at address of 64-bit reg + 32-bit signed offset) into gpr (sign - // * extended) - // */ - // static Instruction load16_gpr16s_r64off32s(uint8_t dst, uint8_t src, int32_t offset) { - // Instruction instr(0x0f); - // instr.set_op2(0xbf); - // instr.set_modrm_rex_sib_for_reg_reg_disp32(dst, 2, src, true); - // instr.set_disp(Imm(4, offset)); - // return instr; - // } - // - // /*! - // * Load 32-bits from memory (at address of 64-bit reg + 32-bit signed offset) into gpr. - // * Use the sext flag to enable sign extension. - // */ - // static Instruction load32_gpr32sz_r64off32s(uint8_t dst_reg, - // int32_t offset, - // uint8_t src_reg, - // bool sext = false) { - // Instruction instr(0x8b); - // if (sext) { - // instr.op = 0x63; - // } - // instr.set_modrm_rex_sib_for_reg_reg_disp32(dst_reg, 2, src_reg, sext); - // instr.set_disp(Imm(4, offset)); - // return instr; - // } - // - // /*! - // * Load 64-bits from memory located at 64-bit reg + 32-bit signed offset into gpr - // */ - // static Instruction load64_gpr64_r64off32s(uint8_t dst_reg, int32_t offset, uint8_t src_reg) { - // Instruction instr(0x8b); - // instr.set_modrm_rex_sib_for_reg_reg_disp32(dst_reg, 2, src_reg, true); - // instr.set_disp(Imm(4, offset)); - // return instr; - // } - // - // /*! - // * Load 32-bits form memory located at 64-bit reg + 32-bit signed offset into xmm (32-bits) - // * movss - // */ - // static Instruction load32_xmm32_r64off32s(uint8_t dst, uint8_t src, int32_t offset) { - // Instruction instr(0xf3); - // instr.set_op2(0x0f); - // instr.set_op3(0x10); - // instr.set_modrm_rex_sib_for_reg_reg_disp32(dst, 2, src, false); - // instr.set_disp(Imm(4, offset)); - // instr.swap_op0_rex(); - // return instr; - // } - //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; // FUNCTION STUFF //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - /*! - * Return instruction - */ - static Instruction ret() { return Instruction(0xc3); } - - /*! - * Instruction to push gpr (64-bits) onto the stack - */ - static Instruction push_gpr64(uint8_t reg) { - if (reg >= 8) { - auto i = Instruction(0x50 + reg - 8); - i.set(REX(false, false, false, true)); - return i; - } - return Instruction(0x50 + reg); - } - - /*! - * Instruction to pop 64 bit gpr from the stack - */ - static Instruction pop_gpr64(uint8_t reg) { - if (reg >= 8) { - auto i = Instruction(0x58 + reg - 8); - i.set(REX(false, false, false, true)); - return i; - } - return Instruction(0x58 + reg); - } - // /*! // * Call a function stored in a 64-bit gpr // */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 38374ded9c..eabc23b71b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -9,7 +9,8 @@ add_executable(goalc-test test_type_system.cpp test_CodeTester.cpp test_emitter_slow.cpp - test_emitter_fast.cpp + test_emitter_loads_and_store.cpp + test_emitter_xmm32.cpp ) target_link_libraries(goalc-test goos util listener runtime emitter type_system gtest) \ No newline at end of file diff --git a/test/test_emitter_fast.cpp b/test/test_emitter_loads_and_store.cpp similarity index 79% rename from test/test_emitter_fast.cpp rename to test/test_emitter_loads_and_store.cpp index fbb6791ca4..d1663f4877 100644 --- a/test/test_emitter_fast.cpp +++ b/test/test_emitter_loads_and_store.cpp @@ -1,5 +1,5 @@ /*! - * @file test_emitter_slow.cpp + * @file test_emitter_loads_and_stores.cpp * Tests for the emitter which are fast (checking 100's of functions) */ @@ -10,7 +10,7 @@ // using namespace emitter; -TEST(Emitter, load_constant_64_and_move_gpr_gpr_64) { +TEST(EmitterLoadsAndStores, load_constant_64_and_move_gpr_gpr_64) { std::vector u64_constants = {0, UINT64_MAX, INT64_MAX, 7, 12}; // test we can load a 64-bit constant into all gprs, move it to any other gpr, and return it. @@ -42,7 +42,7 @@ TEST(Emitter, load_constant_64_and_move_gpr_gpr_64) { } } -TEST(Emitter, load_constant_32_unsigned) { +TEST(EmitterLoadsAndStores, load_constant_32_unsigned) { std::vector u64_constants = {0, UINT32_MAX, INT32_MAX, 7, 12}; // test loading 32-bit constants, with all upper 32-bits zero. @@ -68,7 +68,7 @@ TEST(Emitter, load_constant_32_unsigned) { } } -TEST(Emitter, load_constant_32_signed) { +TEST(EmitterLoadsAndStores, load_constant_32_signed) { std::vector s32_constants = {0, 1, INT32_MAX, INT32_MIN, 12, -1}; // test loading signed 32-bit constants. for values < 0 this will sign extend. @@ -92,7 +92,7 @@ TEST(Emitter, load_constant_32_signed) { } } -TEST(Emitter, load8s_gpr64_goal_ptr_gpr64) { +TEST(EmitterLoadsAndStores, load8s_gpr64_goal_ptr_gpr64) { CodeTester tester; tester.init_code_buffer(512); @@ -167,7 +167,7 @@ TEST(Emitter, load8s_gpr64_goal_ptr_gpr64) { } } -TEST(Emitter, load8s_gpr64_gpr64_gpr64_s8) { +TEST(EmitterLoadsAndStores, load8s_gpr64_gpr64_gpr64_s8) { CodeTester tester; tester.init_code_buffer(512); @@ -236,7 +236,7 @@ TEST(Emitter, load8s_gpr64_gpr64_gpr64_s8) { } } -TEST(Emitter, load8s_gpr64_gpr64_gpr64_s32) { +TEST(EmitterLoadsAndStores, load8s_gpr64_gpr64_gpr64_s32) { CodeTester tester; tester.init_code_buffer(512); @@ -305,7 +305,7 @@ TEST(Emitter, load8s_gpr64_gpr64_gpr64_s32) { } } -TEST(Emitter, load8u_gpr64_goal_ptr_gpr64) { +TEST(EmitterLoadsAndStores, load8u_gpr64_goal_ptr_gpr64) { CodeTester tester; tester.init_code_buffer(512); @@ -380,7 +380,7 @@ TEST(Emitter, load8u_gpr64_goal_ptr_gpr64) { } } -TEST(Emitter, load8u_gpr64_gpr64_gpr64_s8) { +TEST(EmitterLoadsAndStores, load8u_gpr64_gpr64_gpr64_s8) { CodeTester tester; tester.init_code_buffer(512); @@ -449,7 +449,7 @@ TEST(Emitter, load8u_gpr64_gpr64_gpr64_s8) { } } -TEST(Emitter, load8u_gpr64_gpr64_gpr64_s32) { +TEST(EmitterLoadsAndStores, load8u_gpr64_gpr64_gpr64_s32) { CodeTester tester; tester.init_code_buffer(512); @@ -518,7 +518,7 @@ TEST(Emitter, load8u_gpr64_gpr64_gpr64_s32) { } } -TEST(Emitter, load16s_gpr64_goal_ptr_gpr64) { +TEST(EmitterLoadsAndStores, load16s_gpr64_goal_ptr_gpr64) { CodeTester tester; tester.init_code_buffer(512); @@ -593,7 +593,7 @@ TEST(Emitter, load16s_gpr64_goal_ptr_gpr64) { } } -TEST(Emitter, load16s_gpr64_gpr64_plus_gpr64_plus_s8) { +TEST(EmitterLoadsAndStores, load16s_gpr64_gpr64_plus_gpr64_plus_s8) { CodeTester tester; tester.init_code_buffer(512); @@ -662,7 +662,7 @@ TEST(Emitter, load16s_gpr64_gpr64_plus_gpr64_plus_s8) { } } -TEST(Emitter, load16s_gpr64_gpr64_plus_gpr64_plus_s32) { +TEST(EmitterLoadsAndStores, load16s_gpr64_gpr64_plus_gpr64_plus_s32) { CodeTester tester; tester.init_code_buffer(512); @@ -731,7 +731,7 @@ TEST(Emitter, load16s_gpr64_gpr64_plus_gpr64_plus_s32) { } } -TEST(Emitter, load16u_gpr64_goal_ptr_gpr64) { +TEST(EmitterLoadsAndStores, load16u_gpr64_goal_ptr_gpr64) { CodeTester tester; tester.init_code_buffer(512); @@ -806,7 +806,7 @@ TEST(Emitter, load16u_gpr64_goal_ptr_gpr64) { } } -TEST(Emitter, load16u_gpr64_gpr64_plus_gpr64_plus_s8) { +TEST(EmitterLoadsAndStores, load16u_gpr64_gpr64_plus_gpr64_plus_s8) { CodeTester tester; tester.init_code_buffer(512); @@ -875,7 +875,7 @@ TEST(Emitter, load16u_gpr64_gpr64_plus_gpr64_plus_s8) { } } -TEST(Emitter, load16u_gpr64_gpr64_plus_gpr64_plus_s32) { +TEST(EmitterLoadsAndStores, load16u_gpr64_gpr64_plus_gpr64_plus_s32) { CodeTester tester; tester.init_code_buffer(512); @@ -944,7 +944,7 @@ TEST(Emitter, load16u_gpr64_gpr64_plus_gpr64_plus_s32) { } } -TEST(Emitter, load32s_gpr64_goal_ptr_gpr64) { +TEST(EmitterLoadsAndStores, load32s_gpr64_goal_ptr_gpr64) { CodeTester tester; tester.init_code_buffer(512); @@ -1007,7 +1007,7 @@ TEST(Emitter, load32s_gpr64_goal_ptr_gpr64) { } } -TEST(Emitter, load32s_gpr64_gpr64_plus_gpr64_plus_s8) { +TEST(EmitterLoadsAndStores, load32s_gpr64_gpr64_plus_gpr64_plus_s8) { CodeTester tester; tester.init_code_buffer(512); @@ -1076,7 +1076,7 @@ TEST(Emitter, load32s_gpr64_gpr64_plus_gpr64_plus_s8) { } } -TEST(Emitter, load32s_gpr64_gpr64_plus_gpr64_plus_s32) { +TEST(EmitterLoadsAndStores, load32s_gpr64_gpr64_plus_gpr64_plus_s32) { CodeTester tester; tester.init_code_buffer(512); @@ -1145,7 +1145,7 @@ TEST(Emitter, load32s_gpr64_gpr64_plus_gpr64_plus_s32) { } } -TEST(Emitter, load32u_gpr64_goal_ptr_gpr64) { +TEST(EmitterLoadsAndStores, load32u_gpr64_goal_ptr_gpr64) { CodeTester tester; tester.init_code_buffer(512); @@ -1208,7 +1208,7 @@ TEST(Emitter, load32u_gpr64_goal_ptr_gpr64) { } } -TEST(Emitter, load32u_gpr64_gpr64_plus_gpr64_plus_s8) { +TEST(EmitterLoadsAndStores, load32u_gpr64_gpr64_plus_gpr64_plus_s8) { CodeTester tester; tester.init_code_buffer(512); @@ -1277,7 +1277,7 @@ TEST(Emitter, load32u_gpr64_gpr64_plus_gpr64_plus_s8) { } } -TEST(Emitter, load32u_gpr64_gpr64_plus_gpr64_plus_s32) { +TEST(EmitterLoadsAndStores, load32u_gpr64_gpr64_plus_gpr64_plus_s32) { CodeTester tester; tester.init_code_buffer(512); @@ -1346,7 +1346,7 @@ TEST(Emitter, load32u_gpr64_gpr64_plus_gpr64_plus_s32) { } } -TEST(Emitter, load64_gpr64_goal_ptr_gpr64) { +TEST(EmitterLoadsAndStores, load64_gpr64_goal_ptr_gpr64) { CodeTester tester; tester.init_code_buffer(512); @@ -1409,7 +1409,7 @@ TEST(Emitter, load64_gpr64_goal_ptr_gpr64) { } } -TEST(Emitter, load64_gpr64_gpr64_plus_gpr64_plus_s8) { +TEST(EmitterLoadsAndStores, load64_gpr64_gpr64_plus_gpr64_plus_s8) { CodeTester tester; tester.init_code_buffer(512); @@ -1478,7 +1478,7 @@ TEST(Emitter, load64_gpr64_gpr64_plus_gpr64_plus_s8) { } } -TEST(Emitter, load64_gpr64_gpr64_plus_gpr64_plus_s32) { +TEST(EmitterLoadsAndStores, load64_gpr64_gpr64_plus_gpr64_plus_s32) { CodeTester tester; tester.init_code_buffer(512); @@ -1547,7 +1547,7 @@ TEST(Emitter, load64_gpr64_gpr64_plus_gpr64_plus_s32) { } } -TEST(Emitter, store8_gpr64_gpr64_plus_gpr64) { +TEST(EmitterLoadsAndStores, store8_gpr64_gpr64_plus_gpr64) { CodeTester tester; tester.init_code_buffer(512); @@ -1591,13 +1591,13 @@ TEST(Emitter, store8_gpr64_gpr64_plus_gpr64) { tester.emit_return(); // prepare the memory: - s8 memory[8] = {0, 0, -3, -2, -1, 0, 0, 0}; + s8 memory[8] = {0, 0, 3, -2, 1, 0, 0, 0}; // run! - tester.execute((u64)memory, 3, 7, 0); - EXPECT_EQ(memory[2], -3); + tester.execute((u64)memory, 3, 0xffffffffffffff07, 0); + EXPECT_EQ(memory[2], 3); EXPECT_EQ(memory[3], 7); - EXPECT_EQ(memory[4], -1); + EXPECT_EQ(memory[4], 1); if (memory[3] != 7) { fmt::print("test {}, {}, {}\n", tester.reg_name(i), tester.reg_name(j), @@ -1610,7 +1610,7 @@ TEST(Emitter, store8_gpr64_gpr64_plus_gpr64) { } } -TEST(Emitter, store8_gpr64_gpr64_plus_gpr64_plus_s8) { +TEST(EmitterLoadsAndStores, store8_gpr64_gpr64_plus_gpr64_plus_s8) { CodeTester tester; tester.init_code_buffer(512); @@ -1659,13 +1659,13 @@ TEST(Emitter, store8_gpr64_gpr64_plus_gpr64_plus_s8) { tester.emit_return(); // prepare the memory: - s8 memory[8] = {0, 0, -3, -2, -1, 0, 0, 0}; + s8 memory[8] = {0, 0, 3, -2, 1, 0, 0, 0}; // run! - tester.execute((u64)memory, 6, 7, 0); - EXPECT_EQ(memory[2], -3); + tester.execute((u64)memory, 6, 0xffffffffffffff07, 0); + EXPECT_EQ(memory[2], 3); EXPECT_EQ(memory[3], 7); - EXPECT_EQ(memory[4], -1); + EXPECT_EQ(memory[4], 1); if (memory[3] != 7) { fmt::print("test {}, {}, {}\n", tester.reg_name(i), tester.reg_name(j), @@ -1678,7 +1678,7 @@ TEST(Emitter, store8_gpr64_gpr64_plus_gpr64_plus_s8) { } } -TEST(Emitter, store8_gpr64_gpr64_plus_gpr64_plus_s32) { +TEST(EmitterLoadsAndStores, store8_gpr64_gpr64_plus_gpr64_plus_s32) { CodeTester tester; tester.init_code_buffer(512); @@ -1727,13 +1727,13 @@ TEST(Emitter, store8_gpr64_gpr64_plus_gpr64_plus_s32) { tester.emit_return(); // prepare the memory: - s8 memory[8] = {0, 0, -3, -2, -1, 0, 0, 0}; + s8 memory[8] = {0, 0, 3, -2, 1, 0, 0, 0}; // run! - tester.execute((u64)memory, 6, 7, 0); - EXPECT_EQ(memory[2], -3); + tester.execute((u64)memory, 6, 0xffffffffffffff07, 0); + EXPECT_EQ(memory[2], 3); EXPECT_EQ(memory[3], 7); - EXPECT_EQ(memory[4], -1); + EXPECT_EQ(memory[4], 1); if (memory[3] != 7) { fmt::print("test {}, {}, {}\n", tester.reg_name(i), tester.reg_name(j), @@ -1746,7 +1746,7 @@ TEST(Emitter, store8_gpr64_gpr64_plus_gpr64_plus_s32) { } } -TEST(Emitter, store16_gpr64_gpr64_plus_gpr64) { +TEST(EmitterLoadsAndStores, store16_gpr64_gpr64_plus_gpr64) { CodeTester tester; tester.init_code_buffer(512); @@ -1790,26 +1790,21 @@ TEST(Emitter, store16_gpr64_gpr64_plus_gpr64) { tester.emit_return(); // prepare the memory: - s16 memory[8] = {0, 0, -3, -2, -1, 0, 0, 0}; + s16 memory[8] = {0, 0, 3, -2, 1, 0, 0, 0}; // run! - tester.execute((u64)memory, 6, 7, 0); - EXPECT_EQ(memory[2], -3); - EXPECT_EQ(memory[3], 7); - EXPECT_EQ(memory[4], -1); + tester.execute((u64)memory, 6, 0xffffffffffffff07, 0); + EXPECT_EQ(memory[2], 3); + EXPECT_EQ(memory[3], s16(0xff07)); + EXPECT_EQ(memory[4], 1); - if (memory[3] != 7) { - fmt::print("test {}, {}, {}\n", tester.reg_name(i), tester.reg_name(j), - tester.reg_name(k)); - printf("%s\n", tester.dump_to_hex_string().c_str()); - } iter++; } } } } -TEST(Emitter, store16_gpr64_gpr64_plus_gpr64_plus_s8) { +TEST(EmitterLoadsAndStores, store16_gpr64_gpr64_plus_gpr64_plus_s8) { CodeTester tester; tester.init_code_buffer(512); @@ -1858,26 +1853,21 @@ TEST(Emitter, store16_gpr64_gpr64_plus_gpr64_plus_s8) { tester.emit_return(); // prepare the memory: - s16 memory[8] = {0, 0, -3, -2, -1, 0, 0, 0}; + s16 memory[8] = {0, 0, 3, -2, 1, 0, 0, 0}; // run! - tester.execute((u64)memory, 6 + 3, 7, 0); - EXPECT_EQ(memory[2], -3); - EXPECT_EQ(memory[3], 7); - EXPECT_EQ(memory[4], -1); + tester.execute((u64)memory, 6 + 3, 0xffffffffffffff07, 0); + EXPECT_EQ(memory[2], 3); + EXPECT_EQ(memory[3], s16(0xff07)); + EXPECT_EQ(memory[4], 1); - if (memory[3] != 7) { - fmt::print("test {}, {}, {}\n", tester.reg_name(i), tester.reg_name(j), - tester.reg_name(k)); - printf("%s\n", tester.dump_to_hex_string().c_str()); - } iter++; } } } } -TEST(Emitter, store16_gpr64_gpr64_plus_gpr64_plus_s32) { +TEST(EmitterLoadsAndStores, store16_gpr64_gpr64_plus_gpr64_plus_s32) { CodeTester tester; tester.init_code_buffer(512); @@ -1926,19 +1916,382 @@ TEST(Emitter, store16_gpr64_gpr64_plus_gpr64_plus_s32) { tester.emit_return(); // prepare the memory: - s16 memory[8] = {0, 0, -3, -2, -1, 0, 0, 0}; + s16 memory[8] = {0, 0, 3, -2, 1, 0, 0, 0}; // run! - tester.execute((u64)memory, 6 + 3, 7, 0); - EXPECT_EQ(memory[2], -3); - EXPECT_EQ(memory[3], 7); - EXPECT_EQ(memory[4], -1); + tester.execute((u64)memory, 6 + 3, 0xffffffffffffff07, 0); + EXPECT_EQ(memory[2], 3); + EXPECT_EQ(memory[3], s16(0xff07)); + EXPECT_EQ(memory[4], 1); - if (memory[3] != 7) { - fmt::print("test {}, {}, {}\n", tester.reg_name(i), tester.reg_name(j), - tester.reg_name(k)); - printf("%s\n", tester.dump_to_hex_string().c_str()); + iter++; + } + } + } +} + +TEST(EmitterLoadsAndStores, store32_gpr64_gpr64_plus_gpr64) { + CodeTester tester; + tester.init_code_buffer(512); + + tester.clear(); + tester.emit(IGen::store32_gpr64_gpr64_plus_gpr64(RCX, RAX, R8)); + EXPECT_EQ(tester.dump_to_hex_string(), "44 89 04 08"); + + int iter = 0; + for (int i = 0; i < 16; i++) { + if (i == RSP) { + continue; + } + + for (int j = 0; j < 16; j++) { + if (j == RSP || j == i) { + continue; + } + + for (int k = 0; k < 16; k++) { + if (k == RSP || k == j || k == i) { + continue; } + + tester.clear(); + tester.emit_push_all_gprs(true); + // push args to the stack + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(2))); + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1))); + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0))); + + // pop args into appropriate register + tester.emit(IGen::pop_gpr64(i)); // i will have offset 0 + tester.emit(IGen::pop_gpr64(j)); // j will have offset 1 + tester.emit(IGen::pop_gpr64(k)); // k will have the value to store. + + // store! + tester.emit(IGen::store32_gpr64_gpr64_plus_gpr64(i, j, k)); + + // return! + tester.emit_pop_all_gprs(true); + tester.emit_return(); + + // prepare the memory: + s32 memory[8] = {0, 0, 3, -2, 1, 0, 0, 0}; + + // run! + tester.execute((u64)memory, 12, 0xffffffff12341234, 0); + EXPECT_EQ(memory[2], 3); + EXPECT_EQ(memory[3], 0x12341234); + EXPECT_EQ(memory[4], 1); + + iter++; + } + } + } +} + +TEST(EmitterLoadsAndStores, store32_gpr64_gpr64_plus_gpr64_plus_s8) { + CodeTester tester; + tester.init_code_buffer(512); + + tester.clear(); + tester.emit(IGen::store32_gpr64_gpr64_plus_gpr64_plus_s8(RAX, RCX, R8, 12)); + EXPECT_EQ(tester.dump_to_hex_string(), "44 89 44 01 0c"); + + auto instr = IGen::store32_gpr64_gpr64_plus_gpr64_plus_s8(RAX, RCX, RDX, -3); + u8 buff[256]; + instr.emit(buff); + EXPECT_EQ(*(s8*)(buff + instr.offset_of_disp()), -3); + + int iter = 0; + for (int i = 0; i < 16; i++) { + if (i == RSP) { + continue; + } + + for (int j = 0; j < 16; j++) { + if (j == RSP || j == i) { + continue; + } + + for (int k = 0; k < 16; k++) { + if (k == RSP || k == j || k == i) { + continue; + } + + tester.clear(); + tester.emit_push_all_gprs(true); + // push args to the stack + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(2))); + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1))); + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0))); + + // pop args into appropriate register + tester.emit(IGen::pop_gpr64(i)); // i will have offset 0 + tester.emit(IGen::pop_gpr64(j)); // j will have offset 1 + tester.emit(IGen::pop_gpr64(k)); // k will have the value to store. + + // store + tester.emit(IGen::store32_gpr64_gpr64_plus_gpr64_plus_s8(i, j, k, -3)); + + // return! + tester.emit_pop_all_gprs(true); + tester.emit_return(); + + // prepare the memory: + s32 memory[8] = {0, 0, 3, -2, 1, 0, 0, 0}; + + // run! + tester.execute((u64)memory, 12 + 3, 0xffffffffffffff07, 0); + EXPECT_EQ(memory[2], 3); + EXPECT_EQ(memory[3], s32(0xffffff07)); + EXPECT_EQ(memory[4], 1); + + iter++; + } + } + } +} + +TEST(EmitterLoadsAndStores, store32_gpr64_gpr64_plus_gpr64_plus_s32) { + CodeTester tester; + tester.init_code_buffer(512); + + tester.clear(); + tester.emit(IGen::store32_gpr64_gpr64_plus_gpr64_plus_s32(RAX, RCX, R8, 12)); + EXPECT_EQ(tester.dump_to_hex_string(), "44 89 84 01 0c 00 00 00"); + + auto instr = IGen::store32_gpr64_gpr64_plus_gpr64_plus_s32(RAX, RCX, RDX, -3); + u8 buff[256]; + instr.emit(buff); + EXPECT_EQ(*(s8*)(buff + instr.offset_of_disp()), -3); + + int iter = 0; + for (int i = 0; i < 16; i++) { + if (i == RSP) { + continue; + } + + for (int j = 0; j < 16; j++) { + if (j == RSP || j == i) { + continue; + } + + for (int k = 0; k < 16; k++) { + if (k == RSP || k == j || k == i) { + continue; + } + + tester.clear(); + tester.emit_push_all_gprs(true); + // push args to the stack + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(2))); + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1))); + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0))); + + // pop args into appropriate register + tester.emit(IGen::pop_gpr64(i)); // i will have offset 0 + tester.emit(IGen::pop_gpr64(j)); // j will have offset 1 + tester.emit(IGen::pop_gpr64(k)); // k will have the value to store. + + // store + tester.emit(IGen::store32_gpr64_gpr64_plus_gpr64_plus_s32(i, j, k, -3)); + + // return! + tester.emit_pop_all_gprs(true); + tester.emit_return(); + + // prepare the memory: + s32 memory[8] = {0, 0, 3, -2, 1, 0, 0, 0}; + + // run! + tester.execute((u64)memory, 12 + 3, 0xffffffffffffff07, 0); + EXPECT_EQ(memory[2], 3); + EXPECT_EQ(memory[3], s32(0xffffff07)); + EXPECT_EQ(memory[4], 1); + + iter++; + } + } + } +} + +TEST(EmitterLoadsAndStores, store64_gpr64_gpr64_plus_gpr64) { + CodeTester tester; + tester.init_code_buffer(512); + + tester.clear(); + tester.emit(IGen::store64_gpr64_gpr64_plus_gpr64(RCX, RAX, R8)); + EXPECT_EQ(tester.dump_to_hex_string(), "4c 89 04 08"); + + int iter = 0; + for (int i = 0; i < 16; i++) { + if (i == RSP) { + continue; + } + + for (int j = 0; j < 16; j++) { + if (j == RSP || j == i) { + continue; + } + + for (int k = 0; k < 16; k++) { + if (k == RSP || k == j || k == i) { + continue; + } + + tester.clear(); + tester.emit_push_all_gprs(true); + // push args to the stack + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(2))); + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1))); + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0))); + + // pop args into appropriate register + tester.emit(IGen::pop_gpr64(i)); // i will have offset 0 + tester.emit(IGen::pop_gpr64(j)); // j will have offset 1 + tester.emit(IGen::pop_gpr64(k)); // k will have the value to store. + + // store! + tester.emit(IGen::store64_gpr64_gpr64_plus_gpr64(i, j, k)); + + // return! + tester.emit_pop_all_gprs(true); + tester.emit_return(); + + // prepare the memory: + s64 memory[8] = {0, 0, 3, -2, 1, 0, 0, 0}; + + // run! + tester.execute((u64)memory, 24, 0xffffffff12341234, 0); + EXPECT_EQ(memory[2], 3); + EXPECT_EQ(memory[3], 0xffffffff12341234); + EXPECT_EQ(memory[4], 1); + + iter++; + } + } + } +} + +TEST(EmitterLoadsAndStores, store64_gpr64_gpr64_plus_gpr64_plus_s8) { + CodeTester tester; + tester.init_code_buffer(512); + + tester.clear(); + tester.emit(IGen::store64_gpr64_gpr64_plus_gpr64_plus_s8(RAX, RCX, R8, 12)); + EXPECT_EQ(tester.dump_to_hex_string(), "4c 89 44 01 0c"); + + auto instr = IGen::store64_gpr64_gpr64_plus_gpr64_plus_s8(RAX, RCX, RDX, -3); + u8 buff[256]; + instr.emit(buff); + EXPECT_EQ(*(s8*)(buff + instr.offset_of_disp()), -3); + + int iter = 0; + for (int i = 0; i < 16; i++) { + if (i == RSP) { + continue; + } + + for (int j = 0; j < 16; j++) { + if (j == RSP || j == i) { + continue; + } + + for (int k = 0; k < 16; k++) { + if (k == RSP || k == j || k == i) { + continue; + } + + tester.clear(); + tester.emit_push_all_gprs(true); + // push args to the stack + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(2))); + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1))); + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0))); + + // pop args into appropriate register + tester.emit(IGen::pop_gpr64(i)); // i will have offset 0 + tester.emit(IGen::pop_gpr64(j)); // j will have offset 1 + tester.emit(IGen::pop_gpr64(k)); // k will have the value to store. + + // store + tester.emit(IGen::store64_gpr64_gpr64_plus_gpr64_plus_s8(i, j, k, -3)); + + // return! + tester.emit_pop_all_gprs(true); + tester.emit_return(); + + // prepare the memory: + s64 memory[8] = {0, 0, 3, -2, 1, 0, 0, 0}; + + // run! + tester.execute((u64)memory, 24 + 3, 0xffffffffffffff07, 0); + EXPECT_EQ(memory[2], 3); + EXPECT_EQ(memory[3], s64(0xffffffffffffff07)); + EXPECT_EQ(memory[4], 1); + + iter++; + } + } + } +} + +TEST(EmitterLoadsAndStores, store64_gpr64_gpr64_plus_gpr64_plus_s32) { + CodeTester tester; + tester.init_code_buffer(512); + + tester.clear(); + tester.emit(IGen::store64_gpr64_gpr64_plus_gpr64_plus_s32(RAX, RCX, R8, 12)); + EXPECT_EQ(tester.dump_to_hex_string(), "4c 89 84 01 0c 00 00 00"); + + auto instr = IGen::store64_gpr64_gpr64_plus_gpr64_plus_s32(RAX, RCX, RDX, -3); + u8 buff[256]; + instr.emit(buff); + EXPECT_EQ(*(s8*)(buff + instr.offset_of_disp()), -3); + + int iter = 0; + for (int i = 0; i < 16; i++) { + if (i == RSP) { + continue; + } + + for (int j = 0; j < 16; j++) { + if (j == RSP || j == i) { + continue; + } + + for (int k = 0; k < 16; k++) { + if (k == RSP || k == j || k == i) { + continue; + } + + tester.clear(); + tester.emit_push_all_gprs(true); + // push args to the stack + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(2))); + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1))); + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0))); + + // pop args into appropriate register + tester.emit(IGen::pop_gpr64(i)); // i will have offset 0 + tester.emit(IGen::pop_gpr64(j)); // j will have offset 1 + tester.emit(IGen::pop_gpr64(k)); // k will have the value to store. + + // store + tester.emit(IGen::store64_gpr64_gpr64_plus_gpr64_plus_s32(i, j, k, -3)); + + // return! + tester.emit_pop_all_gprs(true); + tester.emit_return(); + + // prepare the memory: + s64 memory[8] = {0, 0, 3, -2, 1, 0, 0, 0}; + + // run! + tester.execute((u64)memory, 24 + 3, 0xffffffffffffff07, 0); + EXPECT_EQ(memory[2], 3); + EXPECT_EQ(memory[3], s64(0xffffffffffffff07)); + EXPECT_EQ(memory[4], 1); + iter++; } } diff --git a/test/test_emitter_xmm32.cpp b/test/test_emitter_xmm32.cpp new file mode 100644 index 0000000000..e92a352330 --- /dev/null +++ b/test/test_emitter_xmm32.cpp @@ -0,0 +1,391 @@ +#include "gtest/gtest.h" +#include "goalc/emitter/CodeTester.h" +#include "goalc/emitter/IGen.h" +#include "third-party/fmt/core.h" + +using namespace emitter; + +TEST(EmitterXmm32, load32_xmm32_gpr64_plus_gpr64) { + CodeTester tester; + tester.init_code_buffer(512); + tester.emit(IGen::load32_xmm32_gpr64_plus_gpr64(XMM3, RAX, RBX)); + EXPECT_EQ(tester.dump_to_hex_string(), "f3 0f 10 1c 03"); + + int iter = 0; + for (int i = 0; i < 16; i++) { + if (i == RSP) { + continue; + } + for (int j = 0; j < 16; j++) { + if (j == RSP || j == i) { + continue; + } + for (int k = 0; k < 16; k++) { + tester.clear(); + tester.emit_push_all_xmms(); + tester.emit_push_all_gprs(true); + // push args to the stack + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1))); + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0))); + + // fill k with junk + tester.emit(IGen::mov_gpr64_u64(i, (iter & 1) ? 0 : UINT64_MAX)); + tester.emit(IGen::movd_xmm32_gpr32(XMM0 + k, i)); + + // pop args into appropriate register + tester.emit(IGen::pop_gpr64(i)); // i will have offset 0 + tester.emit(IGen::pop_gpr64(j)); // j will have offset 1 + + // load into k + tester.emit(IGen::load32_xmm32_gpr64_plus_gpr64(XMM0 + k, i, j)); + // move to return + tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + k)); + + // return! + tester.emit_pop_all_gprs(true); + tester.emit_pop_all_xmms(); + tester.emit_return(); + + // prepare the memory: + float memory[8] = {0, 0, 1.23f, 3.45f, 5.67f, 0, 0, 0}; + + // run! + EXPECT_EQ(tester.execute_ret((u64)memory, 3 * sizeof(float), 0, 0), 3.45f); + EXPECT_EQ(tester.execute_ret((u64)memory, 2 * sizeof(float), 0, 0), 1.23f); + EXPECT_EQ(tester.execute_ret((u64)memory, 4 * sizeof(float), 0, 0), 5.67f); + EXPECT_EQ(tester.execute_ret((u64)memory, 5 * sizeof(float), 0, 0), 0); + + iter++; + } + } + } +} + +TEST(EmitterXmm32, load32_xmm32_gpr64_plus_gpr64_plus_s8) { + CodeTester tester; + tester.init_code_buffer(512); + tester.emit(IGen::load32_xmm32_gpr64_plus_gpr64_plus_s8(XMM3, RAX, RBX, -1)); + EXPECT_EQ(tester.dump_to_hex_string(), "f3 0f 10 5c 03 ff"); + + auto instr = IGen::load32_xmm32_gpr64_plus_gpr64_plus_s8(XMM3, RBX, RSI, -3); + u8 buff[256]; + instr.emit(buff); + EXPECT_EQ(s8(buff[instr.offset_of_disp()]), -3); + + int iter = 0; + for (int i = 0; i < 16; i++) { + if (i == RSP) { + continue; + } + for (int j = 0; j < 16; j++) { + if (j == RSP || j == i) { + continue; + } + for (int k = 0; k < 16; k++) { + tester.clear(); + tester.emit_push_all_xmms(); + tester.emit_push_all_gprs(true); + // push args to the stack + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1))); + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0))); + + // fill k with junk + tester.emit(IGen::mov_gpr64_u64(i, (iter & 1) ? 0 : UINT64_MAX)); + tester.emit(IGen::movd_xmm32_gpr32(XMM0 + k, i)); + + // pop args into appropriate register + tester.emit(IGen::pop_gpr64(i)); // i will have offset 0 + tester.emit(IGen::pop_gpr64(j)); // j will have offset 1 + + // load into k + tester.emit(IGen::load32_xmm32_gpr64_plus_gpr64_plus_s8(XMM0 + k, i, j, -3)); + // move to return + tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + k)); + + // return! + tester.emit_pop_all_gprs(true); + tester.emit_pop_all_xmms(); + tester.emit_return(); + + // prepare the memory: + float memory[8] = {0, 0, 1.23f, 3.45f, 5.67f, 0, 0, 0}; + + // run! + EXPECT_EQ(tester.execute_ret((u64)memory, 3 * sizeof(float) + 3, 0, 0), 3.45f); + EXPECT_EQ(tester.execute_ret((u64)memory, 2 * sizeof(float) + 3, 0, 0), 1.23f); + EXPECT_EQ(tester.execute_ret((u64)memory, 4 * sizeof(float) + 3, 0, 0), 5.67f); + EXPECT_EQ(tester.execute_ret((u64)memory, 5 * sizeof(float) + 3, 0, 0), 0); + + iter++; + } + } + } +} + +TEST(EmitterXmm32, load32_xmm32_gpr64_plus_gpr64_plus_s32) { + CodeTester tester; + tester.init_code_buffer(512); + tester.emit(IGen::load32_xmm32_gpr64_plus_gpr64_plus_s32(XMM3, RAX, RBX, -1)); + EXPECT_EQ(tester.dump_to_hex_string(), "f3 0f 10 9c 03 ff ff ff ff"); + + auto instr = IGen::load32_xmm32_gpr64_plus_gpr64_plus_s32(XMM3, RBX, RSI, -1234); + u8 buff[256]; + instr.emit(buff); + EXPECT_EQ(*(s32*)(buff + instr.offset_of_disp()), -1234); + + int iter = 0; + for (int i = 0; i < 16; i++) { + if (i == RSP) { + continue; + } + for (int j = 0; j < 16; j++) { + if (j == RSP || j == i) { + continue; + } + for (int k = 0; k < 16; k++) { + tester.clear(); + tester.emit_push_all_xmms(); + tester.emit_push_all_gprs(true); + // push args to the stack + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1))); + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0))); + + // fill k with junk + tester.emit(IGen::mov_gpr64_u64(i, (iter & 1) ? 0 : UINT64_MAX)); + tester.emit(IGen::movd_xmm32_gpr32(XMM0 + k, i)); + + // pop args into appropriate register + tester.emit(IGen::pop_gpr64(i)); // i will have offset 0 + tester.emit(IGen::pop_gpr64(j)); // j will have offset 1 + + s64 offset = (iter & 1) ? INT32_MAX : INT32_MIN; + + // load into k + tester.emit(IGen::load32_xmm32_gpr64_plus_gpr64_plus_s32(XMM0 + k, i, j, offset)); + // move to return + tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + k)); + + // return! + tester.emit_pop_all_gprs(true); + tester.emit_pop_all_xmms(); + tester.emit_return(); + + // prepare the memory: + float memory[8] = {0, 0, 1.23f, 3.45f, 5.67f, 0, 0, 0}; + + // run! + EXPECT_EQ(tester.execute_ret((u64)memory, 3 * sizeof(float) - offset, 0, 0), 3.45f); + EXPECT_EQ(tester.execute_ret((u64)memory, 2 * sizeof(float) - offset, 0, 0), 1.23f); + EXPECT_EQ(tester.execute_ret((u64)memory, 4 * sizeof(float) - offset, 0, 0), 5.67f); + EXPECT_EQ(tester.execute_ret((u64)memory, 5 * sizeof(float) - offset, 0, 0), 0); + iter++; + } + } + } +} + +namespace { +template +float as_float(T x) { + float result; + memcpy(&result, &x, sizeof(float)); + return result; +} + +u32 as_u32(float x) { + u32 result; + memcpy(&result, &x, 4); + return result; +} +} // namespace + +TEST(EmitterXmm32, store32_xmm32_gpr64_plus_gpr64) { + CodeTester tester; + tester.init_code_buffer(512); + tester.emit(IGen::store32_xmm32_gpr64_plus_gpr64(RAX, RBX, XMM7)); + EXPECT_EQ(tester.dump_to_hex_string(), "f3 0f 11 3c 03"); + + int iter = 0; + for (int i = 0; i < 16; i++) { + if (i == RSP) { + continue; + } + + for (int j = 0; j < 16; j++) { + if (j == RSP || j == i) { + continue; + } + + for (int k = 0; k < 16; k++) { + tester.clear(); + tester.emit_push_all_xmms(); + tester.emit_push_all_gprs(true); + // push args to the stack + + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1))); // addr2 + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0))); // addr1 + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(2))); // value + + // pop value into addr1 GPR + tester.emit(IGen::pop_gpr64(i)); + // move to XMM + tester.emit(IGen::movd_xmm32_gpr32(XMM0 + k, i)); + + // pop addrs + tester.emit(IGen::pop_gpr64(i)); + tester.emit(IGen::pop_gpr64(j)); + + // store + tester.emit(IGen::store32_xmm32_gpr64_plus_gpr64(i, j, XMM0 + k)); + + // return! + tester.emit_pop_all_gprs(true); + tester.emit_pop_all_xmms(); + tester.emit_return(); + + // prepare the memory: + float memory[8] = {0, 0, 1.23f, 3.45f, 5.67f, 0, 0, 0}; + + // run! + tester.execute((u64)memory, 12, as_u32(1.234f), 0); + EXPECT_EQ(memory[2], 1.23f); + EXPECT_EQ(memory[3], 1.234f); + EXPECT_EQ(memory[4], 5.67f); + + iter++; + } + } + } +} + +TEST(EmitterXmm32, store32_xmm32_gpr64_plus_gpr64_plus_s8) { + CodeTester tester; + tester.init_code_buffer(512); + tester.emit(IGen::store32_xmm32_gpr64_plus_gpr64_plus_s8(RAX, RBX, XMM3, -1)); + EXPECT_EQ(tester.dump_to_hex_string(), "f3 0f 11 5c 03 ff"); + + auto instr = IGen::store32_xmm32_gpr64_plus_gpr64_plus_s8(RBX, RSI, XMM3, -3); + u8 buff[256]; + instr.emit(buff); + EXPECT_EQ(s8(buff[instr.offset_of_disp()]), -3); + + int iter = 0; + for (int i = 0; i < 16; i++) { + if (i == RSP) { + continue; + } + for (int j = 0; j < 16; j++) { + if (j == RSP || j == i) { + continue; + } + for (int k = 0; k < 16; k++) { + tester.clear(); + tester.emit_push_all_xmms(); + tester.emit_push_all_gprs(true); + // push args to the stack + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1))); // addr2 + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0))); // addr1 + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(2))); // value + + // pop value into addr1 GPR + tester.emit(IGen::pop_gpr64(i)); + // move to XMM + tester.emit(IGen::movd_xmm32_gpr32(XMM0 + k, i)); + + // pop addrs + tester.emit(IGen::pop_gpr64(i)); + tester.emit(IGen::pop_gpr64(j)); + + s64 offset = (iter & 1) ? INT8_MAX : INT8_MIN; + + // load into k + tester.emit(IGen::store32_xmm32_gpr64_plus_gpr64_plus_s8(i, j, XMM0 + k, offset)); + + // move to return + tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + k)); + + // return! + tester.emit_pop_all_gprs(true); + tester.emit_pop_all_xmms(); + tester.emit_return(); + + // prepare the memory: + float memory[8] = {0, 0, 1.23f, 3.45f, 5.67f, 0, 0, 0}; + + // run! + tester.execute((u64)memory, 12 - offset, as_u32(1.234f), 0); + EXPECT_EQ(memory[2], 1.23f); + EXPECT_EQ(memory[3], 1.234f); + EXPECT_EQ(memory[4], 5.67f); + + iter++; + } + } + } +} + +TEST(EmitterXmm32, store32_xmm32_gpr64_plus_gpr64_plus_s32) { + CodeTester tester; + tester.init_code_buffer(512); + tester.emit(IGen::store32_xmm32_gpr64_plus_gpr64_plus_s32(RAX, RBX, XMM3, -1)); + EXPECT_EQ(tester.dump_to_hex_string(), "f3 0f 11 9c 03 ff ff ff ff"); + + auto instr = IGen::store32_xmm32_gpr64_plus_gpr64_plus_s32(RBX, RSI, XMM3, -1234); + u8 buff[256]; + instr.emit(buff); + EXPECT_EQ(*(s32*)(buff + instr.offset_of_disp()), -1234); + + int iter = 0; + for (int i = 0; i < 16; i++) { + if (i == RSP) { + continue; + } + for (int j = 0; j < 16; j++) { + if (j == RSP || j == i) { + continue; + } + for (int k = 0; k < 16; k++) { + tester.clear(); + tester.emit_push_all_xmms(); + tester.emit_push_all_gprs(true); + // push args to the stack + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1))); // addr2 + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0))); // addr1 + tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(2))); // value + + // pop value into addr1 GPR + tester.emit(IGen::pop_gpr64(i)); + // move to XMM + tester.emit(IGen::movd_xmm32_gpr32(XMM0 + k, i)); + + // pop addrs + tester.emit(IGen::pop_gpr64(i)); + tester.emit(IGen::pop_gpr64(j)); + + s64 offset = (iter & 1) ? INT32_MAX : INT32_MIN; + + // load into k + tester.emit(IGen::store32_xmm32_gpr64_plus_gpr64_plus_s32(i, j, XMM0 + k, offset)); + + // move to return + tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + k)); + + // return! + tester.emit_pop_all_gprs(true); + tester.emit_pop_all_xmms(); + tester.emit_return(); + + // prepare the memory: + float memory[8] = {0, 0, 1.23f, 3.45f, 5.67f, 0, 0, 0}; + + // run! + tester.execute((u64)memory, 12 - offset, as_u32(1.234f), 0); + EXPECT_EQ(memory[2], 1.23f); + EXPECT_EQ(memory[3], 1.234f); + EXPECT_EQ(memory[4], 5.67f); + + iter++; + } + } + } +} \ No newline at end of file