From cb7650240c3f80bcbcb4afac07eefa587f8dc012 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Sun, 23 Nov 2025 19:39:48 +0300 Subject: [PATCH] rsx/fp: Use CFG for fragment program --- .../RSX/Program/FragmentProgramDecompiler.cpp | 255 +++++++++--------- 1 file changed, 130 insertions(+), 125 deletions(-) diff --git a/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp b/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp index e5742fffda..ad2ac55075 100644 --- a/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp +++ b/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp @@ -1297,7 +1297,7 @@ bool FragmentProgramDecompiler::handle_tex_srb(u32 opcode) std::string FragmentProgramDecompiler::Decompile() { - auto data = static_cast*>(m_prog.get_data()); + const auto graph = rsx::assembler::deconstruct_fragment_program(m_prog); m_size = 0; m_location = 0; m_loop_count = 0; @@ -1314,141 +1314,146 @@ std::string FragmentProgramDecompiler::Decompile() int forced_unit = FORCE_NONE; - while (true) + for (const auto &block : graph.blocks) { - for (auto found = std::find(m_end_offsets.begin(), m_end_offsets.end(), m_size); - found != m_end_offsets.end(); - found = std::find(m_end_offsets.begin(), m_end_offsets.end(), m_size)) + // TODO: Handle block prologue if any + + for (const auto& inst : block.instructions) { - m_end_offsets.erase(found); - m_code_level--; - AddCode("}"); - m_loop_count--; - } - - for (auto found = std::find(m_else_offsets.begin(), m_else_offsets.end(), m_size); - found != m_else_offsets.end(); - found = std::find(m_else_offsets.begin(), m_else_offsets.end(), m_size)) - { - m_else_offsets.erase(found); - m_code_level--; - AddCode("}"); - AddCode("else"); - AddCode("{"); - m_code_level++; - } - - dst.HEX = GetData(data[0]); - src0.HEX = GetData(data[1]); - src1.HEX = GetData(data[2]); - src2.HEX = GetData(data[3]); - - m_offset = 4 * sizeof(u32); - opflags = 0; - - const u32 opcode = dst.opcode | (src1.opcode_is_branch << 6); - - auto SIP = [&]() - { - switch (opcode) + for (auto found = std::find(m_end_offsets.begin(), m_end_offsets.end(), m_size); + found != m_end_offsets.end(); + found = std::find(m_end_offsets.begin(), m_end_offsets.end(), m_size)) { - case RSX_FP_OPCODE_BRK: - if (m_loop_count) AddFlowOp("break"); - else rsx_log.error("BRK opcode found outside of a loop"); - break; - case RSX_FP_OPCODE_CAL: - rsx_log.error("Unimplemented SIP instruction: CAL"); - break; - case RSX_FP_OPCODE_FENCT: - AddCode("//FENCT"); - forced_unit = FORCE_SCT; - break; - case RSX_FP_OPCODE_FENCB: - AddCode("//FENCB"); - forced_unit = FORCE_SCB; - break; - case RSX_FP_OPCODE_IFE: - AddCode("if($cond)"); - if (src2.end_offset != src1.else_offset) - m_else_offsets.push_back(src1.else_offset << 2); - m_end_offsets.push_back(src2.end_offset << 2); - AddCode("{"); - m_code_level++; - break; - case RSX_FP_OPCODE_LOOP: - if (!src0.exec_if_eq && !src0.exec_if_gr && !src0.exec_if_lt) - { - AddCode(fmt::format("//$ifcond for(int i%u = %u; i%u < %u; i%u += %u) {} //-> %u //LOOP", - m_loop_count, src1.init_counter, m_loop_count, src1.end_counter, m_loop_count, src1.increment, src2.end_offset)); - } - else - { - AddCode(fmt::format("$ifcond for(int i%u = %u; i%u < %u; i%u += %u) //LOOP", - m_loop_count, src1.init_counter, m_loop_count, src1.end_counter, m_loop_count, src1.increment)); - m_loop_count++; - m_end_offsets.push_back(src2.end_offset << 2); - AddCode("{"); - m_code_level++; - } - break; - case RSX_FP_OPCODE_REP: - if (!src0.exec_if_eq && !src0.exec_if_gr && !src0.exec_if_lt) - { - AddCode(fmt::format("//$ifcond for(int i%u = %u; i%u < %u; i%u += %u) {} //-> %u //REP", - m_loop_count, src1.init_counter, m_loop_count, src1.end_counter, m_loop_count, src1.increment, src2.end_offset)); - } - else - { - AddCode(fmt::format("if($cond) for(int i%u = %u; i%u < %u; i%u += %u) //REP", - m_loop_count, src1.init_counter, m_loop_count, src1.end_counter, m_loop_count, src1.increment)); - m_loop_count++; - m_end_offsets.push_back(src2.end_offset << 2); - AddCode("{"); - m_code_level++; - } - break; - case RSX_FP_OPCODE_RET: - AddFlowOp("return"); - break; - - default: - return false; + m_end_offsets.erase(found); + m_code_level--; + AddCode("}"); + m_loop_count--; } - return true; - }; + for (auto found = std::find(m_else_offsets.begin(), m_else_offsets.end(), m_size); + found != m_else_offsets.end(); + found = std::find(m_else_offsets.begin(), m_else_offsets.end(), m_size)) + { + m_else_offsets.erase(found); + m_code_level--; + AddCode("}"); + AddCode("else"); + AddCode("{"); + m_code_level++; + } - switch (opcode) - { - case RSX_FP_OPCODE_NOP: - break; - case RSX_FP_OPCODE_KIL: - properties.has_discard_op = true; - AddFlowOp("_kill()"); - break; - default: - int prev_force_unit = forced_unit; + dst.HEX = inst.bytecode[0]; + src0.HEX = inst.bytecode[1]; + src1.HEX = inst.bytecode[2]; + src2.HEX = inst.bytecode[3]; - // Some instructions do not respect forced unit - // Tested with Tales of Vesperia - if (SIP()) break; - if (handle_tex_srb(opcode)) break; + m_offset = 4 * sizeof(u32); + opflags = 0; - // FENCT/FENCB do not actually reject instructions if they dont match the forced unit - // Looks like they are optimization hints and not hard-coded forced paths - if (handle_sct_scb(opcode)) break; - forced_unit = FORCE_NONE; + const u32 opcode = dst.opcode | (src1.opcode_is_branch << 6); - rsx_log.error("Unknown/illegal instruction: 0x%x (forced unit %d)", opcode, prev_force_unit); - break; + auto SIP = [&]() + { + switch (opcode) + { + case RSX_FP_OPCODE_BRK: + if (m_loop_count) AddFlowOp("break"); + else rsx_log.error("BRK opcode found outside of a loop"); + break; + case RSX_FP_OPCODE_CAL: + rsx_log.error("Unimplemented SIP instruction: CAL"); + break; + case RSX_FP_OPCODE_FENCT: + AddCode("//FENCT"); + forced_unit = FORCE_SCT; + break; + case RSX_FP_OPCODE_FENCB: + AddCode("//FENCB"); + forced_unit = FORCE_SCB; + break; + case RSX_FP_OPCODE_IFE: + AddCode("if($cond)"); + if (src2.end_offset != src1.else_offset) + m_else_offsets.push_back(src1.else_offset << 2); + m_end_offsets.push_back(src2.end_offset << 2); + AddCode("{"); + m_code_level++; + break; + case RSX_FP_OPCODE_LOOP: + if (!src0.exec_if_eq && !src0.exec_if_gr && !src0.exec_if_lt) + { + AddCode(fmt::format("//$ifcond for(int i%u = %u; i%u < %u; i%u += %u) {} //-> %u //LOOP", + m_loop_count, src1.init_counter, m_loop_count, src1.end_counter, m_loop_count, src1.increment, src2.end_offset)); + } + else + { + AddCode(fmt::format("$ifcond for(int i%u = %u; i%u < %u; i%u += %u) //LOOP", + m_loop_count, src1.init_counter, m_loop_count, src1.end_counter, m_loop_count, src1.increment)); + m_loop_count++; + m_end_offsets.push_back(src2.end_offset << 2); + AddCode("{"); + m_code_level++; + } + break; + case RSX_FP_OPCODE_REP: + if (!src0.exec_if_eq && !src0.exec_if_gr && !src0.exec_if_lt) + { + AddCode(fmt::format("//$ifcond for(int i%u = %u; i%u < %u; i%u += %u) {} //-> %u //REP", + m_loop_count, src1.init_counter, m_loop_count, src1.end_counter, m_loop_count, src1.increment, src2.end_offset)); + } + else + { + AddCode(fmt::format("if($cond) for(int i%u = %u; i%u < %u; i%u += %u) //REP", + m_loop_count, src1.init_counter, m_loop_count, src1.end_counter, m_loop_count, src1.increment)); + m_loop_count++; + m_end_offsets.push_back(src2.end_offset << 2); + AddCode("{"); + m_code_level++; + } + break; + case RSX_FP_OPCODE_RET: + AddFlowOp("return"); + break; + + default: + return false; + } + + return true; + }; + + switch (opcode) + { + case RSX_FP_OPCODE_NOP: + break; + case RSX_FP_OPCODE_KIL: + properties.has_discard_op = true; + AddFlowOp("_kill()"); + break; + default: + int prev_force_unit = forced_unit; + + // Some instructions do not respect forced unit + // Tested with Tales of Vesperia + if (SIP()) break; + if (handle_tex_srb(opcode)) break; + + // FENCT/FENCB do not actually reject instructions if they dont match the forced unit + // Looks like they are optimization hints and not hard-coded forced paths + if (handle_sct_scb(opcode)) break; + forced_unit = FORCE_NONE; + + rsx_log.error("Unknown/illegal instruction: 0x%x (forced unit %d)", opcode, prev_force_unit); + break; + } + + m_size += m_offset; + ensure((m_offset & 15) == 0); // Must be aligned to 16 bytes + + if (dst.end) break; } - m_size += m_offset; - - if (dst.end) break; - - ensure(m_offset % sizeof(u32) == 0); - data += m_offset / sizeof(u32); + // TODO: Handle block epilogue if needed } while (m_code_level > 1)