From f088f0a236ece23967ad72d051c149fd159e9aca Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 19 Oct 2021 08:27:13 -0400 Subject: [PATCH] Frontend: Handle VEX source operands This will allow us to begin implementing BMI instructions. --- .../Source/Interface/Core/Frontend.cpp | 77 ++++++++++++++++--- .../FEXCore/Source/Interface/Core/Frontend.h | 10 ++- .../FEXCore/include/FEXCore/Debug/X86Tables.h | 3 + 3 files changed, 80 insertions(+), 10 deletions(-) diff --git a/External/FEXCore/Source/Interface/Core/Frontend.cpp b/External/FEXCore/Source/Interface/Core/Frontend.cpp index 06c045861..784e5907f 100644 --- a/External/FEXCore/Source/Interface/Core/Frontend.cpp +++ b/External/FEXCore/Source/Interface/Core/Frontend.cpp @@ -127,6 +127,54 @@ static uint32_t MapModRMToReg(uint8_t REX, uint8_t bits, bool HighBits, bool Has return (*GPRs)[(REX << 3) | bits]; } +static uint32_t MapVEXToReg(uint8_t vvvv, bool HasXMM) { + using GPRArray = std::array; + + static constexpr GPRArray GPRIndexes = { + FEXCore::X86State::REG_RAX, + FEXCore::X86State::REG_RCX, + FEXCore::X86State::REG_RDX, + FEXCore::X86State::REG_RBX, + FEXCore::X86State::REG_RSP, + FEXCore::X86State::REG_RBP, + FEXCore::X86State::REG_RSI, + FEXCore::X86State::REG_RDI, + FEXCore::X86State::REG_R8, + FEXCore::X86State::REG_R9, + FEXCore::X86State::REG_R10, + FEXCore::X86State::REG_R11, + FEXCore::X86State::REG_R12, + FEXCore::X86State::REG_R13, + FEXCore::X86State::REG_R14, + FEXCore::X86State::REG_R15, + }; + + static constexpr GPRArray XMMIndexes = { + FEXCore::X86State::REG_XMM_0, + FEXCore::X86State::REG_XMM_1, + FEXCore::X86State::REG_XMM_2, + FEXCore::X86State::REG_XMM_3, + FEXCore::X86State::REG_XMM_4, + FEXCore::X86State::REG_XMM_5, + FEXCore::X86State::REG_XMM_6, + FEXCore::X86State::REG_XMM_7, + FEXCore::X86State::REG_XMM_8, + FEXCore::X86State::REG_XMM_9, + FEXCore::X86State::REG_XMM_10, + FEXCore::X86State::REG_XMM_11, + FEXCore::X86State::REG_XMM_12, + FEXCore::X86State::REG_XMM_13, + FEXCore::X86State::REG_XMM_14, + FEXCore::X86State::REG_XMM_15, + }; + + if (HasXMM) { + return XMMIndexes[vvvv]; + } else { + return GPRIndexes[vvvv]; + } +} + Decoder::Decoder(FEXCore::Context::Context *ctx) : CTX {ctx} , OSABI { ctx->SyscallHandler ? ctx->SyscallHandler->GetOSABI() : FEXCore::HLE::SyscallOSABI::OS_UNKNOWN } { @@ -343,7 +391,7 @@ void Decoder::DecodeModRM_64(X86Tables::DecodedOperand *Operand, X86Tables::ModR } } -bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op) { +bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op, DecodedHeader Options) { DecodeInst->OP = Op; DecodeInst->TableInfo = Info; @@ -367,8 +415,9 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op) "Group Ops should have been decoded before this!"); uint8_t DestSize{}; - bool HasWideningDisplacement = FEXCore::X86Tables::DecodeFlags::GetOpAddr(DecodeInst->Flags, 0) & FEXCore::X86Tables::DecodeFlags::FLAG_WIDENING_SIZE_LAST; - bool HasNarrowingDisplacement = FEXCore::X86Tables::DecodeFlags::GetOpAddr(DecodeInst->Flags, 0) & FEXCore::X86Tables::DecodeFlags::FLAG_OPERAND_SIZE_LAST; + const bool HasWideningDisplacement = (FEXCore::X86Tables::DecodeFlags::GetOpAddr(DecodeInst->Flags, 0) & FEXCore::X86Tables::DecodeFlags::FLAG_WIDENING_SIZE_LAST) != 0 || + Options.w; + const bool HasNarrowingDisplacement = (FEXCore::X86Tables::DecodeFlags::GetOpAddr(DecodeInst->Flags, 0) & FEXCore::X86Tables::DecodeFlags::FLAG_OPERAND_SIZE_LAST) != 0; bool HasXMMSrc = !!(Info->Flags & FEXCore::X86Tables::InstFlags::FLAGS_XMM_FLAGS) && !HAS_XMM_SUBFLAG(Info->Flags, FEXCore::X86Tables::InstFlags::FLAGS_SF_SRC_GPR) && @@ -546,6 +595,13 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op) size_t CurrentSrc = 0; + if ((Info->Flags & FEXCore::X86Tables::InstFlags::FLAGS_VEX) != 0) { + DecodeInst->Src[CurrentSrc].Type = DecodedOperand::OpType::GPR; + DecodeInst->Src[CurrentSrc].Data.GPR.HighBits = false; + DecodeInst->Src[CurrentSrc].Data.GPR.GPR = MapVEXToReg(Options.vvvv, HasXMMSrc); + ++CurrentSrc; + } + if (Info->Flags & FEXCore::X86Tables::InstFlags::FLAGS_MODRM) { if (Info->Flags & FEXCore::X86Tables::InstFlags::FLAGS_SF_MOD_DST) { if (!ModRMOperand(DecodeInst->Src[CurrentSrc], DecodeInst->Dest, HasXMMSrc, HasXMMDst, HasMMSrc, HasMMDst, Is8BitSrc, Is8BitDest)) @@ -703,16 +759,19 @@ bool Decoder::NormalOpHeader(FEXCore::X86Tables::X86InstInfo const *Info, uint16 FEXCORE_TELEMETRY_SET(VEXOpTelem, 1); uint16_t map_select = 1; uint16_t pp = 0; - - uint8_t Byte1 = ReadByte(); + const uint8_t Byte1 = ReadByte(); + DecodedHeader options{}; if (Op == 0xC5) { // Two byte VEX pp = Byte1 & 0b11; + options.vvvv = 15 - ((Byte1 & 0b01111000) >> 3); } else { // 0xC4 = Three byte VEX - uint8_t Byte2 = ReadByte(); + const uint8_t Byte2 = ReadByte(); pp = Byte2 & 0b11; map_select = Byte1 & 0b11111; + options.vvvv = 15 - ((Byte2 & 0b01111000) >> 3); + options.w = (Byte2 & 0b10000000) != 0; if (!(map_select >= 1 && map_select <= 3)) { LogMan::Msg::E("We don't understand a map_select of: %d", map_select); return false; @@ -740,10 +799,10 @@ bool Decoder::NormalOpHeader(FEXCore::X86Tables::X86InstInfo const *Info, uint16 #define OPD(group, pp, opcode) (((group - TYPE_VEX_GROUP_12) << 4) | (pp << 3) | (opcode)) Op = OPD(LocalInfo->Type, pp, ModRM.reg); #undef OPD - return NormalOp(&VEXTableGroupOps[Op], Op); + return NormalOp(&VEXTableGroupOps[Op], Op, options); + } else { + return NormalOp(LocalInfo, Op, options); } - else - return NormalOp(LocalInfo, Op); } else if (Info->Type == FEXCore::X86Tables::TYPE_GROUP_EVEX) { FEXCORE_TELEMETRY_SET(EVEXOpTelem, 1); diff --git a/External/FEXCore/Source/Interface/Core/Frontend.h b/External/FEXCore/Source/Interface/Core/Frontend.h index 4a7ff5b25..78b0807d4 100644 --- a/External/FEXCore/Source/Interface/Core/Frontend.h +++ b/External/FEXCore/Source/Interface/Core/Frontend.h @@ -39,6 +39,13 @@ public: void SetSectionMaxAddress(uint64_t v) { SectionMaxAddress = v; } void SetExternalBranches(std::set *v) { ExternalBranches = v; } private: + // To pass any information from instruction prefixes + // down into the actual instruction handling machinery. + struct DecodedHeader { + uint8_t vvvv; // Encoded operand in a VEX prefix. + bool w; // VEX.W bit. + }; + FEXCore::Context::Context *CTX; const FEXCore::HLE::SyscallOSABI OSABI{}; @@ -50,7 +57,8 @@ private: uint8_t PeekByte(uint8_t Offset) const; uint64_t ReadData(uint8_t Size); void SkipBytes(uint8_t Size) { InstructionSize += Size; } - bool NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op); + + bool NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op, DecodedHeader Options = {}); bool NormalOpHeader(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op); static constexpr size_t DefaultDecodedBufferSize = 0x10000; diff --git a/External/FEXCore/include/FEXCore/Debug/X86Tables.h b/External/FEXCore/include/FEXCore/Debug/X86Tables.h index ad9a22977..95f9794aa 100644 --- a/External/FEXCore/include/FEXCore/Debug/X86Tables.h +++ b/External/FEXCore/include/FEXCore/Debug/X86Tables.h @@ -338,6 +338,9 @@ constexpr uint32_t FLAGS_POP = (1 << 22); // Only SEXT if the instruction is operating in 64bit operand size constexpr uint32_t FLAGS_SRC_SEXT64BIT = (1 << 23); +// Whether or not the instruction has a VEX prefix +constexpr uint32_t FLAGS_VEX = (1 << 24); + constexpr uint32_t FLAGS_SIZE_DST_OFF = 26; constexpr uint32_t FLAGS_SIZE_SRC_OFF = FLAGS_SIZE_DST_OFF + 3;