From 18d83a310e4e1d985ea70f0e5848a6ec830d3565 Mon Sep 17 00:00:00 2001 From: Fiora Date: Wed, 10 Sep 2014 23:54:06 -0700 Subject: [PATCH 1/2] X64Emitter: support shorter mov reg, imm opcodes Also refactor WriteNormalOp a little bit and add comments. --- Source/Core/Common/x64Emitter.cpp | 54 ++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/Source/Core/Common/x64Emitter.cpp b/Source/Core/Common/x64Emitter.cpp index 9d9b2551c1..77cb1f9f72 100644 --- a/Source/Core/Common/x64Emitter.cpp +++ b/Source/Core/Common/x64Emitter.cpp @@ -1053,7 +1053,6 @@ void OpArg::WriteNormalOp(XEmitter *emit, bool toRM, NormalOp op, const OpArg &o emit->Write8(0x66); int immToWrite = 0; - bool skip_rest = false; if (operand.IsImm()) { @@ -1066,15 +1065,22 @@ void OpArg::WriteNormalOp(XEmitter *emit, bool toRM, NormalOp op, const OpArg &o if (operand.scale == SCALE_IMM8 && bits == 8) { + // op al, imm8 if (!scale && offsetOrBaseReg == AL && normalops[op].eaximm8 != 0xCC) { emit->Write8(normalops[op].eaximm8); - skip_rest = true; + emit->Write8((u8)operand.offset); + return; } - else + // mov reg, imm8 + if (!scale && op == nrmMOV) { - emit->Write8(normalops[op].imm8); + emit->Write8(0xB0 + (offsetOrBaseReg & 7)); + emit->Write8((u8)operand.offset); + return; } + // op r/m8, imm8 + emit->Write8(normalops[op].imm8); immToWrite = 8; } else if ((operand.scale == SCALE_IMM16 && bits == 16) || @@ -1083,6 +1089,7 @@ void OpArg::WriteNormalOp(XEmitter *emit, bool toRM, NormalOp op, const OpArg &o { // Try to save immediate size if we can, but first check to see // if the instruction supports simm8. + // op r/m, imm8 if (normalops[op].simm8 != 0xCC && ((operand.scale == SCALE_IMM16 && (s16)operand.offset == (s8)operand.offset) || (operand.scale == SCALE_IMM32 && (s32)operand.offset == (s8)operand.offset))) @@ -1092,15 +1099,28 @@ void OpArg::WriteNormalOp(XEmitter *emit, bool toRM, NormalOp op, const OpArg &o } else { + // mov reg, imm + if (!scale && op == nrmMOV && bits != 64) + { + emit->Write8(0xB8 + (offsetOrBaseReg & 7)); + if (bits == 16) + emit->Write16((u16)operand.offset); + else + emit->Write32((u32)operand.offset); + return; + } + // op eax, imm if (!scale && offsetOrBaseReg == EAX && normalops[op].eaximm32 != 0xCC) { emit->Write8(normalops[op].eaximm32); - skip_rest = true; - } - else - { - emit->Write8(normalops[op].imm32); + if (bits == 16) + emit->Write16((u16)operand.offset); + else + emit->Write32((u32)operand.offset); + return; } + // op r/m, imm + emit->Write8(normalops[op].imm32); immToWrite = bits == 16 ? 16 : 32; } } @@ -1108,12 +1128,18 @@ void OpArg::WriteNormalOp(XEmitter *emit, bool toRM, NormalOp op, const OpArg &o (operand.scale == SCALE_IMM8 && bits == 32) || (operand.scale == SCALE_IMM8 && bits == 64)) { + // op r/m, imm8 emit->Write8(normalops[op].simm8); immToWrite = 8; } else if (operand.scale == SCALE_IMM64 && bits == 64) { - if (op == nrmMOV) + if (scale) + { + _assert_msg_(DYNA_REC, 0, "WriteNormalOp - MOV with 64-bit imm requres register destination"); + } + // mov reg64, imm64 + else if (op == nrmMOV) { emit->Write8(0xB8 + (offsetOrBaseReg & 7)); emit->Write64((u64)operand.offset); @@ -1131,20 +1157,18 @@ void OpArg::WriteNormalOp(XEmitter *emit, bool toRM, NormalOp op, const OpArg &o { _operandReg = (X64Reg)operand.offsetOrBaseReg; WriteRex(emit, bits, bits, _operandReg); - // mem/reg or reg/reg op + // op r/m, reg if (toRM) { emit->Write8(bits == 8 ? normalops[op].toRm8 : normalops[op].toRm32); - // _assert_msg_(DYNA_REC, code[-1] != 0xCC, "ARGH4"); } + // op reg, r/m else { emit->Write8(bits == 8 ? normalops[op].fromRm8 : normalops[op].fromRm32); - // _assert_msg_(DYNA_REC, code[-1] != 0xCC, "ARGH5"); } } - if (!skip_rest) - WriteRest(emit, immToWrite>>3, _operandReg); + WriteRest(emit, immToWrite >> 3, _operandReg); switch (immToWrite) { case 0: From 1bd8d1ee98631accc2b3e87d77c033f1fdf090b5 Mon Sep 17 00:00:00 2001 From: Fiora Date: Sat, 13 Sep 2014 16:39:34 -0700 Subject: [PATCH 2/2] Add immediate tests for WriteNormalOp also fix a bug in Bochs that was preventing adc from passing. --- Externals/Bochs_disasm/opcodes.inc | 2 +- Source/UnitTests/Common/x64EmitterTest.cpp | 24 +++++++++++++--------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/Externals/Bochs_disasm/opcodes.inc b/Externals/Bochs_disasm/opcodes.inc index d58bf6e11a..371e14c300 100644 --- a/Externals/Bochs_disasm/opcodes.inc +++ b/Externals/Bochs_disasm/opcodes.inc @@ -36,7 +36,7 @@ Ia_adcl_Ed_sIb = { "adc", "adcl", Ed, sIbd, XX, XX, 0 }, Ia_adcl_Gd_Ed = { "adc", "adcl", Gd, Ed, XX, XX, 0 }, Ia_adcq_Eq_Gq = { "adc", "adcq", Eq, Gq, XX, XX, 0 }, Ia_adcq_Eq_sIb = { "adc", "adcq", Eq, sIbq, XX, XX, 0 }, -Ia_adcq_Eq_sId = { "adc", "adcq", Eq, Iq, XX, XX, 0 }, +Ia_adcq_Eq_sId = { "adc", "adcq", Eq, sIdq, XX, XX, 0 }, Ia_adcq_Gq_Eq = { "adc", "adcq", Gq, Eq, XX, XX, 0 }, Ia_adcq_RAX_sId = { "adc", "adcq", RAX_Reg, sIdq, XX, XX, 0 }, Ia_adcw_AX_Iw = { "adc", "adcw", AX_Reg, Iw, XX, XX, 0 }, diff --git a/Source/UnitTests/Common/x64EmitterTest.cpp b/Source/UnitTests/Common/x64EmitterTest.cpp index 8051e10b07..8cbfad64fe 100644 --- a/Source/UnitTests/Common/x64EmitterTest.cpp +++ b/Source/UnitTests/Common/x64EmitterTest.cpp @@ -471,7 +471,7 @@ SHIFT_TEST(SAR) }; \ for (const auto& regset : regsets) \ for (const auto& r : regset.regs) \ - { \ + { \ emitter->Name(regset.bits, R(r.reg), R(RAX)); \ emitter->Name(regset.bits, R(RAX), R(r.reg)); \ emitter->Name(regset.bits, R(r.reg), Imm8(0x42)); \ @@ -480,7 +480,7 @@ SHIFT_TEST(SAR) #Name " " + regset.out_name + ", " + r.name + " " \ #Name " " + r.name + ", 0x42 " \ #Name " " + regset.size + " ptr ds:[r12], " + r.name); \ - } \ + } \ } BT_TEST(BT) @@ -506,14 +506,14 @@ BT_TEST(BTC) }; \ for (const auto& regset : regsets) \ for (const auto& r : regset.regs) \ - { \ + { \ emitter->Name(regset.bits, R(r.reg)); \ emitter->Name(regset.bits, MatR(RAX)); \ emitter->Name(regset.bits, MatR(R12)); \ ExpectDisassembly(#Name " " + r.name + " " \ #Name " " + regset.size + " ptr ds:[rax] " \ #Name " " + regset.size + " ptr ds:[r12]"); \ - } \ + } \ } ONE_OP_ARITH_TEST(NOT) @@ -527,12 +527,14 @@ ONE_OP_ARITH_TEST(NEG) std::vector regs; \ std::string size; \ std::string rax_name; \ + Gen::OpArg imm; \ + std::string immname; \ } regsets[] = { \ - { 8, reg8names, "byte", "al" }, \ - { 8, reg8hnames, "byte", "al" }, \ - { 16, reg16names, "word", "ax" }, \ - { 32, reg32names, "dword", "eax" }, \ - { 64, reg64names, "qword", "rax" }, \ + { 8, reg8names, "byte", "al", Imm8(0xEF), "0xef" }, \ + { 8, reg8hnames, "byte", "al", Imm8(0xEF), "0xef" }, \ + { 16, reg16names, "word", "ax", Imm16(0xBEEF), "0xbeef" }, \ + { 32, reg32names, "dword", "eax", Imm32(0xDEADBEEF), "0xdeadbeef" }, \ + { 64, reg64names, "qword", "rax", Imm32(0xDEADBEEF), "0xffffffffdeadbeef" }, \ }; \ for (const auto& regset : regsets) \ for (const auto& r : regset.regs) \ @@ -541,10 +543,12 @@ ONE_OP_ARITH_TEST(NEG) emitter->Name(regset.bits, R(RAX), R(r.reg)); \ emitter->Name(regset.bits, R(r.reg), MatR(RAX)); \ emitter->Name(regset.bits, MatR(RAX), R(r.reg)); \ + emitter->Name(regset.bits, R(r.reg), regset.imm); \ ExpectDisassembly(#Name " " + r.name + ", " + regset.rax_name + " " \ #Name " " + regset.rax_name + ", " + r.name + " " \ #Name " " + r.name + ", " + regset.size + " ptr ds:[rax] " \ - #Name " " + regset.size + " ptr ds:[rax], " + r.name); \ + #Name " " + regset.size + " ptr ds:[rax], " + r.name + " " \ + #Name " " + r.name + ", " + regset.immname ); \ } \ }