From 6bb68a3fd97c0ac913d9c22d6c1279549597dd56 Mon Sep 17 00:00:00 2001 From: nmzik Date: Fri, 25 Sep 2026 13:44:07 +0400 Subject: [PATCH] shader: stop repeated CFG merge gateway splitting --- .../recompiler/frontend/cfg/ShaderCFG.cpp | 9 +++++ tests/shaderCfgTests.cpp | 39 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.cpp b/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.cpp index 216e57202..9c279baf7 100644 --- a/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.cpp +++ b/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.cpp @@ -1389,6 +1389,15 @@ bool SplitSharedMergeBlock(Graph& graph, uint32_t merge, if (construct_predecessors.empty() || (!force_split && !has_external_predecessor)) { return false; } + if (construct_predecessors.size() == 1u) { + const auto* predecessor = graph.FindBlock(construct_predecessors.front()); + if (predecessor != nullptr && predecessor->inst_begin == predecessor->inst_end && + predecessor->terminator.kind == TerminatorKind::Branch && + predecessor->terminator.true_block == merge && + predecessor->start_pc == merge_block->start_pc) { + return false; + } + } const auto synthetic_merge = AppendSyntheticBranchBlock(graph, merge); auto* synthetic_block = graph.FindBlock(synthetic_merge); if (synthetic_block != nullptr) { diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index 78847d529..42ba3d5ac 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -7855,6 +7855,44 @@ void TestNewShaderRecompilerCfgSharedOuterAndLoopMerge() { CheckSpirvBinaryValidates(result.spirv); } +void TestNewShaderRecompilerCfgLoopExitSharedWithSelection() { + const uint32_t shader[] = { + EncodeSopc(0x0a, 0, 129), // loop condition + EncodeSopp(0x04, 10), // loop exit -> end + EncodeSopc(0x06, 1, 1), // selection within the loop + EncodeSopp(0x05, 3), // choose either arm + EncodeSopc(0x06, 2, 2), // first arm + EncodeSopp(0x04, 6), // first arm -> shared end + EncodeSopp(0x02, 3), // first arm -> repeat + EncodeSopc(0x06, 3, 3), // second arm + EncodeSopp(0x04, 3), // second arm -> shared end + EncodeSopp(0x02, 0), // second arm -> repeat + EncodeSop2(0x00, 0, 0, 129), // repeat work + EncodeSopp(0x02, 0xfff4u), // backedge + 0xbf810000u, + }; + + ShaderRecompiler::Decoder::Program decoded; + ShaderRecompiler::Decoder::DecodeProgram(std::span{shader}, decoded); + auto graph = ShaderRecompiler::CFG::BuildGraph(decoded); + const auto block_count = graph.blocks.size(); + const auto coverage = CfgInstructionCoverage(graph, decoded.instructions.size()); + Check(block_count == 8u && graph.natural_loops.size() == 1u, + "shared loop-exit fixture has the wrong native CFG"); + Check(!ShaderRecompiler::CFG::Structurize(graph) && + graph.unsupported_reason.find("duplicate structured merge block") != + std::string::npos, + "shared loop exit did not terminate at its structured merge conflict"); + Check(graph.blocks.size() == block_count && + CfgInstructionCoverage(graph, decoded.instructions.size()) == coverage, + "shared loop-exit fallback changed semantic instruction coverage"); + + auto result = RecompileForTest(shader, MakeCompileOptions(ShaderType::Compute)); + Check(result.program.dispatcher_fallback && SpirvContainsOpcode(result.spirv, 251), + "shared loop exit did not emit its dispatcher fallback"); + CheckSpirvBinaryValidates(result.spirv); +} + void TestNewShaderRecompilerCfgLoopEarlyBreakNoSelection() { const uint32_t shader[] = { EncodeSopc(0x0a, 0, 129), // loop: s_cmp_lt_u32 s0, 1 @@ -13586,6 +13624,7 @@ int main() { TestNewShaderRecompilerCfgLoopHeaderDsReadStructured(); TestNewShaderRecompilerCfgLoopHeaderDsRead2B64Structured(); TestNewShaderRecompilerCfgSharedOuterAndLoopMerge(); + TestNewShaderRecompilerCfgLoopExitSharedWithSelection(); TestNewShaderRecompilerCfgLoopEarlyBreakNoSelection(); TestNewShaderRecompilerCfgNestedLoopNonlocalExitDispatcher(); TestNewShaderRecompilerCfgNestedLoopLocalExitNoSelection();