mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 11:00:19 +02:00
Merge pull request #996 from FEX-Emu/skmp/aotirgen
AOTIR: Offline Generation
This commit is contained in:
19 files changed
+464
-59
No files matched your search
@@ -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",
|
||||
|
||||
@@ -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<void(const std::string& fileid, const std::string& filename)> 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);
|
||||
}
|
||||
|
||||
+9
-1
@@ -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<uint64_t, AddrToFileEntry> AddrToFile;
|
||||
|
||||
std::map<std::string, std::string> FilesWithCode;
|
||||
|
||||
#ifdef BLOCKSTATS
|
||||
std::unique_ptr<FEXCore::BlockSamplingData> BlockData;
|
||||
@@ -214,9 +217,14 @@ namespace FEXCore::Context {
|
||||
|
||||
std::tuple<void *, FEXCore::IR::IRListView *, FEXCore::Core::DebugData *, FEXCore::IR::RegisterAllocationData *, bool, uint64_t, uint64_t> 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<std::unique_ptr<std::ostream>(const std::string&)> CacheWriter);
|
||||
void WriteFilesWithCode(std::function<void(const std::string& fileid, const std::string& filename)> 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);
|
||||
|
||||
+48
-5
@@ -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<std::mutex> 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<std::mutex> 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<void(const std::string& fileid, const std::string& filename)> Writer) {
|
||||
std::lock_guard<std::mutex> lk(AOTIRCacheLock);
|
||||
for( const auto &File: FilesWithCode) {
|
||||
Writer(File.first, File.second);
|
||||
}
|
||||
}
|
||||
|
||||
bool Context::WriteAOTIRCache(std::function<std::unique_ptr<std::ostream>(const std::string&)> CacheWriter) {
|
||||
std::lock_guard<std::mutex> 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<std::mutex> lk(AOTIRCacheLock);
|
||||
|
||||
RAData->IsShared = true;
|
||||
@@ -1267,7 +1305,7 @@ namespace FEXCore::Context {
|
||||
|
||||
std::lock_guard<std::mutex> 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<uint64_t> *ExternalBranches, uint64_t SectionMaxAddress) {
|
||||
CTX->ParentThread->FrontendDecoder->SetExternalBranches(ExternalBranches);
|
||||
CTX->ParentThread->FrontendDecoder->SetSectionMaxAddress(SectionMaxAddress);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<uint64_t>(CTX));
|
||||
mov(rsi, STATE);
|
||||
mov(rax, Ptr.Data);
|
||||
mov(rax, GetCompileBlockPtr());
|
||||
|
||||
call(rax);
|
||||
|
||||
|
||||
+38
-5
@@ -15,6 +15,7 @@ $end_info$
|
||||
#include <FEXCore/Core/X86Enums.h>
|
||||
#include <FEXCore/Debug/X86Tables.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
#include <set>
|
||||
|
||||
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;
|
||||
|
||||
@@ -33,6 +33,8 @@ public:
|
||||
uint64_t DecodedMinAddress {};
|
||||
uint64_t DecodedMaxAddress {~0ULL};
|
||||
|
||||
void SetSectionMaxAddress(uint64_t v) { SectionMaxAddress = v; }
|
||||
void SetExternalBranches(std::set<uint64_t> *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<DecodedBlocks> Blocks;
|
||||
std::set<uint64_t> BlocksToDecode;
|
||||
std::set<uint64_t> HasBlocks;
|
||||
std::set<uint64_t> *ExternalBranches {nullptr};
|
||||
|
||||
// ModRM rm decoding
|
||||
using DecodeModRMPtr = void (FEXCore::Frontend::Decoder::*)(X86Tables::DecodedOperand *Operand, X86Tables::ModRMDecoded ModRM);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+96
-20
@@ -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);
|
||||
|
||||
|
||||
@@ -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<FEXCore::Frontend::Decoder::DecodedBlocks> const *Blocks);
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+65
@@ -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) {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
||||
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<int(const std::string&)> CacheReader);
|
||||
__attribute__((visibility("default"))) bool WriteAOTIR(FEXCore::Context::Context *CTX, std::function<std::unique_ptr<std::ostream>(const std::string&)> CacheWriter);
|
||||
__attribute__((visibility("default"))) void WriteFilesWithCode(FEXCore::Context::Context *CTX, std::function<void(const std::string& fileid, const std::string& filename)> 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<uint64_t> *ExternalBranches, uint64_t SectionMaxAddress);
|
||||
}
|
||||
@@ -83,6 +83,9 @@ public:
|
||||
using SymbolAdder = std::function<void(ELFSymbol*)>;
|
||||
void AddSymbols(SymbolAdder Adder);
|
||||
|
||||
using UnwindAdder = std::function<void(uintptr_t)>;
|
||||
void AddUnwindEntries(UnwindAdder Adder);
|
||||
|
||||
void GetInitLocations(uint64_t GuestELFBase, std::vector<uint64_t> *Locations);
|
||||
|
||||
|
||||
@@ -152,6 +155,7 @@ private:
|
||||
std::vector<SectionHeader> SectionHeaders;
|
||||
std::vector<ProgramHeader> ProgramHeaders;
|
||||
std::vector<ELFSymbol> Symbols;
|
||||
std::vector<uintptr_t> UnwindEntries;
|
||||
std::unordered_map<std::string, ELFSymbol *> SymbolMap;
|
||||
std::map<uint64_t, ELFSymbol *> SymbolMapByAddress;
|
||||
|
||||
|
||||
Executable
+37
@@ -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
|
||||
@@ -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<std::function<void(FEXCore::Context::Context *CTX)>> AOTMappers;
|
||||
struct LoadedSection {
|
||||
uintptr_t ElfBase;
|
||||
uintptr_t Base;
|
||||
size_t Size;
|
||||
off_t Offs;
|
||||
std::string Filename;
|
||||
bool Executable;
|
||||
};
|
||||
|
||||
std::vector<LoadedSection> Sections;
|
||||
ELFCodeLoader2(std::string const &Filename, std::string const &RootFS, [[maybe_unused]] std::vector<std::string> const &args, std::vector<std::string> const &ParsedArgs, char **const envp = nullptr, FEXCore::Config::Value<std::string> *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;
|
||||
}
|
||||
|
||||
+111
-8
@@ -29,6 +29,7 @@ $end_info$
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
|
||||
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<uintptr_t> 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<uint64_t> ExternalBranches;
|
||||
|
||||
FEXCore::Context::ConfigureAOTGen(CTX, &ExternalBranches, SectionMaxAddress);
|
||||
|
||||
std::set<uint64_t> 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<std::ostream> {
|
||||
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<std::ofstream>(filepath, std::ios::out | std::ios::binary);
|
||||
if (*AOTWrite) {
|
||||
std::filesystem::resize_file(filepath, 0);
|
||||
|
||||
Reference in new issue
Block a user