|
|
@ -15,7 +15,7 @@ |
|
|
#include "video_core/shader/shader_ir.h"
|
|
|
#include "video_core/shader/shader_ir.h"
|
|
|
|
|
|
|
|
|
namespace VideoCommon::Shader { |
|
|
namespace VideoCommon::Shader { |
|
|
|
|
|
|
|
|
|
|
|
namespace { |
|
|
using Tegra::Shader::Instruction; |
|
|
using Tegra::Shader::Instruction; |
|
|
using Tegra::Shader::OpCode; |
|
|
using Tegra::Shader::OpCode; |
|
|
|
|
|
|
|
|
@ -29,8 +29,7 @@ struct Query { |
|
|
|
|
|
|
|
|
struct BlockStack { |
|
|
struct BlockStack { |
|
|
BlockStack() = default; |
|
|
BlockStack() = default; |
|
|
BlockStack(const BlockStack& b) = default; |
|
|
|
|
|
BlockStack(const Query& q) : ssy_stack{q.ssy_stack}, pbk_stack{q.pbk_stack} {} |
|
|
|
|
|
|
|
|
explicit BlockStack(const Query& q) : ssy_stack{q.ssy_stack}, pbk_stack{q.pbk_stack} {} |
|
|
std::stack<u32> ssy_stack{}; |
|
|
std::stack<u32> ssy_stack{}; |
|
|
std::stack<u32> pbk_stack{}; |
|
|
std::stack<u32> pbk_stack{}; |
|
|
}; |
|
|
}; |
|
|
@ -58,7 +57,7 @@ struct BlockInfo { |
|
|
struct CFGRebuildState { |
|
|
struct CFGRebuildState { |
|
|
explicit CFGRebuildState(const ProgramCode& program_code, const std::size_t program_size, |
|
|
explicit CFGRebuildState(const ProgramCode& program_code, const std::size_t program_size, |
|
|
const u32 start) |
|
|
const u32 start) |
|
|
: program_code{program_code}, program_size{program_size}, start{start} {} |
|
|
|
|
|
|
|
|
: start{start}, program_code{program_code}, program_size{program_size} {} |
|
|
|
|
|
|
|
|
u32 start{}; |
|
|
u32 start{}; |
|
|
std::vector<BlockInfo> block_info{}; |
|
|
std::vector<BlockInfo> block_info{}; |
|
|
@ -85,7 +84,7 @@ std::pair<BlockCollision, u32> TryGetBlock(CFGRebuildState& state, u32 address) |
|
|
return {BlockCollision::Inside, index}; |
|
|
return {BlockCollision::Inside, index}; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
return {BlockCollision::None, -1}; |
|
|
|
|
|
|
|
|
return {BlockCollision::None, 0xFFFFFFFF}; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
struct ParseInfo { |
|
|
struct ParseInfo { |
|
|
@ -365,27 +364,29 @@ bool TryQuery(CFGRebuildState& state) { |
|
|
const auto gather_end = labels.upper_bound(block.end); |
|
|
const auto gather_end = labels.upper_bound(block.end); |
|
|
while (gather_start != gather_end) { |
|
|
while (gather_start != gather_end) { |
|
|
cc.push(gather_start->second); |
|
|
cc.push(gather_start->second); |
|
|
gather_start++; |
|
|
|
|
|
|
|
|
++gather_start; |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
if (state.queries.empty()) { |
|
|
if (state.queries.empty()) { |
|
|
return false; |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
Query& q = state.queries.front(); |
|
|
Query& q = state.queries.front(); |
|
|
const u32 block_index = state.registered[q.address]; |
|
|
const u32 block_index = state.registered[q.address]; |
|
|
BlockInfo& block = state.block_info[block_index]; |
|
|
BlockInfo& block = state.block_info[block_index]; |
|
|
// If the block is visted, check if the stacks match, else gather the ssy/pbk
|
|
|
|
|
|
|
|
|
// If the block is visited, check if the stacks match, else gather the ssy/pbk
|
|
|
// labels into the current stack and look if the branch at the end of the block
|
|
|
// labels into the current stack and look if the branch at the end of the block
|
|
|
// consumes a label. Schedule new queries accordingly
|
|
|
// consumes a label. Schedule new queries accordingly
|
|
|
if (block.visited) { |
|
|
if (block.visited) { |
|
|
BlockStack& stack = state.stacks[q.address]; |
|
|
BlockStack& stack = state.stacks[q.address]; |
|
|
const bool all_okay = (stack.ssy_stack.size() == 0 || q.ssy_stack == stack.ssy_stack) && |
|
|
|
|
|
(stack.pbk_stack.size() == 0 || q.pbk_stack == stack.pbk_stack); |
|
|
|
|
|
|
|
|
const bool all_okay = (stack.ssy_stack.empty() || q.ssy_stack == stack.ssy_stack) && |
|
|
|
|
|
(stack.pbk_stack.empty() || q.pbk_stack == stack.pbk_stack); |
|
|
state.queries.pop_front(); |
|
|
state.queries.pop_front(); |
|
|
return all_okay; |
|
|
return all_okay; |
|
|
} |
|
|
} |
|
|
block.visited = true; |
|
|
block.visited = true; |
|
|
state.stacks[q.address] = BlockStack{q}; |
|
|
|
|
|
|
|
|
state.stacks.insert_or_assign(q.address, BlockStack{q}); |
|
|
|
|
|
|
|
|
Query q2(q); |
|
|
Query q2(q); |
|
|
state.queries.pop_front(); |
|
|
state.queries.pop_front(); |
|
|
gather_labels(q2.ssy_stack, state.ssy_labels, block); |
|
|
gather_labels(q2.ssy_stack, state.ssy_labels, block); |
|
|
@ -394,6 +395,7 @@ bool TryQuery(CFGRebuildState& state) { |
|
|
q2.address = block.end + 1; |
|
|
q2.address = block.end + 1; |
|
|
state.queries.push_back(q2); |
|
|
state.queries.push_back(q2); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
Query conditional_query{q2}; |
|
|
Query conditional_query{q2}; |
|
|
if (block.branch.is_sync) { |
|
|
if (block.branch.is_sync) { |
|
|
if (block.branch.address == unassigned_branch) { |
|
|
if (block.branch.address == unassigned_branch) { |
|
|
@ -408,13 +410,15 @@ bool TryQuery(CFGRebuildState& state) { |
|
|
conditional_query.pbk_stack.pop(); |
|
|
conditional_query.pbk_stack.pop(); |
|
|
} |
|
|
} |
|
|
conditional_query.address = block.branch.address; |
|
|
conditional_query.address = block.branch.address; |
|
|
state.queries.push_back(conditional_query); |
|
|
|
|
|
|
|
|
state.queries.push_back(std::move(conditional_query)); |
|
|
return true; |
|
|
return true; |
|
|
} |
|
|
} |
|
|
|
|
|
} // Anonymous namespace
|
|
|
|
|
|
|
|
|
std::optional<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 program_size, |
|
|
|
|
|
u32 start_address) { |
|
|
|
|
|
|
|
|
std::optional<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, |
|
|
|
|
|
std::size_t program_size, u32 start_address) { |
|
|
CFGRebuildState state{program_code, program_size, start_address}; |
|
|
CFGRebuildState state{program_code, program_size, start_address}; |
|
|
|
|
|
|
|
|
// Inspect Code and generate blocks
|
|
|
// Inspect Code and generate blocks
|
|
|
state.labels.clear(); |
|
|
state.labels.clear(); |
|
|
state.labels.emplace(start_address); |
|
|
state.labels.emplace(start_address); |
|
|
@ -424,10 +428,9 @@ std::optional<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u |
|
|
return {}; |
|
|
return {}; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Decompile Stacks
|
|
|
// Decompile Stacks
|
|
|
Query start_query{}; |
|
|
|
|
|
start_query.address = state.start; |
|
|
|
|
|
state.queries.push_back(start_query); |
|
|
|
|
|
|
|
|
state.queries.push_back(Query{state.start, {}, {}}); |
|
|
bool decompiled = true; |
|
|
bool decompiled = true; |
|
|
while (!state.queries.empty()) { |
|
|
while (!state.queries.empty()) { |
|
|
if (!TryQuery(state)) { |
|
|
if (!TryQuery(state)) { |
|
|
@ -435,14 +438,15 @@ std::optional<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Sort and organize results
|
|
|
// Sort and organize results
|
|
|
std::sort(state.block_info.begin(), state.block_info.end(), |
|
|
std::sort(state.block_info.begin(), state.block_info.end(), |
|
|
[](const BlockInfo& a, const BlockInfo& b) -> bool { return a.start < b.start; }); |
|
|
|
|
|
|
|
|
[](const BlockInfo& a, const BlockInfo& b) { return a.start < b.start; }); |
|
|
ShaderCharacteristics result_out{}; |
|
|
ShaderCharacteristics result_out{}; |
|
|
result_out.decompilable = decompiled; |
|
|
result_out.decompilable = decompiled; |
|
|
result_out.start = start_address; |
|
|
result_out.start = start_address; |
|
|
result_out.end = start_address; |
|
|
result_out.end = start_address; |
|
|
for (auto& block : state.block_info) { |
|
|
|
|
|
|
|
|
for (const auto& block : state.block_info) { |
|
|
ShaderBlock new_block{}; |
|
|
ShaderBlock new_block{}; |
|
|
new_block.start = block.start; |
|
|
new_block.start = block.start; |
|
|
new_block.end = block.end; |
|
|
new_block.end = block.end; |
|
|
@ -457,8 +461,9 @@ std::optional<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u |
|
|
} |
|
|
} |
|
|
if (result_out.decompilable) { |
|
|
if (result_out.decompilable) { |
|
|
result_out.labels = std::move(state.labels); |
|
|
result_out.labels = std::move(state.labels); |
|
|
return {result_out}; |
|
|
|
|
|
|
|
|
return {std::move(result_out)}; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// If it's not decompilable, merge the unlabelled blocks together
|
|
|
// If it's not decompilable, merge the unlabelled blocks together
|
|
|
auto back = result_out.blocks.begin(); |
|
|
auto back = result_out.blocks.begin(); |
|
|
auto next = std::next(back); |
|
|
auto next = std::next(back); |
|
|
@ -469,8 +474,8 @@ std::optional<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u |
|
|
continue; |
|
|
continue; |
|
|
} |
|
|
} |
|
|
back = next; |
|
|
back = next; |
|
|
next++; |
|
|
|
|
|
|
|
|
++next; |
|
|
} |
|
|
} |
|
|
return {result_out}; |
|
|
|
|
|
|
|
|
return {std::move(result_out)}; |
|
|
} |
|
|
} |
|
|
} // namespace VideoCommon::Shader
|
|
|
} // namespace VideoCommon::Shader
|