mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 08:00:15 +02:00
All buffers should be disowned leaving their respective compilation sites, and reowning a buffer should never have the flag already be owned. Throw an assert in both cases because that would be a programming error and result in some squirrely buffer handling
208 lines
6.4 KiB
C++
208 lines
6.4 KiB
C++
// SPDX-License-Identifier: MIT
|
|
#pragma once
|
|
|
|
#include "Interface/Core/X86Tables/X86Tables.h"
|
|
#include "Interface/IR/IR.h"
|
|
|
|
#include <FEXCore/Config/Config.h>
|
|
#include <FEXCore/Core/CodeCache.h>
|
|
#include <FEXCore/Utils/ThreadPoolAllocator.h>
|
|
#include <FEXCore/fextl/set.h>
|
|
#include <FEXCore/fextl/vector.h>
|
|
#include <FEXCore/fextl/robin_map.h>
|
|
|
|
#include <array>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <optional>
|
|
|
|
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<DecodedBlocks> Blocks;
|
|
fextl::set<uint64_t> EntryPoints;
|
|
fextl::set<uint64_t> 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 SetupDecodeInstructionsAtEntry(FEXCore::Core::InternalThreadState* Thread, uint64_t PC, uint64_t MaxInst);
|
|
void DecodeLoop(const uint8_t* InstStream, uint64_t GuestPause = 0);
|
|
|
|
const DecodedBlockInformation* GetDecodedBlockInfo() const {
|
|
return &BlockInfo;
|
|
}
|
|
|
|
uint64_t DecodedMinAddress {};
|
|
uint64_t DecodedMaxAddress {~0ULL};
|
|
|
|
void SetExternalBranches(fextl::set<uint64_t>* v) {
|
|
ExternalBranches = v;
|
|
}
|
|
|
|
void DelayedDisownBuffer() {
|
|
PoolObject.DelayedDisownBuffer();
|
|
}
|
|
|
|
void ValidateDisownedOrFree() const {
|
|
PoolObject.ValidateDisownedOrFree();
|
|
}
|
|
|
|
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<uint8_t> PeekByte(uint8_t Offset);
|
|
std::pair<uint64_t, bool> 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<FEXCore::X86Tables::DecodedInst*, 5000, 500> PoolObject;
|
|
size_t DecodedSize {};
|
|
uint64_t TotalInstructions {};
|
|
uint64_t CurrentCodePage {};
|
|
bool EntryBlock {};
|
|
bool FinalInstruction {};
|
|
uint64_t MaxInst {};
|
|
bool Paused {};
|
|
int64_t BlockResume = -1;
|
|
uint64_t PCOffset {};
|
|
uint64_t BlockStartOffset {};
|
|
bool EraseBlock {};
|
|
|
|
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 {};
|
|
// Contains the full decoded instruction, unless it is a `Thunk` instruction.
|
|
std::array<uint8_t, MAX_INST_SIZE> 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<uint64_t> CurrentBlockTargets;
|
|
fextl::set<uint64_t> BlocksToDecode;
|
|
fextl::set<uint64_t> VisitedBlocks;
|
|
fextl::set<uint64_t>* ExternalBranches {nullptr};
|
|
|
|
const fextl::robin_map<uint32_t, GuestRelocationType>* 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<DecodeModRMPtr, 2> DecodeModRMs_Disp {
|
|
&FEXCore::Frontend::Decoder::DecodeModRM_64,
|
|
&FEXCore::Frontend::Decoder::DecodeModRM_16,
|
|
};
|
|
|
|
const std::array<X86Tables::X86InstInfo, X86Tables::MAX_X87_TABLE_SIZE>* X87Table;
|
|
|
|
const std::array<X86Tables::X86InstInfo, X86Tables::MAX_VEX_TABLE_SIZE>* VEXTable {};
|
|
const std::array<X86Tables::X86InstInfo, X86Tables::MAX_VEX_GROUP_TABLE_SIZE>* VEXTableGroup {};
|
|
|
|
const DecodeStream AdjustAddrForSpecialRegion(const uint8_t* _InstStream, uint64_t EntryPoint, uint64_t RIP);
|
|
};
|
|
} // namespace FEXCore::Frontend
|