mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 14:00:16 +02:00
DiskCache: detect inline data, hash around it and patch it on Lookup
Only detect certain kinds of mov reg,immediate so far, which was the bulk of Mono JIT activity.
This commit is contained in:
1 parent
50e6eee95a
commit
c92805475d
14 files changed
+210
-12
No files matched your search
@@ -132,7 +132,7 @@ public:
|
||||
// Same but on disk cache packed relocations
|
||||
[[nodiscard]]
|
||||
bool ApplyPackedCodeRelocations(uint64_t GuestDelta, std::span<std::byte> Code, std::span<const DiskCache::BlobSmallRelocation> SmallRelocs,
|
||||
std::span<const DiskCache::BlobThunkRelocation> ThunkRelocs, bool ForStorage);
|
||||
std::span<const DiskCache::BlobThunkRelocation> ThunkRelocs);
|
||||
};
|
||||
|
||||
class ContextImpl final : public FEXCore::Context::Context, public CPU::SharedCodeBufferManager {
|
||||
|
||||
@@ -538,16 +538,22 @@ ApplyRIPMoveRelocation(ContextImpl& CTX, uint64_t GuestRIP, uint8_t RegisterInde
|
||||
Emitter.LoadConstant(ARMEmitter::Size::i64Bit, ARMEmitter::Register(RegisterIndex), Pointer, CPU::Arm64Emitter::PadType::DOPAD);
|
||||
}
|
||||
|
||||
static inline void ApplyPatchableDataRelocation(uint64_t SiteAddress, uint8_t ValueSize, uint8_t RegisterIndex, CPU::Arm64Emitter& Emitter) {
|
||||
uint64_t Value = 0;
|
||||
memcpy(&Value, reinterpret_cast<const void*>(SiteAddress), ValueSize);
|
||||
Emitter.LoadConstant(ARMEmitter::Size::i64Bit, ARMEmitter::Register(RegisterIndex), Value, CPU::Arm64Emitter::PadType::DOPAD);
|
||||
}
|
||||
|
||||
bool CodeCache::ApplyPackedCodeRelocations(uint64_t GuestEntry, std::span<std::byte> Code,
|
||||
std::span<const DiskCache::BlobSmallRelocation> SmallRelocs,
|
||||
std::span<const DiskCache::BlobThunkRelocation> ThunkRelocs, bool ForStorage) {
|
||||
std::span<const DiskCache::BlobThunkRelocation> ThunkRelocs) {
|
||||
CPU::Arm64Emitter Emitter(&CTX, Code.data(), Code.size_bytes());
|
||||
for (auto& Reloc : SmallRelocs) {
|
||||
LOGMAN_THROW_A_FMT(Reloc.Offset < Code.size_bytes(), "Invalid relocation offset");
|
||||
Emitter.SetCursorOffset(Reloc.Offset);
|
||||
switch ((CPU::RelocationTypes)Reloc.Type) {
|
||||
case FEXCore::CPU::RelocationTypes::RELOC_NAMED_SYMBOL_LITERAL: {
|
||||
ApplySymbolLiteralRelocation(CTX, (CPU::RelocNamedSymbolLiteral::NamedSymbol)Reloc.Named.Symbol, GuestEntry, Emitter, ForStorage);
|
||||
ApplySymbolLiteralRelocation(CTX, (CPU::RelocNamedSymbolLiteral::NamedSymbol)Reloc.Named.Symbol, GuestEntry, Emitter, false);
|
||||
break;
|
||||
}
|
||||
case FEXCore::CPU::RelocationTypes::RELOC_GUEST_RIP_LITERAL: {
|
||||
@@ -558,13 +564,18 @@ bool CodeCache::ApplyPackedCodeRelocations(uint64_t GuestEntry, std::span<std::b
|
||||
ApplyRIPMoveRelocation(CTX, Reloc.RIPMove.GuestRIP, Reloc.RIPMove.RegisterIndex, GuestEntry, Emitter);
|
||||
break;
|
||||
}
|
||||
case FEXCore::CPU::RelocationTypes::RELOC_GUEST_PATCHABLE_DATA_MOVE: {
|
||||
ApplyPatchableDataRelocation(GuestEntry + Reloc.PatchableData.SiteOffset, Reloc.PatchableData.ValueSize,
|
||||
Reloc.PatchableData.RegisterIndex, Emitter);
|
||||
break;
|
||||
}
|
||||
default: ERROR_AND_DIE_FMT("Unknown packed relocation type {}", ToUnderlying((CPU::RelocationTypes)Reloc.Type));
|
||||
}
|
||||
}
|
||||
for (auto& Reloc : ThunkRelocs) {
|
||||
LOGMAN_THROW_A_FMT(Reloc.Offset < Code.size_bytes(), "Invalid relocation offset");
|
||||
Emitter.SetCursorOffset(Reloc.Offset);
|
||||
if (!ApplyThunkMoveRelocation(CTX, (const IR::SHA256Sum*)Reloc.SymbolHash, Reloc.RegisterIndex, Emitter, ForStorage)) {
|
||||
if (!ApplyThunkMoveRelocation(CTX, (const IR::SHA256Sum*)Reloc.SymbolHash, Reloc.RegisterIndex, Emitter, false)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -435,6 +435,9 @@ namespace DiskCache {
|
||||
RONames.remove_prefix(Delim + 1);
|
||||
}
|
||||
|
||||
WritingDiskCache = (bool)RWCacheDB;
|
||||
ReadingDiskCache = !ROCacheDBs.empty() || RWCacheDB != nullptr;
|
||||
|
||||
if (IsWritingDiskCache()) {
|
||||
FEXCore::Threads::Flags WriterThreadFlags = {.LowPriority = true, .Internal = true};
|
||||
Writer = fextl::make_unique<WorkQueueThread>(WriterThreadFlags);
|
||||
@@ -480,13 +483,23 @@ namespace DiskCache {
|
||||
return std::nullopt;
|
||||
}
|
||||
Thread->FrontendDecoder->DecodeLoop(reinterpret_cast<const uint8_t*>(GuestRIP), AnonPrefixGuestBytes);
|
||||
const auto* BlockInfo = Thread->FrontendDecoder->GetDecodedBlockInfo();
|
||||
|
||||
XXH3_state_t HashState;
|
||||
XXH3_64bits_reset(&HashState);
|
||||
for (auto& SubBlock : Thread->FrontendDecoder->GetDecodedBlockInfo()->Blocks) {
|
||||
XXH3_64bits_update(&HashState, reinterpret_cast<const uint8_t*>(SubBlock.Entry), SubBlock.Size);
|
||||
for (auto& SubBlock : BlockInfo->Blocks) {
|
||||
if (SubBlock.BlockStatus != Frontend::Decoder::DecodedBlockStatus::SUCCESS) {
|
||||
return std::nullopt;
|
||||
}
|
||||
uint64_t HashStart = SubBlock.Entry;
|
||||
// skip over masked in-block data and data/etc gaps between blocks
|
||||
for (auto& DataMask : SubBlock.DataMasks) {
|
||||
XXH3_64bits_update(&HashState, reinterpret_cast<const uint8_t*>(HashStart), DataMask.FieldAddress - HashStart);
|
||||
HashStart = DataMask.FieldAddress + DataMask.ValueSize;
|
||||
}
|
||||
if (HashStart != SubBlock.Entry + SubBlock.Size) {
|
||||
XXH3_64bits_update(&HashState, reinterpret_cast<const uint8_t*>(HashStart), SubBlock.Size - (HashStart - SubBlock.Entry));
|
||||
}
|
||||
}
|
||||
GuestCodeKey = XXH3_64bits_digest(&HashState);
|
||||
// if (TotalSize < AnonPrefixGuestBytes) {
|
||||
@@ -635,7 +648,7 @@ namespace DiskCache {
|
||||
EntryPointRip += GuestRIP;
|
||||
}
|
||||
|
||||
if (!CTX->CodeCache.ApplyPackedCodeRelocations(GuestRIP, std::as_writable_bytes(HitData.HostCode), SmallRelocs, ThunkRelocs, false)) {
|
||||
if (!CTX->CodeCache.ApplyPackedCodeRelocations(GuestRIP, std::as_writable_bytes(HitData.HostCode), SmallRelocs, ThunkRelocs)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -737,6 +750,8 @@ namespace DiskCache {
|
||||
}
|
||||
}
|
||||
|
||||
fextl::set<uint64_t> DataMaskAddresses;
|
||||
|
||||
fextl::vector<uint32_t> ExactGuestCodeExtents;
|
||||
uint64_t CurStartExtent = 0, CurEndExtent = 0;
|
||||
const Frontend::Decoder::DecodedBlocks* LastBlock = nullptr;
|
||||
@@ -758,6 +773,16 @@ namespace DiskCache {
|
||||
CurEndExtent = SubBlock.Entry + SubBlock.Size;
|
||||
}
|
||||
}
|
||||
// split extents according to data masks as well
|
||||
for (auto& Mask : SubBlock.DataMasks) {
|
||||
if (Mask.FieldAddress > CurStartExtent) {
|
||||
ExactGuestCodeExtents.push_back(CurStartExtent - GuestRIP);
|
||||
ExactGuestCodeExtents.push_back(Mask.FieldAddress - CurStartExtent);
|
||||
}
|
||||
CurStartExtent = Mask.FieldAddress + Mask.ValueSize;
|
||||
|
||||
DataMaskAddresses.insert(Mask.FieldAddress);
|
||||
}
|
||||
LastBlock = &SubBlock;
|
||||
}
|
||||
if (LastBlock && (CurStartExtent != GuestRIP || CurEndExtent != GuestRIP + GuestCode.size())) {
|
||||
@@ -860,6 +885,24 @@ namespace DiskCache {
|
||||
SmallRelocs[SmallIdx++] = SmallReloc;
|
||||
break;
|
||||
}
|
||||
case CPU::RelocationTypes::RELOC_GUEST_PATCHABLE_DATA_MOVE: {
|
||||
BlobSmallRelocation SmallReloc = {};
|
||||
SmallReloc.Offset = Reloc.Header.Offset;
|
||||
SmallReloc.Type = uint8_t(Reloc.Header.Type);
|
||||
SmallReloc.PatchableData.RegisterIndex = Reloc.GuestPatchableData.RegisterIndex;
|
||||
SmallReloc.PatchableData.ValueSize = Reloc.GuestPatchableData.ValueSize;
|
||||
SmallReloc.PatchableData.SiteOffset = uint32_t(Reloc.GuestPatchableData.SiteAddress - GuestRIP);
|
||||
SmallRelocs[SmallIdx++] = SmallReloc;
|
||||
|
||||
// mark the corresponding data mask consumed - we might not find one if they got removed due to the smc workaround
|
||||
// the hash will just fail on lookup later
|
||||
auto It = DataMaskAddresses.find(Reloc.GuestPatchableData.SiteAddress);
|
||||
if (It != DataMaskAddresses.end()) {
|
||||
DataMaskAddresses.erase(It);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case CPU::RelocationTypes::RELOC_NAMED_THUNK_MOVE: {
|
||||
BlobThunkRelocation BigReloc = {};
|
||||
BigReloc.Offset = Reloc.Header.Offset;
|
||||
@@ -871,6 +914,12 @@ namespace DiskCache {
|
||||
}
|
||||
}
|
||||
|
||||
if (!DataMaskAddresses.empty()) {
|
||||
LogMan::Msg::IFmt("DiskCache: DataMask unaccounted for! {:x}", GuestCodeKey);
|
||||
// this would mean we omitted contents in the hash that we're not going to patch, which would be loading corrupt code
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(BlobData + GuestCodeOffset, GuestCode.data(), GuestCode.size());
|
||||
|
||||
fextl::vector<uint8_t> IndexBlob;
|
||||
|
||||
@@ -140,6 +140,8 @@ std::pair<uint64_t, bool> Decoder::ReadData(uint8_t Size) {
|
||||
|
||||
uint64_t Res = 0;
|
||||
uint64_t Address = reinterpret_cast<uint64_t>(InstStream.InstStream + InstructionSize);
|
||||
LastFieldReadOffset = (uint8_t)InstructionSize;
|
||||
LastFieldReadSize = Size;
|
||||
if (CheckRangeExecutable(Address, Size)) {
|
||||
std::memcpy(&Res, &InstStream.AdjustedInstStream[InstructionSize], Size);
|
||||
} else {
|
||||
@@ -1334,6 +1336,13 @@ void Decoder::AddBranchTarget(uint64_t Target) {
|
||||
.BlockStatus = BlockIt->BlockStatus,
|
||||
};
|
||||
|
||||
if (BlockIt->DataMasks.size()) {
|
||||
auto MaskIt = std::lower_bound(BlockIt->DataMasks.begin(), BlockIt->DataMasks.end(), SplitAddr,
|
||||
[](const DataMask& Mask, uint64_t Addr) { return Mask.FieldAddress < Addr; });
|
||||
SplitBlock.DataMasks.assign(MaskIt, BlockIt->DataMasks.end());
|
||||
BlockIt->DataMasks.erase(MaskIt, BlockIt->DataMasks.end());
|
||||
}
|
||||
|
||||
BlockIt->Size = SplitOffset;
|
||||
BlockIt->NumInstructions = SplitIdx;
|
||||
|
||||
@@ -1386,7 +1395,48 @@ bool Decoder::CheckIfCacheable(FEXCore::Core::InternalThreadState& Thread, const
|
||||
return !Uncacheable;
|
||||
}
|
||||
|
||||
void Decoder::DetectDataMasks(uint64_t OpAddress, DecodedBlocks& Block) {
|
||||
if (LastFieldReadSize < 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
FEXCore::X86Tables::DecodedOperand* LiteralToPatch = nullptr;
|
||||
|
||||
// mov reg,imm
|
||||
if (DecodeInst->OP >= 0xB8 && DecodeInst->OP <= 0xBF) {
|
||||
for (auto& Src : DecodeInst->Src) {
|
||||
if (Src.IsLiteral()) {
|
||||
LiteralToPatch = &Src;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// we could filter to certain high values that are more likely to be pointers/etc?
|
||||
// const uint64_t Value = Lit->Data.Literal.Value;
|
||||
// if (LiteralToPatch && Value < 0x1000000ULL) {
|
||||
// LiteralToPatch = nullptr;
|
||||
// }
|
||||
}
|
||||
|
||||
// todo add a bunch more
|
||||
|
||||
if (LiteralToPatch) {
|
||||
Block.DataMasks.push_back({OpAddress + LastFieldReadOffset, LastFieldReadSize});
|
||||
|
||||
LiteralToPatch->Type = X86Tables::DecodedOperand::OpType::LiteralPatchable;
|
||||
LiteralToPatch->Data.LiteralPatchable.FieldOffset = LastFieldReadOffset;
|
||||
LiteralToPatch->Data.LiteralPatchable.Width = LastFieldReadSize;
|
||||
}
|
||||
}
|
||||
|
||||
void Decoder::DecodeLoop(const uint8_t* _InstStream, uint64_t GuestSizePause) {
|
||||
// counter-intuitively, the masks are also needed for lookup on anon prefix decodes, not just stores
|
||||
bool WantsDataMasks = CTX->DiskCache.IsReadingDiskCache() || CTX->DiskCache.IsWritingDiskCache();
|
||||
// remove this if we ever fixup ValidateCode crc constant after relocations
|
||||
if (CTX->Config.SMCChecks == FEXCore::Config::CONFIG_SMC_FULL) {
|
||||
WantsDataMasks = false;
|
||||
}
|
||||
|
||||
while (!FinalInstruction && (Paused || !BlocksToDecode.empty())) {
|
||||
bool Pausing = false;
|
||||
fextl::vector<DecodedBlocks>::iterator BlockIt;
|
||||
@@ -1461,6 +1511,7 @@ void Decoder::DecodeLoop(const uint8_t* _InstStream, uint64_t GuestSizePause) {
|
||||
BlockInfo.CodePages.insert(CurrentCodePage);
|
||||
}
|
||||
|
||||
LastFieldReadSize = 0;
|
||||
BlockIt->BlockStatus = DecodeInstruction(OpAddress);
|
||||
if (HitBadRelocation) {
|
||||
BlockInfo.TotalInstructionCount = 0;
|
||||
@@ -1485,6 +1536,11 @@ void Decoder::DecodeLoop(const uint8_t* _InstStream, uint64_t GuestSizePause) {
|
||||
++BlockIt->NumInstructions;
|
||||
BlockIt->Size += DecodeInst->InstSize;
|
||||
|
||||
// if we weren't provided relocations (guest JIT), try to detect what we can
|
||||
if (WantsDataMasks && BlockIt->BlockStatus == DecodedBlockStatus::SUCCESS && BlockInfo.Is64BitMode && !Relocations) {
|
||||
DetectDataMasks(OpAddress, *BlockIt);
|
||||
}
|
||||
|
||||
// Can not continue this block at all on invalid instruction
|
||||
if (BlockIt->BlockStatus != DecodedBlockStatus::SUCCESS) [[unlikely]] {
|
||||
if (!EntryBlock && BlockIt->BlockStatus != DecodedBlockStatus::BAD_RELOCATION) {
|
||||
@@ -1531,7 +1587,12 @@ void Decoder::DecodeLoop(const uint8_t* _InstStream, uint64_t GuestSizePause) {
|
||||
// If the branch target is within our multiblock range then we can keep going on
|
||||
// We don't want to short circuit this since we want to calculate our ranges still
|
||||
// NOTE: This will invalidate BlockIt, this is fine as we immediately break from the loop and EraseBlock cannot be true
|
||||
BlockIt->ForceFullSMCDetection = CTX->AreMonoHacksActive() && IsBranchMonoTailcall(BlockIt->NumInstructions);
|
||||
if (CTX->AreMonoHacksActive() && IsBranchMonoTailcall(BlockIt->NumInstructions)) {
|
||||
BlockIt->ForceFullSMCDetection = true;
|
||||
// todo abandon patching this for now, as the crc will fail and it will lock up redoing it over and over
|
||||
// we should fix the crc at relocation if this is important
|
||||
BlockIt->DataMasks.clear();
|
||||
}
|
||||
BranchTargetInMultiblockRange();
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,11 @@ public:
|
||||
UNIMPLEMENTED_INST,
|
||||
};
|
||||
|
||||
struct DataMask final {
|
||||
uint64_t FieldAddress;
|
||||
uint8_t ValueSize;
|
||||
};
|
||||
|
||||
// New Frontend decoding
|
||||
struct DecodedBlocks final {
|
||||
uint64_t Entry {};
|
||||
@@ -41,6 +46,7 @@ public:
|
||||
DecodedBlockStatus BlockStatus;
|
||||
bool IsEntryPoint {};
|
||||
bool ForceFullSMCDetection {};
|
||||
fextl::vector<DataMask> DataMasks;
|
||||
};
|
||||
|
||||
struct DecodedBlockInformation final {
|
||||
@@ -103,6 +109,8 @@ private:
|
||||
|
||||
void AddBranchTarget(uint64_t Target);
|
||||
|
||||
void DetectDataMasks(uint64_t OpAddress, DecodedBlocks& Block);
|
||||
|
||||
bool CheckRangeExecutable(uint64_t Address, uint64_t Size);
|
||||
|
||||
uint8_t ReadByte();
|
||||
@@ -133,6 +141,9 @@ private:
|
||||
uint64_t BlockStartOffset {};
|
||||
bool EraseBlock {};
|
||||
|
||||
uint8_t LastFieldReadOffset;
|
||||
uint8_t LastFieldReadSize;
|
||||
|
||||
uint64_t ExecutableRangeBase {};
|
||||
uint64_t ExecutableRangeEnd {};
|
||||
bool ExecutableRangeWritable {};
|
||||
|
||||
@@ -64,6 +64,11 @@ DEF_OP(EntrypointOffset) {
|
||||
InsertGuestRIPMove(GetReg(Node), Constant & Mask);
|
||||
}
|
||||
|
||||
DEF_OP(PatchableGuestData) {
|
||||
auto Op = IROp->C<IR::IROp_PatchableGuestData>();
|
||||
InsertGuestPatchableDataMove(GetReg(Node), Op->Value, Op->SiteAddress, (uint8_t)Op->SiteSize);
|
||||
}
|
||||
|
||||
DEF_OP(InlineConstant) {
|
||||
// nop
|
||||
}
|
||||
|
||||
@@ -102,6 +102,18 @@ void Arm64JITCore::InsertGuestRIPMove(ARMEmitter::Register Reg, uint64_t Constan
|
||||
Relocations.emplace_back(MoveABI);
|
||||
}
|
||||
|
||||
void Arm64JITCore::InsertGuestPatchableDataMove(ARMEmitter::Register Reg, uint64_t Value, uint64_t SiteAddress, uint8_t ValueSize) {
|
||||
Relocation MoveABI = Relocation::Default();
|
||||
MoveABI.GuestPatchableData.Header = {.Offset = GetCursorOffset(), .Type = FEXCore::CPU::RelocationTypes::RELOC_GUEST_PATCHABLE_DATA_MOVE};
|
||||
MoveABI.GuestPatchableData.RegisterIndex = Reg.Idx();
|
||||
MoveABI.GuestPatchableData.ValueSize = ValueSize;
|
||||
MoveABI.GuestPatchableData.SiteAddress = SiteAddress;
|
||||
|
||||
// this might get patched on disk cache load
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, Reg, Value, FEXCore::CPU::Arm64Emitter::PadType::DOPAD);
|
||||
Relocations.emplace_back(MoveABI);
|
||||
}
|
||||
|
||||
fextl::vector<FEXCore::CPU::Relocation> Arm64JITCore::TakeRelocations(uint64_t GuestBaseAddress) {
|
||||
// Rebase relocations to library base address
|
||||
for (auto& Relocation : Relocations) {
|
||||
|
||||
@@ -562,6 +562,8 @@ private:
|
||||
*/
|
||||
void InsertGuestRIPMove(ARMEmitter::Register Reg, uint64_t Constant);
|
||||
|
||||
void InsertGuestPatchableDataMove(ARMEmitter::Register Reg, uint64_t Value, uint64_t SiteAddress, uint8_t ValueSize);
|
||||
|
||||
/**
|
||||
* @brief Inserts a named symbol as a literal in memory
|
||||
*
|
||||
|
||||
@@ -25,6 +25,10 @@ enum class RelocationTypes : uint32_t {
|
||||
// 4 instruction constant generation
|
||||
// Aligned to struct RelocGuestRIP
|
||||
RELOC_GUEST_RIP_MOVE,
|
||||
|
||||
// The frontend flagged those regions as patchable by the disk cache
|
||||
// Aligned to struct RelocGuestPatchableData
|
||||
RELOC_GUEST_PATCHABLE_DATA_MOVE,
|
||||
};
|
||||
|
||||
struct FEX_PACKED RelocationHeader final {
|
||||
@@ -73,6 +77,20 @@ struct RelocGuestRIP final {
|
||||
uint32_t pad2[6] {};
|
||||
};
|
||||
|
||||
struct RelocGuestPatchableData final {
|
||||
RelocationHeader Header {};
|
||||
|
||||
uint8_t RegisterIndex;
|
||||
|
||||
uint8_t ValueSize;
|
||||
|
||||
char Pad[2];
|
||||
|
||||
uint64_t SiteAddress;
|
||||
|
||||
uint32_t pad2[6] {};
|
||||
};
|
||||
|
||||
union Relocation {
|
||||
// Clang 16 Can't default-initialize this union
|
||||
static Relocation Default() {
|
||||
@@ -93,6 +111,8 @@ union Relocation {
|
||||
RelocNamedThunkMove NamedThunkMove;
|
||||
|
||||
RelocGuestRIP GuestRIP;
|
||||
|
||||
RelocGuestPatchableData GuestPatchableData;
|
||||
};
|
||||
|
||||
uint64_t GetNamedSymbolLiteral(FEXCore::Context::ContextImpl&, RelocNamedSymbolLiteral::NamedSymbol);
|
||||
|
||||
@@ -4357,6 +4357,9 @@ AddressMode OpDispatchBuilder::DecodeAddress(const X86Tables::DecodedOp& Op, con
|
||||
A.NonTSO |= IsNonTSOReg(AccessType, Operand.Data.SIB.Base) || IsNonTSOReg(AccessType, Operand.Data.SIB.Index);
|
||||
} else if (Operand.IsLiteralRelocation()) {
|
||||
A.Base = _EntrypointOffset(GPRSize, Operand.Data.LiteralRelocation.EntrypointOffset);
|
||||
} else if (Operand.IsLiteralPatchable()) {
|
||||
A.Base = _PatchableGuestData(OpSize::i64Bit, Operand.Data.LiteralPatchable.Value, Op->PC + Operand.Data.LiteralPatchable.FieldOffset,
|
||||
static_cast<uint64_t>(Operand.Data.LiteralPatchable.Width));
|
||||
} else {
|
||||
LOGMAN_MSG_A_FMT("Unknown Src Type: {}\n", Operand.Type);
|
||||
}
|
||||
|
||||
@@ -1521,7 +1521,7 @@ private:
|
||||
[[nodiscard]]
|
||||
static bool IsOperandMem(const X86Tables::DecodedOperand& Operand, bool Load) {
|
||||
// Literals are immediates as sources but memory addresses as destinations.
|
||||
return !(Load && (Operand.IsLiteral() || Operand.IsLiteralRelocation())) && !Operand.IsGPR();
|
||||
return !(Load && (Operand.IsLiteral() || Operand.IsLiteralRelocation() || Operand.IsLiteralPatchable())) && !Operand.IsGPR();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
|
||||
@@ -128,6 +128,7 @@ struct DecodedOperand {
|
||||
RIPRelativeRelocation,
|
||||
Literal,
|
||||
LiteralRelocation,
|
||||
LiteralPatchable,
|
||||
SIB,
|
||||
SIBRelocation
|
||||
};
|
||||
@@ -159,6 +160,9 @@ struct DecodedOperand {
|
||||
bool IsLiteralRelocation() const {
|
||||
return Type == OpType::LiteralRelocation;
|
||||
}
|
||||
bool IsLiteralPatchable() const {
|
||||
return Type == OpType::LiteralPatchable;
|
||||
}
|
||||
bool IsSIB() const {
|
||||
return Type == OpType::SIB;
|
||||
}
|
||||
@@ -196,6 +200,12 @@ struct DecodedOperand {
|
||||
int64_t EntrypointOffset;
|
||||
} LiteralRelocation;
|
||||
|
||||
struct {
|
||||
uint64_t Value;
|
||||
uint8_t Size;
|
||||
uint8_t FieldOffset;
|
||||
uint8_t Width;
|
||||
} LiteralPatchable;
|
||||
struct {
|
||||
int64_t Offset;
|
||||
uint8_t Scale;
|
||||
|
||||
@@ -953,6 +953,13 @@
|
||||
]
|
||||
},
|
||||
|
||||
"GPR = PatchableGuestData OpSize:#Size, i64:$Value, i64:$SiteAddress, i64:$SiteSize": {
|
||||
"Desc": ["Loads Value in a patchable way",
|
||||
"On disk cache load the value is patched from live guest bytes at SiteAddress"
|
||||
],
|
||||
"DestSize": "Size"
|
||||
},
|
||||
|
||||
"GPR = Constant i64:$Constant, ConstPad:$Pad{IR::ConstPad::NoPad}, i32:$MaxBytes{0}": {
|
||||
"Desc": ["Generates a 64bit constant inside of a GPR",
|
||||
"Unsupported to create a constant in FPR"
|
||||
|
||||
@@ -85,6 +85,11 @@ namespace DiskCache {
|
||||
uint8_t RegisterIndex;
|
||||
uint64_t GuestRIP;
|
||||
} RIPMove;
|
||||
struct __attribute__((packed)) {
|
||||
uint8_t RegisterIndex;
|
||||
uint8_t ValueSize;
|
||||
uint32_t SiteOffset;
|
||||
} PatchableData;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -176,10 +181,10 @@ namespace DiskCache {
|
||||
std::span<const FEXCore::CPU::Relocation> Relocations, const Frontend::Decoder::DecodedBlockInformation* DecodedBlockInfo);
|
||||
|
||||
bool IsWritingDiskCache() const {
|
||||
return (bool)RWCacheDB;
|
||||
return WritingDiskCache;
|
||||
}
|
||||
bool IsReadingDiskCache() const {
|
||||
return !ROCacheDBs.empty() || RWCacheDB != nullptr;
|
||||
return ReadingDiskCache;
|
||||
}
|
||||
bool IsValidating() const {
|
||||
return Validation;
|
||||
@@ -189,6 +194,8 @@ namespace DiskCache {
|
||||
bool OpenCacheDB(const fextl::string& CacheDBName, bool ReadOnly);
|
||||
uint64_t MakeBlobKey(Core::InternalThreadState* Thread, const uint64_t ModuleOffset, bool Writable, bool MonoBackpatcher);
|
||||
|
||||
bool ReadingDiskCache {};
|
||||
bool WritingDiskCache {};
|
||||
FEXCore::Context::ContextImpl* CTX;
|
||||
XXH128_hash_t BucketHash;
|
||||
fextl::vector<fextl::unique_ptr<IndexedDB>> ROCacheDBs;
|
||||
@@ -214,7 +221,7 @@ namespace DiskCache {
|
||||
|
||||
// TODO: This header is in global installed header path, but uses internal headers.
|
||||
// Migrate this once that is fixed.
|
||||
static constexpr uint16_t FormatVersion = 16;
|
||||
static constexpr uint16_t FormatVersion = 17;
|
||||
FEX_DEFAULT_VISIBILITY uint16_t GetFormatVersion();
|
||||
|
||||
} // namespace DiskCache
|
||||
|
||||
Reference in new issue
Block a user