rsx/fp: Use CFG for fragment program

This commit is contained in:
kd-11 2025-11-23 19:39:48 +03:00 committed by kd-11
parent 9d92e190eb
commit cb7650240c

View File

@ -1297,7 +1297,7 @@ bool FragmentProgramDecompiler::handle_tex_srb(u32 opcode)
std::string FragmentProgramDecompiler::Decompile() std::string FragmentProgramDecompiler::Decompile()
{ {
auto data = static_cast<be_t<u32>*>(m_prog.get_data()); const auto graph = rsx::assembler::deconstruct_fragment_program(m_prog);
m_size = 0; m_size = 0;
m_location = 0; m_location = 0;
m_loop_count = 0; m_loop_count = 0;
@ -1314,141 +1314,146 @@ std::string FragmentProgramDecompiler::Decompile()
int forced_unit = FORCE_NONE; 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); // TODO: Handle block prologue if any
found != m_end_offsets.end();
found = std::find(m_end_offsets.begin(), m_end_offsets.end(), m_size)) for (const auto& inst : block.instructions)
{ {
m_end_offsets.erase(found); for (auto found = std::find(m_end_offsets.begin(), m_end_offsets.end(), m_size);
m_code_level--; found != m_end_offsets.end();
AddCode("}"); found = std::find(m_end_offsets.begin(), m_end_offsets.end(), m_size))
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)
{ {
case RSX_FP_OPCODE_BRK: m_end_offsets.erase(found);
if (m_loop_count) AddFlowOp("break"); m_code_level--;
else rsx_log.error("BRK opcode found outside of a loop"); AddCode("}");
break; m_loop_count--;
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; 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) dst.HEX = inst.bytecode[0];
{ src0.HEX = inst.bytecode[1];
case RSX_FP_OPCODE_NOP: src1.HEX = inst.bytecode[2];
break; src2.HEX = inst.bytecode[3];
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 m_offset = 4 * sizeof(u32);
// Tested with Tales of Vesperia opflags = 0;
if (SIP()) break;
if (handle_tex_srb(opcode)) break;
// FENCT/FENCB do not actually reject instructions if they dont match the forced unit const u32 opcode = dst.opcode | (src1.opcode_is_branch << 6);
// 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); auto SIP = [&]()
break; {
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; // TODO: Handle block epilogue if needed
if (dst.end) break;
ensure(m_offset % sizeof(u32) == 0);
data += m_offset / sizeof(u32);
} }
while (m_code_level > 1) while (m_code_level > 1)