diff --git a/FEXCore/Source/Interface/Core/Core.cpp b/FEXCore/Source/Interface/Core/Core.cpp index 7cff39c36..a39c9a130 100644 --- a/FEXCore/Source/Interface/Core/Core.cpp +++ b/FEXCore/Source/Interface/Core/Core.cpp @@ -560,11 +560,15 @@ ContextImpl::GenerateIR(FEXCore::Core::InternalThreadState* Thread, uint64_t Gue } #endif - bool HadDispatchError {false}; - bool HadInvalidInst {false}; for (size_t j = 0; j < CodeBlocks.size(); ++j) { const auto& Block = CodeBlocks[j]; + // Dispatch failures and invalid instructions terminate only the decoded + // block that contains them. Other block targets in the same multiblock + // compilation unit are independent entry paths. + bool HadDispatchError {false}; + bool HadInvalidInst {false}; + #ifdef ZYDIS_DISASSEMBLER if (FEXCore::Config::Get_X86DISASSEMBLE() && CodeBlocks.size() > 1) { LogMan::Msg::IFmt(" Block {} Entry={:#x} NumInsts={}", j, Block.Entry, Block.NumInstructions); diff --git a/unittests/ASM/Multiblock/InvalidBlockStateIsolation.asm b/unittests/ASM/Multiblock/InvalidBlockStateIsolation.asm new file mode 100644 index 000000000..942933780 --- /dev/null +++ b/unittests/ASM/Multiblock/InvalidBlockStateIsolation.asm @@ -0,0 +1,30 @@ +%ifdef CONFIG +{ + "Match": "All", + "RegData": { + "RAX": "0x20" + } +} +%endif + +; A decoded block containing an unsupported operation must not poison later, +; independently reachable blocks in the same multiblock unit. +; The conditional branch is always taken, so the invalid block is compiled +; but never executed. +xor eax, eax +test eax, eax +jz valid_block + +invalid_block: +mov fs, ax + +valid_block: +jmp success + +wrong_fallthrough: +mov rax, 0x10 +hlt + +success: +mov rax, 0x20 +hlt