diff --git a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp index 8cf099bda..c5706d75b 100644 --- a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp +++ b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp @@ -2333,6 +2333,39 @@ void OpDispatchBuilder::BLSRBMIOp(OpcodeArgs) { } } +void OpDispatchBuilder::BMI2Shift(OpcodeArgs) { + // Handles SARX, SHLX, and SHRX + + auto* Src = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags, -1); + auto* Shift = LoadSource(GPRClass, Op, Op->Src[1], Op->Flags, -1); + const auto OperandSize = GetSrcSize(Op) * 8; + + // x86 masks the shift by 0x3F or 0x1F depending on size of op + auto SanitizedShift = [&] { + if (OperandSize == 64) { + return _And(Shift, _Constant(0x3F)); + } else { + return _And(Shift, _Constant(0x1F)); + } + }(); + + auto* Result = [&]() -> OrderedNode* { + // SARX + if (Op->OP == 0x6F7) { + return _Ashr(Src, SanitizedShift); + } + // SHLX + if (Op->OP == 0x5F7) { + return _Lshl(Src, SanitizedShift); + } + + // SHRX + return _Lshr(Src, SanitizedShift); + }(); + + StoreResult(GPRClass, Op, Result, -1); +} + void OpDispatchBuilder::RCROp1Bit(OpcodeArgs) { OrderedNode *Dest = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, -1); auto Size = GetSrcSize(Op) * 8; @@ -6002,6 +6035,9 @@ constexpr uint16_t PF_F2 = 3; {OPD(2, 0b00, 0xF2), 1, &OpDispatchBuilder::ANDNBMIOp}, {OPD(2, 0b00, 0xF7), 1, &OpDispatchBuilder::BEXTRBMIOp}, + {OPD(2, 0b01, 0xF7), 1, &OpDispatchBuilder::BMI2Shift}, + {OPD(2, 0b10, 0xF7), 1, &OpDispatchBuilder::BMI2Shift}, + {OPD(2, 0b11, 0xF7), 1, &OpDispatchBuilder::BMI2Shift}, }; #undef OPD diff --git a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h index 5e3e84c45..a6e6c9fba 100644 --- a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h +++ b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h @@ -324,13 +324,16 @@ public: template void PSIGN(OpcodeArgs); - // BMI Ops + // BMI1 Ops void ANDNBMIOp(OpcodeArgs); void BEXTRBMIOp(OpcodeArgs); void BLSIBMIOp(OpcodeArgs); void BLSMSKBMIOp(OpcodeArgs); void BLSRBMIOp(OpcodeArgs); + // BMI2 Ops + void BMI2Shift(OpcodeArgs); + // X87 Ops template void FLD(OpcodeArgs); diff --git a/External/FEXCore/Source/Interface/Core/X86Tables/VEXTables.cpp b/External/FEXCore/Source/Interface/Core/X86Tables/VEXTables.cpp index bb7096585..b8b5db119 100644 --- a/External/FEXCore/Source/Interface/Core/X86Tables/VEXTables.cpp +++ b/External/FEXCore/Source/Interface/Core/X86Tables/VEXTables.cpp @@ -400,9 +400,9 @@ void InitializeVEXTables() { {OPD(2, 0b11, 0xF6), 1, X86InstInfo{"MULX", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}}, {OPD(2, 0b00, 0xF7), 1, X86InstInfo{"BEXTR", TYPE_INST, FLAGS_MODRM | FLAGS_VEX_2ND_SRC, 0, nullptr}}, - {OPD(2, 0b01, 0xF7), 1, X86InstInfo{"SHLX", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}}, - {OPD(2, 0b10, 0xF7), 1, X86InstInfo{"SARX", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}}, - {OPD(2, 0b11, 0xF7), 1, X86InstInfo{"SHRX", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}}, + {OPD(2, 0b01, 0xF7), 1, X86InstInfo{"SHLX", TYPE_INST, FLAGS_MODRM | FLAGS_VEX_2ND_SRC, 0, nullptr}}, + {OPD(2, 0b10, 0xF7), 1, X86InstInfo{"SARX", TYPE_INST, FLAGS_MODRM | FLAGS_VEX_2ND_SRC, 0, nullptr}}, + {OPD(2, 0b11, 0xF7), 1, X86InstInfo{"SHRX", TYPE_INST, FLAGS_MODRM | FLAGS_VEX_2ND_SRC, 0, nullptr}}, // VEX Map 3 {OPD(3, 0b01, 0x00), 1, X86InstInfo{"VPERMQ", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}}, diff --git a/unittests/ASM/VEX/sarx.asm b/unittests/ASM/VEX/sarx.asm new file mode 100644 index 000000000..e8f9e8f0a --- /dev/null +++ b/unittests/ASM/VEX/sarx.asm @@ -0,0 +1,31 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0xF800000000000000", + "RBX": "4", + "RCX": "0xFFFFFFFFFFFFFFFF", + "RDX": "127", + "RSI": "63", + "RDI": "0x00000000FFFFFFFF" + } +} +%endif + +; Trivial right shift +mov rax, 0x8000000000000000 +mov rbx, 4 +sarx rax, rax, rbx + +; This is really a shift by 63. This just ensures we properly +; mask the shift value according to the ISA manual. +mov rcx, 0x8000000000000000 +mov rdx, 127 +sarx rcx, rcx, rdx + +; This is really a shift by 31. This just ensures we properly +; mask the shift value according to the ISA manual. +mov edi, 0x80000000 +mov esi, 63 +sarx edi, edi, esi + +hlt \ No newline at end of file diff --git a/unittests/ASM/VEX/shlx.asm b/unittests/ASM/VEX/shlx.asm new file mode 100644 index 000000000..8aadaba70 --- /dev/null +++ b/unittests/ASM/VEX/shlx.asm @@ -0,0 +1,31 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x10", + "RBX": "4", + "RCX": "0x8000000000000000", + "RDX": "127", + "RSI": "63", + "RDI": "0x80000000" + } +} +%endif + +; Trivial left shift +mov rax, 1 +mov rbx, 4 +shlx rax, rax, rbx + +; This is really a shift by 63. This just ensures we properly +; mask the shift value according to the ISA manual. +mov rcx, 1 +mov rdx, 127 +shlx rcx, rcx, rdx + +; This is really a shift by 31. This just ensures we properly +; mask the shift value according to the ISA manual. +mov edi, 1 +mov esi, 63 +shlx edi, edi, esi + +hlt \ No newline at end of file diff --git a/unittests/ASM/VEX/shrx.asm b/unittests/ASM/VEX/shrx.asm new file mode 100644 index 000000000..7d0784d8c --- /dev/null +++ b/unittests/ASM/VEX/shrx.asm @@ -0,0 +1,31 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "0x0800000000000000", + "RBX": "4", + "RCX": "1", + "RDX": "127", + "RSI": "63", + "RDI": "1" + } +} +%endif + +; Trivial right shift +mov rax, 0x8000000000000000 +mov rbx, 4 +shrx rax, rax, rbx + +; This is really a shift by 63. This just ensures we properly +; mask the shift value according to the ISA manual. +mov rcx, 0x8000000000000000 +mov rdx, 127 +shrx rcx, rcx, rdx + +; This is really a shift by 31. This just ensures we properly +; mask the shift value according to the ISA manual. +mov edi, 0x80000000 +mov esi, 63 +shrx edi, edi, esi + +hlt \ No newline at end of file