diff --git a/rpcs3/Emu/RSX/Program/Assembler/FPToCFG.cpp b/rpcs3/Emu/RSX/Program/Assembler/FPToCFG.cpp index fbdf830cf7..5d8afeba58 100644 --- a/rpcs3/Emu/RSX/Program/Assembler/FPToCFG.cpp +++ b/rpcs3/Emu/RSX/Program/Assembler/FPToCFG.cpp @@ -100,15 +100,16 @@ namespace rsx::assembler if (found) { - if (!bb->pred.empty()) + auto front_edge = std::find_if(bb->pred.begin(), bb->pred.end(), FN(x.type != EdgeType::ENDIF && x.type != EdgeType::ENDLOOP)); + if (front_edge != bb->pred.end()) { - switch (bb->pred.back().type) + auto parent = ensure(front_edge->from); + switch (front_edge->type) { case EdgeType::IF: case EdgeType::ELSE: { // Find the merge node from the parent - auto parent = bb->pred.back().from; auto succ = std::find_if(parent->succ.begin(), parent->succ.end(), FN(x.type == EdgeType::ENDIF)); ensure(succ != parent->succ.end(), "CFG: Broken IF linkage. Please report to developers."); bb->insert_succ(succ->to, EdgeType::ENDIF); @@ -118,7 +119,6 @@ namespace rsx::assembler case EdgeType::LOOP: { // Find the merge node from the parent - auto parent = bb->pred.back().from; auto succ = std::find_if(parent->succ.begin(), parent->succ.end(), FN(x.type == EdgeType::ENDLOOP)); ensure(succ != parent->succ.end(), "CFG: Broken LOOP linkage. Please report to developers."); bb->insert_succ(succ->to, EdgeType::ENDLOOP); @@ -130,7 +130,7 @@ namespace rsx::assembler rsx_log.error("CFG: Unexpected block exit. Report to developers."); } } - else + else if (bb->pred.empty()) { // Impossible situation. rsx_log.error("CFG: Child block has no parent but has successor! Report to developers."); diff --git a/rpcs3/Emu/RSX/Program/Assembler/IR.h b/rpcs3/Emu/RSX/Program/Assembler/IR.h index fff3d5aa83..ef6079a85d 100644 --- a/rpcs3/Emu/RSX/Program/Assembler/IR.h +++ b/rpcs3/Emu/RSX/Program/Assembler/IR.h @@ -114,5 +114,25 @@ namespace rsx::assembler pred.push_back(e); return &pred.back(); } + + bool is_of_type(EdgeType type) const + { + return pred.size() == 1 && + pred.front().type == type; + } + + bool has_sibling_of_type(EdgeType type) const + { + if (pred.size() != 1) + { + return false; + } + + auto source_node = pred.front().from; + return std::find_if( + source_node->succ.begin(), + source_node->succ.end(), + FN(x.type == type)) != source_node->succ.end(); + } }; } diff --git a/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp b/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp index 2ebfd7d8d7..a5a6cd5876 100644 --- a/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp +++ b/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp @@ -21,6 +21,7 @@ namespace rsx } using namespace rsx::fragment_program; +using namespace rsx::assembler; // SIMD vector lanes enum VectorLane : u8 @@ -1295,7 +1296,7 @@ bool FragmentProgramDecompiler::handle_tex_srb(u32 opcode) std::string FragmentProgramDecompiler::Decompile() { - const auto graph = rsx::assembler::deconstruct_fragment_program(m_prog); + const auto graph = deconstruct_fragment_program(m_prog); m_size = 0; m_location = 0; m_loop_count = 0; @@ -1322,22 +1323,41 @@ std::string FragmentProgramDecompiler::Decompile() { switch (pred.type) { - case rsx::assembler::EdgeType::ENDLOOP: - m_loop_count--; - [[ fallthrough ]]; - case rsx::assembler::EdgeType::ENDIF: - m_code_level--; - AddCode("}"); + case EdgeType::ENDLOOP: + // Because of succession rules, endloop is seen twice. + // Once from the the for statement at the end of the parent + // and again at the end of the child block. + if (pred.from->is_of_type(EdgeType::LOOP)) + { + m_loop_count--; + m_code_level--; + AddCode("}"); + } break; - case rsx::assembler::EdgeType::LOOP: + case EdgeType::ENDIF: + { + // Same thing happens with ENDIF + // Once for the IF statement itself + // And again for the child blocks with code for the IF and ELSE paths. + const bool is_else_end = pred.from->is_of_type(EdgeType::ELSE); + const bool is_if_end = pred.from->is_of_type(EdgeType::IF) && + !pred.from->has_sibling_of_type(EdgeType::ELSE); // Avoid double-counting if the IF has an ELSE sibling + if (is_else_end || is_if_end) + { + m_code_level--; + AddCode("}"); + } + break; + } + case EdgeType::LOOP: m_loop_count++; [[ fallthrough ]]; - case rsx::assembler::EdgeType::IF: + case EdgeType::IF: // Instruction will be inserted by the SIP decoder AddCode("{"); m_code_level++; break; - case rsx::assembler::EdgeType::ELSE: + case EdgeType::ELSE: // This one needs more testing m_code_level--; AddCode("}"); @@ -1440,8 +1460,8 @@ std::string FragmentProgramDecompiler::Decompile() while (m_code_level > 1) { - rsx_log.error("Hanging block found at end of shader. Malformed shader?"); - + // Happens if the last block was hanging (no merge) + // FIXME: We must always have a merge block on exit to resolve dependencies on outputs m_code_level--; AddCode("}"); }