From 2b0456520e659adc6a1604f56a216d5c632e1dd3 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Sat, 13 Dec 2025 02:34:49 +0300 Subject: [PATCH] rsx/cfg: Fix edge case where an empty block is defined --- rpcs3/Emu/RSX/Program/Assembler/FPToCFG.cpp | 37 +++++++++++++++++---- rpcs3/tests/test_rsx_cfg.cpp | 18 ++++++++++ 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/rpcs3/Emu/RSX/Program/Assembler/FPToCFG.cpp b/rpcs3/Emu/RSX/Program/Assembler/FPToCFG.cpp index 1f1e5b3678..99e7c56f6e 100644 --- a/rpcs3/Emu/RSX/Program/Assembler/FPToCFG.cpp +++ b/rpcs3/Emu/RSX/Program/Assembler/FPToCFG.cpp @@ -194,22 +194,45 @@ namespace rsx::assembler case RSX_FP_OPCODE_IFE: { // Inserts if and else and end blocks - auto parent = bb; - bb = safe_insert_block(parent, pc + 1, EdgeType::IF); - if (src2.end_offset != src1.else_offset) + const u32 end_addr = src2.end_offset >> 2u; + const u32 else_addr = src1.else_offset >> 2u; + if (end_addr == pc + 1u) { - else_blocks.push_back(safe_insert_block(parent, src1.else_offset >> 2, EdgeType::ELSE)); + // NOP. Empty IF block + bb->instructions.pop_back(); + break; } - end_blocks.push_back(safe_insert_block(parent, src2.end_offset >> 2, EdgeType::ENDIF)); + + if (else_addr > end_addr) + { + // Our systems support this, but it is not verified on real hardware. + rsx_log.error("CFG: Non-contiguous branch detected. Report to developers."); + } + + auto parent = bb; + bb = safe_insert_block(parent, pc + 1u, EdgeType::IF); + if (end_addr != else_addr) + { + else_blocks.push_back(safe_insert_block(parent, else_addr, EdgeType::ELSE)); + } + end_blocks.push_back(safe_insert_block(parent, end_addr, EdgeType::ENDIF)); break; } case RSX_FP_OPCODE_LOOP: case RSX_FP_OPCODE_REP: { // Inserts for and end blocks + const u32 end_addr = src2.end_offset >> 2u; + if (end_addr == pc + 1u) + { + // NOP. Empty LOOP block + bb->instructions.pop_back(); + break; + } + auto parent = bb; - bb = safe_insert_block(parent, pc + 1, EdgeType::LOOP); - end_blocks.push_back(safe_insert_block(parent, src2.end_offset >> 2, EdgeType::ENDLOOP)); + bb = safe_insert_block(parent, pc + 1u, EdgeType::LOOP); + end_blocks.push_back(safe_insert_block(parent, end_addr, EdgeType::ENDLOOP)); break; } default: diff --git a/rpcs3/tests/test_rsx_cfg.cpp b/rpcs3/tests/test_rsx_cfg.cpp index 5e22311ac3..ded749fd24 100644 --- a/rpcs3/tests/test_rsx_cfg.cpp +++ b/rpcs3/tests/test_rsx_cfg.cpp @@ -210,4 +210,22 @@ namespace rsx::assembler EXPECT_EQ(bb6->pred[1].from, bb3); EXPECT_EQ(bb6->pred[2].from, bb0); } + + TEST(CFG, FpToCFG_EmptyIF) + { + auto ir = FPIR::from_source( + "IF.LT;" // Empty branch + "ENDIF;" + "MOV R0, R1;" // False merge block. + ); + + RSXFragmentProgram program{}; + auto bytecode = ir.compile(); + program.data = bytecode.data(); + + FlowGraph graph = deconstruct_fragment_program(program); + + ASSERT_EQ(graph.blocks.size(), 1); + EXPECT_EQ(graph.blocks.front().instructions.size(), 1); + } }