From 183bea3b98ed593dbe9169857e6928b1d7d564d4 Mon Sep 17 00:00:00 2001 From: Ivan Chikish Date: Wed, 19 Apr 2023 13:02:10 +0300 Subject: [PATCH] SPU LLVM: upgrade custom DSE pass --- rpcs3/Emu/Cell/SPURecompiler.cpp | 124 ++++++++++++++++++++++++++++--- 1 file changed, 114 insertions(+), 10 deletions(-) diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index dff7e849fb..3d1e4d0c4d 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -3921,6 +3921,8 @@ void spu_recompiler_base::dump(const spu_program& result, std::string& out) #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Scalar.h" #include "llvm/Analysis/PostDominators.h" +#include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/ADT/PostOrderIterator.h" #ifdef _MSC_VER #pragma warning(pop) #else @@ -5453,11 +5455,19 @@ public: } // Work on register stores. - // 1. Remove stores which are post-dominated. + // 1. Remove stores which are overwritten later. // 2. Sink stores to post-dominating blocks. llvm::PostDominatorTree pdt(*m_function); llvm::DominatorTree dt(*m_function); + // Post-order indices + std::unordered_map pois; + { + usz i = 0; + for (auto* bb : llvm::post_order(m_function)) + pois[bb] = i++; + } + std::vector block_q; block_q.reserve(m_blocks.size()); for (auto& [a, b] : m_blocks) @@ -5465,14 +5475,14 @@ public: block_q.emplace_back(&b); } - for (usz bi = 0; bi < block_q.size(); bi++) + for (usz bi = 0; bi < block_q.size();) { + auto bqbi = block_q[bi++]; + // TODO: process all registers up to s_reg_max for (u32 i = 0; i < 128; i++) { - auto& bs = block_q[bi]->store[i]; - - if (bs) + if (auto& bs = bqbi->store[i]) { for (auto& [a, b] : m_blocks) { @@ -5482,17 +5492,111 @@ public: { bs->eraseFromParent(); bs = nullptr; - - pdt.recalculate(*m_function); - dt.recalculate(*m_function); break; } } } - } + if (!bs) + continue; + + // Set of store instructions which overwrite bs + std::vector killers; + + for (auto& [a, b] : m_blocks) + { + const auto si = b.store[i]; + + if (si && si != bs) + { + if (pois[bs->getParent()] > pois[si->getParent()]) + { + killers.emplace_back(si->getParent()); + } + else + { + // Reset: store is not the first in the set + killers.clear(); + break; + } + } + } + + if (killers.empty()) + continue; + + // Find nearest common post-dominator + llvm::BasicBlock* common_pdom = killers[0]; + for (auto* bbb : llvm::drop_begin(killers)) + { + if (!common_pdom) + break; + common_pdom = pdt.findNearestCommonDominator(common_pdom, bbb); + } + + // Shortcut + if (!pdt.dominates(common_pdom, bs->getParent())) + common_pdom = nullptr; + + // Look for possibly-dead store in CFG starting from the exit nodes + llvm::SetVector work_list; + if (std::count(killers.begin(), killers.end(), common_pdom) == 0) + { + if (common_pdom) + { + // Shortcut + work_list.insert(common_pdom); + } + else + { + // Check all exits + for (auto* r : pdt.roots()) + work_list.insert(r); + } + } + + for (usz wi = 0; wi < work_list.size(); wi++) + { + auto* cur = work_list[wi]; + if (std::count(killers.begin(), killers.end(), cur)) + continue; + + if (cur == bs->getParent()) + { + // Reset: store is not dead + killers.clear(); + break; + } + + for (auto* p : llvm::predecessors(cur)) + work_list.insert(p); + } + + // Finally erase the dead store + if (!killers.empty()) + { + bs->eraseFromParent(); + bs = nullptr; + + // Run the loop from the start + bi = 0; + } + } + } + } + + block_q.clear(); + for (auto& [a, b] : m_blocks) + { + block_q.emplace_back(&b); + } + + for (usz bi = 0; bi < block_q.size(); bi++) + { + for (u32 i = 0; i < 128; i++) + { // If store isn't erased, try to sink it - if (bs) + if (auto& bs = block_q[bi]->store[i]) { std::map> sucs;