From 575f1b309a4e6e9580bd62ab3c3e584811b4a9da Mon Sep 17 00:00:00 2001 From: Sintendo Date: Fri, 14 Sep 2018 21:56:29 +0200 Subject: [PATCH 1/4] x64Emitter: short MOV for 64bit immediates (1) Prior to this commit, the emitter would unconditionally emit a 10-byte instruction known as MOVABS when loading a 64-bit immediate to a register. 0: 48 b8 ef be ad de ff movabs rax,0xffffffffdeadbeef 7: ff ff ff With this change, it will instead emit a 7-byte instruction when it is possible to express the 64-bit immediate using a signed 32-bit value. 0: 48 c7 c0 ef be ad de mov rax,0xffffffffdeadbeef --- Source/Core/Common/x64Emitter.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Source/Core/Common/x64Emitter.cpp b/Source/Core/Common/x64Emitter.cpp index d419f32af5..8e85e146b3 100644 --- a/Source/Core/Common/x64Emitter.cpp +++ b/Source/Core/Common/x64Emitter.cpp @@ -1469,11 +1469,21 @@ void OpArg::WriteNormalOp(XEmitter* emit, bool toRM, NormalOp op, const OpArg& o // mov reg64, imm64 else if (op == NormalOp::MOV) { - emit->Write8(0xB8 + (offsetOrBaseReg & 7)); - emit->Write64((u64)operand.offset); - return; + // movabs reg64, imm64 (10 bytes) + if (static_cast(operand.offset) != static_cast(operand.offset)) + { + emit->Write8(0xB8 + (offsetOrBaseReg & 7)); + emit->Write64(operand.offset); + return; + } + // mov reg64, simm32 (7 bytes) + emit->Write8(op_def.imm32); + immToWrite = 32; + } + else + { + ASSERT_MSG(DYNA_REC, 0, "WriteNormalOp - Only MOV can take 64-bit imm"); } - ASSERT_MSG(DYNA_REC, 0, "WriteNormalOp - Only MOV can take 64-bit imm"); } else { From 53a947749a459c9037b0a9c000d9935ed9b14ac3 Mon Sep 17 00:00:00 2001 From: Sintendo Date: Fri, 14 Sep 2018 21:57:00 +0200 Subject: [PATCH 2/4] x64Emitter: short MOV for 64bit immediates (2) Prior to this commit, the emitter would unconditionally emit a 10-byte instruction known as MOVABS when loading a 64-bit immediate to a register. 0: 48 b8 ef be ad de 00 movabs rax,0xdeadbeef 7: 00 00 00 With this change, it will instead rely on the fact that on x64 writes to 32-bit registers are automatically zero extended to 64-bits, allowing us to emit a 5 or 6-bytes instruction with the same effect for certain immediates. 0: b8 ef be ad de mov eax,0xdeadbeef --- Source/Core/Common/x64Emitter.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/Core/Common/x64Emitter.cpp b/Source/Core/Common/x64Emitter.cpp index 8e85e146b3..2a70ac7265 100644 --- a/Source/Core/Common/x64Emitter.cpp +++ b/Source/Core/Common/x64Emitter.cpp @@ -1591,6 +1591,12 @@ void XEmitter::XOR(int bits, const OpArg& a1, const OpArg& a2) } void XEmitter::MOV(int bits, const OpArg& a1, const OpArg& a2) { + if (bits == 64 && a1.IsSimpleReg() && a2.scale == SCALE_IMM64 && + a2.offset == static_cast(a2.offset)) + { + WriteNormalOp(32, NormalOp::MOV, a1, a2.AsImm32()); + return; + } if (a1.IsSimpleReg() && a2.IsSimpleReg() && a1.GetSimpleReg() == a2.GetSimpleReg()) ERROR_LOG(DYNA_REC, "Redundant MOV @ %p - bug in JIT?", code); WriteNormalOp(bits, NormalOp::MOV, a1, a2); From 58a0d0f117caff819177053b5b9bdf1774a91929 Mon Sep 17 00:00:00 2001 From: Sintendo Date: Fri, 14 Sep 2018 22:08:11 +0200 Subject: [PATCH 3/4] x64EmitterTest: test MOV with 64-bit immediates --- Source/UnitTests/Common/x64EmitterTest.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Source/UnitTests/Common/x64EmitterTest.cpp b/Source/UnitTests/Common/x64EmitterTest.cpp index 4de8bf99ad..77f8db9aea 100644 --- a/Source/UnitTests/Common/x64EmitterTest.cpp +++ b/Source/UnitTests/Common/x64EmitterTest.cpp @@ -547,6 +547,24 @@ TWO_OP_ARITH_TEST(OR) TWO_OP_ARITH_TEST(XOR) TWO_OP_ARITH_TEST(MOV) +TEST_F(x64EmitterTest, MOV_Imm64) +{ + for (size_t i = 0; i < reg64names.size(); i++) + { + emitter->MOV(64, R(reg64names[i].reg), Imm64(0xDEADBEEFDEADBEEF)); + EXPECT_EQ(emitter->GetCodePtr(), code_buffer + 10); + ExpectDisassembly("mov " + reg64names[i].name + ", 0xdeadbeefdeadbeef"); + + emitter->MOV(64, R(reg64names[i].reg), Imm64(0xFFFFFFFFDEADBEEF)); + EXPECT_EQ(emitter->GetCodePtr(), code_buffer + 7); + ExpectDisassembly("mov " + reg64names[i].name + ", 0xffffffffdeadbeef"); + + emitter->MOV(64, R(reg64names[i].reg), Imm64(0xDEADBEEF)); + EXPECT_EQ(emitter->GetCodePtr(), code_buffer + 5 + (i > 7)); + ExpectDisassembly("mov " + reg32names[i].name + ", 0xdeadbeef"); + } +} + // TODO: Disassembler inverts operands here. // TWO_OP_ARITH_TEST(XCHG) // TWO_OP_ARITH_TEST(TEST) From 8a93dd01054f78be15d177e3578f7db0203a3241 Mon Sep 17 00:00:00 2001 From: Sintendo Date: Fri, 14 Sep 2018 23:21:59 +0200 Subject: [PATCH 4/4] x64EmitterTest: Fix linting issues --- Source/UnitTests/Common/x64EmitterTest.cpp | 40 ++++++++++++++-------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/Source/UnitTests/Common/x64EmitterTest.cpp b/Source/UnitTests/Common/x64EmitterTest.cpp index 77f8db9aea..ff0c64b876 100644 --- a/Source/UnitTests/Common/x64EmitterTest.cpp +++ b/Source/UnitTests/Common/x64EmitterTest.cpp @@ -35,7 +35,10 @@ const std::vector reg8names{ }; const std::vector reg8hnames{ - {AH, "ah"}, {BH, "bh"}, {CH, "ch"}, {DH, "dh"}, + {AH, "ah"}, + {BH, "bh"}, + {CH, "ch"}, + {DH, "dh"}, }; const std::vector reg16names{ @@ -306,10 +309,12 @@ TEST_F(x64EmitterTest, CMOVcc_Register) emitter->CMOVcc(32, RAX, R(R12), cc.cc); emitter->CMOVcc(16, RAX, R(R12), cc.cc); - ExpectDisassembly("cmov" + cc.name + " rax, r12 " - "cmov" + - cc.name + " eax, r12d " - "cmov" + + ExpectDisassembly("cmov" + cc.name + + " rax, r12 " + "cmov" + + cc.name + + " eax, r12d " + "cmov" + cc.name + " ax, r12w"); } } @@ -379,10 +384,12 @@ TEST_F(x64EmitterTest, MOVNT_DQ_PS_PD) emitter->MOVNTDQ(MatR(RAX), r.reg); emitter->MOVNTPS(MatR(RAX), r.reg); emitter->MOVNTPD(MatR(RAX), r.reg); - ExpectDisassembly("movntdq dqword ptr ds:[rax], " + r.name + " " - "movntps dqword ptr ds:[rax], " + - r.name + " " - "movntpd dqword ptr ds:[rax], " + + ExpectDisassembly("movntdq dqword ptr ds:[rax], " + r.name + + " " + "movntps dqword ptr ds:[rax], " + + r.name + + " " + "movntpd dqword ptr ds:[rax], " + r.name); } } @@ -576,7 +583,8 @@ TEST_F(x64EmitterTest, BSWAP) int bits; std::vector regs; } regsets[] = { - {32, reg32names}, {64, reg64names}, + {32, reg32names}, + {64, reg64names}, }; for (const auto& regset : regsets) for (const auto& r : regset.regs) @@ -889,7 +897,8 @@ TWO_OP_SSE_TEST(PMOVZXDQ, "qword") std::string out_name; \ std::string size; \ } regsets[] = { \ - {32, reg32names, "eax", "dword"}, {64, reg64names, "rax", "qword"}, \ + {32, reg32names, "eax", "dword"}, \ + {64, reg64names, "rax", "qword"}, \ }; \ for (const auto& regset : regsets) \ for (const auto& r : regset.regs) \ @@ -921,7 +930,8 @@ VEX_RMR_TEST(BZHI) std::string out_name; \ std::string size; \ } regsets[] = { \ - {32, reg32names, "eax", "dword"}, {64, reg64names, "rax", "qword"}, \ + {32, reg32names, "eax", "dword"}, \ + {64, reg64names, "rax", "qword"}, \ }; \ for (const auto& regset : regsets) \ for (const auto& r : regset.regs) \ @@ -952,7 +962,8 @@ VEX_RRM_TEST(ANDN) std::string out_name; \ std::string size; \ } regsets[] = { \ - {32, reg32names, "eax", "dword"}, {64, reg64names, "rax", "qword"}, \ + {32, reg32names, "eax", "dword"}, \ + {64, reg64names, "rax", "qword"}, \ }; \ for (const auto& regset : regsets) \ for (const auto& r : regset.regs) \ @@ -981,7 +992,8 @@ VEX_RM_TEST(BLSI) std::string out_name; \ std::string size; \ } regsets[] = { \ - {32, reg32names, "eax", "dword"}, {64, reg64names, "rax", "qword"}, \ + {32, reg32names, "eax", "dword"}, \ + {64, reg64names, "rax", "qword"}, \ }; \ for (const auto& regset : regsets) \ for (const auto& r : regset.regs) \