// SPDX-License-Identifier: MIT #pragma once #include "Interface/Core/X86Tables/X86Tables.h" #include "Interface/IR/IR.h" #include #include #include #include #include #include #include #include #include #include namespace FEXCore::Context { class ContextImpl; } namespace FEXCore::Frontend { class Decoder final { public: enum class DecodedBlockStatus { SUCCESS, INVALID_INST, NOEXEC_INST, PARTIAL_DECODE_INST, BAD_RELOCATION, UNIMPLEMENTED_INST, }; // New Frontend decoding struct DecodedBlocks final { uint64_t Entry {}; uint64_t Size {}; uint64_t NumInstructions {}; FEXCore::X86Tables::DecodedInst* DecodedInstructions; DecodedBlockStatus BlockStatus; bool IsEntryPoint {}; bool ForceFullSMCDetection {}; }; struct DecodedBlockInformation final { uint64_t TotalInstructionCount; bool Is64BitMode {}; fextl::vector Blocks; fextl::set EntryPoints; fextl::set CodePages; // Start addresses of all pages touching the block }; Decoder(FEXCore::Core::InternalThreadState* Thread); bool CheckIfCacheable(FEXCore::Core::InternalThreadState&, const uint8_t* InstStream, uint64_t PC, uint64_t MaxInst); void DecodeInstructionsAtEntry(FEXCore::Core::InternalThreadState* Thread, const uint8_t* InstStream, uint64_t PC, uint64_t MaxInst); const DecodedBlockInformation* GetDecodedBlockInfo() const { return &BlockInfo; } uint64_t DecodedMinAddress {}; uint64_t DecodedMaxAddress {~0ULL}; void SetExternalBranches(fextl::set* v) { ExternalBranches = v; } void DelayedDisownBuffer() { PoolObject.DelayedDisownBuffer(); } void ResetExecutableRangeCache() { ExecutableRangeBase = ExecutableRangeEnd = 0; } 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. bool L; // VEX.L bit (if set then 256 bit operation, if unset then scalar or 128-bit operation) }; FEXCore::Core::InternalThreadState* Thread; FEXCore::Context::ContextImpl* CTX; FEX_CONFIG_OPT(EnableCodeCacheValidation, ENABLECODECACHEVALIDATION); DecodedBlockStatus DecodeInstructionImpl(uint64_t PC); DecodedBlockStatus DecodeInstruction(uint64_t PC); void BranchTargetInMultiblockRange(); bool IsBranchMonoTailcall(uint64_t NumInstructions) const; bool InstCanContinue() const; void AddBranchTarget(uint64_t Target); bool CheckRangeExecutable(uint64_t Address, uint64_t Size); uint8_t ReadByte(); std::optional PeekByte(uint8_t Offset); std::pair ReadData(uint8_t Size); void SkipBytes(uint8_t Size) { InstructionSize += Size; } DecodedBlockStatus NormalOp(const FEXCore::X86Tables::X86InstInfo* Info, uint16_t Op, DecodedHeader Options = {}); DecodedBlockStatus NormalOpHeader(const FEXCore::X86Tables::X86InstInfo* Info, uint16_t Op); void DecodeREXIfValid(int8_t ExpectedOffset = -1); static constexpr size_t DefaultDecodedBufferSize = 0x10000; FEXCore::X86Tables::DecodedInst* DecodedBuffer {}; Utils::PoolBufferWithTimedRetirement PoolObject; size_t DecodedSize {}; uint64_t ExecutableRangeBase {}; uint64_t ExecutableRangeEnd {}; bool ExecutableRangeWritable {}; bool HitNonExecutableRange {}; bool HitBadRelocation {}; struct DecodeStream { // Original instruction stream RIP location. const uint8_t* InstStream; // Adjusted location for FEX actually decodes from. const uint8_t* AdjustedInstStream; DecodeStream& operator-=(size_t offset) noexcept { InstStream -= offset; AdjustedInstStream -= offset; return *this; } DecodeStream& operator+=(size_t offset) noexcept { InstStream += offset; AdjustedInstStream += offset; return *this; } }; DecodeStream InstStream; IR::OpSize GetGPROpSize() const { return BlockInfo.Is64BitMode ? IR::OpSize::i64Bit : IR::OpSize::i32Bit; } static constexpr size_t MAX_INST_SIZE = 15; uint8_t InstructionSize {}; std::array Instruction; uint8_t LastEscapePrefix {}; FEXCore::X86Tables::DecodedInst* DecodeInst; // This is for multiblock data tracking uint64_t EntryPoint {}; uint64_t MaxCondBranchForward {}; uint64_t MaxCondBranchBackwards {~0ULL}; uint64_t SectionMaxAddress {~0ULL}; uint64_t SectionMinAddress {}; uint64_t NextBlockStartAddress {~0ULL}; DecodedBlockInformation BlockInfo; fextl::set CurrentBlockTargets; fextl::set BlocksToDecode; fextl::set VisitedBlocks; fextl::set* ExternalBranches {nullptr}; const fextl::robin_map* Relocations {nullptr}; // ModRM rm decoding using DecodeModRMPtr = void (FEXCore::Frontend::Decoder::*)(X86Tables::DecodedOperand* Operand, X86Tables::ModRMDecoded ModRM); void DecodeModRM_16(X86Tables::DecodedOperand* Operand, X86Tables::ModRMDecoded ModRM); void DecodeModRM_64(X86Tables::DecodedOperand* Operand, X86Tables::ModRMDecoded ModRM); static constexpr std::array DecodeModRMs_Disp { &FEXCore::Frontend::Decoder::DecodeModRM_64, &FEXCore::Frontend::Decoder::DecodeModRM_16, }; const std::array* X87Table; const std::array* VEXTable {}; const std::array* VEXTableGroup {}; const DecodeStream AdjustAddrForSpecialRegion(const uint8_t* _InstStream, uint64_t EntryPoint, uint64_t RIP); }; } // namespace FEXCore::Frontend