vp: Improve vertex program analyser

- Adds dead code elimination
- Fix absolute branch target addresses to take base address into account
- Patch branch targets relative to base address to improve hash matching
- Bumps shader cache version
- Enables shader logging option to write out vertex program binary,
  helpful when debugging problems.
This commit is contained in:
kd-11 2018-07-01 20:37:05 +03:00 committed by kd-11
parent bd915bfebd
commit 2ca935a26b
12 changed files with 427 additions and 172 deletions

View file

@ -409,106 +409,54 @@ std::string VertexProgramDecompiler::BuildCode()
}
VertexProgramDecompiler::VertexProgramDecompiler(const RSXVertexProgram& prog) :
m_data(prog.data)
m_prog(prog)
{
}
std::string VertexProgramDecompiler::Decompile()
{
for (unsigned i = 0; i < PF_PARAM_COUNT; i++)
m_parr.params[i].clear();
m_instr_count = m_data.size() / 4;
for (int i = 0; i < m_max_instr_count; ++i)
{
m_instructions[i].reset();
}
const auto& data = m_prog.data;
m_instr_count = data.size() / 4;
bool is_has_BRA = false;
bool program_end = false;
u32 i = 1;
u32 last_label_addr = 0;
while (i < m_data.size())
for (unsigned i = 0; i < PF_PARAM_COUNT; i++)
{
if (is_has_BRA)
{
d3.HEX = m_data[i];
i += 4;
}
else
{
d1.HEX = m_data[i++];
switch (d1.sca_opcode)
{
case RSX_SCA_OPCODE_BRA:
{
LOG_ERROR(RSX, "Unimplemented VP opcode BRA");
is_has_BRA = true;
m_jump_lvls.clear();
d3.HEX = m_data[++i];
i += 4;
break;
}
case RSX_SCA_OPCODE_BRB:
case RSX_SCA_OPCODE_BRI:
case RSX_SCA_OPCODE_CAL:
case RSX_SCA_OPCODE_CLI:
case RSX_SCA_OPCODE_CLB:
{
d2.HEX = m_data[i++];
d3.HEX = m_data[i];
i += 2;
const u32 label_addr = GetAddr();
last_label_addr = std::max(last_label_addr, label_addr);
m_jump_lvls.emplace(label_addr);
break;
}
default:
{
d3.HEX = m_data[++i];
i += 2;
break;
}
}
}
m_parr.params[i].clear();
}
uint jump_position = 0;
if (is_has_BRA || !m_jump_lvls.empty())
for (int i = 0; i < m_max_instr_count; ++i)
{
m_cur_instr = &m_instructions[0];
AddCode("int jump_position = 0;");
AddCode("while (true)");
AddCode("{");
m_cur_instr->open_scopes++;
m_instructions[i].reset();
}
AddCode(fmt::format("if (jump_position <= %u)", jump_position++));
AddCode("{");
m_cur_instr->open_scopes++;
if (m_prog.jump_table.size())
{
last_label_addr = *m_prog.jump_table.rbegin();
}
auto find_jump_lvl = [this](u32 address)
{
u32 jump = 1;
for (auto pos : m_jump_lvls)
for (auto pos : m_prog.jump_table)
{
if (address == pos)
break;
return jump;
++jump;
}
return jump;
return UINT32_MAX;
};
auto do_function_call = [this, &i](const std::string& condition)
{
//call function
// Call function
// NOTE: Addresses are assumed to have been patched
m_call_stack.push(i+1);
AddCode(condition);
AddCode("{");
@ -552,17 +500,41 @@ std::string VertexProgramDecompiler::Decompile()
}
};
for (i = 0; i < m_instr_count; ++i)
if (is_has_BRA || !m_prog.jump_table.empty())
{
if (m_call_stack.empty())
m_cur_instr = &m_instructions[0];
u32 jump_position = 0;
if (m_prog.entry != m_prog.base_address)
{
m_cur_instr = &m_instructions[i];
jump_position = find_jump_lvl(m_prog.entry - m_prog.base_address);
verify(HERE), jump_position != UINT32_MAX;
}
d0.HEX = m_data[i * 4 + 0];
d1.HEX = m_data[i * 4 + 1];
d2.HEX = m_data[i * 4 + 2];
d3.HEX = m_data[i * 4 + 3];
AddCode(fmt::format("int jump_position = %u;", jump_position));
AddCode("while (true)");
AddCode("{");
m_cur_instr->open_scopes++;
AddCode("if (jump_position <= 0)");
AddCode("{");
m_cur_instr->open_scopes++;
}
for (i = 0; i < m_instr_count; ++i)
{
if (!m_prog.instruction_mask[i])
{
// Dead code, skip
continue;
}
m_cur_instr = &m_instructions[i];
d0.HEX = data[i * 4 + 0];
d1.HEX = data[i * 4 + 1];
d2.HEX = data[i * 4 + 2];
d3.HEX = data[i * 4 + 3];
src[0].src0l = d2.src0l;
src[0].src0h = d1.src0h;
@ -570,27 +542,29 @@ std::string VertexProgramDecompiler::Decompile()
src[2].src2l = d3.src2l;
src[2].src2h = d2.src2h;
if (!src[0].reg_type || !src[1].reg_type || !src[2].reg_type)
{
AddCode("//Src check failed. Aborting");
program_end = true;
}
if (m_call_stack.empty())
if (m_call_stack.empty() && i)
{
//TODO: Subroutines can also have arbitrary jumps!
if (i && (is_has_BRA || std::find(m_jump_lvls.begin(), m_jump_lvls.end(), i) != m_jump_lvls.end()))
u32 jump_position = find_jump_lvl(i);
if (is_has_BRA || jump_position != UINT32_MAX)
{
m_cur_instr->close_scopes++;
AddCode("}");
AddCode("");
AddCode(fmt::format("if (jump_position <= %u)", jump_position++));
AddCode(fmt::format("if (jump_position <= %u)", jump_position));
AddCode("{");
m_cur_instr->open_scopes++;
}
}
if (!src[0].reg_type || !src[1].reg_type || !src[2].reg_type)
{
AddCode("//Src check failed. Aborting");
program_end = true;
d1.vec_opcode = d1.sca_opcode = 0;
}
switch (d1.vec_opcode)
{
case RSX_VEC_OPCODE_NOP: break;
@ -754,7 +728,7 @@ std::string VertexProgramDecompiler::Decompile()
if ((i + 1) < m_instr_count)
{
// In rare cases, this might be harmless (large coalesced program blocks controlled via branches aka ubershaders)
LOG_ERROR(RSX, "Vertex program aborted prematurely. Expect glitches");
LOG_ERROR(RSX, "Vertex program block aborts prematurely. Expect glitches");
}
break;
@ -762,7 +736,7 @@ std::string VertexProgramDecompiler::Decompile()
}
}
if (is_has_BRA || !m_jump_lvls.empty())
if (is_has_BRA || !m_prog.jump_table.empty())
{
m_cur_instr = &m_instructions[m_instr_count - 1];
m_cur_instr->close_scopes++;
@ -774,8 +748,6 @@ std::string VertexProgramDecompiler::Decompile()
std::string result = BuildCode();
m_jump_lvls.clear();
m_body.clear();
return result;
}