From b47cb20619064771276a6798dec43aed22aa963a Mon Sep 17 00:00:00 2001 From: lioncash Date: Thu, 4 Nov 2021 16:25:27 -0400 Subject: [PATCH] OpcodeDispatcher: Implement handling for BLSI Now all that remains is handling for BLSMSK and BLSR --- .../Interface/Core/OpcodeDispatcher.cpp | 64 +++++++++++++++++++ .../Source/Interface/Core/OpcodeDispatcher.h | 1 + .../Interface/Core/X86Tables/VEXTables.cpp | 2 +- unittests/ASM/VEX/blsi.asm | 34 ++++++++++ 4 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 unittests/ASM/VEX/blsi.asm diff --git a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp index 01b174599..a1aacd267 100644 --- a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp +++ b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp @@ -2204,6 +2204,63 @@ void OpDispatchBuilder::BEXTRBMIOp(OpcodeArgs) { SetRFLAG(ZeroOp); } +void OpDispatchBuilder::BLSIBMIOp(OpcodeArgs) { + // Equivalent to performing: SRC & -SRC + + auto* Src = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags, -1); + auto NegatedSrc = _Neg(Src); + auto Result = _And(Src, NegatedSrc); + + // ...and we're done. Painless! + StoreResult(GPRClass, Op, Result, -1); + + // Now for the flags: + // + // Only CF, SF, ZF and OF are defined as being updated + // CF is cleared if Src is zero, otherwise it's set. + // SF is set to the value of the most significant operand bit of Result. + // OF is always cleared + // ZF is set, as usual, if Result is zero or not. + // + // AF and PF are documented as being in an undefined state after + // a BLSI operation, however, we choose to reliably clear them. + + auto Zero = _Constant(0); + auto One = _Constant(1); + + SetRFLAG(Zero); + SetRFLAG(Zero); + if (CTX->Config.ABINoPF) { + _InvalidateFlags(1UL << X86State::RFLAG_PF_LOC); + } else { + SetRFLAG(Zero); + } + + // ZF + { + auto ZFOp = _Select(IR::COND_EQ, + Result, Zero, + One, Zero); + SetRFLAG(ZFOp); + } + + // CF + { + auto CFOp = _Select(IR::COND_EQ, + Src, Zero, + Zero, One); + SetRFLAG(CFOp); + } + + // SF + { + auto SignBit = _Constant((GetSrcSize(Op) * 8) - 1); + auto SFOp = _Lshr(Result, SignBit); + + SetRFLAG(SFOp); + } +} + void OpDispatchBuilder::RCROp1Bit(OpcodeArgs) { OrderedNode *Dest = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, -1); auto Size = GetSrcSize(Op) * 8; @@ -5876,6 +5933,12 @@ constexpr uint16_t PF_F2 = 3; }; #undef OPD + #define OPD(group, pp, opcode) (((group - X86Tables::InstType::TYPE_VEX_GROUP_12) << 4) | (pp << 3) | (opcode)) + const std::vector> VEXGroupTable = { + {OPD(X86Tables::InstType::TYPE_VEX_GROUP_17, 0, 0b011), 1, &OpDispatchBuilder::BLSIBMIOp}, + }; + #undef OPD + const std::vector> EVEXTable = { {0x10, 2, &OpDispatchBuilder::UnimplementedOp}, {0x59, 1, &OpDispatchBuilder::UnimplementedOp}, @@ -5936,6 +5999,7 @@ constexpr uint16_t PF_F2 = 3; InstallToTable(FEXCore::X86Tables::H0F38TableOps, H0F38Table); InstallToTable(FEXCore::X86Tables::H0F3ATableOps, H0F3ATable); InstallToTable(FEXCore::X86Tables::VEXTableOps, VEXTable); + InstallToTable(FEXCore::X86Tables::VEXTableGroupOps, VEXGroupTable); InstallToTable(FEXCore::X86Tables::EVEXTableOps, EVEXTable); } diff --git a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h index a440b707e..331d46917 100644 --- a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h +++ b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h @@ -327,6 +327,7 @@ public: // BMI Ops void ANDNBMIOp(OpcodeArgs); void BEXTRBMIOp(OpcodeArgs); + void BLSIBMIOp(OpcodeArgs); // X87 Ops template diff --git a/External/FEXCore/Source/Interface/Core/X86Tables/VEXTables.cpp b/External/FEXCore/Source/Interface/Core/X86Tables/VEXTables.cpp index 8b5de202a..e9ae5b6a1 100644 --- a/External/FEXCore/Source/Interface/Core/X86Tables/VEXTables.cpp +++ b/External/FEXCore/Source/Interface/Core/X86Tables/VEXTables.cpp @@ -505,7 +505,7 @@ void InitializeVEXTables() { {OPD(TYPE_VEX_GROUP_17, 0, 0b001), 1, X86InstInfo{"BLSR", TYPE_UNDEC, FLAGS_MODRM, 0, nullptr}}, {OPD(TYPE_VEX_GROUP_17, 0, 0b010), 1, X86InstInfo{"BLSMSK", TYPE_UNDEC, FLAGS_MODRM, 0, nullptr}}, - {OPD(TYPE_VEX_GROUP_17, 0, 0b011), 1, X86InstInfo{"BLSI", TYPE_UNDEC, FLAGS_MODRM, 0, nullptr}}, + {OPD(TYPE_VEX_GROUP_17, 0, 0b011), 1, X86InstInfo{"BLSI", TYPE_INST, FLAGS_MODRM | FLAGS_VEX_DST, 0, nullptr}}, }; #undef OPD diff --git a/unittests/ASM/VEX/blsi.asm b/unittests/ASM/VEX/blsi.asm new file mode 100644 index 000000000..ccddd8802 --- /dev/null +++ b/unittests/ASM/VEX/blsi.asm @@ -0,0 +1,34 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "1", + "RBX": "0xFF00000000000000", + "RCX": "0x0100000000000000", + "RDX": "1", + "RSI": "0xFF000000", + "RDI": "0x01000000" + } +} +%endif + +; Trivial test, this should result in 1. +mov rax, 11 +blsi rax, rax + +; Results in the lowest set bit (bit 56) being extracted +mov rbx, 0xFF00000000000000 +mov rcx, 0 +blsi rcx, rbx + +; Same tests but with 32-bit registers + +; Trivial test, this should result in 1. +mov edx, 11 +blsi edx, edx + +; Results in the lowest set bit (bit 24) being extracted +mov rsi, 0xFF000000 +mov rdi, 0 +blsi edi, esi + +hlt \ No newline at end of file