Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SPU LLVM: improve DSE #13702

Merged
merged 1 commit into from
Apr 20, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 112 additions & 8 deletions rpcs3/Emu/Cell/SPURecompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -5453,26 +5455,34 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
}

// 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<llvm::BasicBlock*, usz> pois;
{
usz i = 0;
for (auto* bb : llvm::post_order(m_function))
pois[bb] = i++;
}

std::vector<block_info*> block_q;
block_q.reserve(m_blocks.size());
for (auto& [a, b] : m_blocks)
{
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)
{
Expand All @@ -5482,17 +5492,111 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
{
bs->eraseFromParent();
bs = nullptr;
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];

pdt.recalculate(*m_function);
dt.recalculate(*m_function);
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 (bs)
if (auto& bs = block_q[bi]->store[i])
{
std::map<u32, block_info*, std::greater<>> sucs;

Expand Down