SPU LLVM: upgrade custom DSE pass

This commit is contained in:
Ivan Chikish 2023-04-19 13:02:10 +03:00 committed by Ivan
parent 6981cbb985
commit 183bea3b98
1 changed files with 114 additions and 10 deletions

View File

@ -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/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Scalar.h" #include "llvm/Transforms/Scalar.h"
#include "llvm/Analysis/PostDominators.h" #include "llvm/Analysis/PostDominators.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/ADT/PostOrderIterator.h"
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning(pop) #pragma warning(pop)
#else #else
@ -5453,11 +5455,19 @@ public:
} }
// Work on register stores. // 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. // 2. Sink stores to post-dominating blocks.
llvm::PostDominatorTree pdt(*m_function); llvm::PostDominatorTree pdt(*m_function);
llvm::DominatorTree dt(*m_function); llvm::DominatorTree dt(*m_function);
// Post-order indices
std::unordered_map<llvm::BasicBlock*, usz> pois;
{
usz i = 0;
for (auto* bb : llvm::post_order(m_function))
pois[bb] = i++;
}
std::vector<block_info*> block_q; std::vector<block_info*> block_q;
block_q.reserve(m_blocks.size()); block_q.reserve(m_blocks.size());
for (auto& [a, b] : m_blocks) for (auto& [a, b] : m_blocks)
@ -5465,14 +5475,14 @@ public:
block_q.emplace_back(&b); 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 // TODO: process all registers up to s_reg_max
for (u32 i = 0; i < 128; i++) for (u32 i = 0; i < 128; i++)
{ {
auto& bs = block_q[bi]->store[i]; if (auto& bs = bqbi->store[i])
if (bs)
{ {
for (auto& [a, b] : m_blocks) for (auto& [a, b] : m_blocks)
{ {
@ -5482,17 +5492,111 @@ public:
{ {
bs->eraseFromParent(); bs->eraseFromParent();
bs = nullptr; bs = nullptr;
pdt.recalculate(*m_function);
dt.recalculate(*m_function);
break; break;
} }
} }
} }
}
if (!bs)
continue;
// Set of store instructions which overwrite bs
std::vector<llvm::BasicBlock*> 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<llvm::BasicBlock*> 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 store isn't erased, try to sink it
if (bs) if (auto& bs = block_q[bi]->store[i])
{ {
std::map<u32, block_info*, std::greater<>> sucs; std::map<u32, block_info*, std::greater<>> sucs;