diff --git a/goalc/emitter/IGen.h b/goalc/emitter/IGen.h index 8e2719923f..c8e9404ebd 100644 --- a/goalc/emitter/IGen.h +++ b/goalc/emitter/IGen.h @@ -115,8 +115,12 @@ class IGen { return instr; } + // todo - GPR64 -> XMM64 (zext) + // todo - XMM -> GPR64 + // todo - XMM128 - XMM128 + //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - // LOADS n' STORES - reg + reg addr + // GOAL Loads //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; /*! @@ -138,6 +142,42 @@ class IGen { return instr; } + static Instruction load8s_gpr64_gpr64_plus_gpr64_plus_s8(Register dst, + Register addr1, + Register addr2, + s64 offset) { + assert(dst.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(0xf); + instr.set_op2(0xbe); + instr.set_modrm_and_rex_for_reg_plus_reg_plus_s8(dst.hw_id(), addr1.hw_id(), addr2.hw_id(), + offset, true); + return instr; + } + + static Instruction load8s_gpr64_gpr64_plus_gpr64_plus_s32(Register dst, + Register addr1, + Register addr2, + s64 offset) { + assert(dst.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(0xf); + instr.set_op2(0xbe); + instr.set_modrm_and_rex_for_reg_plus_reg_plus_s32(dst.hw_id(), addr1.hw_id(), addr2.hw_id(), + offset, true); + return instr; + } + /*! * movzx dst, BYTE PTR [addr1 + addr2] * addr1 and addr2 have to be different registers. @@ -157,6 +197,42 @@ class IGen { return instr; } + static Instruction load8u_gpr64_gpr64_plus_gpr64_plus_s8(Register dst, + Register addr1, + Register addr2, + s64 offset) { + assert(dst.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(0xf); + instr.set_op2(0xb6); + instr.set_modrm_and_rex_for_reg_plus_reg_plus_s8(dst.hw_id(), addr1.hw_id(), addr2.hw_id(), + offset, true); + return instr; + } + + static Instruction load8u_gpr64_gpr64_plus_gpr64_plus_s32(Register dst, + Register addr1, + Register addr2, + s64 offset) { + assert(dst.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(0xf); + instr.set_op2(0xb6); + instr.set_modrm_and_rex_for_reg_plus_reg_plus_s32(dst.hw_id(), addr1.hw_id(), addr2.hw_id(), + offset, true); + return instr; + } + /*! * movsx dst, WORD PTR [addr1 + addr2] * addr1 and addr2 have to be different registers. @@ -176,6 +252,41 @@ class IGen { return instr; } + static Instruction load16s_gpr64_gpr64_plus_gpr64_plus_s8(Register dst, + Register addr1, + Register addr2, + s64 offset) { + assert(dst.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(0xf); + instr.set_op2(0xbf); + instr.set_modrm_and_rex_for_reg_plus_reg_plus_s8(dst.hw_id(), addr1.hw_id(), addr2.hw_id(), + offset, true); + return instr; + } + + static Instruction load16s_gpr64_gpr64_plus_gpr64_plus_s32(Register dst, + Register addr1, + Register addr2, + s64 offset) { + assert(dst.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(0xf); + instr.set_op2(0xbf); + instr.set_modrm_and_rex_for_reg_plus_reg_plus_s32(dst.hw_id(), addr1.hw_id(), addr2.hw_id(), + offset, true); + return instr; + } /*! * movzx dst, WORD PTR [addr1 + addr2] @@ -196,6 +307,42 @@ class IGen { return instr; } + static Instruction load16u_gpr64_gpr64_plus_gpr64_plus_s8(Register dst, + Register addr1, + Register addr2, + s64 offset) { + assert(dst.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(0xf); + instr.set_op2(0xb7); + instr.set_modrm_and_rex_for_reg_plus_reg_plus_s8(dst.hw_id(), addr1.hw_id(), addr2.hw_id(), + offset, true); + return instr; + } + + static Instruction load16u_gpr64_gpr64_plus_gpr64_plus_s32(Register dst, + Register addr1, + Register addr2, + s64 offset) { + assert(dst.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(0xf); + instr.set_op2(0xb7); + instr.set_modrm_and_rex_for_reg_plus_reg_plus_s32(dst.hw_id(), addr1.hw_id(), addr2.hw_id(), + offset, true); + return instr; + } + /*! * movsxd dst, DWORD PTR [addr1 + addr2] * addr1 and addr2 have to be different registers. @@ -213,6 +360,40 @@ class IGen { return instr; } + static Instruction load32s_gpr64_gpr64_plus_gpr64_plus_s8(Register dst, + Register addr1, + Register addr2, + s64 offset) { + assert(dst.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(0x63); + instr.set_modrm_and_rex_for_reg_plus_reg_plus_s8(dst.hw_id(), addr1.hw_id(), addr2.hw_id(), + offset, true); + return instr; + } + + static Instruction load32s_gpr64_gpr64_plus_gpr64_plus_s32(Register dst, + Register addr1, + Register addr2, + s64 offset) { + assert(dst.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(0x63); + instr.set_modrm_and_rex_for_reg_plus_reg_plus_s32(dst.hw_id(), addr1.hw_id(), addr2.hw_id(), + offset, true); + return instr; + } + /*! * movzxd dst, DWORD PTR [addr1 + addr2] * addr1 and addr2 have to be different registers. @@ -230,6 +411,40 @@ class IGen { return instr; } + static Instruction load32u_gpr64_gpr64_plus_gpr64_plus_s8(Register dst, + Register addr1, + Register addr2, + s64 offset) { + assert(dst.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(0x8b); + instr.set_modrm_and_rex_for_reg_plus_reg_plus_s8(dst.hw_id(), addr1.hw_id(), addr2.hw_id(), + offset, false); + return instr; + } + + static Instruction load32u_gpr64_gpr64_plus_gpr64_plus_s32(Register dst, + Register addr1, + Register addr2, + s64 offset) { + assert(dst.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(0x8b); + instr.set_modrm_and_rex_for_reg_plus_reg_plus_s32(dst.hw_id(), addr1.hw_id(), addr2.hw_id(), + offset, false); + return instr; + } + /*! * mov dst, QWORD PTR [addr1 + addr2] * addr1 and addr2 have to be different registers. @@ -247,8 +462,125 @@ class IGen { return instr; } + static Instruction load64_gpr64_gpr64_plus_gpr64_plus_s8(Register dst, + Register addr1, + Register addr2, + s64 offset) { + assert(dst.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(0x8b); + instr.set_modrm_and_rex_for_reg_plus_reg_plus_s8(dst.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, + s64 offset) { + assert(dst.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(0x8b); + instr.set_modrm_and_rex_for_reg_plus_reg_plus_s32(dst.hw_id(), addr1.hw_id(), addr2.hw_id(), + offset, true); + return instr; + } + + static Instruction load_goal_gpr(Register dst, + Register addr, + Register off, + int offset, + int size, + bool sign_extend) { + switch (size) { + case 1: + if (offset == 0) { + if (sign_extend) { + return load8s_gpr64_gpr64_plus_gpr64(dst, addr, off); + } else { + return load8u_gpr64_gpr64_plus_gpr64(dst, addr, off); + } + } else if (offset >= INT8_MIN && offset <= INT8_MAX) { + if (sign_extend) { + return load8s_gpr64_gpr64_plus_gpr64_plus_s8(dst, addr, off, offset); + } else { + return load8u_gpr64_gpr64_plus_gpr64_plus_s8(dst, addr, off, offset); + } + } else if (offset >= INT32_MIN && offset <= INT32_MAX) { + if (sign_extend) { + return load8s_gpr64_gpr64_plus_gpr64_plus_s32(dst, addr, off, offset); + } else { + return load8u_gpr64_gpr64_plus_gpr64_plus_s32(dst, addr, off, offset); + } + } else { + assert(false); + } + case 2: + if (offset == 0) { + if (sign_extend) { + return load16s_gpr64_gpr64_plus_gpr64(dst, addr, off); + } else { + return load16u_gpr64_gpr64_plus_gpr64(dst, addr, off); + } + } else if (offset >= INT8_MIN && offset <= INT8_MAX) { + if (sign_extend) { + return load16s_gpr64_gpr64_plus_gpr64_plus_s8(dst, addr, off, offset); + } else { + return load16u_gpr64_gpr64_plus_gpr64_plus_s8(dst, addr, off, offset); + } + } else if (offset >= INT32_MIN && offset <= INT32_MAX) { + if (sign_extend) { + return load16s_gpr64_gpr64_plus_gpr64_plus_s32(dst, addr, off, offset); + } else { + return load16u_gpr64_gpr64_plus_gpr64_plus_s32(dst, addr, off, offset); + } + } else { + assert(false); + } + case 4: + if (offset == 0) { + if (sign_extend) { + return load32s_gpr64_gpr64_plus_gpr64(dst, addr, off); + } else { + return load32u_gpr64_gpr64_plus_gpr64(dst, addr, off); + } + } else if (offset >= INT8_MIN && offset <= INT8_MAX) { + if (sign_extend) { + return load32s_gpr64_gpr64_plus_gpr64_plus_s8(dst, addr, off, offset); + } else { + return load32u_gpr64_gpr64_plus_gpr64_plus_s8(dst, addr, off, offset); + } + } else if (offset >= INT32_MIN && offset <= INT32_MAX) { + if (sign_extend) { + return load32s_gpr64_gpr64_plus_gpr64_plus_s32(dst, addr, off, offset); + } else { + return load32u_gpr64_gpr64_plus_gpr64_plus_s32(dst, addr, off, offset); + } + } else { + assert(false); + } + case 8: + default: + assert(false); + } + } + + //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + // LOADS n' STORES - XMM128 + //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + /*! - * Store a 128-bit xmm into a register, no offset + * Store a 128-bit xmm into an address stored in a register, no offset */ static Instruction store128_gpr64_xmm128(Register gpr_addr, Register xmm_value) { assert(gpr_addr.is_gpr()); diff --git a/goalc/emitter/Instruction.h b/goalc/emitter/Instruction.h index 4383bca475..e93b1fdbbd 100644 --- a/goalc/emitter/Instruction.h +++ b/goalc/emitter/Instruction.h @@ -169,6 +169,114 @@ struct Instruction { } } + void set_modrm_and_rex_for_reg_plus_reg_plus_s8(uint8_t reg, uint8_t addr1, uint8_t addr2, s8 offset, bool rex_w) { + bool rex_b = false, rex_r = false, rex_x = false; + bool addr1_ext = false; + bool addr2_ext = false; + + if (addr1 >= 8) { + addr1 -= 8; + addr1_ext = true; + } + + if (addr2 >= 8) { + addr2 -= 8; + addr2_ext = true; + } + + if (reg >= 8) { + reg -= 8; + rex_r = true; + } + + ModRM modrm; + modrm.mod = 1; // no disp + modrm.rm = 4; // sib! + modrm.reg_op = reg; + + SIB sib; + sib.scale = 0; + + Imm imm2(1, offset); + + // default addr1 in index + if(addr1 == 4) { + sib.index = addr2; + sib.base = addr1; + rex_x = addr2_ext; + rex_b = addr1_ext; + } else { + // addr1 in index + sib.index = addr1; + sib.base = addr2; + rex_x = addr1_ext; + rex_b = addr2_ext; + } + assert(sib.index != 4); + + if(rex_b || rex_w || rex_r || rex_x) { + set(REX(rex_w, rex_r, rex_x, rex_b)); + } + + set(modrm); + set(sib); + set_disp(imm2); + } + + void set_modrm_and_rex_for_reg_plus_reg_plus_s32(uint8_t reg, uint8_t addr1, uint8_t addr2, s8 offset, bool rex_w) { + bool rex_b = false, rex_r = false, rex_x = false; + bool addr1_ext = false; + bool addr2_ext = false; + + if (addr1 >= 8) { + addr1 -= 8; + addr1_ext = true; + } + + if (addr2 >= 8) { + addr2 -= 8; + addr2_ext = true; + } + + if (reg >= 8) { + reg -= 8; + rex_r = true; + } + + ModRM modrm; + modrm.mod = 2; // no disp + modrm.rm = 4; // sib! + modrm.reg_op = reg; + + SIB sib; + sib.scale = 0; + + Imm imm2(4, offset); + + // default addr1 in index + if(addr1 == 4) { + sib.index = addr2; + sib.base = addr1; + rex_x = addr2_ext; + rex_b = addr1_ext; + } else { + // addr1 in index + sib.index = addr1; + sib.base = addr2; + rex_x = addr1_ext; + rex_b = addr2_ext; + } + assert(sib.index != 4); + + if(rex_b || rex_w || rex_r || rex_x) { + set(REX(rex_w, rex_r, rex_x, rex_b)); + } + + set(modrm); + set(sib); + set_disp(imm2); + } + void set_modrm_and_rex_for_reg_plus_reg_addr(uint8_t reg, uint8_t addr1, uint8_t addr2, bool rex_w = false, bool rex_always = false) { bool rex_b = false, rex_r = false, rex_x = false; bool addr1_ext = false; @@ -203,7 +311,7 @@ struct Instruction { rex_x = addr1_ext; rex_b = addr2_ext; modrm.mod = 1; - set(Imm(1, 0)); + set_disp(Imm(1, 0)); } else { // default addr1 in index @@ -267,7 +375,7 @@ struct Instruction { if (rm == 5) { modrm.mod = 1; // 1 byte imm - set(Imm(1, 0)); + set_disp(Imm(1, 0)); } set(modrm); diff --git a/test/test_emitter_fast.cpp b/test/test_emitter_fast.cpp index a5e70ff80c..f336458c1f 100644 --- a/test/test_emitter_fast.cpp +++ b/test/test_emitter_fast.cpp @@ -167,6 +167,144 @@ TEST(Emitter, load8s_gpr64_goal_ptr_gpr64) { } } +TEST(Emitter, load8s_gpr64_gpr64_gpr64_s8) { + CodeTester tester; + tester.init_code_buffer(512); + + tester.clear(); + tester.emit(IGen::load8s_gpr64_gpr64_plus_gpr64_plus_s8(RAX, RBX, RSI, -3)); + + EXPECT_EQ(tester.dump_to_hex_string(), "48 0f be 44 1e fd"); + + auto instr = IGen::load8s_gpr64_gpr64_plus_gpr64_plus_s8(RAX, 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++) { + if (k == RSP) { + 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(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 + + // fill k with junk + if(k != i && k != j) { + tester.emit(IGen::mov_gpr64_u64(k, (iter&1)?0:UINT64_MAX)); + } + + // load into k + tester.emit(IGen::load8s_gpr64_gpr64_plus_gpr64_plus_s8(k, i, j, -3)); + + // move k to return register + tester.emit(IGen::mov_gpr64_gpr64(RAX, k)); + + // return! + tester.emit_pop_all_gprs(true); + tester.emit_return(); + + // prepare the memory: + u8 memory[8] = {0, 0, 0xfd, 0xfe, 0xff, 0, 0, 0}; + + // run! + EXPECT_EQ(s64(tester.execute((u64)memory, 3 + 3, 0, 0)), -2); + EXPECT_EQ(s64(tester.execute((u64)memory, 2 + 3, 0, 0)), -3); + EXPECT_EQ(s64(tester.execute((u64)memory, 4 + 3, 0, 0)), -1); + EXPECT_EQ(s64(tester.execute((u64)memory, 5 + 3, 0, 0)), 0); + iter++; + } + } + } +} + +TEST(Emitter, load8s_gpr64_gpr64_gpr64_s32) { + CodeTester tester; + tester.init_code_buffer(512); + + tester.clear(); + tester.emit(IGen::load8s_gpr64_gpr64_plus_gpr64_plus_s32(RAX, RBX, RSI, -3)); + + EXPECT_EQ(tester.dump_to_hex_string(), "48 0f be 84 1e fd ff ff ff"); + + auto instr = IGen::load8s_gpr64_gpr64_plus_gpr64_plus_s32(RAX, RBX, RSI, -3); + u8 buff[256]; + instr.emit(buff); + EXPECT_EQ(*(s32*)(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) { + 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(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 + + // fill k with junk + if(k != i && k != j) { + tester.emit(IGen::mov_gpr64_u64(k, (iter&1)?0:UINT64_MAX)); + } + + // load into k + tester.emit(IGen::load8s_gpr64_gpr64_plus_gpr64_plus_s32(k, i, j, -3)); + + // move k to return register + tester.emit(IGen::mov_gpr64_gpr64(RAX, k)); + + // return! + tester.emit_pop_all_gprs(true); + tester.emit_return(); + + // prepare the memory: + u8 memory[8] = {0, 0, 0xfd, 0xfe, 0xff, 0, 0, 0}; + + // run! + EXPECT_EQ(s64(tester.execute((u64)memory, 3 + 3, 0, 0)), -2); + EXPECT_EQ(s64(tester.execute((u64)memory, 2 + 3, 0, 0)), -3); + EXPECT_EQ(s64(tester.execute((u64)memory, 4 + 3, 0, 0)), -1); + EXPECT_EQ(s64(tester.execute((u64)memory, 5 + 3, 0, 0)), 0); + iter++; + } + } + } +} + TEST(Emitter, load8u_gpr64_goal_ptr_gpr64) { CodeTester tester; tester.init_code_buffer(512); @@ -242,6 +380,144 @@ TEST(Emitter, load8u_gpr64_goal_ptr_gpr64) { } } +TEST(Emitter, load8u_gpr64_gpr64_gpr64_s8) { + CodeTester tester; + tester.init_code_buffer(512); + + tester.clear(); + tester.emit(IGen::load8u_gpr64_gpr64_plus_gpr64_plus_s8(RAX, RBX, RSI, -3)); + + EXPECT_EQ(tester.dump_to_hex_string(), "48 0f b6 44 1e fd"); + + auto instr = IGen::load8u_gpr64_gpr64_plus_gpr64_plus_s8(RAX, 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++) { + if (k == RSP) { + 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(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 + + // fill k with junk + if(k != i && k != j) { + tester.emit(IGen::mov_gpr64_u64(k, (iter&1)?0:UINT64_MAX)); + } + + // load into k + tester.emit(IGen::load8u_gpr64_gpr64_plus_gpr64_plus_s8(k, i, j, -3)); + + // move k to return register + tester.emit(IGen::mov_gpr64_gpr64(RAX, k)); + + // return! + tester.emit_pop_all_gprs(true); + tester.emit_return(); + + // prepare the memory: + u8 memory[8] = {0, 0, 0xfd, 0xfe, 0xff, 0, 0, 0}; + + // run! + EXPECT_EQ(s64(tester.execute((u64)memory, 3 + 3, 0, 0)), 0xfe); + EXPECT_EQ(s64(tester.execute((u64)memory, 2 + 3, 0, 0)), 0xfd); + EXPECT_EQ(s64(tester.execute((u64)memory, 4 + 3, 0, 0)), 0xff); + EXPECT_EQ(s64(tester.execute((u64)memory, 5 + 3, 0, 0)), 0); + iter++; + } + } + } +} + +TEST(Emitter, load8u_gpr64_gpr64_gpr64_s32) { + CodeTester tester; + tester.init_code_buffer(512); + + tester.clear(); + tester.emit(IGen::load8u_gpr64_gpr64_plus_gpr64_plus_s32(RAX, RBX, RSI, -3)); + + EXPECT_EQ(tester.dump_to_hex_string(), "48 0f b6 84 1e fd ff ff ff"); + + auto instr = IGen::load8u_gpr64_gpr64_plus_gpr64_plus_s32(RAX, RBX, RSI, -3); + u8 buff[256]; + instr.emit(buff); + EXPECT_EQ(*(s32*)(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) { + 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(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 + + // fill k with junk + if(k != i && k != j) { + tester.emit(IGen::mov_gpr64_u64(k, (iter&1)?0:UINT64_MAX)); + } + + // load into k + tester.emit(IGen::load8u_gpr64_gpr64_plus_gpr64_plus_s32(k, i, j, -3)); + + // move k to return register + tester.emit(IGen::mov_gpr64_gpr64(RAX, k)); + + // return! + tester.emit_pop_all_gprs(true); + tester.emit_return(); + + // prepare the memory: + u8 memory[8] = {0, 0, 0xfd, 0xfe, 0xff, 0, 0, 0}; + + // run! + EXPECT_EQ(s64(tester.execute((u64)memory, 3 + 3, 0, 0)), 0xfe); + EXPECT_EQ(s64(tester.execute((u64)memory, 2 + 3, 0, 0)), 0xfd); + EXPECT_EQ(s64(tester.execute((u64)memory, 4 + 3, 0, 0)), 0xff); + EXPECT_EQ(s64(tester.execute((u64)memory, 5 + 3, 0, 0)), 0); + iter++; + } + } + } +} + TEST(Emitter, load16s_gpr64_goal_ptr_gpr64) { CodeTester tester; tester.init_code_buffer(512); @@ -317,6 +593,144 @@ TEST(Emitter, load16s_gpr64_goal_ptr_gpr64) { } } +TEST(Emitter, load16s_gpr64_gpr64_plus_gpr64_plus_s8) { + CodeTester tester; + tester.init_code_buffer(512); + + tester.clear(); + tester.emit(IGen::load16s_gpr64_gpr64_plus_gpr64_plus_s8(RAX, RBX, RSI, -3)); + + EXPECT_EQ(tester.dump_to_hex_string(), "48 0f bf 44 1e fd"); + + auto instr = IGen::load16s_gpr64_gpr64_plus_gpr64_plus_s8(RAX, 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++) { + if (k == RSP) { + 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(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 + + // fill k with junk + if(k != i && k != j) { + tester.emit(IGen::mov_gpr64_u64(k, (iter&1)?0:UINT64_MAX)); + } + + // load into k + tester.emit(IGen::load16s_gpr64_gpr64_plus_gpr64_plus_s8(k, i, j, -3)); + + // move k to return register + tester.emit(IGen::mov_gpr64_gpr64(RAX, k)); + + // return! + tester.emit_pop_all_gprs(true); + tester.emit_return(); + + // prepare the memory: + u16 memory[8] = {0, 0, 0xfffd, 0xfffe, 0xffff, 0, 0, 0}; + + // run! + EXPECT_EQ(s64(tester.execute((u64)memory, 6 + 3, 0, 0)), -2); + EXPECT_EQ(s64(tester.execute((u64)memory, 4 + 3, 0, 0)), -3); + EXPECT_EQ(s64(tester.execute((u64)memory, 8 + 3, 0, 0)), -1); + EXPECT_EQ(s64(tester.execute((u64)memory, 10 + 3, 0, 0)), 0); + iter++; + } + } + } +} + +TEST(Emitter, load16s_gpr64_gpr64_plus_gpr64_plus_s32) { + CodeTester tester; + tester.init_code_buffer(512); + + tester.clear(); + tester.emit(IGen::load16s_gpr64_gpr64_plus_gpr64_plus_s32(RAX, RBX, RSI, -3)); + + EXPECT_EQ(tester.dump_to_hex_string(), "48 0f bf 84 1e fd ff ff ff"); + + auto instr = IGen::load16s_gpr64_gpr64_plus_gpr64_plus_s32(RAX, RBX, RSI, -3); + u8 buff[256]; + instr.emit(buff); + EXPECT_EQ(*(s32*)(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) { + 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(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 + + // fill k with junk + if(k != i && k != j) { + tester.emit(IGen::mov_gpr64_u64(k, (iter&1)?0:UINT64_MAX)); + } + + // load into k + tester.emit(IGen::load16s_gpr64_gpr64_plus_gpr64_plus_s32(k, i, j, -3)); + + // move k to return register + tester.emit(IGen::mov_gpr64_gpr64(RAX, k)); + + // return! + tester.emit_pop_all_gprs(true); + tester.emit_return(); + + // prepare the memory: + u16 memory[8] = {0, 0, 0xfffd, 0xfffe, 0xffff, 0, 0, 0}; + + // run! + EXPECT_EQ(s64(tester.execute((u64)memory, 6 + 3, 0, 0)), -2); + EXPECT_EQ(s64(tester.execute((u64)memory, 4 + 3, 0, 0)), -3); + EXPECT_EQ(s64(tester.execute((u64)memory, 8 + 3, 0, 0)), -1); + EXPECT_EQ(s64(tester.execute((u64)memory, 10 + 3, 0, 0)), 0); + iter++; + } + } + } +} + TEST(Emitter, load16u_gpr64_goal_ptr_gpr64) { CodeTester tester; tester.init_code_buffer(512); @@ -392,6 +806,144 @@ TEST(Emitter, load16u_gpr64_goal_ptr_gpr64) { } } +TEST(Emitter, load16u_gpr64_gpr64_plus_gpr64_plus_s8) { + CodeTester tester; + tester.init_code_buffer(512); + + tester.clear(); + tester.emit(IGen::load16u_gpr64_gpr64_plus_gpr64_plus_s8(RAX, RBX, RSI, -3)); + + EXPECT_EQ(tester.dump_to_hex_string(), "48 0f b7 44 1e fd"); + + auto instr = IGen::load16u_gpr64_gpr64_plus_gpr64_plus_s8(RAX, 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++) { + if (k == RSP) { + 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(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 + + // fill k with junk + if(k != i && k != j) { + tester.emit(IGen::mov_gpr64_u64(k, (iter&1)?0:UINT64_MAX)); + } + + // load into k + tester.emit(IGen::load16u_gpr64_gpr64_plus_gpr64_plus_s8(k, i, j, -3)); + + // move k to return register + tester.emit(IGen::mov_gpr64_gpr64(RAX, k)); + + // return! + tester.emit_pop_all_gprs(true); + tester.emit_return(); + + // prepare the memory: + u16 memory[8] = {0, 0, 0xfffd, 0xfffe, 0xffff, 0, 0, 0}; + + // run! + EXPECT_EQ(s64(tester.execute((u64)memory, 6 + 3, 0, 0)), 0xfffe); + EXPECT_EQ(s64(tester.execute((u64)memory, 4 + 3, 0, 0)), 0xfffd); + EXPECT_EQ(s64(tester.execute((u64)memory, 8 + 3, 0, 0)), 0xffff); + EXPECT_EQ(s64(tester.execute((u64)memory, 10 + 3, 0, 0)), 0); + iter++; + } + } + } +} + +TEST(Emitter, load16u_gpr64_gpr64_plus_gpr64_plus_s32) { + CodeTester tester; + tester.init_code_buffer(512); + + tester.clear(); + tester.emit(IGen::load16u_gpr64_gpr64_plus_gpr64_plus_s32(RAX, RBX, RSI, -3)); + + EXPECT_EQ(tester.dump_to_hex_string(), "48 0f b7 84 1e fd ff ff ff"); + + auto instr = IGen::load16u_gpr64_gpr64_plus_gpr64_plus_s32(RAX, RBX, RSI, -3); + u8 buff[256]; + instr.emit(buff); + EXPECT_EQ(*(s32*)(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) { + 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(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 + + // fill k with junk + if(k != i && k != j) { + tester.emit(IGen::mov_gpr64_u64(k, (iter&1)?0:UINT64_MAX)); + } + + // load into k + tester.emit(IGen::load16u_gpr64_gpr64_plus_gpr64_plus_s32(k, i, j, -3)); + + // move k to return register + tester.emit(IGen::mov_gpr64_gpr64(RAX, k)); + + // return! + tester.emit_pop_all_gprs(true); + tester.emit_return(); + + // prepare the memory: + u16 memory[8] = {0, 0, 0xfffd, 0xfffe, 0xffff, 0, 0, 0}; + + // run! + EXPECT_EQ(s64(tester.execute((u64)memory, 6 + 3, 0, 0)), 0xfffe); + EXPECT_EQ(s64(tester.execute((u64)memory, 4 + 3, 0, 0)), 0xfffd); + EXPECT_EQ(s64(tester.execute((u64)memory, 8 + 3, 0, 0)), 0xffff); + EXPECT_EQ(s64(tester.execute((u64)memory, 10 + 3, 0, 0)), 0); + iter++; + } + } + } +} + TEST(Emitter, load32s_gpr64_goal_ptr_gpr64) { CodeTester tester; tester.init_code_buffer(512); @@ -456,6 +1008,144 @@ TEST(Emitter, load32s_gpr64_goal_ptr_gpr64) { } } +TEST(Emitter, load32s_gpr64_gpr64_plus_gpr64_plus_s8) { + CodeTester tester; + tester.init_code_buffer(512); + + tester.clear(); + tester.emit(IGen::load32s_gpr64_gpr64_plus_gpr64_plus_s8(RAX, RBX, RSI, -3)); + + EXPECT_EQ(tester.dump_to_hex_string(), "48 63 44 1e fd"); + + auto instr = IGen::load32s_gpr64_gpr64_plus_gpr64_plus_s8(RAX, 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++) { + if (k == RSP) { + 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(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 + + // fill k with junk + if(k != i && k != j) { + tester.emit(IGen::mov_gpr64_u64(k, (iter&1)?0:UINT64_MAX)); + } + + // load into k + tester.emit(IGen::load32s_gpr64_gpr64_plus_gpr64_plus_s8(k, i, j, -3)); + + // move k to return register + tester.emit(IGen::mov_gpr64_gpr64(RAX, k)); + + // return! + tester.emit_pop_all_gprs(true); + tester.emit_return(); + + // prepare the memory: + u32 memory[8] = {0, 0, 0xfffffffd, 0xfffffffe, 0xffffffff, 0, 0, 0}; + + // run! + EXPECT_EQ(s64(tester.execute((u64)memory, 12 + 3, 0, 0)), -2); + EXPECT_EQ(s64(tester.execute((u64)memory, 8 + 3, 0, 0)), -3); + EXPECT_EQ(s64(tester.execute((u64)memory, 16 + 3, 0, 0)), -1); + EXPECT_EQ(s64(tester.execute((u64)memory, 20 + 3, 0, 0)), 0); + iter++; + } + } + } +} + +TEST(Emitter, load32s_gpr64_gpr64_plus_gpr64_plus_s32) { + CodeTester tester; + tester.init_code_buffer(512); + + tester.clear(); + tester.emit(IGen::load32s_gpr64_gpr64_plus_gpr64_plus_s32(RAX, RBX, RSI, -3)); + + EXPECT_EQ(tester.dump_to_hex_string(), "48 63 84 1e fd ff ff ff"); + + auto instr = IGen::load32s_gpr64_gpr64_plus_gpr64_plus_s32(RAX, RBX, RSI, -3); + u8 buff[256]; + instr.emit(buff); + EXPECT_EQ(*(s32*)(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) { + 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(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 + + // fill k with junk + if(k != i && k != j) { + tester.emit(IGen::mov_gpr64_u64(k, (iter&1)?0:UINT64_MAX)); + } + + // load into k + tester.emit(IGen::load32s_gpr64_gpr64_plus_gpr64_plus_s32(k, i, j, -3)); + + // move k to return register + tester.emit(IGen::mov_gpr64_gpr64(RAX, k)); + + // return! + tester.emit_pop_all_gprs(true); + tester.emit_return(); + + // prepare the memory: + u32 memory[8] = {0, 0, 0xfffffffd, 0xfffffffe, 0xffffffff, 0, 0, 0}; + + // run! + EXPECT_EQ(s64(tester.execute((u64)memory, 12 + 3, 0, 0)), -2); + EXPECT_EQ(s64(tester.execute((u64)memory, 8 + 3, 0, 0)), -3); + EXPECT_EQ(s64(tester.execute((u64)memory, 16 + 3, 0, 0)), -1); + EXPECT_EQ(s64(tester.execute((u64)memory, 20 + 3, 0, 0)), 0); + iter++; + } + } + } +} + TEST(Emitter, load32u_gpr64_goal_ptr_gpr64) { CodeTester tester; tester.init_code_buffer(512); @@ -520,6 +1210,144 @@ TEST(Emitter, load32u_gpr64_goal_ptr_gpr64) { } } +TEST(Emitter, load32u_gpr64_gpr64_plus_gpr64_plus_s8) { + CodeTester tester; + tester.init_code_buffer(512); + + tester.clear(); + tester.emit(IGen::load32u_gpr64_gpr64_plus_gpr64_plus_s8(RAX, RBX, RSI, -3)); + + EXPECT_EQ(tester.dump_to_hex_string(), "8b 44 1e fd"); + + auto instr = IGen::load32u_gpr64_gpr64_plus_gpr64_plus_s8(RAX, 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++) { + if (k == RSP) { + 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(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 + + // fill k with junk + if(k != i && k != j) { + tester.emit(IGen::mov_gpr64_u64(k, (iter&1)?0:UINT64_MAX)); + } + + // load into k + tester.emit(IGen::load32u_gpr64_gpr64_plus_gpr64_plus_s8(k, i, j, -3)); + + // move k to return register + tester.emit(IGen::mov_gpr64_gpr64(RAX, 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! + EXPECT_EQ(s64(tester.execute((u64)memory, 12 + 3, 0, 0)), 0xfffffffe); + EXPECT_EQ(s64(tester.execute((u64)memory, 8 + 3, 0, 0)), 0xfffffffd); + EXPECT_EQ(s64(tester.execute((u64)memory, 16 + 3, 0, 0)), 0xffffffff); + EXPECT_EQ(s64(tester.execute((u64)memory, 20 + 3, 0, 0)), 0); + iter++; + } + } + } +} + +TEST(Emitter, load32u_gpr64_gpr64_plus_gpr64_plus_s32) { + CodeTester tester; + tester.init_code_buffer(512); + + tester.clear(); + tester.emit(IGen::load32u_gpr64_gpr64_plus_gpr64_plus_s32(RAX, RBX, RSI, -3)); + + EXPECT_EQ(tester.dump_to_hex_string(), "8b 84 1e fd ff ff ff"); + + auto instr = IGen::load32u_gpr64_gpr64_plus_gpr64_plus_s32(RAX, RBX, RSI, -3); + u8 buff[256]; + instr.emit(buff); + EXPECT_EQ(*(s32*)(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) { + 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(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 + + // fill k with junk + if(k != i && k != j) { + tester.emit(IGen::mov_gpr64_u64(k, (iter&1)?0:UINT64_MAX)); + } + + // load into k + tester.emit(IGen::load32u_gpr64_gpr64_plus_gpr64_plus_s32(k, i, j, -3)); + + // move k to return register + tester.emit(IGen::mov_gpr64_gpr64(RAX, k)); + + // return! + tester.emit_pop_all_gprs(true); + tester.emit_return(); + + // prepare the memory: + u32 memory[8] = {0, 0, 0xfffffffd, 0xfffffffe, 0xffffffff, 0, 0, 0}; + + // run! + EXPECT_EQ(s64(tester.execute((u64)memory, 12 + 3, 0, 0)), 0xfffffffe); + EXPECT_EQ(s64(tester.execute((u64)memory, 8 + 3, 0, 0)), 0xfffffffd); + EXPECT_EQ(s64(tester.execute((u64)memory, 16 + 3, 0, 0)), 0xffffffff); + EXPECT_EQ(s64(tester.execute((u64)memory, 20 + 3, 0, 0)), 0); + iter++; + } + } + } +} + TEST(Emitter, load64_gpr64_goal_ptr_gpr64) { CodeTester tester; tester.init_code_buffer(512); @@ -583,3 +1411,142 @@ TEST(Emitter, load64_gpr64_goal_ptr_gpr64) { } } } + + +TEST(Emitter, load64_gpr64_gpr64_plus_gpr64_plus_s8) { + CodeTester tester; + tester.init_code_buffer(512); + + tester.clear(); + tester.emit(IGen::load64_gpr64_gpr64_plus_gpr64_plus_s8(RAX, RBX, RSI, -3)); + + EXPECT_EQ(tester.dump_to_hex_string(), "48 8b 44 1e fd"); + + auto instr = IGen::load64_gpr64_gpr64_plus_gpr64_plus_s8(RAX, 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++) { + if (k == RSP) { + 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(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 + + // fill k with junk + if(k != i && k != j) { + tester.emit(IGen::mov_gpr64_u64(k, (iter&1)?0:UINT64_MAX)); + } + + // load into k + tester.emit(IGen::load64_gpr64_gpr64_plus_gpr64_plus_s8(k, i, j, -3)); + + // move k to return register + tester.emit(IGen::mov_gpr64_gpr64(RAX, 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! + EXPECT_EQ(s64(tester.execute((u64)memory, 24 + 3, 0, 0)), -2); + EXPECT_EQ(s64(tester.execute((u64)memory, 16 + 3, 0, 0)), -3); + EXPECT_EQ(s64(tester.execute((u64)memory, 32 + 3, 0, 0)), -1); + EXPECT_EQ(s64(tester.execute((u64)memory, 40 + 3, 0, 0)), 0); + iter++; + } + } + } +} + +TEST(Emitter, load64_gpr64_gpr64_plus_gpr64_plus_s32) { + CodeTester tester; + tester.init_code_buffer(512); + + tester.clear(); + tester.emit(IGen::load64_gpr64_gpr64_plus_gpr64_plus_s32(RAX, RBX, RSI, -3)); + + EXPECT_EQ(tester.dump_to_hex_string(), "48 8b 84 1e fd ff ff ff"); + + auto instr = IGen::load64_gpr64_gpr64_plus_gpr64_plus_s32(RAX, RBX, RSI, -3); + u8 buff[256]; + instr.emit(buff); + EXPECT_EQ(*(s32*)(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) { + 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(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 + + // fill k with junk + if(k != i && k != j) { + tester.emit(IGen::mov_gpr64_u64(k, (iter&1)?0:UINT64_MAX)); + } + + // load into k + tester.emit(IGen::load64_gpr64_gpr64_plus_gpr64_plus_s32(k, i, j, -3)); + + // move k to return register + tester.emit(IGen::mov_gpr64_gpr64(RAX, 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! + EXPECT_EQ(s64(tester.execute((u64)memory, 24 + 3, 0, 0)), -2); + EXPECT_EQ(s64(tester.execute((u64)memory, 16 + 3, 0, 0)), -3); + EXPECT_EQ(s64(tester.execute((u64)memory, 32 + 3, 0, 0)), -1); + EXPECT_EQ(s64(tester.execute((u64)memory, 40 + 3, 0, 0)), 0); + iter++; + } + } + } +} \ No newline at end of file