diff --git a/FEXCore/Source/Interface/Context/Context.cpp b/FEXCore/Source/Interface/Context/Context.cpp index b9f5ef235..e21df43e9 100644 --- a/FEXCore/Source/Interface/Context/Context.cpp +++ b/FEXCore/Source/Interface/Context/Context.cpp @@ -48,6 +48,10 @@ namespace FEXCore::Context { CompileBlock(Thread->CurrentFrame, GuestRIP); } + void FEXCore::Context::ContextImpl::CompileRIPCount(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP, uint64_t MaxInst) { + CompileBlock(Thread->CurrentFrame, GuestRIP, MaxInst); + } + FEXCore::Context::ExitReason FEXCore::Context::ContextImpl::GetExitReason() { return ParentThread->ExitReason; } diff --git a/FEXCore/Source/Interface/Context/Context.h b/FEXCore/Source/Interface/Context/Context.h index b15e2d27f..12228cbcc 100644 --- a/FEXCore/Source/Interface/Context/Context.h +++ b/FEXCore/Source/Interface/Context/Context.h @@ -90,6 +90,7 @@ namespace FEXCore::Context { void ExecuteThread(FEXCore::Core::InternalThreadState *Thread) override; void CompileRIP(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) override; + void CompileRIPCount(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP, uint64_t MaxInst) override; int GetProgramStatus() const override; @@ -322,7 +323,7 @@ namespace FEXCore::Context { uint64_t StartAddr; uint64_t Length; }; - [[nodiscard]] GenerateIRResult GenerateIR(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP, bool ExtendedDebugInfo); + [[nodiscard]] GenerateIRResult GenerateIR(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP, bool ExtendedDebugInfo, uint64_t MaxInst); struct CompileCodeResult { void* CompiledCode; @@ -333,8 +334,8 @@ namespace FEXCore::Context { uint64_t StartAddr; uint64_t Length; }; - [[nodiscard]] CompileCodeResult CompileCode(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP); - uintptr_t CompileBlock(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP); + [[nodiscard]] CompileCodeResult CompileCode(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP, uint64_t MaxInst = 0); + uintptr_t CompileBlock(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP, uint64_t MaxInst = 0); // same as CompileBlock, but aborts on failure void CompileBlockJit(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP); diff --git a/FEXCore/Source/Interface/Core/Core.cpp b/FEXCore/Source/Interface/Core/Core.cpp index 4de51d70c..ab6d7b852 100644 --- a/FEXCore/Source/Interface/Core/Core.cpp +++ b/FEXCore/Source/Interface/Core/Core.cpp @@ -833,7 +833,7 @@ namespace FEXCore::Context { } } - ContextImpl::GenerateIRResult ContextImpl::GenerateIR(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP, bool ExtendedDebugInfo) { + ContextImpl::GenerateIRResult ContextImpl::GenerateIR(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP, bool ExtendedDebugInfo, uint64_t MaxInst) { FEXCORE_PROFILE_SCOPED("GenerateIR"); Thread->OpDispatcher->ReownOrClaimBuffer(); @@ -858,7 +858,7 @@ namespace FEXCore::Context { bool HadDispatchError {false}; - Thread->FrontendDecoder->DecodeInstructionsAtEntry(GuestCode, GuestRIP, [Thread](uint64_t BlockEntry, uint64_t Start, uint64_t Length) { + Thread->FrontendDecoder->DecodeInstructionsAtEntry(GuestCode, GuestRIP, MaxInst, [Thread](uint64_t BlockEntry, uint64_t Start, uint64_t Length) { if (Thread->LookupCache->AddBlockExecutableRange(BlockEntry, Start, Length)) { static_cast(Thread->CTX)->SyscallHandler->MarkGuestExecutableRange(Thread, Start, Length); } @@ -1012,7 +1012,7 @@ namespace FEXCore::Context { }; } - ContextImpl::CompileCodeResult ContextImpl::CompileCode(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) { + ContextImpl::CompileCodeResult ContextImpl::CompileCode(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP, uint64_t MaxInst) { FEXCore::IR::IRListView *IRList {}; FEXCore::Core::DebugData *DebugData {}; FEXCore::IR::RegisterAllocationData::UniquePtr RAData {}; @@ -1063,7 +1063,7 @@ namespace FEXCore::Context { if (IRList == nullptr) { // Generate IR + Meta Info - auto [IRCopy, RACopy, TotalInstructions, TotalInstructionsLength, _StartAddr, _Length] = GenerateIR(Thread, GuestRIP, Config.GDBSymbols()); + auto [IRCopy, RACopy, TotalInstructions, TotalInstructionsLength, _StartAddr, _Length] = GenerateIR(Thread, GuestRIP, Config.GDBSymbols(), MaxInst); // Setup pointers to internal structures IRList = IRCopy; @@ -1105,7 +1105,7 @@ namespace FEXCore::Context { } } - uintptr_t ContextImpl::CompileBlock(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP) { + uintptr_t ContextImpl::CompileBlock(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP, uint64_t MaxInst) { FEXCORE_PROFILE_SCOPED("CompileBlock"); auto Thread = Frame->Thread; @@ -1125,7 +1125,7 @@ namespace FEXCore::Context { bool GeneratedIR {}; uint64_t StartAddr {}, Length {}; - auto [Code, IR, Data, RAData, Generated, _StartAddr, _Length] = CompileCode(Thread, GuestRIP); + auto [Code, IR, Data, RAData, Generated, _StartAddr, _Length] = CompileCode(Thread, GuestRIP, MaxInst); CodePtr = Code; IRList = IR; DebugData = Data; diff --git a/FEXCore/Source/Interface/Core/Frontend.cpp b/FEXCore/Source/Interface/Core/Frontend.cpp index 7c6d97664..8673a1502 100644 --- a/FEXCore/Source/Interface/Core/Frontend.cpp +++ b/FEXCore/Source/Interface/Core/Frontend.cpp @@ -1095,7 +1095,7 @@ const uint8_t *Decoder::AdjustAddrForSpecialRegion(uint8_t const* _InstStream, u return _InstStream - EntryPoint + RIP; } -void Decoder::DecodeInstructionsAtEntry(uint8_t const* _InstStream, uint64_t PC, std::function AddContainedCodePage) { +void Decoder::DecodeInstructionsAtEntry(uint8_t const* _InstStream, uint64_t PC, uint64_t MaxInst, std::function AddContainedCodePage) { FEXCORE_PROFILE_SCOPED("DecodeInstructions"); BlockInfo.TotalInstructionCount = 0; BlockInfo.Blocks.clear(); @@ -1133,6 +1133,10 @@ void Decoder::DecodeInstructionsAtEntry(uint8_t const* _InstStream, uint64_t PC, AddContainedCodePage(PC, CurrentCodePage, FHU::FEX_PAGE_SIZE); + if (MaxInst == 0) { + MaxInst = CTX->Config.MaxInstPerBlock; + } + while (!BlocksToDecode.empty()) { auto BlockDecodeIt = BlocksToDecode.begin(); uint64_t RIPToDecode = *BlockDecodeIt; @@ -1195,9 +1199,9 @@ void Decoder::DecodeInstructionsAtEntry(uint8_t const* _InstStream, uint64_t PC, CanContinue = true; } - bool FinalInstruction = DecodedSize >= CTX->Config.MaxInstPerBlock || + bool FinalInstruction = DecodedSize >= MaxInst || DecodedSize >= DefaultDecodedBufferSize || - TotalInstructions >= CTX->Config.MaxInstPerBlock; + TotalInstructions >= MaxInst; if (DecodeInst->TableInfo->Flags & FEXCore::X86Tables::InstFlags::FLAGS_SETS_RIP) { // If we have multiblock enabled diff --git a/FEXCore/Source/Interface/Core/Frontend.h b/FEXCore/Source/Interface/Core/Frontend.h index 6050be162..6f0eab8ef 100644 --- a/FEXCore/Source/Interface/Core/Frontend.h +++ b/FEXCore/Source/Interface/Core/Frontend.h @@ -34,7 +34,7 @@ public: Decoder(FEXCore::Context::ContextImpl *ctx); ~Decoder(); - void DecodeInstructionsAtEntry(uint8_t const* InstStream, uint64_t PC, std::function AddContainedCodePage); + void DecodeInstructionsAtEntry(uint8_t const* InstStream, uint64_t PC, uint64_t MaxInst, std::function AddContainedCodePage); DecodedBlockInformation const *GetDecodedBlockInfo() const { return &BlockInfo; diff --git a/FEXCore/include/FEXCore/Core/Context.h b/FEXCore/include/FEXCore/Core/Context.h index 661280ec3..b70a40923 100644 --- a/FEXCore/include/FEXCore/Core/Context.h +++ b/FEXCore/include/FEXCore/Core/Context.h @@ -182,6 +182,7 @@ namespace FEXCore::Context { FEX_DEFAULT_VISIBILITY virtual void ExecuteThread(FEXCore::Core::InternalThreadState *Thread) = 0; FEX_DEFAULT_VISIBILITY virtual void CompileRIP(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) = 0; + FEX_DEFAULT_VISIBILITY virtual void CompileRIPCount(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP, uint64_t MaxInst) = 0; /** * @brief Gets the program exit status diff --git a/Scripts/InstructionCountParser.py b/Scripts/InstructionCountParser.py index 7f9ba47e1..c7e03616e 100755 --- a/Scripts/InstructionCountParser.py +++ b/Scripts/InstructionCountParser.py @@ -17,11 +17,13 @@ class TestData: optimal: int expectedinstructioncount: int code: bytes - def __init__(self, Name, Optimal, ExpectedInstructionCount, Code): + instructions: list + def __init__(self, Name, Optimal, ExpectedInstructionCount, Code, Instructions): self.name = Name self.expectedinstructioncount = ExpectedInstructionCount self.optimal = Optimal self.code = Code + self.instructions = Instructions @property def Name(self): @@ -39,6 +41,10 @@ class TestData: def Code(self): return self.code + @property + def Instructions(self): + return self.instructions + TestDataMap = {} class HostFeatures(Flag) : FEATURE_ANY = 0 @@ -104,6 +110,7 @@ def parse_json_data(json_filepath, json_filename, json_data, output_binary_path) for key, items in json_data["Instructions"].items(): ExpectedInstructionCount = 0 Optimal = 0 + Instructions = [] if ("ExpectedInstructionCount" in items): ExpectedInstructionCount = int(items["ExpectedInstructionCount"]) @@ -115,14 +122,23 @@ def parse_json_data(json_filepath, json_filename, json_data, output_binary_path) if items["Skip"].upper() == "YES": continue + if "x86Insts" in items: + Instructions = items["x86Insts"] + else: + # No list of instructions, only one which is the key. + Instructions.append(key) TestName = base64.b64encode("{}.{}.{}".format(str(hash(json_filepath)), json_filename, key).encode("ascii")).decode("ascii") tmp_asm = "/tmp/{}.asm".format(TestName) tmp_asm_out = "/tmp/{}.asm.o".format(TestName) logging.info("'{}' -> '{}' -> '{}'".format(key, tmp_asm, tmp_asm_out)) + if TestName in TestDataMap: + sys.exit("Duplicate test name {} in tests".format(TestName)) + with open(tmp_asm, "w") as tmp_asm_file: tmp_asm_file.write("BITS {};\n".format(Bitness)) - tmp_asm_file.write("{}\n".format(key)) + for Inst in Instructions: + tmp_asm_file.write("{}\n".format(Inst)) Process = subprocess.Popen(["nasm", tmp_asm, "-o", tmp_asm_out]) Process.wait() @@ -144,7 +160,7 @@ def parse_json_data(json_filepath, json_filename, json_data, output_binary_path) with open(tmp_asm_out, "rb") as tmp_asm_out_file: binary_hex = tmp_asm_out_file.read() - TestDataMap[TestName] = TestData(key, Optimal, ExpectedInstructionCount, binary_hex) + TestDataMap[TestName] = TestData(key, Optimal, ExpectedInstructionCount, binary_hex, Instructions) os.remove(tmp_asm) os.remove(tmp_asm_out) @@ -165,6 +181,7 @@ def parse_json_data(json_filepath, json_filename, json_data, output_binary_path) # uint64_t Optimal; # int64_t ExpectedInstructionCount; # uint64_t CodeSize; + # uint64_t x86InstCount; # uint32_t Cookie; # uint8_t Code[CodeSize]; # }; @@ -191,6 +208,7 @@ def parse_json_data(json_filepath, json_filename, json_data, output_binary_path) MemData += struct.pack('Q', item.Optimal) MemData += struct.pack('q', item.ExpectedInstructionCount) MemData += struct.pack('Q', len(item.Code)) + MemData += struct.pack('Q', len(item.Instructions)) MemData += struct.pack('I', 0x41424344) MemData += item.Code diff --git a/Source/Tools/CodeSizeValidation/Main.cpp b/Source/Tools/CodeSizeValidation/Main.cpp index e2c81cacf..098326f47 100644 --- a/Source/Tools/CodeSizeValidation/Main.cpp +++ b/Source/Tools/CodeSizeValidation/Main.cpp @@ -230,6 +230,7 @@ struct TestInfo { uint64_t Optimal; int64_t ExpectedInstructionCount; uint64_t CodeSize; + uint64_t x86InstCount; uint32_t Cookie; uint8_t Code[]; }; @@ -258,7 +259,7 @@ static bool TestInstructions(FEXCore::Context::Context *CTX, FEXCore::Core::Inte LogMan::Msg::IFmt("Compiling instruction '{}'", CurrentTest->TestInst); // Compile the INST. - CTX->CompileRIP(Thread, CodeRIP); + CTX->CompileRIPCount(Thread, CodeRIP, CurrentTest->x86InstCount); // Go to the next test. CurrentTest = reinterpret_cast(&CurrentTest->Code[CurrentTest->CodeSize]);