Merge pull request #725 from FEX-Emu/skmp/single-compiler-backend

Core: Single compiler backend
This commit is contained in:
Ryan Houdek 2021-01-29 05:30:47 -08:00 committed by GitHub
commit 2edb14538f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 4730 additions and 4725 deletions

View File

@ -131,6 +131,7 @@ set (SRCS
Interface/Core/X86DebugInfo.cpp
Interface/Core/X86HelperGen.cpp
Interface/Core/Interpreter/InterpreterCore.cpp
Interface/Core/Interpreter/InterpreterOps.cpp
Interface/Core/X86Tables/BaseTables.cpp
Interface/Core/X86Tables/DDDTables.cpp
Interface/Core/X86Tables/EVEXTables.cpp

View File

@ -70,6 +70,9 @@ namespace FEXCore::Context {
} Config;
using IntCallbackReturn = __attribute__((naked)) void(*)(FEXCore::Core::InternalThreadState *Thread, volatile void *Host_RSP);
IntCallbackReturn InterpreterCallbackReturn;
FEXCore::HostFeatures HostFeatures;
std::mutex ThreadCreationMutex;
@ -143,7 +146,6 @@ namespace FEXCore::Context {
std::tuple<void *, FEXCore::IR::IRListView<true> *, 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);
uintptr_t CompileFallbackBlock(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
// Used for thread creation from syscalls
void InitializeCompiler(FEXCore::Core::InternalThreadState* State, bool CompileThread);

View File

@ -68,7 +68,6 @@ namespace FEXCore {
auto SelectedThread = Thread->IsCompileService ? ParentThread : Thread;
SelectedThread->LookupCache->ClearCache();
SelectedThread->CPUBackend->ClearCache();
SelectedThread->IntBackend->ClearCache();
}

View File

@ -457,8 +457,6 @@ namespace FEXCore::Context {
void Context::InitializeThreadData(FEXCore::Core::InternalThreadState *Thread) {
Thread->CPUBackend->Initialize();
Thread->IntBackend->Initialize();
Thread->FallbackBackend->Initialize();
auto IRHandler = [Thread](uint64_t Addr, IR::IREmitter *IR) -> void {
// Run the passmanager over the IR from the dispatcher
@ -520,23 +518,14 @@ namespace FEXCore::Context {
switch (Config.Core) {
case FEXCore::Config::CONFIG_INTERPRETER:
State->CPUBackend.reset(FEXCore::CPU::CreateInterpreterCore(this, State, CompileThread));
State->IntBackend = State->CPUBackend;
break;
case FEXCore::Config::CONFIG_IRJIT:
State->PassManager->InsertRegisterAllocationPass(DoSRA);
// Initialization order matters here, the IR JIT may want to have the interpreter created first to get a pointer to its execution function
// This is useful for JIT to interpreter fallback support
State->IntBackend.reset(FEXCore::CPU::CreateInterpreterCore(this, State, CompileThread));
State->CPUBackend.reset(FEXCore::CPU::CreateJITCore(this, State, CompileThread));
break;
case FEXCore::Config::CONFIG_CUSTOM: State->CPUBackend.reset(CustomCPUFactory(this, &State->State)); break;
default: LogMan::Msg::A("Unknown core configuration");
}
if (!State->IntBackend) {
State->IntBackend.reset(FEXCore::CPU::CreateInterpreterCore(this, State, CompileThread));
}
State->FallbackBackend.reset(FallbackCPUFactory(this, &State->State));
}
FEXCore::Core::InternalThreadState* Context::CreateThread(FEXCore::Core::CPUState *NewThreadState, uint64_t ParentTID) {
@ -557,7 +546,6 @@ namespace FEXCore::Context {
InitializeCompiler(Thread, false);
LogMan::Throw::A(!Thread->FallbackBackend->NeedsOpDispatch(), "Fallback CPU backend must not require OpDispatch");
return Thread;
}
@ -568,7 +556,9 @@ namespace FEXCore::Context {
void Context::ClearCodeCache(FEXCore::Core::InternalThreadState *Thread, bool AlsoClearIRCache) {
Thread->LookupCache->ClearCache();
Thread->CPUBackend->ClearCache();
Thread->IntBackend->ClearCache();
if (Thread->CompileService) {
Thread->CompileService->ClearCache(Thread);
}
if (AlsoClearIRCache) {
Thread->IRLists.clear();
@ -857,49 +847,40 @@ namespace FEXCore::Context {
GeneratedIR = Generated;
}
if (CodePtr != nullptr) {
// The core managed to compile the code.
LogMan::Throw::A(CodePtr != nullptr, "Failed to compile code %lX", GuestRIP);
// The core managed to compile the code.
#if ENABLE_JITSYMBOLS
if (DebugData) {
if (DebugData->Subblocks.size()) {
for (auto& Subblock: DebugData->Subblocks) {
Symbols.Register((void*)Subblock.HostCodeStart, GuestRIP, Subblock.HostCodeSize);
}
} else {
Symbols.Register(CodePtr, GuestRIP, DebugData->HostCodeSize);
if (DebugData) {
if (DebugData->Subblocks.size()) {
for (auto& Subblock: DebugData->Subblocks) {
Symbols.Register((void*)Subblock.HostCodeStart, GuestRIP, Subblock.HostCodeSize);
}
} else {
Symbols.Register(CodePtr, GuestRIP, DebugData->HostCodeSize);
}
}
#endif
// Insert to caches if we generated IR
if (GeneratedIR) {
Thread->IRLists.emplace(GuestRIP, IRList);
Thread->DebugData.emplace(GuestRIP, DebugData);
Thread->RALists.emplace(GuestRIP, RAData);
}
if (DecrementRefCount)
--Thread->CompileBlockReentrantRefCount;
// Insert to lookup cache
AddBlockMapping(Thread, GuestRIP, CodePtr);
return (uintptr_t)CodePtr;
// Insert to caches if we generated IR
if (GeneratedIR) {
Thread->IRLists.emplace(GuestRIP, IRList);
Thread->DebugData.emplace(GuestRIP, DebugData);
Thread->RALists.emplace(GuestRIP, RAData);
}
if (DecrementRefCount)
--Thread->CompileBlockReentrantRefCount;
return 0;
}
uintptr_t Context::CompileFallbackBlock(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
// We have ONE more chance to try and fallback to the fallback CPU backend
// This will most likely fail since regular code use won't be using a fallback core.
// It's mainly for testing new instruction encodings
void *CodePtr = Thread->FallbackBackend->CompileCode(nullptr, nullptr, nullptr);
// Insert to lookup cache
AddBlockMapping(Thread, GuestRIP, CodePtr);
return (uintptr_t)CodePtr;
if (DecrementRefCount)
--Thread->CompileBlockReentrantRefCount;
return 0;
}
void Context::ExecutionThread(FEXCore::Core::InternalThreadState *Thread) {

View File

@ -540,6 +540,10 @@ void InterpreterCore::CreateAsmDispatch(FEXCore::Context::Context *ctx, FEXCore:
Generator = new DispatchGenerator(ctx, Thread);
DispatchPtr = Generator->DispatchPtr;
CallbackPtr = Generator->CallbackPtr;
// TODO: Implement this. It is missing from the dispatcher
// TODO: It feels wrong to initialize this way
ctx->InterpreterCallbackReturn = nullptr;
}
bool InterpreterCore::HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack) {

View File

@ -28,14 +28,9 @@ public:
bool NeedsOpDispatch() override { return true; }
void ExecuteCode(FEXCore::Core::InternalThreadState *Thread);
void CreateAsmDispatch(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread);
void DeleteAsmDispatch();
using CallbackReturn = __attribute__((naked)) void(*)(FEXCore::Core::InternalThreadState *Thread, volatile void *Host_RSP);
CallbackReturn ReturnPtr;
bool HandleSIGBUS(int Signal, void *info, void *ucontext);
private:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,19 @@
namespace FEXCore::Core {
struct InternalThreadState;
}
namespace FEXCore::IR {
template<bool copy>
class IRListView;
}
namespace FEXCore::Core{
struct DebugData;
}
namespace FEXCore::CPU {
class InterpreterOps {
public:
static void InterpretIR(FEXCore::Core::InternalThreadState *Thread, FEXCore::IR::IRListView<true> *CurrentIR, FEXCore::Core::DebugData *DebugData);
};
};

View File

@ -1,5 +1,6 @@
#include "Common/MathUtils.h"
#include "Interface/Core/Interpreter/InterpreterClass.h"
#include "Interface/Context/Context.h"
#include <FEXCore/Core/X86Enums.h>
#include <cmath>
@ -17,7 +18,7 @@ class DispatchGenerator : public Xbyak::CodeGenerator {
CPUBackend::AsmDispatch DispatchPtr;
CPUBackend::JITCallback CallbackPtr;
InterpreterCore::CallbackReturn ReturnPtr;
FEXCore::Context::Context::IntCallbackReturn ReturnPtr;
uint64_t ThreadStopHandlerAddress;
uint64_t AbsoluteLoopTopAddress;
@ -238,7 +239,7 @@ DispatchGenerator::DispatchGenerator(FEXCore::Context::Context *ctx, FEXCore::Co
{
ReturnPtr = getCurr<InterpreterCore::CallbackReturn>();
ReturnPtr = getCurr<FEXCore::Context::Context::IntCallbackReturn>();
// using CallbackReturn = __attribute__((naked)) void(*)(FEXCore::Core::InternalThreadState *Thread, volatile void *Host_RSP);
// rdi = thread
@ -447,7 +448,9 @@ void InterpreterCore::CreateAsmDispatch(FEXCore::Context::Context *ctx, FEXCore:
Generator = new DispatchGenerator(ctx, Thread);
DispatchPtr = Generator->DispatchPtr;
CallbackPtr = Generator->CallbackPtr;
ReturnPtr = Generator->ReturnPtr;
// TODO: It feels wrong to initialize this way
ctx->InterpreterCallbackReturn = Generator->ReturnPtr;
}
bool InterpreterCore::HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack) {

View File

@ -8,6 +8,7 @@
#include <FEXCore/Core/X86Enums.h>
#include <FEXCore/Core/UContext.h>
#include "Interface/Core/Interpreter/InterpreterOps.h"
#include <sys/mman.h>
#include <stdio.h>
@ -854,6 +855,12 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView<true> const
str(x0, MemOperand(STATE, offsetof(FEXCore::Core::ThreadState, State.rip)));
LoadConstant(x0, ThreadSharedData.InterpreterFallbackHelperAddress);
LoadConstant(x1, (uintptr_t)IR);
// Debug data is only used in debug builds
#ifndef NDEBUG
LoadConstant(x2, (uintptr_t)DebugData);
#endif
br(x0);
} else {
//LogMan::Throw::A(RAData->HasFullRA(), "Arm64 JIT only works with RA");
@ -1092,11 +1099,10 @@ void JITCore::CreateCustomDispatch(FEXCore::Core::InternalThreadState *Thread) {
Literal l_VirtualMemory {VirtualMemorySize};
Literal l_PagePtr {Thread->LookupCache->GetPagePointer()};
Literal l_CTX {reinterpret_cast<uintptr_t>(CTX)};
Literal l_Interpreter {reinterpret_cast<uint64_t>(State->IntBackend->CompileCode(nullptr, nullptr, nullptr))};
Literal l_Interpreter {reinterpret_cast<uint64_t>(&InterpreterOps::InterpretIR)};
Literal l_Sleep {reinterpret_cast<uint64_t>(SleepThread)};
uintptr_t CompileBlockPtr{};
uintptr_t CompileFallbackPtr{};
{
using ClassPtrType = uintptr_t (FEXCore::Context::Context::*)(FEXCore::Core::InternalThreadState *, uint64_t);
union PtrCast {
@ -1108,20 +1114,8 @@ void JITCore::CreateCustomDispatch(FEXCore::Core::InternalThreadState *Thread) {
Ptr.ClassPtr = &FEXCore::Context::Context::CompileBlock;
CompileBlockPtr = Ptr.Data;
}
{
using ClassPtrType = uintptr_t (FEXCore::Context::Context::*)(FEXCore::Core::InternalThreadState *, uint64_t);
union PtrCast {
ClassPtrType ClassPtr;
uintptr_t Data;
};
PtrCast Ptr;
Ptr.ClassPtr = &FEXCore::Context::Context::CompileFallbackBlock;
CompileFallbackPtr = Ptr.Data;
}
Literal l_CompileBlock {CompileBlockPtr};
Literal l_CompileFallback {CompileFallbackPtr};
Literal l_ExitFunctionLink {(uintptr_t)&ExitFunctionLink};
// Push all the register we need to save
@ -1257,7 +1251,6 @@ void JITCore::CreateCustomDispatch(FEXCore::Core::InternalThreadState *Thread) {
br(x0);
}
aarch64::Label FallbackCore;
// Need to create the block
{
bind(&NoBlock);
@ -1271,31 +1264,12 @@ void JITCore::CreateCustomDispatch(FEXCore::Core::InternalThreadState *Thread) {
blr(x3); // { CTX, ThreadState, RIP}
FillStaticRegs();
// X0 now contains either nullptr or block pointer
cbz(x0, &FallbackCore);
// X0 now contains the block pointer
blr(x0);
b(&LoopTop);
}
// We need to fallback to our fallback core
{
bind(&FallbackCore);
ldr(x0, &l_CTX);
mov(x1, STATE);
ldr(x3, &l_CompileFallback);
// X2 contains our guest RIP
SpillStaticRegs();
blr(x3); // {ThreadState, RIP}
FillStaticRegs();
// X0 now contains either nullptr or block pointer
cbz(x0, &ExitSpillSRA);
br(x0);
}
{
Label RestoreContextStateHelperLabel{};
bind(&RestoreContextStateHelperLabel);
@ -1312,9 +1286,9 @@ void JITCore::CreateCustomDispatch(FEXCore::Core::InternalThreadState *Thread) {
ThreadSharedData.InterpreterFallbackHelperAddress = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
SpillStaticRegs();
mov(x0, STATE);
ldr(x1, &l_Interpreter);
ldr(x3, &l_Interpreter);
blr(x1);
blr(x3);
FillStaticRegs();
b(&LoopTop);
}
@ -1396,7 +1370,6 @@ void JITCore::CreateCustomDispatch(FEXCore::Core::InternalThreadState *Thread) {
place(&l_Interpreter);
place(&l_Sleep);
place(&l_CompileBlock);
place(&l_CompileFallback);
place(&l_ExitFunctionLink);
FinalizeCode();

View File

@ -11,6 +11,8 @@
#include <cmath>
#include <signal.h>
#include "Interface/Core/Interpreter/InterpreterOps.h"
// #define DEBUG_RA 1
// #define DEBUG_CYCLES
@ -641,6 +643,13 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView<true> const
if (HeaderOp->ShouldInterpret) {
mov(rax, HeaderOp->Entry);
mov(qword [STATE + offsetof(FEXCore::Core::CPUState, rip)], rax);
mov(rsi, (uint64_t)IR);
// Debug data is only used in debug builds
#ifndef NDEBUG
mov(rdx, (uint64_t)DebugData);
#endif
mov(rax, (uintptr_t)ThreadSharedData.InterpreterFallbackHelperAddress);
jmp(rax);
} else {
@ -994,9 +1003,8 @@ void JITCore::CreateCustomDispatch(FEXCore::Core::InternalThreadState *Thread) {
// Interpreter fallback helper code
ThreadSharedData.InterpreterFallbackHelperAddress = getCurr<void*>();
// This will get called so our stack is now misaligned
mov(rax, reinterpret_cast<uint64_t>(&InterpreterOps::InterpretIR));
mov(rdi, STATE);
mov(rax, reinterpret_cast<uint64_t>(ThreadState->IntBackend->CompileCode(nullptr, nullptr, nullptr)));
call(rax);
jmp(LoopTop);

View File

@ -73,10 +73,7 @@ namespace FEXCore::Core {
std::unique_ptr<FEXCore::IR::OpDispatchBuilder> OpDispatcher;
std::shared_ptr<FEXCore::CPU::CPUBackend> CPUBackend;
std::shared_ptr<FEXCore::CPU::CPUBackend> IntBackend;
std::unique_ptr<FEXCore::CPU::CPUBackend> FallbackBackend;
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;