diff --git a/External/FEXCore/Source/Interface/Config/Config.json b/External/FEXCore/Source/Interface/Config/Config.json index 5f003e8e5..7519d9284 100644 --- a/External/FEXCore/Source/Interface/Config/Config.json +++ b/External/FEXCore/Source/Interface/Config/Config.json @@ -218,6 +218,14 @@ "Captures both the loaded executable and libraries it loads." ] }, + "AOTIRGenerate": { + "Type": "bool", + "Default": "false", + "Desc": [ + "Scans file for executable code and generates an AOT IR cache.", + "Does not run the executable." + ] + }, "AOTIRLoad": { "Type": "bool", "Default": "false", diff --git a/External/FEXCore/Source/Interface/Context/Context.cpp b/External/FEXCore/Source/Interface/Context/Context.cpp index 2f25596f7..1c4789a06 100644 --- a/External/FEXCore/Source/Interface/Context/Context.cpp +++ b/External/FEXCore/Source/Interface/Context/Context.cpp @@ -50,6 +50,9 @@ namespace FEXCore::Context { CTX->Step(); } + void CompileRIP(FEXCore::Context::Context *CTX, uint64_t GuestRIP) { + CTX->CompileBlock(CTX->ParentThread->CurrentFrame, GuestRIP); + } FEXCore::Context::ExitReason RunUntilExit(FEXCore::Context::Context *CTX) { return CTX->RunUntilExit(); @@ -150,6 +153,10 @@ namespace FEXCore::Context { return CTX->WriteAOTIRCache(CacheWriter); } + void WriteFilesWithCode(FEXCore::Context::Context *CTX, std::function Writer) { + CTX->WriteFilesWithCode(Writer); + } + void AddNamedRegion(FEXCore::Context::Context *CTX, uintptr_t Base, uintptr_t Length, uintptr_t Offset, const std::string& Name) { return CTX->AddNamedRegion(Base, Length, Offset, Name); } diff --git a/External/FEXCore/Source/Interface/Context/Context.h b/External/FEXCore/Source/Interface/Context/Context.h index 5d0ffcb6b..5f922ef46 100644 --- a/External/FEXCore/Source/Interface/Context/Context.h +++ b/External/FEXCore/Source/Interface/Context/Context.h @@ -111,6 +111,7 @@ namespace FEXCore::Context { FEX_CONFIG_OPT(ABILocalFlags, ABILOCALFLAGS); FEX_CONFIG_OPT(ABINoPF, ABINOPF); FEX_CONFIG_OPT(AOTIRCapture, AOTIRCAPTURE); + FEX_CONFIG_OPT(AOTIRGenerate, AOTIRGENERATE); FEX_CONFIG_OPT(AOTIRLoad, AOTIRLOAD); FEX_CONFIG_OPT(SMCChecks, SMCCHECKS); FEX_CONFIG_OPT(Core, CORE); @@ -160,11 +161,13 @@ namespace FEXCore::Context { uint64_t Len; uint64_t Offset; std::string fileid; + std::string filename; void *CachedFileEntry; + bool ContainsCode; }; std::map AddrToFile; - + std::map FilesWithCode; #ifdef BLOCKSTATS std::unique_ptr BlockData; @@ -214,9 +217,14 @@ namespace FEXCore::Context { std::tuple CompileCode(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP); uintptr_t CompileBlock(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP); + + // same as CompileBlock, but aborts on failure + void CompileBlockJit(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP); bool LoadAOTIRCache(int streamfd); bool WriteAOTIRCache(std::function(const std::string&)> CacheWriter); + void WriteFilesWithCode(std::function Writer); + // 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); diff --git a/External/FEXCore/Source/Interface/Core/Core.cpp b/External/FEXCore/Source/Interface/Core/Core.cpp index 4c3b4eb17..d3c8cfc85 100644 --- a/External/FEXCore/Source/Interface/Core/Core.cpp +++ b/External/FEXCore/Source/Interface/Core/Core.cpp @@ -666,12 +666,16 @@ namespace FEXCore::Context { if (TableInfo->OpcodeDispatcher) { auto Fn = TableInfo->OpcodeDispatcher; Thread->OpDispatcher->HandledLock = false; + Thread->OpDispatcher->ResetDecodeFailure(); std::invoke(Fn, Thread->OpDispatcher, DecodedInfo); if (Thread->OpDispatcher->HadDecodeFailure()) { HadDispatchError = true; } else { - LOGMAN_THROW_A(Thread->OpDispatcher->HandledLock == IsLocked, "Missing LOCK HANDLER at 0x%lx{'%s'}\n", Block.Entry + BlockInstructionsLength, TableInfo->Name); + if (Thread->OpDispatcher->HandledLock != IsLocked) { + HadDispatchError = true; + LogMan::Msg::E("Missing LOCK HANDLER at 0x%lx{'%s'}", Block.Entry + BlockInstructionsLength, TableInfo->Name); + } BlockInstructionsLength += DecodedInfo->InstSize; TotalInstructionsLength += DecodedInfo->InstSize; ++TotalInstructions; @@ -687,7 +691,7 @@ namespace FEXCore::Context { if (TotalInstructions == 0) { // Couldn't handle any instruction in op dispatcher Thread->OpDispatcher->ResetWorkingList(); - return { nullptr, nullptr, 0, 0, 0, 0}; + return { nullptr, nullptr, 0, 0, 0, 0 }; } else { uint8_t GPRSize = Config.Is64BitMode ? 8 : 4; @@ -843,6 +847,18 @@ namespace FEXCore::Context { GeneratedIR = false; } + { + std::lock_guard lk(AOTIRCacheLock); + auto file = AddrToFile.lower_bound(GuestRIP); + if (file != AddrToFile.begin()) { + --file; + if (!file->second.ContainsCode) { + file->second.ContainsCode = true; + FilesWithCode[file->second.fileid] = file->second.filename; + } + } + } + if (IRList == nullptr && Config.AOTIRLoad) { std::lock_guard lk(AOTIRCacheLock); auto file = AddrToFile.lower_bound(GuestRIP); @@ -905,6 +921,9 @@ namespace FEXCore::Context { GeneratedIR = true; } + if (IRList == nullptr) { + return { nullptr, nullptr, nullptr, nullptr, false, 0, 0 }; + } // Attempt to get the CPU backend to compile this code return { Thread->CPUBackend->CompileCode(GuestRIP, IRList, DebugData, RAData), IRList, DebugData, RAData, GeneratedIR, StartAddr, Length}; } @@ -954,6 +973,13 @@ namespace FEXCore::Context { } + void Context::WriteFilesWithCode(std::function Writer) { + std::lock_guard lk(AOTIRCacheLock); + for( const auto &File: FilesWithCode) { + Writer(File.first, File.second); + } + } + bool Context::WriteAOTIRCache(std::function(const std::string&)> CacheWriter) { std::lock_guard lk(AOTIRCacheLock); @@ -1027,7 +1053,15 @@ namespace FEXCore::Context { return rv; } + void Context::CompileBlockJit(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP) { + auto NewBlock = CompileBlock(Frame, GuestRIP); + if (NewBlock == 0) { + LogMan::Msg::E("CompileBlockJit: Failed to compile code %lX - aborting process", GuestRIP); + abort(); + } + } + uintptr_t Context::CompileBlock(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP) { auto Thread = Frame->Thread; @@ -1080,7 +1114,11 @@ namespace FEXCore::Context { Length = _Length; } - LOGMAN_THROW_A(CodePtr != nullptr, "Failed to compile code %lX", GuestRIP); + if (CodePtr == nullptr) { + if (DecrementRefCount) + --Thread->CompileBlockReentrantRefCount; + return 0; + } // The core managed to compile the code. #if ENABLE_JITSYMBOLS @@ -1101,7 +1139,7 @@ namespace FEXCore::Context { Thread->LocalIRCache.insert({GuestRIP, std::move(Entry)}); // Add to AOT cache if aot generation is enabled - if (Config.AOTIRCapture && RAData) { + if ((Config.AOTIRCapture() || Config.AOTIRGenerate()) && RAData) { std::lock_guard lk(AOTIRCacheLock); RAData->IsShared = true; @@ -1267,7 +1305,7 @@ namespace FEXCore::Context { std::lock_guard lk(AOTIRCacheLock); - AddrToFile.insert({ Base, { Base, Size, Offset, fileid, nullptr } }); + AddrToFile.insert({ Base, { Base, Size, Offset, fileid, filename, nullptr, false} }); if (Config.AOTIRLoad && !AOTIRCache.contains(fileid) && AOTIRLoader) { auto streamfd = AOTIRLoader(fileid); @@ -1284,4 +1322,9 @@ namespace FEXCore::Context { // TODO: Support partial removing AddrToFile.erase(Base); } + + void ConfigureAOTGen(FEXCore::Context::Context *CTX, std::set *ExternalBranches, uint64_t SectionMaxAddress) { + CTX->ParentThread->FrontendDecoder->SetExternalBranches(ExternalBranches); + CTX->ParentThread->FrontendDecoder->SetSectionMaxAddress(SectionMaxAddress); + } } diff --git a/External/FEXCore/Source/Interface/Core/Dispatcher/Dispatcher.cpp b/External/FEXCore/Source/Interface/Core/Dispatcher/Dispatcher.cpp index a1549f97d..cdb9aa005 100644 --- a/External/FEXCore/Source/Interface/Core/Dispatcher/Dispatcher.cpp +++ b/External/FEXCore/Source/Interface/Core/Dispatcher/Dispatcher.cpp @@ -323,14 +323,14 @@ bool Dispatcher::HandleSignalPause(int Signal, void *info, void *ucontext) { } uint64_t Dispatcher::GetCompileBlockPtr() { - using ClassPtrType = uintptr_t (FEXCore::Context::Context::*)(FEXCore::Core::CpuStateFrame *, uint64_t); + using ClassPtrType = void (FEXCore::Context::Context::*)(FEXCore::Core::CpuStateFrame *, uint64_t); union PtrCast { ClassPtrType ClassPtr; uintptr_t Data; }; PtrCast CompileBlockPtr; - CompileBlockPtr.ClassPtr = &FEXCore::Context::Context::CompileBlock; + CompileBlockPtr.ClassPtr = &FEXCore::Context::Context::CompileBlockJit; return CompileBlockPtr.Data; } diff --git a/External/FEXCore/Source/Interface/Core/Dispatcher/X86Dispatcher.cpp b/External/FEXCore/Source/Interface/Core/Dispatcher/X86Dispatcher.cpp index 485846d37..63d475bad 100644 --- a/External/FEXCore/Source/Interface/Core/Dispatcher/X86Dispatcher.cpp +++ b/External/FEXCore/Source/Interface/Core/Dispatcher/X86Dispatcher.cpp @@ -181,19 +181,10 @@ X86Dispatcher::X86Dispatcher(FEXCore::Context::Context *ctx, FEXCore::Core::Inte { L(NoBlock); - using ClassPtrType = uintptr_t (FEXCore::Context::Context::*)(FEXCore::Core::CpuStateFrame *, uint64_t); - union PtrCast { - ClassPtrType ClassPtr; - uintptr_t Data; - }; - - PtrCast Ptr; - Ptr.ClassPtr = &FEXCore::Context::Context::CompileBlock; - // {rdi, rsi, rdx} mov(rdi, reinterpret_cast(CTX)); mov(rsi, STATE); - mov(rax, Ptr.Data); + mov(rax, GetCompileBlockPtr()); call(rax); diff --git a/External/FEXCore/Source/Interface/Core/Frontend.cpp b/External/FEXCore/Source/Interface/Core/Frontend.cpp index e38949afb..3785291c4 100644 --- a/External/FEXCore/Source/Interface/Core/Frontend.cpp +++ b/External/FEXCore/Source/Interface/Core/Frontend.cpp @@ -15,6 +15,7 @@ $end_info$ #include #include #include +#include namespace FEXCore::Frontend { using namespace FEXCore::X86Tables; @@ -475,6 +476,9 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op) CurrentDest->TypeGPR.Type = DecodedOperand::TYPE_GPR; DecodeInst->Dest.TypeGPR.HighBits = (Is8BitDest && !HasREX && (Op & 0b111) >= 0b100) || HasHighXMM; CurrentDest->TypeGPR.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, Op & 0b111, Is8BitDest, HasREX, false, false); + + if (CurrentDest->TypeGPR.GPR == FEXCore::X86State::REG_INVALID) + return false; } uint8_t Bytes = Info->MoreBytes; @@ -501,27 +505,36 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op) GPR.TypeGPR.HighBits = (GPR8Bit && ModRM.reg >= 0b100 && !HasREX) || HasHighXMM; GPR.TypeGPR.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_R ? 1 : 0, ModRM.reg, GPR8Bit, HasREX, HasXMMGPR, HasMMGPR); + if (GPR.TypeGPR.GPR == FEXCore::X86State::REG_INVALID) + return false; + // ModRM.mod == 0b11 == Register // ModRM.Mod != 0b11 == Register-direct addressing if (ModRM.mod == 0b11) { NonGPR.TypeGPR.Type = DecodedOperand::TYPE_GPR; NonGPR.TypeGPR.HighBits = (NonGPR8Bit && ModRM.rm >= 0b100 && !HasREX) || HasHighXMM; NonGPR.TypeGPR.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, ModRM.rm, NonGPR8Bit, HasREX, HasXMMNonGPR, HasMMNonGPR); + if (NonGPR.TypeGPR.GPR == FEXCore::X86State::REG_INVALID) + return false; } else { auto Disp = DecodeModRMs_Disp[Has16BitAddressing]; (this->*Disp)(&NonGPR, ModRM); } + + return true; }; size_t CurrentSrc = 0; if (Info->Flags & FEXCore::X86Tables::InstFlags::FLAGS_MODRM) { if (Info->Flags & FEXCore::X86Tables::InstFlags::FLAGS_SF_MOD_DST) { - ModRMOperand(DecodeInst->Src[CurrentSrc], DecodeInst->Dest, HasXMMSrc, HasXMMDst, HasMMSrc, HasMMDst, Is8BitSrc, Is8BitDest); + if (!ModRMOperand(DecodeInst->Src[CurrentSrc], DecodeInst->Dest, HasXMMSrc, HasXMMDst, HasMMSrc, HasMMDst, Is8BitSrc, Is8BitDest)) + return false; } else { - ModRMOperand(DecodeInst->Dest, DecodeInst->Src[CurrentSrc], HasXMMDst, HasXMMSrc, HasMMDst, HasMMSrc, Is8BitDest, Is8BitSrc); + if (!ModRMOperand(DecodeInst->Dest, DecodeInst->Src[CurrentSrc], HasXMMDst, HasXMMSrc, HasMMDst, HasMMSrc, Is8BitDest, Is8BitSrc)) + return false; } ++CurrentSrc; } @@ -681,7 +694,10 @@ bool Decoder::NormalOpHeader(FEXCore::X86Tables::X86InstInfo const *Info, uint16 uint8_t Byte2 = ReadByte(); pp = Byte2 & 0b11; map_select = Byte1 & 0b11111; - LOGMAN_THROW_A(map_select >= 1 && map_select <= 3, "We don't understand a map_select of: %d", map_select); + if (!(map_select >= 1 && map_select <= 3)) { + LogMan::Msg::E("We don't understand a map_select of: %d", map_select); + return false; + } } uint16_t VEXOp = ReadByte(); @@ -729,6 +745,8 @@ bool Decoder::DecodeInstruction(uint64_t PC) { DecodeInst->PC = PC; for(;;) { + if (InstructionSize >= MAX_INST_SIZE) + return false; uint8_t Op = ReadByte(); switch (Op) { case 0x0F: {// Escape Op @@ -909,6 +927,10 @@ bool Decoder::DecodeInstruction(uint64_t PC) { } + if (DecodeInst->Dest.TypeNone.Type == FEXCore::X86Tables::DecodedOperand::TYPE_GPR) { + assert(DecodeInst->Dest.TypeGPR.GPR != 255); + } + return true; } @@ -938,9 +960,13 @@ void Decoder::BranchTargetInMultiblockRange() { TargetRIP = DecodeInst->PC + DecodeInst->InstSize + DecodeInst->Src[0].TypeLiteral.Literal; Conditional = false; break; + case 0xE8: // Call - Immediate target, We don't want to inline calls + if (ExternalBranches) { + ExternalBranches->insert(DecodeInst->PC + DecodeInst->InstSize); + } + [[fallthrough]]; case 0xC2: // RET imm case 0xC3: // RET - case 0xE8: // Call - Immediate target, We don't want to inline calls default: return; break; @@ -970,6 +996,10 @@ void Decoder::BranchTargetInMultiblockRange() { BlocksToDecode.find(TargetRIP) == BlocksToDecode.end()) { BlocksToDecode.emplace(TargetRIP); } + } else { + if (ExternalBranches) { + ExternalBranches->insert(TargetRIP); + } } } @@ -993,7 +1023,7 @@ bool Decoder::DecodeInstructionsAtEntry(uint8_t const* _InstStream, uint64_t PC) // If we don't have symbols available then we become a bit optimistic about multiblock ranges if (!SymbolAvailable) { // If we don't have a symbol available then assume all branches are valid for multiblock - SymbolMaxAddress = ~0ULL; + SymbolMaxAddress = SectionMaxAddress; SymbolMinAddress = EntryPoint; } @@ -1023,6 +1053,9 @@ bool Decoder::DecodeInstructionsAtEntry(uint8_t const* _InstStream, uint64_t PC) if (ErrorDuringDecoding) { LogMan::Msg::D("Couldn't Decode something at 0x%lx, Started at 0x%lx", PC + PCOffset, PC); + if (Blocks.size() == 1) { + return false; + } LOGMAN_THROW_A(Blocks.size() != 1, "Decode Error in entry block"); CurrentBlockDecoding.HasInvalidInstruction = true; diff --git a/External/FEXCore/Source/Interface/Core/Frontend.h b/External/FEXCore/Source/Interface/Core/Frontend.h index 723177dc6..5430447ac 100644 --- a/External/FEXCore/Source/Interface/Core/Frontend.h +++ b/External/FEXCore/Source/Interface/Core/Frontend.h @@ -33,6 +33,8 @@ public: uint64_t DecodedMinAddress {}; uint64_t DecodedMaxAddress {~0ULL}; + void SetSectionMaxAddress(uint64_t v) { SectionMaxAddress = v; } + void SetExternalBranches(std::set *v) { ExternalBranches = v; } private: FEXCore::Context::Context *CTX; @@ -65,10 +67,12 @@ private: uint64_t MaxCondBranchBackwards {~0ULL}; uint64_t SymbolMaxAddress {}; uint64_t SymbolMinAddress {~0ULL}; + uint64_t SectionMaxAddress {~0ULL}; std::vector Blocks; std::set BlocksToDecode; std::set HasBlocks; + std::set *ExternalBranches {nullptr}; // ModRM rm decoding using DecodeModRMPtr = void (FEXCore::Frontend::Decoder::*)(X86Tables::DecodedOperand *Operand, X86Tables::ModRMDecoded ModRM); diff --git a/External/FEXCore/Source/Interface/Core/JIT/Arm64/MiscOps.cpp b/External/FEXCore/Source/Interface/Core/JIT/Arm64/MiscOps.cpp index b70248ab5..ab7c19fbc 100644 --- a/External/FEXCore/Source/Interface/Core/JIT/Arm64/MiscOps.cpp +++ b/External/FEXCore/Source/Interface/Core/JIT/Arm64/MiscOps.cpp @@ -42,6 +42,12 @@ DEF_OP(Break) { case 5: // Guest ud2 hlt(4); break; + case 2: // overflow + hlt(4); + break; + case 3: // int 1 + hlt(4); + break; case 4: { // HLT // Time to quit // Set our stack to the starting stack location diff --git a/External/FEXCore/Source/Interface/Core/JIT/x86_64/MiscOps.cpp b/External/FEXCore/Source/Interface/Core/JIT/x86_64/MiscOps.cpp index c0a928dac..177c3c325 100644 --- a/External/FEXCore/Source/Interface/Core/JIT/x86_64/MiscOps.cpp +++ b/External/FEXCore/Source/Interface/Core/JIT/x86_64/MiscOps.cpp @@ -37,6 +37,12 @@ DEF_OP(Break) { case 5: // Guest ud2 ud2(); break; + case 2: // overflow + ud2(); + break; + case 3: // int 1 + ud2(); + break; case 4: { // HLT // Time to quit // Set our stack to the starting stack location diff --git a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp index b1a7397e4..fcf917d04 100644 --- a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp +++ b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp @@ -189,7 +189,11 @@ RSP SS */ void OpDispatchBuilder::IRETOp(OpcodeArgs) { - LOGMAN_THROW_A(CTX->Config.Is64BitMode == true, "IRET only implemented for x64"); + if (CTX->Config.Is64BitMode == false) { + LogMan::Msg::E("IRET only implemented for x64"); + DecodeFailure = true; + return; + } uint8_t GPRSize = CTX->Config.Is64BitMode ? 8 : 4; @@ -1493,18 +1497,25 @@ void OpDispatchBuilder::MOVSegOp(OpcodeArgs) { _StoreContext(GPRClass, 2, offsetof(FEXCore::Core::CPUState, ss), Src); break; case 6: // GS - LOGMAN_THROW_A(!CTX->Config.Is64BitMode, "We don't support modifying GS selector in 64bit mode!"); if (!CTX->Config.Is64BitMode) { _StoreContext(GPRClass, 2, offsetof(FEXCore::Core::CPUState, gs), Src); + } else { + LogMan::Msg::E("We don't support modifying GS selector in 64bit mode!"); + DecodeFailure = true; } break; case 7: // FS - LOGMAN_THROW_A(!CTX->Config.Is64BitMode, "We don't support modifying FS selector in 64bit mode!"); if (!CTX->Config.Is64BitMode) { _StoreContext(GPRClass, 2, offsetof(FEXCore::Core::CPUState, fs), Src); + } else { + LogMan::Msg::E("We don't support modifying FS selector in 64bit mode!"); + DecodeFailure = true; } break; - default: LOGMAN_MSG_A("Unknown segment register: %d", Op->Dest.TypeGPR.GPR); + default: + LogMan::Msg::E("Unknown segment register: %d", Op->Dest.TypeGPR.GPR); + DecodeFailure = true; + break; } } else { @@ -1539,7 +1550,10 @@ void OpDispatchBuilder::MOVSegOp(OpcodeArgs) { Segment = _LoadContext(2, offsetof(FEXCore::Core::CPUState, fs), GPRClass); } break; - default: LOGMAN_MSG_A("Unknown segment register: %d", Op->Src[0].TypeGPR.GPR); + default: + LogMan::Msg::E("Unknown segment register: %d", Op->Dest.TypeGPR.GPR); + DecodeFailure = true; + return; } StoreResult(GPRClass, Op, Segment, -1); } @@ -2784,7 +2798,11 @@ void OpDispatchBuilder::IMULOp(OpcodeArgs) { _StoreContext(GPRClass, 8, offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RDX]), LocalResultHigh); } else if (Size == 8) { - LOGMAN_THROW_A(CTX->Config.Is64BitMode, "Doesn't exist in 32bit mode"); + if (!CTX->Config.Is64BitMode) { + LogMan::Msg::E("Doesn't exist in 32bit mode"); + DecodeFailure = true; + return; + } // 64bits stored in RAX // 64bits stored in RDX ResultHigh = _MulH(Src1, Src2); @@ -2829,7 +2847,11 @@ void OpDispatchBuilder::MULOp(OpcodeArgs) { _StoreContext(GPRClass, GPRSize, offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RDX]), ResultHigh); } else if (Size == 8) { - LOGMAN_THROW_A(CTX->Config.Is64BitMode, "Doesn't exist in 32bit mode"); + if (!CTX->Config.Is64BitMode) { + LogMan::Msg::E("Doesn't exist in 32bit mode"); + DecodeFailure = true; + return; + } // 64bits stored in RAX // 64bits stored in RDX ResultHigh = _UMulH(Src1, Src2); @@ -3008,7 +3030,11 @@ void OpDispatchBuilder::RDTSCOp(OpcodeArgs) { } void OpDispatchBuilder::INCOp(OpcodeArgs) { - LOGMAN_THROW_A(!(Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_REP_PREFIX), "Can't handle REP on this\n"); + if (Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_REP_PREFIX) { + LogMan::Msg::E("Can't handle REP on this"); + DecodeFailure = true; + return; + } OrderedNode *Dest; OrderedNode *Result; @@ -3039,7 +3065,11 @@ void OpDispatchBuilder::INCOp(OpcodeArgs) { } void OpDispatchBuilder::DECOp(OpcodeArgs) { - LOGMAN_THROW_A(!(Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_REP_PREFIX), "Can't handle REP on this\n"); + if (Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_REP_PREFIX) { + LogMan::Msg::E("Can't handle REP on this"); + DecodeFailure = true; + return; + } OrderedNode *Dest; OrderedNode *Result; @@ -3070,8 +3100,17 @@ void OpDispatchBuilder::DECOp(OpcodeArgs) { void OpDispatchBuilder::STOSOp(OpcodeArgs) { uint8_t GPRSize = CTX->Config.Is64BitMode ? 8 : 4; - LOGMAN_THROW_A(!(Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_REPNE_PREFIX), "Invalid REPNE on STOS"); - LOGMAN_THROW_A(!(Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_ADDRESS_SIZE), "Can't handle adddress size\n"); + if (Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_REPNE_PREFIX) { + LogMan::Msg::E("Invalid REPNE on STOS"); + DecodeFailure = true; + return; + } + if (Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_ADDRESS_SIZE) { + LogMan::Msg::E("Can't handle adddress size"); + DecodeFailure = true; + return; + } + auto Size = GetSrcSize(Op); bool Repeat = Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_REP_PREFIX; @@ -3170,8 +3209,16 @@ void OpDispatchBuilder::STOSOp(OpcodeArgs) { void OpDispatchBuilder::MOVSOp(OpcodeArgs) { uint8_t GPRSize = CTX->Config.Is64BitMode ? 8 : 4; - LOGMAN_THROW_A(!(Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_REPNE_PREFIX), "Invalid REPNE on MOVS\n"); - LOGMAN_THROW_A(!(Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_ADDRESS_SIZE), "Can't handle adddress size\n"); + if (Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_REPNE_PREFIX) { + LogMan::Msg::E("Invalid REPNE on MOVS"); + DecodeFailure = true; + return; + } + if (Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_ADDRESS_SIZE) { + LogMan::Msg::E("Can't handle adddress size"); + DecodeFailure = true; + return; + } // RA now can handle these to be here, to avoud DF accesses auto Size = GetSrcSize(Op); @@ -3260,7 +3307,12 @@ void OpDispatchBuilder::MOVSOp(OpcodeArgs) { void OpDispatchBuilder::CMPSOp(OpcodeArgs) { uint8_t GPRSize = CTX->Config.Is64BitMode ? 8 : 4; - LOGMAN_THROW_A(!(Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_ADDRESS_SIZE), "Can't handle adddress size\n"); + if (Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_ADDRESS_SIZE) { + LogMan::Msg::E("Can't handle adddress size"); + DecodeFailure = true; + return; + } + auto Size = GetSrcSize(Op); bool Repeat = Op->Flags & (FEXCore::X86Tables::DecodeFlags::FLAG_REPNE_PREFIX | FEXCore::X86Tables::DecodeFlags::FLAG_REP_PREFIX); @@ -3375,8 +3427,16 @@ void OpDispatchBuilder::CMPSOp(OpcodeArgs) { void OpDispatchBuilder::LODSOp(OpcodeArgs) { uint8_t GPRSize = CTX->Config.Is64BitMode ? 8 : 4; - LOGMAN_THROW_A(!(Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_ADDRESS_SIZE), "Can't handle adddress size\n"); - LOGMAN_THROW_A(!(Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_REPNE_PREFIX), "LODS doesn't support REPNE"); + if (Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_REPNE_PREFIX) { + LogMan::Msg::E("Invalid REPNE on LODS"); + DecodeFailure = true; + return; + } + if (Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_ADDRESS_SIZE) { + LogMan::Msg::E("Can't handle adddress size"); + DecodeFailure = true; + return; + } auto Size = GetSrcSize(Op); bool Repeat = Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_REP_PREFIX; @@ -3468,7 +3528,11 @@ void OpDispatchBuilder::LODSOp(OpcodeArgs) { } void OpDispatchBuilder::SCASOp(OpcodeArgs) { - LOGMAN_THROW_A(!(Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_ADDRESS_SIZE), "Can't handle adddress size\n"); + if (Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_ADDRESS_SIZE) { + LogMan::Msg::E("Can't handle adddress size"); + DecodeFailure = true; + return; + } uint8_t GPRSize = CTX->Config.Is64BitMode ? 8 : 4; @@ -3634,7 +3698,11 @@ void OpDispatchBuilder::POPFOp(OpcodeArgs) { } void OpDispatchBuilder::NEGOp(OpcodeArgs) { - LOGMAN_THROW_A(!DestIsLockedMem(Op), "Can't handle LOCK on NEG\n"); + if (DestIsLockedMem(Op)) { + LogMan::Msg::E("Can't handle LOCK on NEG"); + DecodeFailure = true; + return; + } OrderedNode *Dest = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, -1); auto ZeroConst = _Constant(0); OrderedNode *Result = _Sub(ZeroConst, Dest); @@ -3685,7 +3753,11 @@ void OpDispatchBuilder::DIVOp(OpcodeArgs) { _StoreContext(GPRClass, GPRSize, offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RDX]), URemOp); } else if (Size == 8) { - LOGMAN_THROW_A(CTX->Config.Is64BitMode, "Doesn't exist in 32bit mode"); + if (!CTX->Config.Is64BitMode) { + LogMan::Msg::E("Doesn't exist in 32bit mode"); + DecodeFailure = true; + return; + } OrderedNode *Src1 = _LoadContext(Size, offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RAX]), GPRClass); OrderedNode *Src2 = _LoadContext(Size, offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RDX]), GPRClass); @@ -3736,7 +3808,11 @@ void OpDispatchBuilder::IDIVOp(OpcodeArgs) { _StoreContext(GPRClass, GPRSize, offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RDX]), URemOp); } else if (Size == 8) { - LOGMAN_THROW_A(CTX->Config.Is64BitMode, "Doesn't exist in 32bit mode"); + if (!CTX->Config.Is64BitMode) { + LogMan::Msg::E("Doesn't exist in 32bit mode"); + DecodeFailure = true; + return; + } OrderedNode *Src1 = _LoadContext(Size, offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RAX]), GPRClass); OrderedNode *Src2 = _LoadContext(Size, offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RDX]), GPRClass); diff --git a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h index eaa7cad08..5a0efb76e 100644 --- a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h +++ b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h @@ -81,9 +81,9 @@ public: // rdi, 0x8 // cmp qword [rdi-8], 0 // jne .label - if (!BlockSetRIP) { + if (LastOp && !BlockSetRIP) { auto it = JumpTargets.find(NextRIP); - if (it == JumpTargets.end() && LastOp) { + if (it == JumpTargets.end()) { uint8_t GPRSize = CTX->Config.Is64BitMode ? 8 : 4; // If we don't have a jump target to a new block then we have to leave @@ -104,6 +104,7 @@ public: OpDispatchBuilder(FEXCore::Context::Context *ctx); void ResetWorkingList(); + void ResetDecodeFailure() { DecodeFailure = false; } bool HadDecodeFailure() { return DecodeFailure; } void BeginFunction(uint64_t RIP, std::vector const *Blocks); diff --git a/External/FEXCore/Source/Interface/IR/Passes/DeadContextStoreElimination.cpp b/External/FEXCore/Source/Interface/IR/Passes/DeadContextStoreElimination.cpp index ac99bf715..f387ff472 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/DeadContextStoreElimination.cpp +++ b/External/FEXCore/Source/Interface/IR/Passes/DeadContextStoreElimination.cpp @@ -578,7 +578,7 @@ bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::IREmitter *IREmit) { if (LastStoreNode != nullptr) { IREmit->SetWriteCursor(CodeNode); - RecordAccess(&LocalInfo, FEXCore::IR::GPRClass, offsetof(FEXCore::Core::CPUState, flags[0]) + F, 1, ACCESS_WRITE, IREmit->_InlineConstant(0), CodeNode); + RecordAccess(&LocalInfo, FEXCore::IR::GPRClass, offsetof(FEXCore::Core::CPUState, flags[0]) + F, 1, ACCESS_WRITE, IREmit->_Constant(0), CodeNode); IREmit->Remove(LastStoreNode); Changed = true; diff --git a/External/FEXCore/Source/Utils/ELFContainer.cpp b/External/FEXCore/Source/Utils/ELFContainer.cpp index d256d8983..8b48c1c22 100644 --- a/External/FEXCore/Source/Utils/ELFContainer.cpp +++ b/External/FEXCore/Source/Utils/ELFContainer.cpp @@ -477,6 +477,36 @@ void ELFContainer::CalculateSymbols() { } } } + + Elf32_Shdr const *StrHeader = SectionHeaders.at(Header._32.e_shstrndx)._32; + char const *SHStrings = &RawFile.at(StrHeader->sh_offset); + for (uint32_t i = 0; i < SectionHeaders.size(); ++i) { + Elf32_Shdr const *hdr = SectionHeaders.at(i)._32; + if (strcmp(&SHStrings[hdr->sh_name], ".eh_frame_hdr") == 0) { + auto eh_frame_hdr = &RawFile.at(hdr->sh_offset); + // we only handle this specific unwind table encoding + if (eh_frame_hdr[0] == 1 && eh_frame_hdr[1] == 0x1B && eh_frame_hdr[2] == 0x3 && eh_frame_hdr[3] == 0x3b) { + // ptr enc : 4 bytes, signed, pcrel + // fde count : 4 bytes udata + // table enc : 4 bytes, signed, datarel + int fde_count = *(int*)(eh_frame_hdr + 8); + UnwindEntries.clear(); + UnwindEntries.reserve(fde_count); + + struct entry { + int32_t pc; + int32_t fde; + }; + + entry *Table = (entry*)(eh_frame_hdr+12); + for (int f = 0; f < fde_count; f++) { + uintptr_t Entry = (uintptr_t)(Table[f].pc + hdr->sh_offset); + UnwindEntries.push_back(Entry); + } + } + break; + } + } } else { Elf64_Shdr const *SymTabHeader{nullptr}; @@ -580,6 +610,36 @@ void ELFContainer::CalculateSymbols() { } } } + + Elf64_Shdr const *StrHeader = SectionHeaders.at(Header._64.e_shstrndx)._64; + char const *SHStrings = &RawFile.at(StrHeader->sh_offset); + for (uint32_t i = 0; i < SectionHeaders.size(); ++i) { + Elf64_Shdr const *hdr = SectionHeaders.at(i)._64; + if (strcmp(&SHStrings[hdr->sh_name], ".eh_frame_hdr") == 0) { + auto eh_frame_hdr = &RawFile.at(hdr->sh_offset); + // we only handle this specific unwind table encoding + if (eh_frame_hdr[0] == 1 && eh_frame_hdr[1] == 0x1B && eh_frame_hdr[2] == 0x3 && eh_frame_hdr[3] == 0x3b) { + // ptr enc : 4 bytes, signed, pcrel + // fde count : 4 bytes udata + // table enc : 4 bytes, signed, datarel + int fde_count = *(int*)(eh_frame_hdr + 8); + UnwindEntries.clear(); + UnwindEntries.reserve(fde_count); + + struct entry { + int32_t pc; + int32_t fde; + }; + + entry *Table = (entry*)(eh_frame_hdr+12); + for (int f = 0; f < fde_count; f++) { + uintptr_t Entry = (uintptr_t)(Table[f].pc + hdr->sh_offset); + UnwindEntries.push_back(Entry); + } + } + break; + } + } } } @@ -629,6 +689,11 @@ void ELFContainer::AddSymbols(SymbolAdder Adder) { } } } +void ELFContainer::AddUnwindEntries(UnwindAdder Adder) { + for (auto Entry : UnwindEntries) { + Adder(Entry); + } +} void ELFContainer::PrintHeader() const { if (Mode == MODE_32BIT) { diff --git a/External/FEXCore/include/FEXCore/Core/Context.h b/External/FEXCore/include/FEXCore/Core/Context.h index cf029cbc1..fdd3cef71 100644 --- a/External/FEXCore/include/FEXCore/Core/Context.h +++ b/External/FEXCore/include/FEXCore/Core/Context.h @@ -9,6 +9,7 @@ #include #include #include +#include namespace FEXCore { class CodeLoader; @@ -118,6 +119,8 @@ namespace FEXCore::Context { */ __attribute__((visibility("default"))) ExitReason RunUntilExit(FEXCore::Context::Context *CTX); + __attribute__((visibility("default"))) void CompileRIP(FEXCore::Context::Context *CTX, uint64_t GuestRIP); + /** * @brief Gets the program exit status * @@ -228,5 +231,8 @@ namespace FEXCore::Context { __attribute__((visibility("default"))) void RemoveNamedRegion(FEXCore::Context::Context *CTX, uintptr_t Base, uintptr_t Length); __attribute__((visibility("default"))) void SetAOTIRLoader(FEXCore::Context::Context *CTX, std::function CacheReader); __attribute__((visibility("default"))) bool WriteAOTIR(FEXCore::Context::Context *CTX, std::function(const std::string&)> CacheWriter); + __attribute__((visibility("default"))) void WriteFilesWithCode(FEXCore::Context::Context *CTX, std::function Writer); __attribute__((visibility("default"))) void FlushCodeRange(FEXCore::Core::InternalThreadState *Thread, uint64_t Start, uint64_t Length); + + __attribute__((visibility("default"))) void ConfigureAOTGen(FEXCore::Context::Context *CTX, std::set *ExternalBranches, uint64_t SectionMaxAddress); } diff --git a/External/FEXCore/include/FEXCore/Utils/ELFContainer.h b/External/FEXCore/include/FEXCore/Utils/ELFContainer.h index 588c9c0c2..cb05a10dc 100644 --- a/External/FEXCore/include/FEXCore/Utils/ELFContainer.h +++ b/External/FEXCore/include/FEXCore/Utils/ELFContainer.h @@ -83,6 +83,9 @@ public: using SymbolAdder = std::function; void AddSymbols(SymbolAdder Adder); + using UnwindAdder = std::function; + void AddUnwindEntries(UnwindAdder Adder); + void GetInitLocations(uint64_t GuestELFBase, std::vector *Locations); @@ -152,6 +155,7 @@ private: std::vector SectionHeaders; std::vector ProgramHeaders; std::vector Symbols; + std::vector UnwindEntries; std::unordered_map SymbolMap; std::map SymbolMapByAddress; diff --git a/Scripts/FEXUpdateAOTIRCache.sh b/Scripts/FEXUpdateAOTIRCache.sh new file mode 100755 index 000000000..c8581c06f --- /dev/null +++ b/Scripts/FEXUpdateAOTIRCache.sh @@ -0,0 +1,37 @@ +#!/bin/bash +FEX=${1:-FEXLoader} +echo Using $FEX +for fileid in ~/.fex-emu/aotir/*.path; do + filename=`cat "$fileid"` + args="" + if [ "${fileid: -6 : 1}" == "P" ]; then + args="$args --no-abinopf" + else + args="$args --abinopf" + fi + + if [ "${fileid: -7 : 1}" == "L" ]; then + args="$args --abilocalflags" + else + args="$args --no-abilocalflags" + fi + + if [ "${fileid: -8 : 1}" == "T" ]; then + args="$args --tsoenabled" + else + args="$args --no-tsoenabled" + fi + + if [ "${fileid: -9 : 1}" == "S" ]; then + args="$args --smc=full" + else + args="$args --smc=mman" + fi + + if [ -f "${fileid%.path}.aotir" ]; then + echo "`basename $fileid` has already been generated" + else + echo "Processing `basename $fileid` ($filename) with $args" + $FEX --aotirgenerate $args "$filename" + fi +done diff --git a/Source/Tests/ELFCodeLoader2.h b/Source/Tests/ELFCodeLoader2.h index 77fd2bbb8..b00f1469d 100644 --- a/Source/Tests/ELFCodeLoader2.h +++ b/Source/Tests/ELFCodeLoader2.h @@ -80,9 +80,7 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader { return false; } else { auto Filename = get_fdpath(file.fd); - AOTMappers.push_back([=](auto CTX){ - FEXCore::Context::AddNamedRegion(CTX, (uintptr_t)rv, size, off, Filename); - }); + Sections.push_back({Base, (uintptr_t)rv, size, (off_t)off, Filename, (prot & PROT_EXEC) != 0}); return true; } @@ -196,7 +194,16 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader { } public: - std::vector> AOTMappers; + struct LoadedSection { + uintptr_t ElfBase; + uintptr_t Base; + size_t Size; + off_t Offs; + std::string Filename; + bool Executable; + }; + + std::vector Sections; ELFCodeLoader2(std::string const &Filename, std::string const &RootFS, [[maybe_unused]] std::vector const &args, std::vector const &ParsedArgs, char **const envp = nullptr, FEXCore::Config::Value *AdditionalEnvp = nullptr) : Args {args} { @@ -559,7 +566,7 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader { addr = AuxTabBase; size = AuxTabSize; } - + bool Is64BitMode() { return MainElf.type == ::ELFLoader::ELFContainer::TYPE_X86_64; } diff --git a/Source/Tests/FEXLoader.cpp b/Source/Tests/FEXLoader.cpp index e41a3bcc1..3aa626438 100644 --- a/Source/Tests/FEXLoader.cpp +++ b/Source/Tests/FEXLoader.cpp @@ -29,6 +29,7 @@ $end_info$ #include #include #include +#include namespace { static bool SilentLog; @@ -206,6 +207,7 @@ int main(int argc, char **argv, char **const envp) { FEX_CONFIG_OPT(SilentLog, SILENTLOG); FEX_CONFIG_OPT(AOTIRCapture, AOTIRCAPTURE); + FEX_CONFIG_OPT(AOTIRGenerate, AOTIRGENERATE); FEX_CONFIG_OPT(AOTIRLoad, AOTIRLOAD); FEX_CONFIG_OPT(OutputLog, OUTPUTLOG); FEX_CONFIG_OPT(LDPath, ROOTFS); @@ -326,27 +328,128 @@ int main(int argc, char **argv, char **const envp) { } - if (AOTIRLoad() || AOTIRCapture()) { + if (AOTIRLoad() || AOTIRCapture() || AOTIRGenerate()) { LogMan::Msg::I("Warning: AOTIR is experimental, and might lead to crashes. Capture doesn't work with programs that fork."); } FEXCore::Context::SetAOTIRLoader(CTX, [](const std::string &fileid) -> int { - auto filepath = std::filesystem::path(FEXCore::Config::GetDataDirectory()) / "aotir" / fileid; + auto filepath = std::filesystem::path(FEXCore::Config::GetDataDirectory()) / "aotir" / (fileid + ".aotir"); return open(filepath.c_str(), O_RDONLY); }); - for(auto handler: Loader.AOTMappers) { - handler(CTX); + for(auto Section: Loader.Sections) { + FEXCore::Context::AddNamedRegion(CTX, Section.Base, Section.Size, Section.Offs, Section.Filename); } - FEXCore::Context::RunUntilExit(CTX); + if (AOTIRGenerate()) { + for(auto Section: Loader.Sections) { + if (Section.Executable && Section.Size > 16) { + ELFLoader::ELFContainer container{Section.Filename, "", false}; - if (AOTIRCapture()) { - std::filesystem::create_directories(std::filesystem::path(FEXCore::Config::GetDataDirectory()) / "aotir"); + std::set BranchTargets; + + container.AddSymbols([&](ELFLoader::ELFSymbol* sym) { + auto Destination = sym->Address + Section.ElfBase; + + if (! (Destination >= Section.Base && Destination <= (Section.Base + Section.Size)) ) { + //printf("Sym : %lx %lx out of range\n", sym->Address, Destination); + return; // outside of current section, unlikely to be real code + } + + BranchTargets.insert(Destination); + }); + + LogMan::Msg::I("Symbol seed: %ld", BranchTargets.size()); + + container.AddUnwindEntries([&](uintptr_t Entry) { + auto Destination = Entry + Section.ElfBase; + + if (! (Destination >= Section.Base && Destination <= (Section.Base + Section.Size)) ) { + //printf("Sym : %lx %lx out of range\n", sym->Address, Destination); + return; // outside of current section, unlikely to be real code + } + + BranchTargets.insert(Destination); + }); + + + LogMan::Msg::I("Symbol + Unwind seed: %ld", BranchTargets.size()); + + for (size_t Offset = 0; Offset < (Section.Size - 16); Offset++) { + uint8_t *pCode = (uint8_t *)(Section.Base + Offset); + + if (*pCode == 0xE8) { + uintptr_t Destination = (int)(pCode[1] | (pCode[2] << 8) | (pCode[3] << 16) | (pCode[4] << 24)); + Destination += (uintptr_t)pCode + 5; + + auto DestinationPtr = (uint8_t*)Destination; + + if (! (Destination >= Section.Base && Destination <= (Section.Base + Section.Size)) ) + continue; // outside of current section, unlikely to be real code + + if (DestinationPtr[0] == 0 && DestinationPtr[1] == 0) + continue; // add al, [rax], unlikely to be real code +/* + if (DestinationPtr[0] == 0x44 && DestinationPtr[1] == 0x0f && DestinationPtr[2] == 0x6f) + continue; // REX.W + movq leads to frontend bugs +*/ + BranchTargets.insert(Destination); + } + + if (pCode[0] == 0xf3 && pCode[1] == 0x0f && pCode[2] == 0x1e && pCode[3] == 0xfa) { + BranchTargets.insert((uintptr_t)pCode); + } + } + + uint64_t SectionMaxAddress = Section.Base + Section.Size; + std::set ExternalBranches; + + FEXCore::Context::ConfigureAOTGen(CTX, &ExternalBranches, SectionMaxAddress); + + std::set Compiled; + int counter = 0; + do { + LogMan::Msg::I("Discovered %ld Branch Targets in this pass", BranchTargets.size()); + for (auto RIP: BranchTargets) { + if ((counter++) % 1000 == 0) + LogMan::Msg::I("Compiling %d %lX", counter, RIP - Section.ElfBase); + FEXCore::Context::CompileRIP(CTX, RIP); + Compiled.insert(RIP); + } + LogMan::Msg::I("\nPass Done"); + BranchTargets.clear(); + for (auto Destination: ExternalBranches) { + if (! (Destination >= Section.Base && Destination <= (Section.Base + Section.Size)) ) + continue; + if (Compiled.contains(Destination)) + continue; + BranchTargets.insert(Destination); + } + ExternalBranches.clear(); + } while (BranchTargets.size() > 0); + LogMan::Msg::I("\nAll Done: %d", counter); + } + } + } else { + FEXCore::Context::RunUntilExit(CTX); + } + + std::filesystem::create_directories(std::filesystem::path(FEXCore::Config::GetDataDirectory()) / "aotir"); + + FEXCore::Context::WriteFilesWithCode(CTX, [](const std::string& fileid, const std::string& filename) { + auto filepath = std::filesystem::path(FEXCore::Config::GetDataDirectory()) / "aotir" / (fileid + ".path"); + int fd = open(filepath.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0644); + if (fd != -1) { + write(fd, filename.c_str(), filename.size()); + close(fd); + } + }); + + if (AOTIRCapture() || AOTIRGenerate()) { auto WroteCache = FEXCore::Context::WriteAOTIR(CTX, [](const std::string& fileid) -> std::unique_ptr { - auto filepath = std::filesystem::path(FEXCore::Config::GetDataDirectory()) / "aotir" / fileid; + auto filepath = std::filesystem::path(FEXCore::Config::GetDataDirectory()) / "aotir" / (fileid + ".aotir"); auto AOTWrite = std::make_unique(filepath, std::ios::out | std::ios::binary); if (*AOTWrite) { std::filesystem::resize_file(filepath, 0);