#include "llvm/Transforms/Utils/FixIrreducible.h"
#include "llvm/ADT/SCCIterator.h"
#include "llvm/Analysis/DomTreeUpdater.h"
#include "llvm/Analysis/LoopIterator.h"
#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
#include "llvm/Transforms/Utils.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#define DEBUG_TYPE "fix-irreducible"
using namespace llvm;
namespace {
struct FixIrreducible : public FunctionPass {
static char ID;
FixIrreducible() : FunctionPass(ID) {
initializeFixIrreduciblePass(*PassRegistry::getPassRegistry());
}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.addRequiredID(LowerSwitchID);
AU.addRequired<DominatorTreeWrapperPass>();
AU.addRequired<LoopInfoWrapperPass>();
AU.addPreservedID(LowerSwitchID);
AU.addPreserved<DominatorTreeWrapperPass>();
AU.addPreserved<LoopInfoWrapperPass>();
}
bool runOnFunction(Function &F) override;
};
}
char FixIrreducible::ID = 0;
FunctionPass *llvm::createFixIrreduciblePass() { return new FixIrreducible(); }
INITIALIZE_PASS_BEGIN(FixIrreducible, "fix-irreducible",
"Convert irreducible control-flow into natural loops",
false , false )
INITIALIZE_PASS_DEPENDENCY(LowerSwitchLegacyPass)
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass)
INITIALIZE_PASS_END(FixIrreducible, "fix-irreducible",
"Convert irreducible control-flow into natural loops",
false , false )
static void reconnectChildLoops(LoopInfo &LI, Loop *ParentLoop, Loop *NewLoop,
SetVector<BasicBlock *> &Blocks,
SetVector<BasicBlock *> &Headers) {
auto &CandidateLoops = ParentLoop ? ParentLoop->getSubLoopsVector()
: LI.getTopLevelLoopsVector();
auto FirstChild = std::partition(
CandidateLoops.begin(), CandidateLoops.end(), [&](Loop *L) {
return L == NewLoop || !Blocks.contains(L->getHeader());
});
SmallVector<Loop *, 8> ChildLoops(FirstChild, CandidateLoops.end());
CandidateLoops.erase(FirstChild, CandidateLoops.end());
for (Loop *Child : ChildLoops) {
LLVM_DEBUG(dbgs() << "child loop: " << Child->getHeader()->getName()
<< "\n");
if (Headers.count(Child->getHeader())) {
for (auto BB : Child->blocks()) {
if (LI.getLoopFor(BB) != Child)
continue;
LI.changeLoopFor(BB, NewLoop);
LLVM_DEBUG(dbgs() << "moved block from child: " << BB->getName()
<< "\n");
}
std::vector<Loop *> GrandChildLoops;
std::swap(GrandChildLoops, Child->getSubLoopsVector());
for (auto GrandChildLoop : GrandChildLoops) {
GrandChildLoop->setParentLoop(nullptr);
NewLoop->addChildLoop(GrandChildLoop);
}
LI.destroy(Child);
LLVM_DEBUG(dbgs() << "subsumed child loop (common header)\n");
continue;
}
Child->setParentLoop(nullptr);
NewLoop->addChildLoop(Child);
LLVM_DEBUG(dbgs() << "added child loop to new loop\n");
}
}
static void createNaturalLoopInternal(LoopInfo &LI, DominatorTree &DT,
Loop *ParentLoop,
SetVector<BasicBlock *> &Blocks,
SetVector<BasicBlock *> &Headers) {
#ifndef NDEBUG
for (auto H : Headers) {
assert(Blocks.count(H));
}
#endif
SetVector<BasicBlock *> Predecessors;
for (auto H : Headers) {
for (auto P : predecessors(H)) {
Predecessors.insert(P);
}
}
LLVM_DEBUG(
dbgs() << "Found predecessors:";
for (auto P : Predecessors) {
dbgs() << " " << P->getName();
}
dbgs() << "\n");
SmallVector<BasicBlock *, 8> GuardBlocks;
DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Eager);
CreateControlFlowHub(&DTU, GuardBlocks, Predecessors, Headers, "irr");
#if defined(EXPENSIVE_CHECKS)
assert(DT.verify(DominatorTree::VerificationLevel::Full));
#else
assert(DT.verify(DominatorTree::VerificationLevel::Fast));
#endif
auto NewLoop = LI.AllocateLoop();
if (ParentLoop) {
ParentLoop->addChildLoop(NewLoop);
} else {
LI.addTopLevelLoop(NewLoop);
}
for (auto G : GuardBlocks) {
LLVM_DEBUG(dbgs() << "added guard block: " << G->getName() << "\n");
NewLoop->addBasicBlockToLoop(G, LI);
}
for (auto BB : Blocks) {
NewLoop->addBlockEntry(BB);
if (LI.getLoopFor(BB) == ParentLoop) {
LLVM_DEBUG(dbgs() << "moved block from parent: " << BB->getName()
<< "\n");
LI.changeLoopFor(BB, NewLoop);
} else {
LLVM_DEBUG(dbgs() << "added block from child: " << BB->getName() << "\n");
}
}
LLVM_DEBUG(dbgs() << "header for new loop: "
<< NewLoop->getHeader()->getName() << "\n");
reconnectChildLoops(LI, ParentLoop, NewLoop, Blocks, Headers);
NewLoop->verifyLoop();
if (ParentLoop) {
ParentLoop->verifyLoop();
}
#if defined(EXPENSIVE_CHECKS)
LI.verify(DT);
#endif }
namespace llvm {
template <> struct GraphTraits<Loop> : LoopBodyTraits {};
}
static BasicBlock *unwrapBlock(BasicBlock *B) { return B; }
static BasicBlock *unwrapBlock(LoopBodyTraits::NodeRef &N) { return N.second; }
static void createNaturalLoop(LoopInfo &LI, DominatorTree &DT, Function *F,
SetVector<BasicBlock *> &Blocks,
SetVector<BasicBlock *> &Headers) {
createNaturalLoopInternal(LI, DT, nullptr, Blocks, Headers);
}
static void createNaturalLoop(LoopInfo &LI, DominatorTree &DT, Loop &L,
SetVector<BasicBlock *> &Blocks,
SetVector<BasicBlock *> &Headers) {
createNaturalLoopInternal(LI, DT, &L, Blocks, Headers);
}
template <class Graph>
static bool makeReducible(LoopInfo &LI, DominatorTree &DT, Graph &&G) {
bool Changed = false;
for (auto Scc = scc_begin(G); !Scc.isAtEnd(); ++Scc) {
if (Scc->size() < 2)
continue;
SetVector<BasicBlock *> Blocks;
LLVM_DEBUG(dbgs() << "Found SCC:");
for (auto N : *Scc) {
auto BB = unwrapBlock(N);
LLVM_DEBUG(dbgs() << " " << BB->getName());
Blocks.insert(BB);
}
LLVM_DEBUG(dbgs() << "\n");
SetVector<BasicBlock *> Headers;
LLVM_DEBUG(dbgs() << "Found headers:");
for (auto BB : reverse(Blocks)) {
for (const auto P : predecessors(BB)) {
if (!DT.isReachableFromEntry(P))
continue;
if (!Blocks.count(P)) {
LLVM_DEBUG(dbgs() << " " << BB->getName());
Headers.insert(BB);
break;
}
}
}
LLVM_DEBUG(dbgs() << "\n");
if (Headers.size() == 1) {
assert(LI.isLoopHeader(Headers.front()));
LLVM_DEBUG(dbgs() << "Natural loop with a single header: skipped\n");
continue;
}
createNaturalLoop(LI, DT, G, Blocks, Headers);
Changed = true;
}
return Changed;
}
static bool FixIrreducibleImpl(Function &F, LoopInfo &LI, DominatorTree &DT) {
LLVM_DEBUG(dbgs() << "===== Fix irreducible control-flow in function: "
<< F.getName() << "\n");
bool Changed = false;
SmallVector<Loop *, 8> WorkList;
LLVM_DEBUG(dbgs() << "visiting top-level\n");
Changed |= makeReducible(LI, DT, &F);
append_range(WorkList, LI);
while (!WorkList.empty()) {
auto L = WorkList.pop_back_val();
LLVM_DEBUG(dbgs() << "visiting loop with header "
<< L->getHeader()->getName() << "\n");
Changed |= makeReducible(LI, DT, *L);
WorkList.append(L->begin(), L->end());
}
return Changed;
}
bool FixIrreducible::runOnFunction(Function &F) {
auto &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
auto &DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree();
return FixIrreducibleImpl(F, LI, DT);
}
PreservedAnalyses FixIrreduciblePass::run(Function &F,
FunctionAnalysisManager &AM) {
auto &LI = AM.getResult<LoopAnalysis>(F);
auto &DT = AM.getResult<DominatorTreeAnalysis>(F);
if (!FixIrreducibleImpl(F, LI, DT))
return PreservedAnalyses::all();
PreservedAnalyses PA;
PA.preserve<LoopAnalysis>();
PA.preserve<DominatorTreeAnalysis>();
return PA;
}