From 38ff936c8194891e99a81b0937cb090e6226a46b Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Tue, 26 May 2020 18:22:28 -0700 Subject: [PATCH] Adds support for interpreter fallback The IRHeader itself contains a flag for if a block should fallback to the interpreter. This is necessary for the case that an IR is loaded and the Frontend object no longer has the data necessary to know if it should do an interpreter fallback. This is super useful for bisecting JIT versus Intepreter failures, and also cases where falling back to intepreter for compatibility is a lot more simple than wiring things through the JIT (ala x87) This isn't currently utilized, but will be once x87 work lands --- .../FEXCore/Source/Interface/Core/Core.cpp | 11 +++++++++- .../Core/Interpreter/InterpreterCore.cpp | 3 ++- .../Source/Interface/Core/JIT/Arm64/JIT.cpp | 16 +++++++++----- .../Source/Interface/Core/JIT/x86_64/JIT.cpp | 4 ++++ .../Interface/Core/LLVMJIT/LLVMCore.cpp | 22 +++++++++++-------- .../Interface/Core/OpcodeDispatcher.cpp | 3 ++- .../Source/Interface/Core/OpcodeDispatcher.h | 1 + External/FEXCore/Source/Interface/IR/IR.json | 3 ++- .../Interface/IR/Passes/IRCompaction.cpp | 2 +- .../IR/Passes/RegisterAllocationPass.cpp | 15 +++++++++++++ .../FEXCore/Debug/InternalThreadState.h | 3 ++- Source/Tests/IRLoader/Loader.cpp | 2 +- 12 files changed, 63 insertions(+), 22 deletions(-) diff --git a/External/FEXCore/Source/Interface/Core/Core.cpp b/External/FEXCore/Source/Interface/Core/Core.cpp index 560a1b611..b664c1384 100644 --- a/External/FEXCore/Source/Interface/Core/Core.cpp +++ b/External/FEXCore/Source/Interface/Core/Core.cpp @@ -386,6 +386,7 @@ namespace FEXCore::Context { void Context::InitializeThreadData(FEXCore::Core::InternalThreadState *Thread) { Thread->CPUBackend->Initialize(); + Thread->IntBackend->Initialize(); Thread->FallbackBackend->Initialize(); auto IRHandler = [this, Thread](uint64_t Addr, IR::IREmitter *IR) -> void { @@ -442,13 +443,19 @@ namespace FEXCore::Context { // Create CPU backend switch (Config.Core) { - case FEXCore::Config::CONFIG_INTERPRETER: Thread->CPUBackend.reset(FEXCore::CPU::CreateInterpreterCore(this)); break; + case FEXCore::Config::CONFIG_INTERPRETER: + Thread->CPUBackend.reset(FEXCore::CPU::CreateInterpreterCore(this)); + Thread->IntBackend = Thread->CPUBackend; + break; case FEXCore::Config::CONFIG_IRJIT: Thread->CPUBackend.reset(FEXCore::CPU::CreateJITCore(this, Thread)); break; case FEXCore::Config::CONFIG_LLVMJIT: Thread->CPUBackend.reset(FEXCore::CPU::CreateLLVMCore(Thread)); break; case FEXCore::Config::CONFIG_CUSTOM: Thread->CPUBackend.reset(CustomCPUFactory(this, &Thread->State)); break; default: LogMan::Msg::A("Unknown core configuration"); } + if (!Thread->IntBackend) { + Thread->IntBackend.reset(FEXCore::CPU::CreateInterpreterCore(this)); + } Thread->FallbackBackend.reset(FallbackCPUFactory(this, &Thread->State)); LogMan::Throw::A(!Thread->FallbackBackend->NeedsOpDispatch(), "Fallback CPU backend must not require OpDispatch"); @@ -485,6 +492,8 @@ namespace FEXCore::Context { void Context::ClearCodeCache(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) { Thread->BlockCache->ClearCache(); Thread->CPUBackend->ClearCache(); + Thread->IntBackend->ClearCache(); + if (GuestRIP != 0) { auto IR = Thread->IRLists.find(GuestRIP)->second.release(); Thread->IRLists.clear(); diff --git a/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterCore.cpp b/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterCore.cpp index 35ddf2f62..7b631cb2b 100644 --- a/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterCore.cpp +++ b/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterCore.cpp @@ -54,7 +54,8 @@ private: }; static void InterpreterExecution(FEXCore::Core::InternalThreadState *Thread) { - InterpreterCore *Core = reinterpret_cast(Thread->CPUBackend.get()); + // IntBackend will always point to this interpreter object + InterpreterCore *Core = reinterpret_cast(Thread->IntBackend.get()); Core->ExecuteCode(Thread); } diff --git a/External/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp b/External/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp index 4cbf6f4fd..20baea0d6 100644 --- a/External/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp +++ b/External/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp @@ -374,6 +374,16 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const uintptr_t ListBegin = CurrentIR->GetListData(); uintptr_t DataBegin = CurrentIR->GetData(); + auto HeaderIterator = CurrentIR->begin(); + IR::OrderedNodeWrapper *HeaderNodeWrapper = HeaderIterator(); + IR::OrderedNode *HeaderNode = HeaderNodeWrapper->GetNode(ListBegin); + auto HeaderOp = HeaderNode->Op(DataBegin)->CW(); + LogMan::Throw::A(HeaderOp->Header.Op == IR::OP_IRHEADER, "First op wasn't IRHeader"); + + if (HeaderOp->ShouldInterpret) { + return State->IntBackend->CompileCode(IR, DebugData); + } + LogMan::Throw::A(RAPass->HasFullRA(), "Arm64 JIT only works with RA"); uint32_t SpillSlots = RAPass->SpillSlots(); @@ -409,12 +419,6 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const sub(sp, sp, SpillSlots * 16); } - auto HeaderIterator = CurrentIR->begin(); - IR::OrderedNodeWrapper *HeaderNodeWrapper = HeaderIterator(); - IR::OrderedNode *HeaderNode = HeaderNodeWrapper->GetNode(ListBegin); - auto HeaderOp = HeaderNode->Op(DataBegin)->CW(); - LogMan::Throw::A(HeaderOp->Header.Op == IR::OP_IRHEADER, "First op wasn't IRHeader"); - IR::OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin); while (1) { diff --git a/External/FEXCore/Source/Interface/Core/JIT/x86_64/JIT.cpp b/External/FEXCore/Source/Interface/Core/JIT/x86_64/JIT.cpp index 5c107f8cd..3a2e51a6e 100644 --- a/External/FEXCore/Source/Interface/Core/JIT/x86_64/JIT.cpp +++ b/External/FEXCore/Source/Interface/Core/JIT/x86_64/JIT.cpp @@ -235,6 +235,10 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const auto HeaderOp = HeaderNode->Op(DataBegin)->CW(); LogMan::Throw::A(HeaderOp->Header.Op == IR::OP_IRHEADER, "First op wasn't IRHeader"); + if (HeaderOp->ShouldInterpret) { + return ThreadState->IntBackend->CompileCode(IR, DebugData); + } + // Fairly excessive buffer range to make sure we don't overflow uint32_t BufferRange = SSACount * 16; if ((getSize() + BufferRange) > MAX_CODE_SIZE) { diff --git a/External/FEXCore/Source/Interface/Core/LLVMJIT/LLVMCore.cpp b/External/FEXCore/Source/Interface/Core/LLVMJIT/LLVMCore.cpp index c90846a5c..dfd20d0c9 100644 --- a/External/FEXCore/Source/Interface/Core/LLVMJIT/LLVMCore.cpp +++ b/External/FEXCore/Source/Interface/Core/LLVMJIT/LLVMCore.cpp @@ -3916,15 +3916,6 @@ void* FEXCore::CPU::LLVMJITCore::CompileCode(FEXCore::IR::IRListView const CurrentIR = IR; -#if DESTMAP_AS_MAP - DestMap.clear(); -#else - uintptr_t ListSize = CurrentIR->GetListSize(); - if (ListSize > DestMap.size()) { - DestMap.resize(std::max(DestMap.size() * 2, ListSize)); - } -#endif - uintptr_t ListBegin = CurrentIR->GetListData(); uintptr_t DataBegin = CurrentIR->GetData(); @@ -3934,6 +3925,19 @@ void* FEXCore::CPU::LLVMJITCore::CompileCode(FEXCore::IR::IRListView const auto HeaderOp = HeaderNode->Op(DataBegin)->CW(); LogMan::Throw::A(HeaderOp->Header.Op == IR::OP_IRHEADER, "First op wasn't IRHeader"); + if (HeaderOp->ShouldInterpret) { + return ThreadState->IntBackend->CompileCode(IR, DebugData); + } + +#if DESTMAP_AS_MAP + DestMap.clear(); +#else + uintptr_t ListSize = CurrentIR->GetListSize(); + if (ListSize > DestMap.size()) { + DestMap.resize(std::max(DestMap.size() * 2, ListSize)); + } +#endif + std::ostringstream FunctionName; FunctionName << "Function_0x"; FunctionName << std::hex << HeaderOp->Entry; diff --git a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp index 5a67acfed..8fa8224bf 100644 --- a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp +++ b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp @@ -3338,7 +3338,8 @@ void OpDispatchBuilder::CreateJumpBlocks(std::vector const *Blocks) { Entry = RIP; - auto IRHeader = _IRHeader(InvalidNode, RIP, 0); + auto IRHeader = _IRHeader(InvalidNode, RIP, 0, false); + Current_Header = IRHeader.first; CreateJumpBlocks(Blocks); auto Block = GetNewJumpBlock(RIP); diff --git a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h index d42dc230f..f574c70af 100644 --- a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h +++ b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h @@ -343,6 +343,7 @@ public: private: bool DecodeFailure{false}; + FEXCore::IR::IROp_IRHeader *Current_Header{}; OrderedNode *LoadSource(FEXCore::IR::RegisterClassType Class, FEXCore::X86Tables::DecodedOp const& Op, FEXCore::X86Tables::DecodedOperand const& Operand, uint32_t Flags, int8_t Align, bool LoadData = true, bool ForceLoad = false); OrderedNode *LoadSource_WithOpSize(FEXCore::IR::RegisterClassType Class, FEXCore::X86Tables::DecodedOp const& Op, FEXCore::X86Tables::DecodedOperand const& Operand, uint8_t OpSize, uint32_t Flags, int8_t Align, bool LoadData = true, bool ForceLoad = false); diff --git a/External/FEXCore/Source/Interface/IR/IR.json b/External/FEXCore/Source/Interface/IR/IR.json index 714bf9e40..4b4edabfc 100644 --- a/External/FEXCore/Source/Interface/IR/IR.json +++ b/External/FEXCore/Source/Interface/IR/IR.json @@ -54,7 +54,8 @@ ], "Args": [ "uint64_t", "Entry", - "uint32_t", "BlockCount" + "uint32_t", "BlockCount", + "bool", "ShouldInterpret" ] }, "CodeBlock": { diff --git a/External/FEXCore/Source/Interface/IR/Passes/IRCompaction.cpp b/External/FEXCore/Source/Interface/IR/Passes/IRCompaction.cpp index f1d701874..2cb939bb8 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/IRCompaction.cpp +++ b/External/FEXCore/Source/Interface/IR/Passes/IRCompaction.cpp @@ -61,7 +61,7 @@ bool IRCompaction::Run(IREmitter *IREmit) { // Zero is always zero(invalid) OldToNewRemap[0] = 0; - auto LocalHeaderOp = LocalBuilder._IRHeader(OrderedNodeWrapper::WrapOffset(0).GetNode(ListBegin), HeaderOp->Entry, HeaderOp->BlockCount); + auto LocalHeaderOp = LocalBuilder._IRHeader(OrderedNodeWrapper::WrapOffset(0).GetNode(ListBegin), HeaderOp->Entry, HeaderOp->BlockCount, HeaderOp->ShouldInterpret); OldToNewRemap[HeaderNode->Wrapped(ListBegin).ID()] = LocalHeaderOp.Node->Wrapped(LocalListBegin).ID(); struct CodeBlockData { diff --git a/External/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp b/External/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp index 0013f1151..b77b7bacf 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp +++ b/External/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp @@ -1220,6 +1220,21 @@ namespace FEXCore::IR { bool ConstrainedRAPass::Run(IREmitter *IREmit) { bool Changed = false; + auto IR = IREmit->ViewIR(); + uintptr_t ListBegin = IR.GetListData(); + uintptr_t DataBegin = IR.GetData(); + + auto Begin = IR.begin(); + auto Op = Begin(); + + IR::OrderedNode *RealNode = Op->GetNode(ListBegin); + auto HeaderOp = RealNode->Op(DataBegin)->CW(); + LogMan::Throw::A(HeaderOp->Header.Op == IR::OP_IRHEADER, "First op wasn't IRHeader"); + + if (HeaderOp->ShouldInterpret) { + return false; + } + SpillSlotCount = 0; Graph->SpillStack.clear(); diff --git a/External/FEXCore/include/FEXCore/Debug/InternalThreadState.h b/External/FEXCore/include/FEXCore/Debug/InternalThreadState.h index c5c416c22..3720fa0ed 100644 --- a/External/FEXCore/include/FEXCore/Debug/InternalThreadState.h +++ b/External/FEXCore/include/FEXCore/Debug/InternalThreadState.h @@ -49,7 +49,8 @@ namespace FEXCore::Core { std::unique_ptr OpDispatcher; - std::unique_ptr CPUBackend; + std::shared_ptr CPUBackend; + std::shared_ptr IntBackend; std::unique_ptr FallbackBackend; std::unique_ptr BlockCache; diff --git a/Source/Tests/IRLoader/Loader.cpp b/Source/Tests/IRLoader/Loader.cpp index 865080589..e3458163d 100644 --- a/Source/Tests/IRLoader/Loader.cpp +++ b/Source/Tests/IRLoader/Loader.cpp @@ -363,7 +363,7 @@ namespace FEX::IRLoader { if (!CheckPrintError(Def, CodeBlockCount.first)) return false; EntryRIP = Entry.second; - IRHeader = _IRHeader(InvalidNode, Entry.second, CodeBlockCount.second); + IRHeader = _IRHeader(InvalidNode, Entry.second, CodeBlockCount.second, false); } // Spin through the blocks and generate basic block ops