mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-08-18 22:42:23 +00:00
shader: repair nested merge structurization
This commit is contained in:
@@ -1051,15 +1051,6 @@ bool IsolateSemanticLoopHeader(Graph& graph, uint32_t old_header) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsSyntheticMergeForwarder(const Graph& graph, uint32_t block_id, uint32_t merge) {
|
||||
const auto* block = graph.FindBlock(block_id);
|
||||
return block != nullptr && block->inst_begin == block->inst_end &&
|
||||
block->successors.size() == 1u && block->successors.front() == merge &&
|
||||
block->terminator.kind == TerminatorKind::Branch &&
|
||||
block->terminator.condition == BranchCondition::Always &&
|
||||
block->terminator.true_block == merge;
|
||||
}
|
||||
|
||||
const NaturalLoop* FindInnermostContainingLoop(const Graph& graph, uint32_t block_id) {
|
||||
const NaturalLoop* innermost = nullptr;
|
||||
for (const auto& loop: graph.natural_loops) {
|
||||
@@ -1232,24 +1223,14 @@ bool SplitSharedMergeBlock(Graph& graph, uint32_t merge,
|
||||
if (construct_predecessors.empty() || (!force_split && external_predecessors.empty())) {
|
||||
return false;
|
||||
}
|
||||
std::vector<uint32_t> predecessors_to_split;
|
||||
for (auto pred: construct_predecessors) {
|
||||
if (!IsSyntheticMergeForwarder(graph, pred, merge)) {
|
||||
AddUnique(predecessors_to_split, pred);
|
||||
}
|
||||
}
|
||||
if (predecessors_to_split.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto synthetic_merge = AppendSyntheticBranchBlock(graph, merge);
|
||||
auto* synthetic_block = graph.FindBlock(synthetic_merge);
|
||||
if (synthetic_block != nullptr) {
|
||||
synthetic_block->predecessors = predecessors_to_split;
|
||||
synthetic_block->predecessors = construct_predecessors;
|
||||
SortUnique(synthetic_block->predecessors);
|
||||
}
|
||||
|
||||
for (auto pred: predecessors_to_split) {
|
||||
for (auto pred: construct_predecessors) {
|
||||
auto* block = graph.FindBlock(pred);
|
||||
if (block == nullptr) {
|
||||
continue;
|
||||
@@ -1260,7 +1241,7 @@ bool SplitSharedMergeBlock(Graph& graph, uint32_t merge,
|
||||
|
||||
auto* old_merge = graph.FindBlock(merge);
|
||||
if (old_merge != nullptr) {
|
||||
for (auto pred: predecessors_to_split) {
|
||||
for (auto pred: construct_predecessors) {
|
||||
RemoveValue(old_merge->predecessors, pred);
|
||||
}
|
||||
AddUnique(old_merge->predecessors, synthetic_merge);
|
||||
@@ -1313,11 +1294,24 @@ bool SplitOneSelectionMerge(Graph& graph, std::string* error) {
|
||||
AddUnique(loop_headers, loop.header);
|
||||
}
|
||||
|
||||
const auto original_block_count = static_cast<uint32_t>(graph.blocks.size());
|
||||
for (uint32_t block_id = 0; block_id < original_block_count; block_id++) {
|
||||
std::vector<uint32_t> selection_headers;
|
||||
for (const auto& block: graph.blocks) {
|
||||
if (block.terminator.kind == TerminatorKind::ConditionalBranch &&
|
||||
!Contains(loop_headers, block.id)) {
|
||||
selection_headers.push_back(block.id);
|
||||
}
|
||||
}
|
||||
std::sort(selection_headers.begin(), selection_headers.end(), [&](uint32_t lhs, uint32_t rhs) {
|
||||
const auto* lhs_block = graph.FindBlock(lhs);
|
||||
const auto* rhs_block = graph.FindBlock(rhs);
|
||||
const auto lhs_depth = lhs_block != nullptr ? lhs_block->dominators.size() : 0u;
|
||||
const auto rhs_depth = rhs_block != nullptr ? rhs_block->dominators.size() : 0u;
|
||||
return lhs_depth != rhs_depth ? lhs_depth > rhs_depth : lhs < rhs;
|
||||
});
|
||||
|
||||
for (const auto block_id: selection_headers) {
|
||||
const auto* block = graph.FindBlock(block_id);
|
||||
if (block == nullptr || block->terminator.kind != TerminatorKind::ConditionalBranch ||
|
||||
Contains(loop_headers, block_id)) {
|
||||
if (block == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (IsInnermostLoopControlConditional(graph, *block)) {
|
||||
@@ -1352,8 +1346,7 @@ bool SplitOneSelectionMerge(Graph& graph, std::string* error) {
|
||||
|
||||
bool SplitSharedMergeBlocks(Graph& graph, std::string* error) {
|
||||
const auto original_block_count = static_cast<uint32_t>(graph.blocks.size());
|
||||
const auto split_budget =
|
||||
std::max<uint32_t>(16u, std::min<uint32_t>(128u, original_block_count * 4u));
|
||||
const auto split_budget = std::max<uint32_t>(16u, original_block_count * 4u);
|
||||
for (uint32_t splits = 0; splits < split_budget; splits++) {
|
||||
if (!SplitOneLoopMerge(graph) && !SplitOneSelectionMerge(graph, error)) {
|
||||
return !graph.unsupported;
|
||||
|
||||
@@ -6943,6 +6943,80 @@ void TestNewShaderRecompilerCfgDuplicateMergeStructuredSplit() {
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerCfgNestedEarlyExitLoopForwarders() {
|
||||
const uint32_t shader[] = {
|
||||
EncodeSopc(0x06, 0, 0), // preheader condition
|
||||
EncodeSopp(0x05, 10), // preheader early exit -> end
|
||||
EncodeSopc(0x06, 1, 1), // outer condition
|
||||
EncodeSopp(0x05, 8), // outer early exit -> end
|
||||
EncodeSopc(0x06, 2, 2), // inner condition
|
||||
EncodeSopp(0x05, 2), // inner -> loop, else linear arm
|
||||
EncodeSMovB32(3, 129), // linear arm work
|
||||
EncodeSopp(0x02, 4), // linear arm -> end
|
||||
EncodeSopc(0x06, 4, 4), // loop header condition
|
||||
EncodeSopp(0x04, 2), // loop exit -> end
|
||||
EncodeSop2(0x00, 5, 5, 129), // loop work
|
||||
EncodeSopp(0x02, 0xfffcu), // backedge -> loop header
|
||||
0xbf810000u,
|
||||
};
|
||||
|
||||
ShaderRecompiler::Decoder::Program decoded;
|
||||
std::string error;
|
||||
Check(ShaderRecompiler::Decoder::DecodeProgram(std::span{shader}, decoded,
|
||||
&error),
|
||||
error.c_str());
|
||||
ShaderRecompiler::CFG::Graph graph;
|
||||
Check(ShaderRecompiler::CFG::BuildGraph(decoded, graph, &error),
|
||||
error.c_str());
|
||||
const auto original_block_count = graph.blocks.size();
|
||||
const auto original_coverage =
|
||||
CfgInstructionCoverage(graph, decoded.instructions.size());
|
||||
const auto original_empty_blocks =
|
||||
std::ranges::count_if(graph.blocks, [](const auto &block) {
|
||||
return block.inst_begin == block.inst_end;
|
||||
});
|
||||
Check(original_block_count == 8u && graph.natural_loops.size() == 1u,
|
||||
"nested early-exit fixture has the wrong native CFG");
|
||||
const bool structured = ShaderRecompiler::CFG::Structurize(graph, &error);
|
||||
Check(structured, error.c_str());
|
||||
Check(graph.blocks.size() == original_block_count + 3u &&
|
||||
CfgInstructionCoverage(graph, decoded.instructions.size()) ==
|
||||
original_coverage &&
|
||||
std::ranges::count_if(graph.blocks,
|
||||
[](const auto &block) {
|
||||
return block.inst_begin == block.inst_end;
|
||||
}) == original_empty_blocks + 3,
|
||||
"nested early-exit structurization changed semantic coverage");
|
||||
const auto *preheader = graph.FindBlockByPc(0x00u);
|
||||
const auto *outer = graph.FindBlockByPc(0x08u);
|
||||
const auto *inner = graph.FindBlockByPc(0x10u);
|
||||
const auto *loop = graph.FindBlockByPc(0x20u);
|
||||
Check(
|
||||
preheader != nullptr && outer != nullptr && inner != nullptr &&
|
||||
loop != nullptr && loop->terminator.loop_header &&
|
||||
preheader->terminator.merge_block != UINT32_MAX &&
|
||||
outer->terminator.merge_block != UINT32_MAX &&
|
||||
inner->terminator.merge_block != UINT32_MAX &&
|
||||
preheader->terminator.merge_block != outer->terminator.merge_block &&
|
||||
preheader->terminator.merge_block != inner->terminator.merge_block &&
|
||||
outer->terminator.merge_block != inner->terminator.merge_block,
|
||||
"nested early-exit constructs do not have distinct structured merges");
|
||||
|
||||
auto options = MakeCompileOptions(ShaderType::Compute);
|
||||
options.dump_ir = true;
|
||||
ShaderRecompiler::CompileResult result;
|
||||
Check(ShaderRecompiler::TryRecompile(shader, options, result, &error),
|
||||
error.c_str());
|
||||
Check(!result.program.dispatcher_fallback &&
|
||||
Common::ContainsStr(result.ir_dump, "mode=structured"),
|
||||
"nested early-exit loop unexpectedly selected dispatcher fallback");
|
||||
Check(SpirvInstructionOpcodeCount(result.spirv, 247) == 3u &&
|
||||
SpirvInstructionOpcodeCount(result.spirv, 246) == 1u &&
|
||||
SpirvInstructionOpcodeCount(result.spirv, 251) == 0u,
|
||||
"nested early-exit SPIR-V has the wrong structured control flow");
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerCfgExecSccSharedArm() {
|
||||
const uint32_t shader[] = {
|
||||
EncodeSop2(0x15, 126, 4, 126), // s_andn2_b64 exec, s4, exec
|
||||
@@ -9333,6 +9407,7 @@ int main() {
|
||||
TestNewShaderRecompilerCfgConditionalLoopHeaderSelection();
|
||||
TestNewShaderRecompilerCfgMultipleLoopLatches();
|
||||
TestNewShaderRecompilerCfgDuplicateMergeStructuredSplit();
|
||||
TestNewShaderRecompilerCfgNestedEarlyExitLoopForwarders();
|
||||
TestNewShaderRecompilerCfgExecSccSharedArm();
|
||||
TestNewShaderRecompilerCfgOverlappingEarlyExitLadder();
|
||||
TestNewShaderRecompilerCfgExternallyEnteredSelectionDispatcher();
|
||||
|
||||
Reference in New Issue
Block a user