mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-14 09:00:40 +02:00
Lots going on here. This moves OS thread object lifetime management and internal thread state lifetime management to the frontend. This causes a bunch of thread handling to move from the FEXCore Context to the frontend. Looking at `FEXCore/include/FEXCore/Core/Context.h` really shows how much of the API has moved to the frontend that FEXCore no longer needs to manage. Primarily this makes FEXCore itself no longer need to care about most of the management of the emulation state. A large amount of the behaviour moved wholesale from Core.cpp to LinuxEmulation's ThreadManager.cpp. Which this manages the lifetimes of both the OS threads and the FEXCore thread state objects. One feature lost was the instruction capability, but this was already buggy and is going to be rewritten/fixed when gdbserver work continues. Now that all of this management is moved to the frontend, the gdbserver can start improving since it can start managing all thread state directly.
230 lines
6.6 KiB
C++
230 lines
6.6 KiB
C++
// SPDX-License-Identifier: MIT
|
|
/*
|
|
$info$
|
|
tags: Bin|IRLoader
|
|
desc: Used to run IR Tests
|
|
$end_info$
|
|
*/
|
|
|
|
#include "Common/ArgumentLoader.h"
|
|
#include "LinuxSyscalls/SignalDelegator.h"
|
|
|
|
#include <FEXCore/Config/Config.h>
|
|
#include <FEXCore/Core/CodeLoader.h>
|
|
#include <FEXCore/Core/Context.h>
|
|
#include <FEXCore/Core/CoreState.h>
|
|
#include <FEXCore/Core/HostFeatures.h>
|
|
#include <FEXCore/Utils/Allocator.h>
|
|
#include <FEXCore/Utils/EnumUtils.h>
|
|
#include <FEXCore/Utils/FileLoading.h>
|
|
#include <FEXCore/Utils/LogManager.h>
|
|
#include <FEXCore/HLE/SyscallHandler.h>
|
|
#include <FEXCore/IR/IREmitter.h>
|
|
#include <FEXCore/fextl/fmt.h>
|
|
#include <FEXCore/fextl/sstream.h>
|
|
|
|
#include <csetjmp>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <sys/mman.h>
|
|
#include <vector>
|
|
|
|
#include "HarnessHelpers.h"
|
|
|
|
namespace FEXCore::Context {
|
|
class Context;
|
|
}
|
|
|
|
void MsgHandler(LogMan::DebugLevels Level, char const *Message)
|
|
{
|
|
fextl::fmt::print("[{}] {}\n", LogMan::DebugLevelStr(Level), Message);
|
|
fflush(stdout);
|
|
}
|
|
|
|
void AssertHandler(char const *Message)
|
|
{
|
|
fextl::fmt::print("[ASSERT] {}\n", Message);
|
|
fflush(stdout);
|
|
}
|
|
|
|
using namespace FEXCore::IR;
|
|
class IRCodeLoader final {
|
|
public:
|
|
IRCodeLoader(fextl::string const &Filename, fextl::string const &ConfigFilename) {
|
|
Config.Init(ConfigFilename);
|
|
|
|
fextl::string IRFile;
|
|
if (!FEXCore::FileLoading::LoadFile(IRFile, Filename)) {
|
|
LogMan::Msg::EFmt("Couldn't open IR file '{}'", Filename);
|
|
return;
|
|
}
|
|
fextl::stringstream IRStream(IRFile);
|
|
ParsedCode = FEXCore::IR::Parse(Allocator, IRStream);
|
|
|
|
if (ParsedCode) {
|
|
EntryRIP = 0x40000;
|
|
|
|
fextl::stringstream out;
|
|
auto IR = ParsedCode->ViewIR();
|
|
FEXCore::IR::Dump(&out, &IR, nullptr);
|
|
fextl::fmt::print("IR:\n{}\n@@@@@\n", out.str());
|
|
|
|
for (auto &[region, size] : Config.GetMemoryRegions()) {
|
|
FEXCore::Allocator::mmap(reinterpret_cast<void *>(region), size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
|
}
|
|
|
|
Config.LoadMemory();
|
|
}
|
|
}
|
|
|
|
bool LoadIR(FEXCore::Context::Context *CTX) const {
|
|
if (!ParsedCode) {
|
|
return false;
|
|
}
|
|
|
|
return !!CTX->AddCustomIREntrypoint(EntryRIP, [ParsedCodePtr = ParsedCode.get()](uintptr_t Entrypoint, FEXCore::IR::IREmitter *emit) {
|
|
emit->CopyData(*ParsedCodePtr);
|
|
});
|
|
}
|
|
|
|
bool CompareStates(FEXCore::Core::CPUState const *State, bool SupportsAVX) {
|
|
return Config.CompareStates(State, nullptr, SupportsAVX);
|
|
}
|
|
|
|
uint64_t GetStackPointer()
|
|
{
|
|
return reinterpret_cast<uint64_t>(FEXCore::Allocator::mmap(nullptr, STACK_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
|
}
|
|
|
|
uint64_t DefaultRIP() const
|
|
{
|
|
return EntryRIP;
|
|
}
|
|
|
|
bool RequiresAVX() const {
|
|
return Config.RequiresAVX();
|
|
}
|
|
|
|
private:
|
|
uint64_t EntryRIP{};
|
|
fextl::unique_ptr<IREmitter> ParsedCode;
|
|
|
|
FEXCore::Utils::PooledAllocatorMalloc Allocator;
|
|
FEX::HarnessHelper::ConfigLoader Config;
|
|
constexpr static uint64_t STACK_SIZE = 8 * 1024 * 1024;
|
|
};
|
|
|
|
class DummySyscallHandler: public FEXCore::HLE::SyscallHandler, public FEXCore::Allocator::FEXAllocOperators {
|
|
public:
|
|
uint64_t HandleSyscall(FEXCore::Core::CpuStateFrame *Frame, FEXCore::HLE::SyscallArguments *Args) override {
|
|
LOGMAN_MSG_A_FMT("Syscalls not implemented");
|
|
return 0;
|
|
}
|
|
|
|
FEXCore::HLE::SyscallABI GetSyscallABI(uint64_t Syscall) override {
|
|
LOGMAN_MSG_A_FMT("Syscalls not implemented");
|
|
return {0, false, 0 };
|
|
}
|
|
|
|
// These are no-ops implementations of the SyscallHandler API
|
|
FEXCore::HLE::AOTIRCacheEntryLookupResult LookupAOTIRCacheEntry(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestAddr) override {
|
|
return {0, 0};
|
|
}
|
|
};
|
|
|
|
int main(int argc, char **argv, char **const envp)
|
|
{
|
|
FEXCore::Allocator::GLIBCScopedFault GLIBFaultScope;
|
|
LogMan::Throw::InstallHandler(AssertHandler);
|
|
LogMan::Msg::InstallHandler(MsgHandler);
|
|
|
|
FEXCore::Config::Initialize();
|
|
FEXCore::Config::AddLayer(fextl::make_unique<FEX::ArgLoader::ArgLoader>(argc, argv));
|
|
FEXCore::Config::AddLayer(FEX::Config::CreateEnvironmentLayer(envp));
|
|
FEXCore::Config::Load();
|
|
// Ensure the IRLoader runs in 64-bit mode.
|
|
// This is to ensure that static register allocation in the JIT
|
|
// is configured correctly for accesses to the top 8 GPRs and 8 XMM registers.
|
|
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_IS64BIT_MODE, "1");
|
|
#ifdef VIXL_SIMULATOR
|
|
// If running under the vixl simulator, ensure that indirect runtime calls are enabled.
|
|
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_DISABLE_VIXL_INDIRECT_RUNTIME_CALLS, "0");
|
|
#endif
|
|
|
|
auto Args = FEX::ArgLoader::Get();
|
|
auto ParsedArgs = FEX::ArgLoader::GetParsedArgs();
|
|
|
|
LOGMAN_THROW_A_FMT(Args.size() > 1, "Not enough arguments");
|
|
|
|
FEXCore::Context::InitializeStaticTables();
|
|
auto CTX = FEXCore::Context::Context::CreateNewContext();
|
|
|
|
auto SignalDelegation = FEX::HLE::CreateSignalDelegator(CTX.get(), {});
|
|
|
|
CTX->SetSignalDelegator(SignalDelegation.get());
|
|
CTX->SetSyscallHandler(new DummySyscallHandler());
|
|
|
|
IRCodeLoader Loader(Args[0], Args[1]);
|
|
|
|
// Skip tests that require AVX on hosts that don't support it.
|
|
const bool SupportsAVX = CTX->GetHostFeatures().SupportsAVX;
|
|
if (!SupportsAVX && Loader.RequiresAVX()) {
|
|
return 0;
|
|
}
|
|
|
|
int Return{};
|
|
|
|
if (Loader.LoadIR(CTX.get()))
|
|
{
|
|
if (!CTX->InitCore()) {
|
|
return -1;
|
|
}
|
|
auto ParentThread = CTX->CreateThread(Loader.DefaultRIP(), Loader.GetStackPointer());
|
|
|
|
auto ShutdownReason = FEXCore::Context::ExitReason::EXIT_SHUTDOWN;
|
|
|
|
// There might already be an exit handler, leave it installed
|
|
if (!CTX->GetExitHandler())
|
|
{
|
|
CTX->SetExitHandler([&](uint64_t thread, FEXCore::Context::ExitReason reason) {
|
|
if (reason != FEXCore::Context::ExitReason::EXIT_DEBUG)
|
|
{
|
|
ShutdownReason = reason;
|
|
}
|
|
});
|
|
}
|
|
|
|
jmp_buf LongJump{};
|
|
int LongJumpVal{};
|
|
|
|
SignalDelegation->RegisterFrontendHostSignalHandler(SIGSEGV, [&LongJump](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) {
|
|
longjmp(LongJump, 1);
|
|
return false;
|
|
}, true);
|
|
|
|
LongJumpVal = setjmp(LongJump);
|
|
if (!LongJumpVal) {
|
|
CTX->RunUntilExit(ParentThread);
|
|
}
|
|
|
|
LogMan::Msg::DFmt("Reason we left VM: {}", FEXCore::ToUnderlying(ShutdownReason));
|
|
|
|
// Just re-use compare state. It also checks against the expected values in config.
|
|
const bool Passed = Loader.CompareStates(&ParentThread->CurrentFrame->State, SupportsAVX);
|
|
|
|
LogMan::Msg::IFmt("Passed? {}\n", Passed ? "Yes" : "No");
|
|
|
|
Return = Passed ? 0 : -1;
|
|
CTX->DestroyThread(ParentThread);
|
|
}
|
|
else
|
|
{
|
|
LogMan::Msg::EFmt("Couldn't load IR");
|
|
Return = -1;
|
|
}
|
|
|
|
return Return;
|
|
}
|