mirror of
https://github.com/FEX-Emu/FEX.git
synced 2025-01-11 00:26:15 +00:00
IR: Add AOT Cache
This commit is contained in:
parent
7b97c3fa23
commit
816c4656df
@ -151,6 +151,13 @@ namespace FEXCore::Context {
|
||||
return CTX->CPUID.RunFunction(Function);
|
||||
}
|
||||
|
||||
bool ReadAOT(FEXCore::Context::Context *CTX, std::istream& stream) {
|
||||
return CTX->LoadAOTCache(stream);
|
||||
}
|
||||
void WriteAOT(FEXCore::Context::Context *CTX, std::ostream& stream) {
|
||||
CTX->WriteAOTCache(stream);
|
||||
}
|
||||
|
||||
namespace Debug {
|
||||
void CompileRIP(FEXCore::Context::Context *CTX, uint64_t RIP) {
|
||||
CTX->CompileRIP(CTX->ParentThread, RIP);
|
||||
|
@ -6,13 +6,18 @@
|
||||
#include "Interface/Core/InternalThreadState.h"
|
||||
#include "Interface/Core/X86HelperGen.h"
|
||||
#include "Interface/IR/PassManager.h"
|
||||
#include "Interface/IR/Passes/RegisterAllocationPass.h"
|
||||
#include <FEXCore/Config/Config.h>
|
||||
#include <FEXCore/Core/CPUBackend.h>
|
||||
#include <FEXCore/Utils/Event.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <mutex>
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
|
||||
namespace FEXCore {
|
||||
class ThunkHandler;
|
||||
@ -30,6 +35,8 @@ class SyscallHandler;
|
||||
|
||||
namespace FEXCore::IR {
|
||||
class RegisterAllocationPass;
|
||||
class RegisterAllocationData;
|
||||
class IRListView;
|
||||
namespace Validation {
|
||||
class IRValidation;
|
||||
}
|
||||
@ -96,6 +103,14 @@ namespace FEXCore::Context {
|
||||
CustomCPUFactoryType FallbackCPUFactory;
|
||||
std::function<void(uint64_t ThreadId, FEXCore::Context::ExitReason)> CustomExitHandler;
|
||||
|
||||
struct AOTCacheEntry {
|
||||
uint64_t start;
|
||||
uint64_t len;
|
||||
uint64_t crc;
|
||||
IR::IRListView *IR;
|
||||
IR::RegisterAllocationData *RAData;
|
||||
};
|
||||
std::map<uint64_t, AOTCacheEntry> AOTCache;
|
||||
#ifdef BLOCKSTATS
|
||||
std::unique_ptr<FEXCore::BlockSamplingData> BlockData;
|
||||
#endif
|
||||
@ -142,11 +157,13 @@ namespace FEXCore::Context {
|
||||
FEXCore::Core::ThreadState *GetThreadState();
|
||||
void LoadEntryList();
|
||||
|
||||
std::tuple<FEXCore::IR::IRListView<true> *, FEXCore::IR::RegisterAllocationData *, uint64_t, uint64_t> GenerateIR(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
|
||||
std::tuple<FEXCore::IR::IRListView *, FEXCore::IR::RegisterAllocationData *, uint64_t, uint64_t> GenerateIR(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
|
||||
|
||||
std::tuple<void *, FEXCore::IR::IRListView<true> *, FEXCore::Core::DebugData *, FEXCore::IR::RegisterAllocationData *, bool> CompileCode(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
|
||||
std::tuple<void *, FEXCore::IR::IRListView *, FEXCore::Core::DebugData *, FEXCore::IR::RegisterAllocationData *, bool> CompileCode(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
|
||||
uintptr_t CompileBlock(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
|
||||
|
||||
bool LoadAOTCache(std::istream &stream);
|
||||
void WriteAOTCache(std::ostream &stream);
|
||||
// Used for thread creation from syscalls
|
||||
void InitializeCompiler(FEXCore::Core::InternalThreadState* State, bool CompileThread);
|
||||
FEXCore::Core::InternalThreadState* CreateThread(FEXCore::Core::CPUState *NewThreadState, uint64_t ParentTID);
|
||||
|
@ -31,7 +31,7 @@ class CompileService final {
|
||||
|
||||
// Outgoing
|
||||
void *CodePtr{};
|
||||
FEXCore::IR::IRListView<true> *IRList{};
|
||||
FEXCore::IR::IRListView *IRList{};
|
||||
FEXCore::IR::RegisterAllocationData *RAData{};
|
||||
FEXCore::Core::DebugData *DebugData{};
|
||||
|
||||
|
102
External/FEXCore/Source/Interface/Core/Core.cpp
vendored
102
External/FEXCore/Source/Interface/Core/Core.cpp
vendored
@ -35,6 +35,8 @@ namespace FEXCore::CPU {
|
||||
}
|
||||
}
|
||||
|
||||
static std::mutex AOTCacheLock;
|
||||
|
||||
namespace FEXCore::Core {
|
||||
struct ThreadLocalData {
|
||||
FEXCore::Core::InternalThreadState* Thread;
|
||||
@ -111,7 +113,7 @@ namespace DefaultFallbackCore {
|
||||
void Initialize() override {}
|
||||
bool NeedsOpDispatch() override { return false; }
|
||||
|
||||
void *CompileCode(FEXCore::IR::IRListView<true> const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) override {
|
||||
void *CompileCode(FEXCore::IR::IRListView const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) override {
|
||||
LogMan::Msg::E("Fell back to default code handler at RIP: 0x%lx", ThreadState->State.State.rip);
|
||||
return nullptr;
|
||||
}
|
||||
@ -572,7 +574,7 @@ namespace FEXCore::Context {
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<FEXCore::IR::IRListView<true> *, FEXCore::IR::RegisterAllocationData *, uint64_t, uint64_t> Context::GenerateIR(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
|
||||
std::tuple<FEXCore::IR::IRListView *, FEXCore::IR::RegisterAllocationData *, uint64_t, uint64_t> Context::GenerateIR(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
|
||||
uint8_t const *GuestCode{};
|
||||
GuestCode = reinterpret_cast<uint8_t const*>(GuestRIP);
|
||||
|
||||
@ -764,8 +766,8 @@ namespace FEXCore::Context {
|
||||
return {IRList, RAData.release(), TotalInstructions, TotalInstructionsLength};
|
||||
}
|
||||
|
||||
std::tuple<void *, FEXCore::IR::IRListView<true> *, FEXCore::Core::DebugData *, FEXCore::IR::RegisterAllocationData *, bool> Context::CompileCode(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
|
||||
FEXCore::IR::IRListView<true> *IRList {};
|
||||
std::tuple<void *, FEXCore::IR::IRListView *, FEXCore::Core::DebugData *, FEXCore::IR::RegisterAllocationData *, bool> Context::CompileCode(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
|
||||
FEXCore::IR::IRListView *IRList {};
|
||||
FEXCore::Core::DebugData *DebugData {};
|
||||
FEXCore::IR::RegisterAllocationData *RAData {};
|
||||
bool GeneratedIR {};
|
||||
@ -781,8 +783,21 @@ namespace FEXCore::Context {
|
||||
RAData = Thread->RALists.find(GuestRIP)->second.get();
|
||||
|
||||
GeneratedIR = false;
|
||||
} else {
|
||||
}
|
||||
|
||||
if (IRList == nullptr) {
|
||||
std::lock_guard<std::mutex> lk(AOTCacheLock);
|
||||
auto AOTEntry = AOTCache.find(GuestRIP);
|
||||
if (AOTEntry != AOTCache.end()) {
|
||||
IRList = AOTEntry->second.IR;
|
||||
RAData = AOTEntry->second.RAData;
|
||||
DebugData = new FEXCore::Core::DebugData();
|
||||
|
||||
GeneratedIR = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (IRList == nullptr) {
|
||||
// Generate IR + Meta Info
|
||||
auto [IRCopy, RACopy, TotalInstructions, TotalInstructionsLength] = GenerateIR(Thread, GuestRIP);
|
||||
|
||||
@ -806,6 +821,76 @@ namespace FEXCore::Context {
|
||||
return { Thread->CPUBackend->CompileCode(IRList, DebugData, RAData), IRList, DebugData, RAData, GeneratedIR};
|
||||
}
|
||||
|
||||
bool Context::LoadAOTCache(std::istream &stream) {
|
||||
std::lock_guard<std::mutex> lk(AOTCacheLock);
|
||||
AOTCache.clear();
|
||||
uint64_t tag;
|
||||
stream.read((char*)&tag, sizeof(tag));
|
||||
if (!stream || tag != 0xDEADBEEFC0D30000)
|
||||
return false;
|
||||
do {
|
||||
uint64_t addr, start, crc, len;
|
||||
stream.read((char*)&addr, sizeof(addr));
|
||||
if (!stream)
|
||||
return true;
|
||||
|
||||
stream.read((char*)&start, sizeof(start));
|
||||
if (!stream)
|
||||
return false;
|
||||
stream.read((char*)&len, sizeof(len));
|
||||
if (!stream)
|
||||
return false;
|
||||
stream.read((char*)&crc, sizeof(crc));
|
||||
if (!stream)
|
||||
return false;
|
||||
auto IR = new IR::IRListView(stream);
|
||||
if (!stream) {
|
||||
delete IR;
|
||||
return false;
|
||||
}
|
||||
uint64_t RASize;
|
||||
stream.read((char*)&RASize, sizeof(RASize));
|
||||
if (!stream) {
|
||||
delete IR;
|
||||
return false;
|
||||
}
|
||||
IR::RegisterAllocationData *RAData = (IR::RegisterAllocationData *)malloc(IR::RegisterAllocationData::Size(RASize));
|
||||
RAData->MapCount = RASize;
|
||||
|
||||
stream.read((char*)&RAData->Map[0], sizeof(RAData->Map[0]) * RASize);
|
||||
|
||||
if (!stream) {
|
||||
delete IR;
|
||||
return false;
|
||||
}
|
||||
stream.read((char*)&RAData->SpillSlotCount, sizeof(RAData->SpillSlotCount));
|
||||
if (!stream) {
|
||||
delete IR;
|
||||
return false;
|
||||
}
|
||||
AOTCache.insert({addr, {start, len, crc, IR, RAData}});
|
||||
} while(!stream.eof());
|
||||
return true;
|
||||
}
|
||||
|
||||
void Context::WriteAOTCache(std::ostream &stream) {
|
||||
std::lock_guard<std::mutex> lk(AOTCacheLock);
|
||||
uint64_t tag = 0xDEADBEEFC0D30000;
|
||||
stream.write((char*)&tag, sizeof(tag));
|
||||
for (auto entry: AOTCache) {
|
||||
stream.write((char*)&entry.first, sizeof(entry.first));
|
||||
stream.write((char*)&entry.second.start, sizeof(entry.second.start));
|
||||
stream.write((char*)&entry.second.len, sizeof(entry.second.len));
|
||||
stream.write((char*)&entry.second.crc, sizeof(entry.second.crc));
|
||||
entry.second.IR->Serialize(stream);
|
||||
uint64_t RASize = entry.second.RAData->MapCount;
|
||||
stream.write((char*)&RASize, sizeof(RASize));
|
||||
stream.write((char*)&entry.second.RAData->Map[0], sizeof(entry.second.RAData->Map[0]) * RASize);
|
||||
stream.write((char*)&entry.second.RAData->SpillSlotCount, sizeof(entry.second.RAData->SpillSlotCount));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uintptr_t Context::CompileBlock(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
|
||||
|
||||
// Is the code in the cache?
|
||||
@ -815,7 +900,7 @@ namespace FEXCore::Context {
|
||||
}
|
||||
|
||||
void *CodePtr {};
|
||||
FEXCore::IR::IRListView<true> *IRList {};
|
||||
FEXCore::IR::IRListView *IRList {};
|
||||
FEXCore::Core::DebugData *DebugData {};
|
||||
FEXCore::IR::RegisterAllocationData *RAData {};
|
||||
|
||||
@ -872,6 +957,11 @@ namespace FEXCore::Context {
|
||||
Thread->IRLists.emplace(GuestRIP, IRList);
|
||||
Thread->DebugData.emplace(GuestRIP, DebugData);
|
||||
Thread->RALists.emplace(GuestRIP, RAData);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(AOTCacheLock);
|
||||
AOTCache.insert({GuestRIP, {0, 0, 0, IRList, RAData}});
|
||||
}
|
||||
}
|
||||
|
||||
if (DecrementRefCount)
|
||||
|
@ -992,6 +992,9 @@ bool Decoder::DecodeInstructionsAtEntry(uint8_t const* _InstStream, uint64_t PC)
|
||||
SymbolMinAddress = EntryPoint;
|
||||
}
|
||||
|
||||
DecodedMinAddress = EntryPoint;
|
||||
DecodedMaxAddress = EntryPoint;
|
||||
|
||||
// Entry is a jump target
|
||||
BlocksToDecode.emplace(PC);
|
||||
|
||||
@ -1024,6 +1027,9 @@ bool Decoder::DecodeInstructionsAtEntry(uint8_t const* _InstStream, uint64_t PC)
|
||||
break;
|
||||
}
|
||||
|
||||
DecodedMinAddress = std::min(DecodedMinAddress, PCOffset);
|
||||
DecodedMaxAddress = std::max(DecodedMaxAddress, PCOffset + DecodeInst->InstSize);
|
||||
|
||||
++TotalInstructions;
|
||||
++BlockNumberOfInstructions;
|
||||
++DecodedSize;
|
||||
|
@ -30,6 +30,9 @@ public:
|
||||
return &Blocks;
|
||||
}
|
||||
|
||||
uint64_t DecodedMinAddress {};
|
||||
uint64_t DecodedMaxAddress {~0ULL};
|
||||
|
||||
private:
|
||||
FEXCore::Context::Context *CTX;
|
||||
|
||||
|
@ -22,7 +22,7 @@ public:
|
||||
explicit InterpreterCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread);
|
||||
~InterpreterCore() override;
|
||||
std::string GetName() override { return "Interpreter"; }
|
||||
void *CompileCode(FEXCore::IR::IRListView<true> const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) override;
|
||||
void *CompileCode(FEXCore::IR::IRListView const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) override;
|
||||
|
||||
void *MapRegion(void* HostPtr, uint64_t, uint64_t) override { return HostPtr; }
|
||||
|
||||
|
@ -111,7 +111,7 @@ InterpreterCore::~InterpreterCore() {
|
||||
}
|
||||
|
||||
|
||||
void *InterpreterCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView<true> const *IR, [[maybe_unused]] FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) {
|
||||
void *InterpreterCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const *IR, [[maybe_unused]] FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) {
|
||||
return reinterpret_cast<void*>(InterpreterExecution);
|
||||
}
|
||||
|
||||
|
@ -922,7 +922,7 @@ bool InterpreterOps::GetFallbackHandler(IR::IROp_Header *IROp, FallbackInfo *Inf
|
||||
return false;
|
||||
}
|
||||
|
||||
void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, FEXCore::IR::IRListView<true> *CurrentIR, FEXCore::Core::DebugData *DebugData) {
|
||||
void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, FEXCore::IR::IRListView *CurrentIR, FEXCore::Core::DebugData *DebugData) {
|
||||
volatile void* stack = alloca(0);
|
||||
|
||||
// Debug data is only passed in debug builds
|
||||
|
@ -3,8 +3,7 @@ namespace FEXCore::Core {
|
||||
}
|
||||
|
||||
namespace FEXCore::IR {
|
||||
template<bool copy>
|
||||
class IRListView;
|
||||
class IRListView;
|
||||
}
|
||||
|
||||
namespace FEXCore::Core{
|
||||
@ -37,7 +36,7 @@ namespace FEXCore::CPU {
|
||||
class InterpreterOps {
|
||||
|
||||
public:
|
||||
static void InterpretIR(FEXCore::Core::InternalThreadState *Thread, FEXCore::IR::IRListView<true> *CurrentIR, FEXCore::Core::DebugData *DebugData);
|
||||
static void InterpretIR(FEXCore::Core::InternalThreadState *Thread, FEXCore::IR::IRListView *CurrentIR, FEXCore::Core::DebugData *DebugData);
|
||||
static bool GetFallbackHandler(IR::IROp_Header *IROp, FallbackInfo *Info);
|
||||
};
|
||||
};
|
@ -1033,7 +1033,7 @@ bool JITCore::IsGPR(uint32_t Node) {
|
||||
return Class == IR::GPRClass || Class == IR::GPRFixedClass;
|
||||
}
|
||||
|
||||
void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView<true> const *IR, [[maybe_unused]] FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) {
|
||||
void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const *IR, [[maybe_unused]] FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) {
|
||||
using namespace aarch64;
|
||||
JumpTargets.clear();
|
||||
uint32_t SSACount = IR->GetSSACount();
|
||||
|
@ -78,7 +78,7 @@ public:
|
||||
|
||||
~JITCore() override;
|
||||
std::string GetName() override { return "JIT"; }
|
||||
void *CompileCode(FEXCore::IR::IRListView<true> const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) override;
|
||||
void *CompileCode(FEXCore::IR::IRListView const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) override;
|
||||
|
||||
void *MapRegion(void* HostPtr, uint64_t, uint64_t) override { return HostPtr; }
|
||||
|
||||
@ -100,7 +100,7 @@ private:
|
||||
Label *PendingTargetLabel;
|
||||
FEXCore::Context::Context *CTX;
|
||||
FEXCore::Core::InternalThreadState *State;
|
||||
FEXCore::IR::IRListView<true> const *IR;
|
||||
FEXCore::IR::IRListView const *IR;
|
||||
|
||||
std::map<IR::OrderedNodeWrapper::NodeOffsetType, aarch64::Label> JumpTargets;
|
||||
|
||||
|
@ -838,7 +838,7 @@ std::tuple<JITCore::SetCC, JITCore::CMovCC, JITCore::JCC> JITCore::GetCC(IR::Con
|
||||
return { &CodeGenerator::sete , &CodeGenerator::cmove , &CodeGenerator::je };
|
||||
}
|
||||
|
||||
void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView<true> const *IR, [[maybe_unused]] FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) {
|
||||
void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const *IR, [[maybe_unused]] FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) {
|
||||
JumpTargets.clear();
|
||||
uint32_t SSACount = IR->GetSSACount();
|
||||
|
||||
|
@ -59,7 +59,7 @@ public:
|
||||
explicit JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, CodeBuffer Buffer, bool CompileThread);
|
||||
~JITCore() override;
|
||||
std::string GetName() override { return "JIT"; }
|
||||
void *CompileCode(FEXCore::IR::IRListView<true> const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) override;
|
||||
void *CompileCode(FEXCore::IR::IRListView const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) override;
|
||||
|
||||
void *MapRegion(void* HostPtr, uint64_t, uint64_t) override { return HostPtr; }
|
||||
|
||||
@ -79,7 +79,7 @@ private:
|
||||
Label* PendingTargetLabel{};
|
||||
FEXCore::Context::Context *CTX;
|
||||
FEXCore::Core::InternalThreadState *ThreadState;
|
||||
FEXCore::IR::IRListView<true> const *IR;
|
||||
FEXCore::IR::IRListView const *IR;
|
||||
|
||||
std::unordered_map<IR::OrderedNodeWrapper::NodeOffsetType, Label> JumpTargets;
|
||||
Xbyak::util::Cpu Features{};
|
||||
|
@ -12,15 +12,15 @@ namespace FEXCore::IR {
|
||||
|
||||
#include <FEXCore/IR/IRDefines.inc>
|
||||
|
||||
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView<false> const* IR, uint64_t Arg) {
|
||||
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const* IR, uint64_t Arg) {
|
||||
*out << "#0x" << std::hex << Arg;
|
||||
}
|
||||
|
||||
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView<false> const* IR, const char* Arg) {
|
||||
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const* IR, const char* Arg) {
|
||||
*out << Arg;
|
||||
}
|
||||
|
||||
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView<false> const* IR, CondClassType Arg) {
|
||||
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const* IR, CondClassType Arg) {
|
||||
std::array<std::string, 22> CondNames = {
|
||||
"EQ",
|
||||
"NEQ",
|
||||
@ -49,7 +49,7 @@ static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView<false>
|
||||
*out << CondNames[Arg];
|
||||
}
|
||||
|
||||
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView<false> const* IR, MemOffsetType Arg) {
|
||||
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const* IR, MemOffsetType Arg) {
|
||||
std::array<std::string, 3> Names = {
|
||||
"SXTX",
|
||||
"UXTW",
|
||||
@ -59,7 +59,7 @@ static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView<false>
|
||||
*out << Names[Arg];
|
||||
}
|
||||
|
||||
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView<false> const* IR, RegisterClassType Arg) {
|
||||
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const* IR, RegisterClassType Arg) {
|
||||
if (Arg == GPRClass.Val)
|
||||
*out << "GPR";
|
||||
else if (Arg == GPRFixedClass.Val)
|
||||
@ -74,7 +74,7 @@ static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView<false>
|
||||
*out << "Unknown Registerclass " << Arg;
|
||||
}
|
||||
|
||||
static void PrintArg(std::stringstream *out, IRListView<false> const* IR, OrderedNodeWrapper Arg, IR::RegisterAllocationData *RAData) {
|
||||
static void PrintArg(std::stringstream *out, IRListView const* IR, OrderedNodeWrapper Arg, IR::RegisterAllocationData *RAData) {
|
||||
auto [CodeNode, IROp] = IR->at(Arg)();
|
||||
|
||||
if (Arg.ID() == 0) {
|
||||
@ -123,7 +123,7 @@ static void PrintArg(std::stringstream *out, IRListView<false> const* IR, Ordere
|
||||
}
|
||||
}
|
||||
|
||||
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView<false> const* IR, FEXCore::IR::FenceType Arg) {
|
||||
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const* IR, FEXCore::IR::FenceType Arg) {
|
||||
if (Arg == IR::Fence_Load) {
|
||||
*out << "Loads";
|
||||
}
|
||||
@ -138,7 +138,7 @@ static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView<false>
|
||||
}
|
||||
}
|
||||
|
||||
void Dump(std::stringstream *out, IRListView<false> const* IR, IR::RegisterAllocationData *RAData) {
|
||||
void Dump(std::stringstream *out, IRListView const* IR, IR::RegisterAllocationData *RAData) {
|
||||
auto HeaderOp = IR->GetHeader();
|
||||
|
||||
int8_t CurrentIndent = 0;
|
||||
|
@ -258,6 +258,7 @@ namespace {
|
||||
Graph->AllocData.reset();
|
||||
Graph->AllocData.reset((FEXCore::IR::RegisterAllocationData*)malloc(FEXCore::IR::RegisterAllocationData::Size(NodeCount)));
|
||||
memset(&Graph->AllocData->Map[0], INVALID_REGCLASS.Raw, NodeCount);
|
||||
Graph->AllocData->MapCount = NodeCount;
|
||||
Graph->NodeCount = NodeCount;
|
||||
}
|
||||
|
||||
@ -302,7 +303,7 @@ namespace {
|
||||
}
|
||||
#endif
|
||||
|
||||
FEXCore::IR::RegisterClassType GetRegClassFromNode(FEXCore::IR::IRListView<false> *IR, FEXCore::IR::IROp_Header *IROp) {
|
||||
FEXCore::IR::RegisterClassType GetRegClassFromNode(FEXCore::IR::IRListView *IR, FEXCore::IR::IROp_Header *IROp) {
|
||||
using namespace FEXCore;
|
||||
|
||||
FEXCore::IR::RegisterClassType Class = IR::GetRegClass(IROp->Op);
|
||||
@ -356,7 +357,7 @@ namespace {
|
||||
};
|
||||
|
||||
// Walk the IR and set the node classes
|
||||
void FindNodeClasses(RegisterGraph *Graph, FEXCore::IR::IRListView<false> *IR) {
|
||||
void FindNodeClasses(RegisterGraph *Graph, FEXCore::IR::IRListView *IR) {
|
||||
for (auto [CodeNode, IROp] : IR->GetAllCode()) {
|
||||
// If the destination hasn't yet been set then set it now
|
||||
if (IROp->HasDest) {
|
||||
@ -410,14 +411,14 @@ namespace FEXCore::IR {
|
||||
std::unordered_map<uint32_t, BlockInterferences> LocalBlockInterferences;
|
||||
BlockInterferences GlobalBlockInterferences;
|
||||
|
||||
void CalculateLiveRange(FEXCore::IR::IRListView<false> *IR);
|
||||
void OptimizeStaticRegisters(FEXCore::IR::IRListView<false> *IR);
|
||||
void CalculateBlockInterferences(FEXCore::IR::IRListView<false> *IR);
|
||||
void CalculateBlockNodeInterference(FEXCore::IR::IRListView<false> *IR);
|
||||
void CalculateNodeInterference(FEXCore::IR::IRListView<false> *IR);
|
||||
void CalculateLiveRange(FEXCore::IR::IRListView *IR);
|
||||
void OptimizeStaticRegisters(FEXCore::IR::IRListView *IR);
|
||||
void CalculateBlockInterferences(FEXCore::IR::IRListView *IR);
|
||||
void CalculateBlockNodeInterference(FEXCore::IR::IRListView *IR);
|
||||
void CalculateNodeInterference(FEXCore::IR::IRListView *IR);
|
||||
void AllocateVirtualRegisters();
|
||||
void CalculatePredecessors(FEXCore::IR::IRListView<false> *IR);
|
||||
void RecursiveLiveRangeExpansion(FEXCore::IR::IRListView<false> *IR, uint32_t Node, uint32_t DefiningBlockID, LiveRange *LiveRange, const std::unordered_set<uint32_t> &Predecessors, std::unordered_set<uint32_t> &VisitedPredecessors);
|
||||
void CalculatePredecessors(FEXCore::IR::IRListView *IR);
|
||||
void RecursiveLiveRangeExpansion(FEXCore::IR::IRListView *IR, uint32_t Node, uint32_t DefiningBlockID, LiveRange *LiveRange, const std::unordered_set<uint32_t> &Predecessors, std::unordered_set<uint32_t> &VisitedPredecessors);
|
||||
|
||||
FEXCore::IR::AllNodesIterator FindFirstUse(FEXCore::IR::IREmitter *IREmit, FEXCore::IR::OrderedNode* Node, FEXCore::IR::AllNodesIterator Begin, FEXCore::IR::AllNodesIterator End);
|
||||
FEXCore::IR::AllNodesIterator FindLastUseBefore(FEXCore::IR::IREmitter *IREmit, FEXCore::IR::OrderedNode* Node, FEXCore::IR::AllNodesIterator Begin, FEXCore::IR::AllNodesIterator End);
|
||||
@ -468,7 +469,7 @@ namespace FEXCore::IR {
|
||||
return std::move(Graph->AllocData);
|
||||
}
|
||||
|
||||
void ConstrainedRAPass::RecursiveLiveRangeExpansion(FEXCore::IR::IRListView<false> *IR, uint32_t Node, uint32_t DefiningBlockID, LiveRange *LiveRange, const std::unordered_set<uint32_t> &Predecessors, std::unordered_set<uint32_t> &VisitedPredecessors) {
|
||||
void ConstrainedRAPass::RecursiveLiveRangeExpansion(FEXCore::IR::IRListView *IR, uint32_t Node, uint32_t DefiningBlockID, LiveRange *LiveRange, const std::unordered_set<uint32_t> &Predecessors, std::unordered_set<uint32_t> &VisitedPredecessors) {
|
||||
for (auto PredecessorId: Predecessors) {
|
||||
if (DefiningBlockID != PredecessorId && !VisitedPredecessors.contains(PredecessorId)) {
|
||||
// do the magic
|
||||
@ -491,7 +492,7 @@ namespace FEXCore::IR {
|
||||
}
|
||||
}
|
||||
|
||||
void ConstrainedRAPass::CalculateLiveRange(FEXCore::IR::IRListView<false> *IR) {
|
||||
void ConstrainedRAPass::CalculateLiveRange(FEXCore::IR::IRListView *IR) {
|
||||
using namespace FEXCore;
|
||||
size_t Nodes = IR->GetSSACount();
|
||||
LiveRanges.clear();
|
||||
@ -579,7 +580,7 @@ namespace FEXCore::IR {
|
||||
}
|
||||
}
|
||||
|
||||
void ConstrainedRAPass::OptimizeStaticRegisters(FEXCore::IR::IRListView<false> *IR) {
|
||||
void ConstrainedRAPass::OptimizeStaticRegisters(FEXCore::IR::IRListView *IR) {
|
||||
|
||||
// Helpers
|
||||
|
||||
@ -790,7 +791,7 @@ namespace FEXCore::IR {
|
||||
}
|
||||
}
|
||||
|
||||
void ConstrainedRAPass::CalculateBlockInterferences(FEXCore::IR::IRListView<false> *IR) {
|
||||
void ConstrainedRAPass::CalculateBlockInterferences(FEXCore::IR::IRListView *IR) {
|
||||
using namespace FEXCore;
|
||||
|
||||
for (auto [BlockNode, BlockHeader] : IR->GetBlocks()) {
|
||||
@ -818,7 +819,7 @@ namespace FEXCore::IR {
|
||||
}
|
||||
}
|
||||
|
||||
void ConstrainedRAPass::CalculateBlockNodeInterference(FEXCore::IR::IRListView<false> *IR) {
|
||||
void ConstrainedRAPass::CalculateBlockNodeInterference(FEXCore::IR::IRListView *IR) {
|
||||
#if 0
|
||||
auto AddInterference = [&](uint32_t Node1, uint32_t Node2) {
|
||||
RegisterNode *Node = &Graph->Nodes[Node1];
|
||||
@ -877,7 +878,7 @@ namespace FEXCore::IR {
|
||||
#endif
|
||||
}
|
||||
|
||||
void ConstrainedRAPass::CalculateNodeInterference(FEXCore::IR::IRListView<false> *IR) {
|
||||
void ConstrainedRAPass::CalculateNodeInterference(FEXCore::IR::IRListView *IR) {
|
||||
auto AddInterference = [this](uint32_t Node1, uint32_t Node2) {
|
||||
RegisterNode *Node = &Graph->Nodes[Node1];
|
||||
Node->Interferences.Append(Node2);
|
||||
@ -1477,7 +1478,7 @@ namespace FEXCore::IR {
|
||||
}
|
||||
|
||||
|
||||
void ConstrainedRAPass::CalculatePredecessors(FEXCore::IR::IRListView<false> *IR) {
|
||||
void ConstrainedRAPass::CalculatePredecessors(FEXCore::IR::IRListView *IR) {
|
||||
Graph->BlockPredecessors.clear();
|
||||
|
||||
for (auto [BlockNode, BlockIROp] : IR->GetBlocks()) {
|
||||
|
@ -4,7 +4,6 @@
|
||||
#include <vector>
|
||||
|
||||
namespace FEXCore::IR {
|
||||
template<bool>
|
||||
class IRListView;
|
||||
|
||||
class RegisterAllocationPass : public FEXCore::IR::Pass {
|
||||
|
4
External/FEXCore/docs/IR.md
vendored
4
External/FEXCore/docs/IR.md
vendored
@ -79,10 +79,10 @@ This is an intrusive allocator that is used by the `OpDispatchBuilder` for stori
|
||||
|
||||
### OpDispatchBuilder
|
||||
OpDispatchBuilder provides two routines for handling the IR outside of the class
|
||||
* `IRListView<false> ViewIR();`
|
||||
* `IRListView ViewIR();`
|
||||
* Returns a wrapper container class the allows you to view the IR. This doesn't take ownership of the IR data.
|
||||
* If the OpDispatcherBuilder changes its IR then changes are also visible to this class
|
||||
* `IRListView<true> *CreateIRCopy()`
|
||||
* `IRListView *CreateIRCopy()`
|
||||
* As the name says, it creates a new copy of the IR that is in the OpDispatchBuilder
|
||||
* Copying the IR only copies the memory used and doesn't have any free space for optimizations after this copy operation
|
||||
* Useful for tiered recompilers, AOT, and offline analysis
|
||||
|
@ -5,7 +5,6 @@
|
||||
namespace FEXCore {
|
||||
|
||||
namespace IR {
|
||||
template<bool Copy>
|
||||
class IRListView;
|
||||
class RegisterAllocationData;
|
||||
}
|
||||
@ -45,7 +44,7 @@ class LLVMCore;
|
||||
* @return An executable function pointer that is theoretically compiled from this point.
|
||||
* Is actually a function pointer of type `void (FEXCore::Core::ThreadState *Thread)
|
||||
*/
|
||||
virtual void *CompileCode(FEXCore::IR::IRListView<true> const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) = 0;
|
||||
virtual void *CompileCode(FEXCore::IR::IRListView const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) = 0;
|
||||
|
||||
/**
|
||||
* @brief Function for mapping memory in to the CPUBackend's visible space. Allows setting up virtual mappings if required
|
||||
|
@ -6,6 +6,9 @@
|
||||
#include <FEXCore/Core/SignalDelegator.h>
|
||||
#include <FEXCore/Core/CPUID.h>
|
||||
|
||||
#include<istream>
|
||||
#include<ostream>
|
||||
|
||||
namespace FEXCore {
|
||||
class CodeLoader;
|
||||
}
|
||||
@ -229,4 +232,7 @@ namespace FEXCore::Context {
|
||||
void SetSignalDelegator(FEXCore::Context::Context *CTX, FEXCore::SignalDelegator *SignalDelegation);
|
||||
void SetSyscallHandler(FEXCore::Context::Context *CTX, FEXCore::HLE::SyscallHandler *Handler);
|
||||
FEXCore::CPUID::FunctionResults RunCPUIDFunction(FEXCore::Context::Context *CTX, uint32_t Function, uint32_t Leaf);
|
||||
|
||||
bool ReadAOT(FEXCore::Context::Context *CTX, std::istream& stream);
|
||||
void WriteAOT(FEXCore::Context::Context *CTX, std::ostream& stream);
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ namespace FEXCore::Core {
|
||||
std::unique_ptr<FEXCore::CPU::CPUBackend> CPUBackend;
|
||||
std::unique_ptr<FEXCore::LookupCache> LookupCache;
|
||||
|
||||
std::unordered_map<uint64_t, std::unique_ptr<FEXCore::IR::IRListView<true>>> IRLists;
|
||||
std::unordered_map<uint64_t, std::unique_ptr<FEXCore::IR::IRListView>> IRLists;
|
||||
std::unordered_map<uint64_t, std::unique_ptr<FEXCore::IR::RegisterAllocationData, FEXCore::IR::RegisterAllocationDataDeleter>> RALists;
|
||||
std::unordered_map<uint64_t, std::unique_ptr<FEXCore::Core::DebugData>> DebugData;
|
||||
|
||||
|
3
External/FEXCore/include/FEXCore/IR/IR.h
vendored
3
External/FEXCore/include/FEXCore/IR/IR.h
vendored
@ -456,11 +456,10 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template<bool>
|
||||
class IRListView;
|
||||
class IREmitter;
|
||||
|
||||
void Dump(std::stringstream *out, IRListView<false> const* IR, IR::RegisterAllocationData *RAData);
|
||||
void Dump(std::stringstream *out, IRListView const* IR, IR::RegisterAllocationData *RAData);
|
||||
IREmitter* Parse(std::istream *in);
|
||||
|
||||
template<typename Type>
|
||||
|
@ -21,8 +21,8 @@ friend class FEXCore::IR::PassManager;
|
||||
ResetWorkingList();
|
||||
}
|
||||
|
||||
IRListView<false> ViewIR() { return IRListView<false>(&Data, &ListData); }
|
||||
IRListView<true> *CreateIRCopy() { return new IRListView<true>(&Data, &ListData); }
|
||||
IRListView ViewIR() { return IRListView(&Data, &ListData, false); }
|
||||
IRListView *CreateIRCopy() { return new IRListView(&Data, &ListData, true); }
|
||||
void ResetWorkingList();
|
||||
|
||||
/**
|
||||
|
@ -8,6 +8,8 @@
|
||||
#include <cstring>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
|
||||
namespace FEXCore::IR {
|
||||
/**
|
||||
@ -62,17 +64,16 @@ class IntrusiveAllocator final {
|
||||
uintptr_t Data;
|
||||
};
|
||||
|
||||
template<bool Copy>
|
||||
class IRListView final {
|
||||
public:
|
||||
IRListView() = delete;
|
||||
IRListView(IRListView<Copy> &&) = delete;
|
||||
IRListView(IRListView &&) = delete;
|
||||
|
||||
IRListView(IntrusiveAllocator *Data, IntrusiveAllocator *List) {
|
||||
IRListView(IntrusiveAllocator *Data, IntrusiveAllocator *List, bool _IsCopy) : IsCopy(_IsCopy) {
|
||||
DataSize = Data->Size();
|
||||
ListSize = List->Size();
|
||||
|
||||
if (Copy) {
|
||||
if (IsCopy) {
|
||||
IRData = malloc(DataSize + ListSize);
|
||||
ListData = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(IRData) + DataSize);
|
||||
memcpy(IRData, reinterpret_cast<void*>(Data->Begin()), DataSize);
|
||||
@ -85,24 +86,46 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
IRListView<true>(IRListView<true> *Old) {
|
||||
IRListView(IRListView *Old, bool _IsCopy) : IsCopy(_IsCopy) {
|
||||
DataSize = Old->DataSize;
|
||||
ListSize = Old->ListSize;
|
||||
if (IsCopy) {
|
||||
IRData = malloc(DataSize + ListSize);
|
||||
ListData = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(IRData) + DataSize);
|
||||
memcpy(IRData, Old->IRData, DataSize);
|
||||
memcpy(ListData, Old->ListData, ListSize);
|
||||
} else {
|
||||
IRData = Old->IRData;
|
||||
ListData = Old->ListData;
|
||||
}
|
||||
}
|
||||
|
||||
IRListView(std::istream& stream) : IsCopy(true) {
|
||||
stream.read((char*)&DataSize, sizeof(DataSize));
|
||||
stream.read((char*)&ListSize, sizeof(ListSize));
|
||||
|
||||
IRData = malloc(DataSize + ListSize);
|
||||
ListData = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(IRData) + DataSize);
|
||||
memcpy(IRData, Old->IRData, DataSize);
|
||||
memcpy(ListData, Old->ListData, ListSize);
|
||||
stream.read((char*)IRData, DataSize);
|
||||
stream.read((char*)ListData, ListSize);
|
||||
}
|
||||
|
||||
~IRListView() {
|
||||
if (Copy) {
|
||||
if (IsCopy) {
|
||||
free (IRData);
|
||||
// ListData is just offset from IRData
|
||||
}
|
||||
}
|
||||
|
||||
IRListView<true> *CreateCopy() {
|
||||
return new IRListView<true>(this);
|
||||
void Serialize(std::ostream& stream) {
|
||||
stream.write((char*)&DataSize, sizeof(DataSize));
|
||||
stream.write((char*)&ListSize, sizeof(ListSize));
|
||||
stream.write((char*)IRData, DataSize);
|
||||
stream.write((char*)ListData, ListSize);
|
||||
}
|
||||
|
||||
IRListView *CreateCopy() {
|
||||
return new IRListView(this, true);
|
||||
}
|
||||
|
||||
uintptr_t const GetData() const { return reinterpret_cast<uintptr_t>(IRData); }
|
||||
@ -259,6 +282,7 @@ private:
|
||||
void *ListData;
|
||||
size_t DataSize;
|
||||
size_t ListSize;
|
||||
bool IsCopy;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -37,6 +37,7 @@ struct RegisterAllocationDataDeleter {
|
||||
class RegisterAllocationData {
|
||||
public:
|
||||
uint32_t SpillSlotCount {};
|
||||
uint32_t MapCount {};
|
||||
PhysicalRegister Map[0];
|
||||
|
||||
PhysicalRegister GetNodeRegister(uint32_t Node) const {
|
||||
|
@ -30,7 +30,7 @@ namespace HostFactory {
|
||||
explicit HostCore(FEXCore::Context::Context* CTX, FEXCore::Core::ThreadState *Thread, bool Fallback);
|
||||
~HostCore() override;
|
||||
std::string GetName() override { return "Host Core"; }
|
||||
void* CompileCode(FEXCore::IR::IRListView<true> const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) override;
|
||||
void* CompileCode(FEXCore::IR::IRListView const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) override;
|
||||
|
||||
void *MapRegion(void *HostPtr, uint64_t VirtualGuestPtr, uint64_t Size) override {
|
||||
return HostPtr;
|
||||
@ -170,7 +170,7 @@ namespace HostFactory {
|
||||
ready();
|
||||
}
|
||||
|
||||
void* HostCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView<true> const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) {
|
||||
void* HostCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,8 @@
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
|
||||
namespace {
|
||||
static bool SilentLog;
|
||||
@ -287,8 +289,31 @@ int main(int argc, char **argv, char **const envp) {
|
||||
});
|
||||
}
|
||||
|
||||
std::string base_filename = Program.substr(Program.find_last_of("/\\") + 1) + ".fex-emu.aot";
|
||||
|
||||
{
|
||||
std::ifstream AOTRead(base_filename, std::ios::in | std::ios::binary);
|
||||
|
||||
if (AOTRead) {
|
||||
if (FEXCore::Context::ReadAOT(CTX, AOTRead)) {
|
||||
LogMan::Msg::I("AOT Cache Loaded\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FEXCore::Context::RunUntilExit(CTX);
|
||||
|
||||
{
|
||||
std::ofstream AOTWrite(base_filename, std::ios::out | std::ios::binary );
|
||||
|
||||
if (AOTWrite) {
|
||||
std::filesystem::resize_file(base_filename, 0);
|
||||
AOTWrite.seekp(0);
|
||||
FEXCore::Context::WriteAOT(CTX, AOTWrite);
|
||||
LogMan::Msg::I("AOT Cache Stored\n");
|
||||
}
|
||||
}
|
||||
|
||||
auto ProgramStatus = FEXCore::Context::GetProgramStatus(CTX);
|
||||
|
||||
SyscallHandler.reset();
|
||||
|
Loading…
x
Reference in New Issue
Block a user