diff --git a/core/libs/prx/libSceAgcDriver/tests/Structurizer.cpp b/core/libs/prx/libSceAgcDriver/tests/Structurizer.cpp index c9771f1fe..d6dfb7861 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Structurizer.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Structurizer.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -103,6 +104,108 @@ void requireExactPostDominators(const ControlFlowGraph& graph, const char* name) } } +std::vector> originalPostDominators(const ControlFlowGraph& graph) { + std::vector all; + for (std::uint32_t id = 0; id < graph.blocks.size(); ++id) all.push_back(id); + std::vector> result; + for (const auto& block : graph.blocks) result.push_back(block.successors.empty() ? std::vector{block.id} : all); + bool changed = true; + while (changed) { + changed = false; + for (const auto& block : graph.blocks) { + std::vector next; + if (block.successors.empty()) { + next = {block.id}; + } else { + next = result[block.successors.front()]; + for (std::size_t index = 1; index < block.successors.size(); ++index) { + std::vector intersection; + const auto& successor = result[block.successors[index]]; + std::set_intersection(next.begin(), next.end(), successor.begin(), successor.end(), std::back_inserter(intersection)); + next = std::move(intersection); + } + if (std::find(next.begin(), next.end(), block.id) == next.end()) next.push_back(block.id); + std::sort(next.begin(), next.end()); + next.erase(std::unique(next.begin(), next.end()), next.end()); + } + if (next != result[block.id]) { + result[block.id] = std::move(next); + changed = true; + } + } + } + return result; +} + +std::vector> originalDominators(const ControlFlowGraph& graph) { + std::vector all; + for (std::uint32_t id = 0; id < graph.blocks.size(); ++id) all.push_back(id); + std::vector> result; + for (const auto& block : graph.blocks) result.push_back(block.id == graph.entryBlock ? std::vector{block.id} : all); + bool changed = true; + while (changed) { + changed = false; + for (const auto& block : graph.blocks) { + if (block.id == graph.entryBlock) continue; + std::vector next; + if (block.predecessors.empty()) { + next = {block.id}; + } else { + next = result[block.predecessors.front()]; + for (std::size_t index = 1; index < block.predecessors.size(); ++index) { + std::vector intersection; + const auto& predecessor = result[block.predecessors[index]]; + std::set_intersection(next.begin(), next.end(), predecessor.begin(), predecessor.end(), std::back_inserter(intersection)); + next = std::move(intersection); + } + if (std::find(next.begin(), next.end(), block.id) == next.end()) next.push_back(block.id); + std::sort(next.begin(), next.end()); + next.erase(std::unique(next.begin(), next.end()), next.end()); + } + if (next != result[block.id]) { + result[block.id] = std::move(next); + changed = true; + } + } + } + return result; +} + +void requireOriginalDominanceSets(ControlFlowGraph graph, const char* name) { + for (auto& block : graph.blocks) { + block.dominators.clear(); + block.postDominators.clear(); + } + const auto expected = originalPostDominators(graph); + const auto expectedDominators = originalDominators(graph); + const auto original = graph; + const std::string stopAfterAnalyses = "dominance differential fixture: stop before CFG rewrites"; + graph.unsupported = true; + graph.unsupportedReason = stopAfterAnalyses; + bool stopped = false; + try { + Structurizer{}.Structurize(graph); + } catch (const std::runtime_error& error) { + if (error.what() != stopAfterAnalyses) throw; + stopped = true; + } + if (!stopped || graph.blocks.size() != original.blocks.size() || graph.entryBlock != original.entryBlock) { + throw std::runtime_error(std::string(name) + ": differential fixture did not stop before CFG rewrites"); + } + for (std::size_t id = 0; id < graph.blocks.size(); ++id) { + const auto& block = graph.blocks[id]; + if (block.id != original.blocks[id].id || block.successors != original.blocks[id].successors || block.predecessors != original.blocks[id].predecessors) { + throw std::runtime_error(std::string(name) + ": differential fixture changed the CFG"); + } + if (block.postDominators != expected[id]) { + throw std::runtime_error(std::string(name) + ": block " + std::to_string(id) + " differs from the original post-dominator fixed point"); + } + if (block.dominators != expectedDominators[id]) { + throw std::runtime_error(std::string(name) + ": block " + std::to_string(id) + " differs from the original dominator fixed point"); + } + } +} + } int main() { @@ -165,6 +268,14 @@ int main() { } Structurizer{}.Structurize(sharedDiscard); requireStructuredBranches(sharedDiscard, "early discards that share one ending block"); + requireOriginalDominanceSets(makeGraph(diamonds), "raw diamond chain"); + requireOriginalDominanceSets(makeGraph({{1}, {2, 3}, {1}, {}}), "a loop with an exit"); + requireOriginalDominanceSets(makeGraph({{1}, {2}, {1}}), "a non-exiting SCC"); + requireOriginalDominanceSets(makeGraph({{1, 3}, {2}, {1}, {}}), "an exit-or-spin branch"); + requireOriginalDominanceSets(makeGraph({{1}, {}, {3}, {}, {5}, {4}}), "unreachable exit and non-exiting islands"); + auto reordered = makeGraph({{1}, {}, {2}, {0, 4}, {1}}); + reordered.entryBlock = 3; + requireOriginalDominanceSets(std::move(reordered), "a nonzero entry with permuted block IDs"); } catch (const std::exception& error) { std::fprintf(stderr, "%s\n", error.what()); return 1; diff --git a/core/shader/recompiler/ControlFlow/src/Structurizer.cpp b/core/shader/recompiler/ControlFlow/src/Structurizer.cpp index 2401a6290..0b59967fe 100644 --- a/core/shader/recompiler/ControlFlow/src/Structurizer.cpp +++ b/core/shader/recompiler/ControlFlow/src/Structurizer.cpp @@ -20,6 +20,13 @@ std::vector allBlockIds(std::uint32_t count) { return ids; } +void addSortedUnique(std::vector& values, std::uint32_t value) { + const auto position = std::lower_bound(values.begin(), values.end(), value); + if (position == values.end() || *position != value) { + values.insert(position, value); + } +} + bool replaceValue(std::vector& values, std::uint32_t oldValue, std::uint32_t newValue) { bool changed = false; for (auto& value : values) { @@ -1330,8 +1337,7 @@ void Structurizer::computeDominatorTree(ControlFlowGraph& graph) const { for (std::size_t i = 1; i < block.predecessors.size(); ++i) { next = intersectSorted(next, graph.blocks[block.predecessors[i]].dominators); } - addUnique(next, block.id); - sortUnique(next); + addSortedUnique(next, block.id); } if (next != block.dominators) { @@ -1412,8 +1418,7 @@ void Structurizer::computePostDominators(ControlFlowGraph& graph) const { for (std::size_t i = 1; i < block.successors.size(); ++i) { next = intersectSorted(next, graph.blocks[block.successors[i]].postDominators); } - addUnique(next, block.id); - sortUnique(next); + addSortedUnique(next, block.id); } if (next != block.postDominators) {