diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index c23e2c71f..f427e5ed3 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -51,7 +51,7 @@ jobs: # Note the current convention is to use the -S and -B options here to specify source # and build directories, but this is only available with CMake 3.13 and higher. # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True -DENABLE_INTERPRETER=True - name: Build working-directory: ${{runner.workspace}}/build diff --git a/CMakeLists.txt b/CMakeLists.txt index 69f4b405a..825742012 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,7 @@ option(ENABLE_JEMALLOC "Enables jemalloc allocator" TRUE) option(ENABLE_OFFLINE_TELEMETRY "Enables FEX offline telemetry" TRUE) option(ENABLE_COMPILE_TIME_TRACE "Enables time trace compile option" FALSE) option(ENABLE_LIBCXX "Enables LLVM libc++" FALSE) +option(ENABLE_INTERPRETER "Enables FEX's Interpreter" FALSE) set (X86_C_COMPILER "x86_64-linux-gnu-gcc" CACHE STRING "c compiler for compiling x86 guest libs") set (X86_CXX_COMPILER "x86_64-linux-gnu-g++" CACHE STRING "c++ compiler for compiling x86 guest libs") @@ -39,6 +40,11 @@ if (ENABLE_ASSERTIONS) add_definitions(-DASSERTIONS_ENABLED=1) endif() +if (ENABLE_INTERPRETER) + message(STATUS "Interpreter enabled") + add_definitions(-DINTERPRETER_ENABLED=1) +endif() + set(CMAKE_CXX_STANDARD 20) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/Bin) diff --git a/External/FEXCore/Source/CMakeLists.txt b/External/FEXCore/Source/CMakeLists.txt index c943c4ddb..123327598 100644 --- a/External/FEXCore/Source/CMakeLists.txt +++ b/External/FEXCore/Source/CMakeLists.txt @@ -100,19 +100,7 @@ set (SRCS Interface/Core/Dispatcher/Dispatcher.cpp Interface/Core/Dispatcher/X86Dispatcher.cpp Interface/Core/Dispatcher/Arm64Dispatcher.cpp - Interface/Core/Interpreter/InterpreterCore.cpp - Interface/Core/Interpreter/InterpreterOps.cpp - Interface/Core/Interpreter/ALUOps.cpp - Interface/Core/Interpreter/AtomicOps.cpp - Interface/Core/Interpreter/BranchOps.cpp - Interface/Core/Interpreter/ConversionOps.cpp - Interface/Core/Interpreter/EncryptionOps.cpp - Interface/Core/Interpreter/F80Ops.cpp - Interface/Core/Interpreter/FlagOps.cpp - Interface/Core/Interpreter/MemoryOps.cpp - Interface/Core/Interpreter/MiscOps.cpp - Interface/Core/Interpreter/MoveOps.cpp - Interface/Core/Interpreter/VectorOps.cpp + Interface/Core/Interpreter/InterpreterFallbacks.cpp Interface/Core/X86Tables/BaseTables.cpp Interface/Core/X86Tables/DDDTables.cpp Interface/Core/X86Tables/EVEXTables.cpp @@ -152,6 +140,23 @@ set (SRCS Utils/Threads.cpp ) +if (ENABLE_INTERPRETER) + list(APPEND SRCS + Interface/Core/Interpreter/InterpreterCore.cpp + Interface/Core/Interpreter/InterpreterOps.cpp + Interface/Core/Interpreter/ALUOps.cpp + Interface/Core/Interpreter/AtomicOps.cpp + Interface/Core/Interpreter/BranchOps.cpp + Interface/Core/Interpreter/ConversionOps.cpp + Interface/Core/Interpreter/EncryptionOps.cpp + Interface/Core/Interpreter/F80Ops.cpp + Interface/Core/Interpreter/FlagOps.cpp + Interface/Core/Interpreter/MemoryOps.cpp + Interface/Core/Interpreter/MiscOps.cpp + Interface/Core/Interpreter/MoveOps.cpp + Interface/Core/Interpreter/VectorOps.cpp) +endif() + if(_M_ARM_64) list(APPEND SRCS Interface/Core/ArchHelpers/Arm64.cpp) diff --git a/External/FEXCore/Source/Interface/Config/Config.cpp b/External/FEXCore/Source/Interface/Config/Config.cpp index f85b6d197..aabeb3c47 100644 --- a/External/FEXCore/Source/Interface/Config/Config.cpp +++ b/External/FEXCore/Source/Interface/Config/Config.cpp @@ -434,7 +434,12 @@ namespace JSON { #else constexpr uint32_t MaxCoreNumber = 1; #endif - if (Core > MaxCoreNumber) { +#ifdef INTERPRETER_ENABLED + constexpr uint32_t MinCoreNumber = 0; +#else + constexpr uint32_t MinCoreNumber = 1; +#endif + if (Core > MaxCoreNumber || Core < MinCoreNumber) { // Sanitize the core option by setting the core to the JIT if invalid FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_CORE, std::to_string(FEXCore::Config::CONFIG_IRJIT)); } diff --git a/External/FEXCore/Source/Interface/Core/Core.cpp b/External/FEXCore/Source/Interface/Core/Core.cpp index ee189a66e..497b224a1 100644 --- a/External/FEXCore/Source/Interface/Core/Core.cpp +++ b/External/FEXCore/Source/Interface/Core/Core.cpp @@ -493,9 +493,11 @@ namespace FEXCore::Context { // Create CPU backend switch (Config.Core) { +#ifdef INTERPRETER_ENABLED case FEXCore::Config::CONFIG_INTERPRETER: State->CPUBackend = FEXCore::CPU::CreateInterpreterCore(this, State, CompileThread); break; +#endif case FEXCore::Config::CONFIG_IRJIT: State->PassManager->InsertRegisterAllocationPass(DoSRA); diff --git a/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterFallbacks.cpp b/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterFallbacks.cpp new file mode 100644 index 000000000..1434affa5 --- /dev/null +++ b/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterFallbacks.cpp @@ -0,0 +1,209 @@ +#include "Interface/Core/Interpreter/InterpreterOps.h" +#include "Interface/Core/Interpreter/F80Ops.h" + +#include +#include + +namespace FEXCore::CPU { + +template +static FallbackInfo GetFallbackInfo(R(*fn)(Args...)) { + return {FABI_UNKNOWN, (void*)fn}; +} + +template<> +FallbackInfo GetFallbackInfo(X80SoftFloat(*fn)(float)) { + return {FABI_F80_F32, (void*)fn}; +} + +template<> +FallbackInfo GetFallbackInfo(X80SoftFloat(*fn)(double)) { + return {FABI_F80_F64, (void*)fn}; +} + +template<> +FallbackInfo GetFallbackInfo(X80SoftFloat(*fn)(int16_t)) { + return {FABI_F80_I16, (void*)fn}; +} + +template<> +FallbackInfo GetFallbackInfo(void(*fn)(uint16_t)) { + return {FABI_VOID_U16, (void*)fn}; +} + +template<> +FallbackInfo GetFallbackInfo(X80SoftFloat(*fn)(int32_t)) { + return {FABI_F80_I32, (void*)fn}; +} + +template<> +FallbackInfo GetFallbackInfo(float(*fn)(X80SoftFloat)) { + return {FABI_F32_F80, (void*)fn}; +} + +template<> +FallbackInfo GetFallbackInfo(double(*fn)(X80SoftFloat)) { + return {FABI_F64_F80, (void*)fn}; +} + +template<> +FallbackInfo GetFallbackInfo(int16_t(*fn)(X80SoftFloat)) { + return {FABI_I16_F80, (void*)fn}; +} + +template<> +FallbackInfo GetFallbackInfo(int32_t(*fn)(X80SoftFloat)) { + return {FABI_I32_F80, (void*)fn}; +} + +template<> +FallbackInfo GetFallbackInfo(int64_t(*fn)(X80SoftFloat)) { + return {FABI_I64_F80, (void*)fn}; +} + +template<> +FallbackInfo GetFallbackInfo(uint64_t(*fn)(X80SoftFloat, X80SoftFloat)) { + return {FABI_I64_F80_F80, (void*)fn}; +} + +template<> +FallbackInfo GetFallbackInfo(X80SoftFloat(*fn)(X80SoftFloat)) { + return {FABI_F80_F80, (void*)fn}; +} + +template<> +FallbackInfo GetFallbackInfo(X80SoftFloat(*fn)(X80SoftFloat, X80SoftFloat)) { + return {FABI_F80_F80_F80, (void*)fn}; +} + +bool InterpreterOps::GetFallbackHandler(IR::IROp_Header *IROp, FallbackInfo *Info) { + uint8_t OpSize = IROp->Size; + switch(IROp->Op) { + case IR::OP_F80LOADFCW: { + *Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers::handle); + return true; + } + + case IR::OP_F80CVTTO: { + auto Op = IROp->C(); + + switch (Op->Size) { + case 4: { + *Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers::handle4); + return true; + } + case 8: { + *Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers::handle8); + return true; + } + default: LogMan::Msg::DFmt("Unhandled size: {}", OpSize); + } + break; + } + case IR::OP_F80CVT: { + switch (OpSize) { + case 4: { + *Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers::handle4); + return true; + } + case 8: { + *Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers::handle8); + return true; + } + default: LogMan::Msg::DFmt("Unhandled size: {}", OpSize); + } + break; + } + case IR::OP_F80CVTINT: { + auto Op = IROp->C(); + + switch (OpSize) { + case 2: { + *Info = GetFallbackInfo(Op->Truncate ? &FEXCore::CPU::OpHandlers::handle2t : &FEXCore::CPU::OpHandlers::handle2); + return true; + } + case 4: { + *Info = GetFallbackInfo(Op->Truncate ? &FEXCore::CPU::OpHandlers::handle4t : &FEXCore::CPU::OpHandlers::handle4); + return true; + } + case 8: { + *Info = GetFallbackInfo(Op->Truncate ? &FEXCore::CPU::OpHandlers::handle8t : &FEXCore::CPU::OpHandlers::handle8); + return true; + } + default: LogMan::Msg::DFmt("Unhandled size: {}", OpSize); + } + break; + } + case IR::OP_F80CMP: { + auto Op = IROp->C(); + + static constexpr std::array handlers{ + &FEXCore::CPU::OpHandlers::handle<0>, + &FEXCore::CPU::OpHandlers::handle<1>, + &FEXCore::CPU::OpHandlers::handle<2>, + &FEXCore::CPU::OpHandlers::handle<3>, + &FEXCore::CPU::OpHandlers::handle<4>, + &FEXCore::CPU::OpHandlers::handle<5>, + &FEXCore::CPU::OpHandlers::handle<6>, + &FEXCore::CPU::OpHandlers::handle<7>, + }; + + *Info = GetFallbackInfo(handlers[Op->Flags]); + return true; + } + + case IR::OP_F80CVTTOINT: { + auto Op = IROp->C(); + + switch (Op->Size) { + case 2: { + *Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers::handle2); + return true; + } + case 4: { + *Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers::handle4); + return true; + } + default: LogMan::Msg::DFmt("Unhandled size: {}", OpSize); + } + break; + } + +#define COMMON_X87_OP(OP) \ + case IR::OP_F80##OP: { \ + *Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers::handle); \ + return true; \ + } + + // Unary + COMMON_X87_OP(ROUND) + COMMON_X87_OP(F2XM1) + COMMON_X87_OP(TAN) + COMMON_X87_OP(SQRT) + COMMON_X87_OP(SIN) + COMMON_X87_OP(COS) + COMMON_X87_OP(XTRACT_EXP) + COMMON_X87_OP(XTRACT_SIG) + COMMON_X87_OP(BCDSTORE) + COMMON_X87_OP(BCDLOAD) + + // Binary + COMMON_X87_OP(ADD) + COMMON_X87_OP(SUB) + COMMON_X87_OP(MUL) + COMMON_X87_OP(DIV) + COMMON_X87_OP(FYL2X) + COMMON_X87_OP(ATAN) + COMMON_X87_OP(FPREM1) + COMMON_X87_OP(FPREM) + COMMON_X87_OP(SCALE) + + default: + break; + } + + return false; +} + + +} diff --git a/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterOps.cpp b/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterOps.cpp index a43cf0290..81f2e4324 100644 --- a/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterOps.cpp +++ b/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterOps.cpp @@ -322,205 +322,6 @@ void InterpreterOps::Op_Unhandled(FEXCore::IR::IROp_Header *IROp, IROpData *Data void InterpreterOps::Op_NoOp(FEXCore::IR::IROp_Header *IROp, IROpData *Data, IR::NodeID Node) { } -template -static FallbackInfo GetFallbackInfo(R(*fn)(Args...)) { - return {FABI_UNKNOWN, (void*)fn}; -} - -template<> -FallbackInfo GetFallbackInfo(X80SoftFloat(*fn)(float)) { - return {FABI_F80_F32, (void*)fn}; -} - -template<> -FallbackInfo GetFallbackInfo(X80SoftFloat(*fn)(double)) { - return {FABI_F80_F64, (void*)fn}; -} - -template<> -FallbackInfo GetFallbackInfo(X80SoftFloat(*fn)(int16_t)) { - return {FABI_F80_I16, (void*)fn}; -} - -template<> -FallbackInfo GetFallbackInfo(void(*fn)(uint16_t)) { - return {FABI_VOID_U16, (void*)fn}; -} - -template<> -FallbackInfo GetFallbackInfo(X80SoftFloat(*fn)(int32_t)) { - return {FABI_F80_I32, (void*)fn}; -} - -template<> -FallbackInfo GetFallbackInfo(float(*fn)(X80SoftFloat)) { - return {FABI_F32_F80, (void*)fn}; -} - -template<> -FallbackInfo GetFallbackInfo(double(*fn)(X80SoftFloat)) { - return {FABI_F64_F80, (void*)fn}; -} - -template<> -FallbackInfo GetFallbackInfo(int16_t(*fn)(X80SoftFloat)) { - return {FABI_I16_F80, (void*)fn}; -} - -template<> -FallbackInfo GetFallbackInfo(int32_t(*fn)(X80SoftFloat)) { - return {FABI_I32_F80, (void*)fn}; -} - -template<> -FallbackInfo GetFallbackInfo(int64_t(*fn)(X80SoftFloat)) { - return {FABI_I64_F80, (void*)fn}; -} - -template<> -FallbackInfo GetFallbackInfo(uint64_t(*fn)(X80SoftFloat, X80SoftFloat)) { - return {FABI_I64_F80_F80, (void*)fn}; -} - -template<> -FallbackInfo GetFallbackInfo(X80SoftFloat(*fn)(X80SoftFloat)) { - return {FABI_F80_F80, (void*)fn}; -} - -template<> -FallbackInfo GetFallbackInfo(X80SoftFloat(*fn)(X80SoftFloat, X80SoftFloat)) { - return {FABI_F80_F80_F80, (void*)fn}; -} - -bool InterpreterOps::GetFallbackHandler(IR::IROp_Header *IROp, FallbackInfo *Info) { - uint8_t OpSize = IROp->Size; - switch(IROp->Op) { - case IR::OP_F80LOADFCW: { - *Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers::handle); - return true; - } - - case IR::OP_F80CVTTO: { - auto Op = IROp->C(); - - switch (Op->Size) { - case 4: { - *Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers::handle4); - return true; - } - case 8: { - *Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers::handle8); - return true; - } - default: LogMan::Msg::DFmt("Unhandled size: {}", OpSize); - } - break; - } - case IR::OP_F80CVT: { - switch (OpSize) { - case 4: { - *Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers::handle4); - return true; - } - case 8: { - *Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers::handle8); - return true; - } - default: LogMan::Msg::DFmt("Unhandled size: {}", OpSize); - } - break; - } - case IR::OP_F80CVTINT: { - auto Op = IROp->C(); - - switch (OpSize) { - case 2: { - *Info = GetFallbackInfo(Op->Truncate ? &FEXCore::CPU::OpHandlers::handle2t : &FEXCore::CPU::OpHandlers::handle2); - return true; - } - case 4: { - *Info = GetFallbackInfo(Op->Truncate ? &FEXCore::CPU::OpHandlers::handle4t : &FEXCore::CPU::OpHandlers::handle4); - return true; - } - case 8: { - *Info = GetFallbackInfo(Op->Truncate ? &FEXCore::CPU::OpHandlers::handle8t : &FEXCore::CPU::OpHandlers::handle8); - return true; - } - default: LogMan::Msg::DFmt("Unhandled size: {}", OpSize); - } - break; - } - case IR::OP_F80CMP: { - auto Op = IROp->C(); - - static constexpr std::array handlers{ - &FEXCore::CPU::OpHandlers::handle<0>, - &FEXCore::CPU::OpHandlers::handle<1>, - &FEXCore::CPU::OpHandlers::handle<2>, - &FEXCore::CPU::OpHandlers::handle<3>, - &FEXCore::CPU::OpHandlers::handle<4>, - &FEXCore::CPU::OpHandlers::handle<5>, - &FEXCore::CPU::OpHandlers::handle<6>, - &FEXCore::CPU::OpHandlers::handle<7>, - }; - - *Info = GetFallbackInfo(handlers[Op->Flags]); - return true; - } - - case IR::OP_F80CVTTOINT: { - auto Op = IROp->C(); - - switch (Op->Size) { - case 2: { - *Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers::handle2); - return true; - } - case 4: { - *Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers::handle4); - return true; - } - default: LogMan::Msg::DFmt("Unhandled size: {}", OpSize); - } - break; - } - -#define COMMON_X87_OP(OP) \ - case IR::OP_F80##OP: { \ - *Info = GetFallbackInfo(&FEXCore::CPU::OpHandlers::handle); \ - return true; \ - } - - // Unary - COMMON_X87_OP(ROUND) - COMMON_X87_OP(F2XM1) - COMMON_X87_OP(TAN) - COMMON_X87_OP(SQRT) - COMMON_X87_OP(SIN) - COMMON_X87_OP(COS) - COMMON_X87_OP(XTRACT_EXP) - COMMON_X87_OP(XTRACT_SIG) - COMMON_X87_OP(BCDSTORE) - COMMON_X87_OP(BCDLOAD) - - // Binary - COMMON_X87_OP(ADD) - COMMON_X87_OP(SUB) - COMMON_X87_OP(MUL) - COMMON_X87_OP(DIV) - COMMON_X87_OP(FYL2X) - COMMON_X87_OP(ATAN) - COMMON_X87_OP(FPREM1) - COMMON_X87_OP(FPREM) - COMMON_X87_OP(SCALE) - - default: - break; - } - - return false; -} - void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uint64_t Entry, FEXCore::IR::IRListView *CurrentIR, FEXCore::Core::DebugData *DebugData) { volatile void *StackEntry = alloca(0); diff --git a/Source/Tools/FEXConfig/Main.cpp b/Source/Tools/FEXConfig/Main.cpp index 8d5527b7b..23e1ef5fd 100644 --- a/Source/Tools/FEXConfig/Main.cpp +++ b/Source/Tools/FEXConfig/Main.cpp @@ -201,6 +201,7 @@ namespace { char EmulatedCPUCores[32]{}; if (ImGui::BeginTabItem("CPU")) { +#ifdef INTERPRETER_ENABLED ImGui::Text("Core:"); auto Value = LoadedConfig->Get(FEXCore::Config::ConfigOption::CONFIG_CORE); @@ -214,7 +215,7 @@ namespace { LoadedConfig->EraseSet(FEXCore::Config::ConfigOption::CONFIG_CORE, "1"); ConfigChanged = true; } - +#endif Value = LoadedConfig->Get(FEXCore::Config::ConfigOption::CONFIG_MAXINST); if (Value.has_value() && !(*Value)->empty()) { strncpy(BlockSize, &(*Value)->at(0), 32);