Merge pull request #996 from FEX-Emu/skmp/aotirgen

AOTIR: Offline Generation
This commit is contained in:
Ryan Houdek authored and GitHub committed 2021-05-03 15:58:57 -07:00
commit fd6d33d197
19 files changed
+464 -59

No files matched your search

+8
View File
@@ -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",
+7
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+4
View File
@@ -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
View File
@@ -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);
+3 -2
View File
@@ -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);
@@ -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
View File
@@ -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) {
+6
View File
@@ -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);
}
+4
View File
@@ -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;
+37
View File
@@ -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
+12 -5
View File
@@ -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
View File
@@ -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);