diff --git a/External/FEXCore/Source/Interface/Core/Frontend.cpp b/External/FEXCore/Source/Interface/Core/Frontend.cpp index 60ad0722d..ad83c9f0b 100644 --- a/External/FEXCore/Source/Interface/Core/Frontend.cpp +++ b/External/FEXCore/Source/Interface/Core/Frontend.cpp @@ -450,8 +450,8 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op, // New instruction size decoding { // Decode destinations first - uint32_t DstSizeFlag = FEXCore::X86Tables::InstFlags::GetSizeDstFlags(Info->Flags); - uint32_t SrcSizeFlag = FEXCore::X86Tables::InstFlags::GetSizeSrcFlags(Info->Flags); + const auto DstSizeFlag = FEXCore::X86Tables::InstFlags::GetSizeDstFlags(Info->Flags); + const auto SrcSizeFlag = FEXCore::X86Tables::InstFlags::GetSizeSrcFlags(Info->Flags); if (DstSizeFlag == FEXCore::X86Tables::InstFlags::SIZE_8BIT) { DecodeInst->Flags |= DecodeFlags::GenSizeDstSize(DecodeFlags::SIZE_8BIT); @@ -634,6 +634,12 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op, ++CurrentSrc; } + if ((Info->Flags & FEXCore::X86Tables::InstFlags::FLAGS_VEX_DST) != 0) { + CurrentDest->Type = DecodedOperand::OpType::GPR; + CurrentDest->Data.GPR.HighBits = false; + CurrentDest->Data.GPR.GPR = MapVEXToReg(Options.vvvv, HasXMMDst); + } + if (Bytes != 0) { LOGMAN_THROW_A(Bytes <= 8, "Number of bytes should be <= 8 for literal src"); 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/External/FEXCore/include/FEXCore/Debug/X86Tables.h b/External/FEXCore/include/FEXCore/Debug/X86Tables.h index 43a58c214..b0fef1c39 100644 --- a/External/FEXCore/include/FEXCore/Debug/X86Tables.h +++ b/External/FEXCore/include/FEXCore/Debug/X86Tables.h @@ -272,97 +272,102 @@ enum InstType { }; namespace InstFlags { -constexpr uint32_t FLAGS_NONE = 0; -constexpr uint32_t FLAGS_DEBUG = (1 << 1); -constexpr uint32_t FLAGS_DEBUG_MEM_ACCESS = (1 << 2); -constexpr uint32_t FLAGS_SUPPORTS_REP = (1 << 3); -constexpr uint32_t FLAGS_BLOCK_END = (1 << 4); -constexpr uint32_t FLAGS_SETS_RIP = (1 << 5); -constexpr uint32_t FLAGS_DISPLACE_SIZE_MUL_2 = (1 << 6); -constexpr uint32_t FLAGS_DISPLACE_SIZE_DIV_2 = (1 << 7); -constexpr uint32_t FLAGS_SRC_SEXT = (1 << 8); -constexpr uint32_t FLAGS_MEM_OFFSET = (1 << 9); +using InstFlagType = uint64_t; + +constexpr InstFlagType FLAGS_NONE = 0; +constexpr InstFlagType FLAGS_DEBUG = (1ULL << 1); +constexpr InstFlagType FLAGS_DEBUG_MEM_ACCESS = (1ULL << 2); +constexpr InstFlagType FLAGS_SUPPORTS_REP = (1ULL << 3); +constexpr InstFlagType FLAGS_BLOCK_END = (1ULL << 4); +constexpr InstFlagType FLAGS_SETS_RIP = (1ULL << 5); + +constexpr InstFlagType FLAGS_DISPLACE_SIZE_MUL_2 = (1ULL << 6); +constexpr InstFlagType FLAGS_DISPLACE_SIZE_DIV_2 = (1ULL << 7); +constexpr InstFlagType FLAGS_SRC_SEXT = (1ULL << 8); +constexpr InstFlagType FLAGS_MEM_OFFSET = (1ULL << 9); // Enables XMM based subflags // Current reserved range for this SF is [10, 15] -constexpr uint32_t FLAGS_XMM_FLAGS = (1 << 10); +constexpr InstFlagType FLAGS_XMM_FLAGS = (1ULL << 10); // X87 flags aliased to XMM flags selection // Allows X87 instruction table that is abusing the flag for 64BIT selection to work -constexpr uint32_t FLAGS_X87_FLAGS = (1 << 10); +constexpr InstFlagType FLAGS_X87_FLAGS = (1ULL << 10); // Non-XMM subflags - constexpr uint32_t FLAGS_SF_DST_RAX = (1 << 11); - constexpr uint32_t FLAGS_SF_DST_RDX = (1 << 12); - constexpr uint32_t FLAGS_SF_SRC_RAX = (1 << 13); - constexpr uint32_t FLAGS_SF_SRC_RCX = (1 << 14); - constexpr uint32_t FLAGS_SF_REX_IN_BYTE = (1 << 15); + constexpr InstFlagType FLAGS_SF_DST_RAX = (1ULL << 11); + constexpr InstFlagType FLAGS_SF_DST_RDX = (1ULL << 12); + constexpr InstFlagType FLAGS_SF_SRC_RAX = (1ULL << 13); + constexpr InstFlagType FLAGS_SF_SRC_RCX = (1ULL << 14); + constexpr InstFlagType FLAGS_SF_REX_IN_BYTE = (1ULL << 15); // XMM subflags - constexpr uint32_t FLAGS_SF_HIGH_XMM_REG = (1 << 11); - constexpr uint32_t FLAGS_SF_DST_GPR = (1 << 12); - constexpr uint32_t FLAGS_SF_SRC_GPR = (1 << 13); - constexpr uint32_t FLAGS_SF_MMX = (3 << 14); // MMX_DST | MMX_SRC - constexpr uint32_t FLAGS_SF_MMX_DST = (1 << 14); - constexpr uint32_t FLAGS_SF_MMX_SRC = (1 << 15); + constexpr InstFlagType FLAGS_SF_HIGH_XMM_REG = (1ULL << 11); + constexpr InstFlagType FLAGS_SF_DST_GPR = (1ULL << 12); + constexpr InstFlagType FLAGS_SF_SRC_GPR = (1ULL << 13); + constexpr InstFlagType FLAGS_SF_MMX_DST = (1ULL << 14); + constexpr InstFlagType FLAGS_SF_MMX_SRC = (1ULL << 15); + constexpr InstFlagType FLAGS_SF_MMX = FLAGS_SF_MMX_DST | FLAGS_SF_MMX_SRC; // Enables MODRM specific subflags // Current reserved range for this SF is [14, 17] -constexpr uint32_t FLAGS_MODRM = (1 << 16); +constexpr InstFlagType FLAGS_MODRM = (1ULL << 16); // With ModRM SF flag enabled // Direction of ModRM. Dst ^ Src // Set means destination is rm bits // Unset means src is rm bits - constexpr uint32_t FLAGS_SF_MOD_DST = (1 << 17); + constexpr InstFlagType FLAGS_SF_MOD_DST = (1ULL << 17); // If the instruction is restricted to mem or reg only // 0b00 = Regular ModRM support // 0b01 = Memory accesses only // 0b10 = Register accesses only // 0b11 = - constexpr uint32_t FLAGS_SF_MOD_MEM_ONLY = (1 << 18); - constexpr uint32_t FLAGS_SF_MOD_REG_ONLY = (1 << 19); + constexpr InstFlagType FLAGS_SF_MOD_MEM_ONLY = (1ULL << 18); + constexpr InstFlagType FLAGS_SF_MOD_REG_ONLY = (1ULL << 19); // The secondary Opcode Map uses prefix bytes to overlay more instruction // But some instructions need to ignore this overlay and consume these prefixes. -constexpr uint32_t FLAGS_NO_OVERLAY = (1 << 20); +constexpr InstFlagType FLAGS_NO_OVERLAY = (1ULL << 20); // Some instructions partially ignore overlay // Ignore OpSize (0x66) in this case -constexpr uint32_t FLAGS_NO_OVERLAY66 = (1 << 21); +constexpr InstFlagType FLAGS_NO_OVERLAY66 = (1ULL << 21); // x87 -constexpr uint32_t FLAGS_POP = (1 << 22); +constexpr InstFlagType FLAGS_POP = (1ULL << 22); // Only SEXT if the instruction is operating in 64bit operand size -constexpr uint32_t FLAGS_SRC_SEXT64BIT = (1 << 23); +constexpr InstFlagType FLAGS_SRC_SEXT64BIT = (1ULL << 23); // Whether or not the instruction has a VEX prefix for the first source operand -constexpr uint32_t FLAGS_VEX_1ST_SRC = (1 << 24); +constexpr InstFlagType FLAGS_VEX_1ST_SRC = (1ULL << 24); // Whether or not the instruction has a VEX prefix for the second source operand -constexpr uint32_t FLAGS_VEX_2ND_SRC = (1 << 25); +constexpr InstFlagType FLAGS_VEX_2ND_SRC = (1ULL << 25); +// Whether or not the instruction has a VEX prefix for the destination +constexpr InstFlagType FLAGS_VEX_DST = (1ULL << 26); -constexpr uint32_t FLAGS_SIZE_DST_OFF = 26; -constexpr uint32_t FLAGS_SIZE_SRC_OFF = FLAGS_SIZE_DST_OFF + 3; +constexpr InstFlagType FLAGS_SIZE_DST_OFF = 58; +constexpr InstFlagType FLAGS_SIZE_SRC_OFF = FLAGS_SIZE_DST_OFF + 3; -constexpr uint32_t SIZE_MASK = 0b111; -constexpr uint32_t SIZE_DEF = 0b000; -constexpr uint32_t SIZE_8BIT = 0b001; -constexpr uint32_t SIZE_16BIT = 0b010; -constexpr uint32_t SIZE_32BIT = 0b011; -constexpr uint32_t SIZE_64BIT = 0b100; -constexpr uint32_t SIZE_128BIT = 0b101; -constexpr uint32_t SIZE_256BIT = 0b110; -constexpr uint32_t SIZE_64BITDEF = 0b111; // Default mode is 64bit instead of typical 32bit +constexpr InstFlagType SIZE_MASK = 0b111; +constexpr InstFlagType SIZE_DEF = 0b000; +constexpr InstFlagType SIZE_8BIT = 0b001; +constexpr InstFlagType SIZE_16BIT = 0b010; +constexpr InstFlagType SIZE_32BIT = 0b011; +constexpr InstFlagType SIZE_64BIT = 0b100; +constexpr InstFlagType SIZE_128BIT = 0b101; +constexpr InstFlagType SIZE_256BIT = 0b110; +constexpr InstFlagType SIZE_64BITDEF = 0b111; // Default mode is 64bit instead of typical 32bit -constexpr uint32_t GetSizeDstFlags(uint32_t Flags) { return (Flags >> FLAGS_SIZE_DST_OFF) & SIZE_MASK; } -constexpr uint32_t GetSizeSrcFlags(uint32_t Flags) { return (Flags >> FLAGS_SIZE_SRC_OFF) & SIZE_MASK; } +constexpr InstFlagType GetSizeDstFlags(InstFlagType Flags) { return (Flags >> FLAGS_SIZE_DST_OFF) & SIZE_MASK; } +constexpr InstFlagType GetSizeSrcFlags(InstFlagType Flags) { return (Flags >> FLAGS_SIZE_SRC_OFF) & SIZE_MASK; } -constexpr uint32_t GenFlagsDstSize(uint32_t Size) { return Size << FLAGS_SIZE_DST_OFF; } -constexpr uint32_t GenFlagsSrcSize(uint32_t Size) { return Size << FLAGS_SIZE_SRC_OFF; } -constexpr uint32_t GenFlagsSameSize(uint32_t Size) { return (Size << FLAGS_SIZE_DST_OFF) | (Size << FLAGS_SIZE_SRC_OFF); } -constexpr uint32_t GenFlagsSizes(uint32_t Dest, uint32_t Src) { return (Dest << FLAGS_SIZE_DST_OFF) | (Src << FLAGS_SIZE_SRC_OFF); } +constexpr InstFlagType GenFlagsDstSize(InstFlagType Size) { return Size << FLAGS_SIZE_DST_OFF; } +constexpr InstFlagType GenFlagsSrcSize(InstFlagType Size) { return Size << FLAGS_SIZE_SRC_OFF; } +constexpr InstFlagType GenFlagsSameSize(InstFlagType Size) { return (Size << FLAGS_SIZE_DST_OFF) | (Size << FLAGS_SIZE_SRC_OFF); } +constexpr InstFlagType GenFlagsSizes(InstFlagType Dest, InstFlagType Src) { return (Dest << FLAGS_SIZE_DST_OFF) | (Src << FLAGS_SIZE_SRC_OFF); } // If it has an xmm subflag #define HAS_XMM_SUBFLAG(x, flag) (((x) & (FEXCore::X86Tables::InstFlags::FLAGS_XMM_FLAGS | (flag))) == (FEXCore::X86Tables::InstFlags::FLAGS_XMM_FLAGS | (flag))) @@ -424,7 +429,7 @@ void InstallDebugInfo(); struct X86InstInfo { char const *Name; InstType Type; - uint32_t Flags; ///< Must be larger than InstFlags enum + InstFlags::InstFlagType Flags; ///< Must be larger than InstFlags enum uint8_t MoreBytes; OpDispatchPtr OpcodeDispatcher; #ifndef NDEBUG 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