From eb41cb2261d265b159d09182e2ed9b91b8caa52f Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 16 Oct 2025 13:14:26 -0700 Subject: [PATCH] Frontend: Detect partial decoded instructions Currently FEX doesn't properly support partial decoded instructions, which behave slightly differently than full noexec or invalid instruction decodings. Before this commit we didn't even have a way to detect the difference. Primary difference is that the faulting RIP is the beginning of instruction decode, while the fault address is the first byte that couldn't be fetched due to memory permissions. This shows up as a difference between the RIP in mcontext and si_addr in siginfo in the Linux signal handler. Right now just change the log so we can determine if we need to support this edge case. --- FEXCore/Source/Interface/Core/Core.cpp | 6 +++--- FEXCore/Source/Interface/Core/Frontend.cpp | 10 ++++++++-- FEXCore/Source/Interface/Core/Frontend.h | 1 + 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/FEXCore/Source/Interface/Core/Core.cpp b/FEXCore/Source/Interface/Core/Core.cpp index 92d785424..ff3ab764b 100644 --- a/FEXCore/Source/Interface/Core/Core.cpp +++ b/FEXCore/Source/Interface/Core/Core.cpp @@ -644,10 +644,10 @@ ContextImpl::GenerateIR(FEXCore::Core::InternalThreadState* Thread, uint64_t Gue LogMan::Msg::EFmt("Invalid or Unknown instruction: {} 0x{:x}", TableInfo->Name ?: "UND", Block.Entry - GuestRIP); } - if (Block.BlockStatus == Frontend::Decoder::DecodedBlockStatus::NOEXEC_INST) { - Thread->OpDispatcher->NoExecOp(DecodedInfo); - } else { + if (Block.BlockStatus == Frontend::Decoder::DecodedBlockStatus::INVALID_INST) { Thread->OpDispatcher->InvalidOp(DecodedInfo); + } else { + Thread->OpDispatcher->NoExecOp(DecodedInfo); } } diff --git a/FEXCore/Source/Interface/Core/Frontend.cpp b/FEXCore/Source/Interface/Core/Frontend.cpp index 88e8f6cfa..a776ea48c 100644 --- a/FEXCore/Source/Interface/Core/Frontend.cpp +++ b/FEXCore/Source/Interface/Core/Frontend.cpp @@ -1047,8 +1047,11 @@ Decoder::DecodedBlockStatus Decoder::DecodeInstruction(uint64_t PC) { // Put an invalid instruction in the stream so the core can raise SIGILL if hit // Error while decoding instruction. We don't know the table or instruction size DecodeInst->TableInfo = nullptr; + auto Result = ErrorDuringDecoding ? DecodedBlockStatus::INVALID_INST : + DecodeInst->InstSize ? DecodedBlockStatus::PARTIAL_DECODE_INST : + DecodedBlockStatus::NOEXEC_INST; DecodeInst->InstSize = 0; - return ErrorDuringDecoding ? DecodedBlockStatus::INVALID_INST : DecodedBlockStatus::NOEXEC_INST; + return Result; } else if (!DecodeInst->TableInfo || (DecodeInst->TableInfo->Type == TYPE_INST && !DecodeInst->TableInfo->OpcodeDispatcher.OpDispatch)) { // If there wasn't an error during decoding but we have no dispatcher for the instruction then claim invalid instruction. return DecodedBlockStatus::INVALID_INST; @@ -1450,7 +1453,10 @@ void Decoder::DecodeInstructionsAtEntry(FEXCore::Core::InternalThreadState* Thre EraseBlock = true; } else { LogMan::Msg::EFmt("{} instruction in entry block: {:X}", - BlockIt->BlockStatus == DecodedBlockStatus::INVALID_INST ? "Invalid" : "NoExec", OpAddress); + BlockIt->BlockStatus == DecodedBlockStatus::INVALID_INST ? "Invalid" : + BlockIt->BlockStatus == DecodedBlockStatus::NOEXEC_INST ? "NoExec" : + "PartialDecode", + OpAddress); } break; } diff --git a/FEXCore/Source/Interface/Core/Frontend.h b/FEXCore/Source/Interface/Core/Frontend.h index bc8386655..66e790fb8 100644 --- a/FEXCore/Source/Interface/Core/Frontend.h +++ b/FEXCore/Source/Interface/Core/Frontend.h @@ -27,6 +27,7 @@ public: SUCCESS, INVALID_INST, NOEXEC_INST, + PARTIAL_DECODE_INST, }; // New Frontend decoding