#include "llvm/Transforms/Scalar/StructurizeCFG.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/SCCIterator.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Analysis/InstructionSimplify.h"
#include "llvm/Analysis/LegacyDivergenceAnalysis.h"
#include "llvm/Analysis/RegionInfo.h"
#include "llvm/Analysis/RegionIterator.h"
#include "llvm/Analysis/RegionPass.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/CFG.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/InstrTypes.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Metadata.h"
#include "llvm/IR/PassManager.h"
#include "llvm/IR/PatternMatch.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Use.h"
#include "llvm/IR/Value.h"
#include "llvm/IR/ValueHandle.h"
#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/Utils.h"
#include "llvm/Transforms/Utils/Local.h"
#include "llvm/Transforms/Utils/SSAUpdater.h"
#include <algorithm>
#include <cassert>
#include <utility>
using namespace llvm;
using namespace llvm::PatternMatch;
#define DEBUG_TYPE "structurizecfg"
const char FlowBlockName[] = "Flow";
namespace {
static cl::opt<bool> ForceSkipUniformRegions(
"structurizecfg-skip-uniform-regions",
cl::Hidden,
cl::desc("Force whether the StructurizeCFG pass skips uniform regions"),
cl::init(false));
static cl::opt<bool>
RelaxedUniformRegions("structurizecfg-relaxed-uniform-regions", cl::Hidden,
cl::desc("Allow relaxed uniform region checks"),
cl::init(true));
using BBValuePair = std::pair<BasicBlock *, Value *>;
using RNVector = SmallVector<RegionNode *, 8>;
using BBVector = SmallVector<BasicBlock *, 8>;
using BranchVector = SmallVector<BranchInst *, 8>;
using BBValueVector = SmallVector<BBValuePair, 2>;
using BBSet = SmallPtrSet<BasicBlock *, 8>;
using PhiMap = MapVector<PHINode *, BBValueVector>;
using BB2BBVecMap = MapVector<BasicBlock *, BBVector>;
using BBPhiMap = DenseMap<BasicBlock *, PhiMap>;
using BBPredicates = DenseMap<BasicBlock *, Value *>;
using PredMap = DenseMap<BasicBlock *, BBPredicates>;
using BB2BBMap = DenseMap<BasicBlock *, BasicBlock *>;
struct SubGraphTraits {
using NodeRef = std::pair<RegionNode *, SmallDenseSet<RegionNode *> *>;
using BaseSuccIterator = GraphTraits<RegionNode *>::ChildIteratorType;
class WrappedSuccIterator
: public iterator_adaptor_base<
WrappedSuccIterator, BaseSuccIterator,
typename std::iterator_traits<BaseSuccIterator>::iterator_category,
NodeRef, std::ptrdiff_t, NodeRef *, NodeRef> {
SmallDenseSet<RegionNode *> *Nodes;
public:
WrappedSuccIterator(BaseSuccIterator It, SmallDenseSet<RegionNode *> *Nodes)
: iterator_adaptor_base(It), Nodes(Nodes) {}
NodeRef operator*() const { return {*I, Nodes}; }
};
static bool filterAll(const NodeRef &N) { return true; }
static bool filterSet(const NodeRef &N) { return N.second->count(N.first); }
using ChildIteratorType =
filter_iterator<WrappedSuccIterator, bool (*)(const NodeRef &)>;
static NodeRef getEntryNode(Region *R) {
return {GraphTraits<Region *>::getEntryNode(R), nullptr};
}
static NodeRef getEntryNode(NodeRef N) { return N; }
static iterator_range<ChildIteratorType> children(const NodeRef &N) {
auto *filter = N.second ? &filterSet : &filterAll;
return make_filter_range(
make_range<WrappedSuccIterator>(
{GraphTraits<RegionNode *>::child_begin(N.first), N.second},
{GraphTraits<RegionNode *>::child_end(N.first), N.second}),
filter);
}
static ChildIteratorType child_begin(const NodeRef &N) {
return children(N).begin();
}
static ChildIteratorType child_end(const NodeRef &N) {
return children(N).end();
}
};
class NearestCommonDominator {
DominatorTree *DT;
BasicBlock *Result = nullptr;
bool ResultIsRemembered = false;
void addBlock(BasicBlock *BB, bool Remember) {
if (!Result) {
Result = BB;
ResultIsRemembered = Remember;
return;
}
BasicBlock *NewResult = DT->findNearestCommonDominator(Result, BB);
if (NewResult != Result)
ResultIsRemembered = false;
if (NewResult == BB)
ResultIsRemembered |= Remember;
Result = NewResult;
}
public:
explicit NearestCommonDominator(DominatorTree *DomTree) : DT(DomTree) {}
void addBlock(BasicBlock *BB) {
addBlock(BB, false);
}
void addAndRememberBlock(BasicBlock *BB) {
addBlock(BB, true);
}
BasicBlock *result() { return Result; }
bool resultIsRememberedBlock() { return ResultIsRemembered; }
};
class StructurizeCFG {
Type *Boolean;
ConstantInt *BoolTrue;
ConstantInt *BoolFalse;
UndefValue *BoolUndef;
Function *Func;
Region *ParentRegion;
LegacyDivergenceAnalysis *DA = nullptr;
DominatorTree *DT;
SmallVector<RegionNode *, 8> Order;
BBSet Visited;
SmallVector<WeakVH, 8> AffectedPhis;
BBPhiMap DeletedPhis;
BB2BBVecMap AddedPhis;
PredMap Predicates;
BranchVector Conditions;
BB2BBMap Loops;
PredMap LoopPreds;
BranchVector LoopConds;
RegionNode *PrevNode;
void orderNodes();
void analyzeLoops(RegionNode *N);
Value *buildCondition(BranchInst *Term, unsigned Idx, bool Invert);
void gatherPredicates(RegionNode *N);
void collectInfos();
void insertConditions(bool Loops);
void simplifyConditions();
void delPhiValues(BasicBlock *From, BasicBlock *To);
void addPhiValues(BasicBlock *From, BasicBlock *To);
void setPhiValues();
void simplifyAffectedPhis();
void killTerminator(BasicBlock *BB);
void changeExit(RegionNode *Node, BasicBlock *NewExit,
bool IncludeDominator);
BasicBlock *getNextFlow(BasicBlock *Dominator);
BasicBlock *needPrefix(bool NeedEmpty);
BasicBlock *needPostfix(BasicBlock *Flow, bool ExitUseAllowed);
void setPrevNode(BasicBlock *BB);
bool dominatesPredicates(BasicBlock *BB, RegionNode *Node);
bool isPredictableTrue(RegionNode *Node);
void wireFlow(bool ExitUseAllowed, BasicBlock *LoopEnd);
void handleLoops(bool ExitUseAllowed, BasicBlock *LoopEnd);
void createFlow();
void rebuildSSA();
public:
void init(Region *R);
bool run(Region *R, DominatorTree *DT);
bool makeUniformRegion(Region *R, LegacyDivergenceAnalysis *DA);
};
class StructurizeCFGLegacyPass : public RegionPass {
bool SkipUniformRegions;
public:
static char ID;
explicit StructurizeCFGLegacyPass(bool SkipUniformRegions_ = false)
: RegionPass(ID), SkipUniformRegions(SkipUniformRegions_) {
if (ForceSkipUniformRegions.getNumOccurrences())
SkipUniformRegions = ForceSkipUniformRegions.getValue();
initializeStructurizeCFGLegacyPassPass(*PassRegistry::getPassRegistry());
}
bool runOnRegion(Region *R, RGPassManager &RGM) override {
StructurizeCFG SCFG;
SCFG.init(R);
if (SkipUniformRegions) {
LegacyDivergenceAnalysis *DA = &getAnalysis<LegacyDivergenceAnalysis>();
if (SCFG.makeUniformRegion(R, DA))
return false;
}
DominatorTree *DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
return SCFG.run(R, DT);
}
StringRef getPassName() const override { return "Structurize control flow"; }
void getAnalysisUsage(AnalysisUsage &AU) const override {
if (SkipUniformRegions)
AU.addRequired<LegacyDivergenceAnalysis>();
AU.addRequiredID(LowerSwitchID);
AU.addRequired<DominatorTreeWrapperPass>();
AU.addPreserved<DominatorTreeWrapperPass>();
RegionPass::getAnalysisUsage(AU);
}
};
}
char StructurizeCFGLegacyPass::ID = 0;
INITIALIZE_PASS_BEGIN(StructurizeCFGLegacyPass, "structurizecfg",
"Structurize the CFG", false, false)
INITIALIZE_PASS_DEPENDENCY(LegacyDivergenceAnalysis)
INITIALIZE_PASS_DEPENDENCY(LowerSwitchLegacyPass)
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
INITIALIZE_PASS_DEPENDENCY(RegionInfoPass)
INITIALIZE_PASS_END(StructurizeCFGLegacyPass, "structurizecfg",
"Structurize the CFG", false, false)
void StructurizeCFG::orderNodes() {
Order.resize(std::distance(GraphTraits<Region *>::nodes_begin(ParentRegion),
GraphTraits<Region *>::nodes_end(ParentRegion)));
if (Order.empty())
return;
SmallDenseSet<RegionNode *> Nodes;
auto EntryNode = SubGraphTraits::getEntryNode(ParentRegion);
SmallVector<std::pair<unsigned, unsigned>, 8> WorkList;
unsigned I = 0, E = Order.size();
while (true) {
for (auto SCCI =
scc_iterator<SubGraphTraits::NodeRef, SubGraphTraits>::begin(
EntryNode);
!SCCI.isAtEnd(); ++SCCI) {
auto &SCC = *SCCI;
unsigned Size = SCC.size();
if (Size > 2)
WorkList.emplace_back(I, I + Size);
for (auto &N : SCC) {
assert(I < E && "SCC size mismatch!");
Order[I++] = N.first;
}
}
assert(I == E && "SCC size mismatch!");
if (WorkList.empty())
break;
std::tie(I, E) = WorkList.pop_back_val();
Nodes.clear();
Nodes.insert(Order.begin() + I, Order.begin() + E - 1);
EntryNode.first = Order[E - 1];
EntryNode.second = &Nodes;
}
}
void StructurizeCFG::analyzeLoops(RegionNode *N) {
if (N->isSubRegion()) {
BasicBlock *Exit = N->getNodeAs<Region>()->getExit();
if (Visited.count(Exit))
Loops[Exit] = N->getEntry();
} else {
BasicBlock *BB = N->getNodeAs<BasicBlock>();
BranchInst *Term = cast<BranchInst>(BB->getTerminator());
for (BasicBlock *Succ : Term->successors())
if (Visited.count(Succ))
Loops[Succ] = BB;
}
}
Value *StructurizeCFG::buildCondition(BranchInst *Term, unsigned Idx,
bool Invert) {
Value *Cond = Invert ? BoolFalse : BoolTrue;
if (Term->isConditional()) {
Cond = Term->getCondition();
if (Idx != (unsigned)Invert)
Cond = invertCondition(Cond);
}
return Cond;
}
void StructurizeCFG::gatherPredicates(RegionNode *N) {
RegionInfo *RI = ParentRegion->getRegionInfo();
BasicBlock *BB = N->getEntry();
BBPredicates &Pred = Predicates[BB];
BBPredicates &LPred = LoopPreds[BB];
for (BasicBlock *P : predecessors(BB)) {
if (!ParentRegion->contains(P))
continue;
Region *R = RI->getRegionFor(P);
if (R == ParentRegion) {
BranchInst *Term = cast<BranchInst>(P->getTerminator());
for (unsigned i = 0, e = Term->getNumSuccessors(); i != e; ++i) {
BasicBlock *Succ = Term->getSuccessor(i);
if (Succ != BB)
continue;
if (Visited.count(P)) {
if (Term->isConditional()) {
BasicBlock *Other = Term->getSuccessor(!i);
if (Visited.count(Other) && !Loops.count(Other) &&
!Pred.count(Other) && !Pred.count(P)) {
Pred[Other] = BoolFalse;
Pred[P] = BoolTrue;
continue;
}
}
Pred[P] = buildCondition(Term, i, false);
} else {
LPred[P] = buildCondition(Term, i, true);
}
}
} else {
while (R->getParent() != ParentRegion)
R = R->getParent();
if (*R == *N)
continue;
BasicBlock *Entry = R->getEntry();
if (Visited.count(Entry))
Pred[Entry] = BoolTrue;
else
LPred[Entry] = BoolFalse;
}
}
}
void StructurizeCFG::collectInfos() {
Predicates.clear();
Loops.clear();
LoopPreds.clear();
Visited.clear();
for (RegionNode *RN : reverse(Order)) {
LLVM_DEBUG(dbgs() << "Visiting: "
<< (RN->isSubRegion() ? "SubRegion with entry: " : "")
<< RN->getEntry()->getName() << "\n");
gatherPredicates(RN);
Visited.insert(RN->getEntry());
analyzeLoops(RN);
}
}
void StructurizeCFG::insertConditions(bool Loops) {
BranchVector &Conds = Loops ? LoopConds : Conditions;
Value *Default = Loops ? BoolTrue : BoolFalse;
SSAUpdater PhiInserter;
for (BranchInst *Term : Conds) {
assert(Term->isConditional());
BasicBlock *Parent = Term->getParent();
BasicBlock *SuccTrue = Term->getSuccessor(0);
BasicBlock *SuccFalse = Term->getSuccessor(1);
PhiInserter.Initialize(Boolean, "");
PhiInserter.AddAvailableValue(&Func->getEntryBlock(), Default);
PhiInserter.AddAvailableValue(Loops ? SuccFalse : Parent, Default);
BBPredicates &Preds = Loops ? LoopPreds[SuccFalse] : Predicates[SuccTrue];
NearestCommonDominator Dominator(DT);
Dominator.addBlock(Parent);
Value *ParentValue = nullptr;
for (std::pair<BasicBlock *, Value *> BBAndPred : Preds) {
BasicBlock *BB = BBAndPred.first;
Value *Pred = BBAndPred.second;
if (BB == Parent) {
ParentValue = Pred;
break;
}
PhiInserter.AddAvailableValue(BB, Pred);
Dominator.addAndRememberBlock(BB);
}
if (ParentValue) {
Term->setCondition(ParentValue);
} else {
if (!Dominator.resultIsRememberedBlock())
PhiInserter.AddAvailableValue(Dominator.result(), Default);
Term->setCondition(PhiInserter.GetValueInMiddleOfBlock(Parent));
}
}
}
void StructurizeCFG::simplifyConditions() {
SmallVector<Instruction *> InstToErase;
for (auto &I : concat<PredMap::value_type>(Predicates, LoopPreds)) {
auto &Preds = I.second;
for (auto &J : Preds) {
auto &Cond = J.second;
Instruction *Inverted;
if (match(Cond, m_Not(m_OneUse(m_Instruction(Inverted)))) &&
!Cond->use_empty()) {
if (auto *InvertedCmp = dyn_cast<CmpInst>(Inverted)) {
InvertedCmp->setPredicate(InvertedCmp->getInversePredicate());
Cond->replaceAllUsesWith(InvertedCmp);
InstToErase.push_back(cast<Instruction>(Cond));
}
}
}
}
for (auto *I : InstToErase)
I->eraseFromParent();
}
void StructurizeCFG::delPhiValues(BasicBlock *From, BasicBlock *To) {
PhiMap &Map = DeletedPhis[To];
for (PHINode &Phi : To->phis()) {
bool Recorded = false;
while (Phi.getBasicBlockIndex(From) != -1) {
Value *Deleted = Phi.removeIncomingValue(From, false);
Map[&Phi].push_back(std::make_pair(From, Deleted));
if (!Recorded) {
AffectedPhis.push_back(&Phi);
Recorded = true;
}
}
}
}
void StructurizeCFG::addPhiValues(BasicBlock *From, BasicBlock *To) {
for (PHINode &Phi : To->phis()) {
Value *Undef = UndefValue::get(Phi.getType());
Phi.addIncoming(Undef, From);
}
AddedPhis[To].push_back(From);
}
void StructurizeCFG::setPhiValues() {
SmallVector<PHINode *, 8> InsertedPhis;
SSAUpdater Updater(&InsertedPhis);
for (const auto &AddedPhi : AddedPhis) {
BasicBlock *To = AddedPhi.first;
const BBVector &From = AddedPhi.second;
if (!DeletedPhis.count(To))
continue;
PhiMap &Map = DeletedPhis[To];
for (const auto &PI : Map) {
PHINode *Phi = PI.first;
Value *Undef = UndefValue::get(Phi->getType());
Updater.Initialize(Phi->getType(), "");
Updater.AddAvailableValue(&Func->getEntryBlock(), Undef);
Updater.AddAvailableValue(To, Undef);
NearestCommonDominator Dominator(DT);
Dominator.addBlock(To);
for (const auto &VI : PI.second) {
Updater.AddAvailableValue(VI.first, VI.second);
Dominator.addAndRememberBlock(VI.first);
}
if (!Dominator.resultIsRememberedBlock())
Updater.AddAvailableValue(Dominator.result(), Undef);
for (BasicBlock *FI : From)
Phi->setIncomingValueForBlock(FI, Updater.GetValueAtEndOfBlock(FI));
AffectedPhis.push_back(Phi);
}
DeletedPhis.erase(To);
}
assert(DeletedPhis.empty());
AffectedPhis.append(InsertedPhis.begin(), InsertedPhis.end());
}
void StructurizeCFG::simplifyAffectedPhis() {
bool Changed;
do {
Changed = false;
SimplifyQuery Q(Func->getParent()->getDataLayout());
Q.DT = DT;
for (WeakVH VH : AffectedPhis) {
if (auto Phi = dyn_cast_or_null<PHINode>(VH)) {
if (auto NewValue = simplifyInstruction(Phi, Q)) {
Phi->replaceAllUsesWith(NewValue);
Phi->eraseFromParent();
Changed = true;
}
}
}
} while (Changed);
}
void StructurizeCFG::killTerminator(BasicBlock *BB) {
Instruction *Term = BB->getTerminator();
if (!Term)
return;
for (BasicBlock *Succ : successors(BB))
delPhiValues(BB, Succ);
if (DA)
DA->removeValue(Term);
Term->eraseFromParent();
}
void StructurizeCFG::changeExit(RegionNode *Node, BasicBlock *NewExit,
bool IncludeDominator) {
if (Node->isSubRegion()) {
Region *SubRegion = Node->getNodeAs<Region>();
BasicBlock *OldExit = SubRegion->getExit();
BasicBlock *Dominator = nullptr;
for (BasicBlock *BB : llvm::make_early_inc_range(predecessors(OldExit))) {
if (!SubRegion->contains(BB))
continue;
delPhiValues(BB, OldExit);
BB->getTerminator()->replaceUsesOfWith(OldExit, NewExit);
addPhiValues(BB, NewExit);
if (IncludeDominator) {
if (!Dominator)
Dominator = BB;
else
Dominator = DT->findNearestCommonDominator(Dominator, BB);
}
}
if (Dominator)
DT->changeImmediateDominator(NewExit, Dominator);
SubRegion->replaceExit(NewExit);
} else {
BasicBlock *BB = Node->getNodeAs<BasicBlock>();
killTerminator(BB);
BranchInst::Create(NewExit, BB);
addPhiValues(BB, NewExit);
if (IncludeDominator)
DT->changeImmediateDominator(NewExit, BB);
}
}
BasicBlock *StructurizeCFG::getNextFlow(BasicBlock *Dominator) {
LLVMContext &Context = Func->getContext();
BasicBlock *Insert = Order.empty() ? ParentRegion->getExit() :
Order.back()->getEntry();
BasicBlock *Flow = BasicBlock::Create(Context, FlowBlockName,
Func, Insert);
DT->addNewBlock(Flow, Dominator);
ParentRegion->getRegionInfo()->setRegionFor(Flow, ParentRegion);
return Flow;
}
BasicBlock *StructurizeCFG::needPrefix(bool NeedEmpty) {
BasicBlock *Entry = PrevNode->getEntry();
if (!PrevNode->isSubRegion()) {
killTerminator(Entry);
if (!NeedEmpty || Entry->getFirstInsertionPt() == Entry->end())
return Entry;
}
BasicBlock *Flow = getNextFlow(Entry);
changeExit(PrevNode, Flow, true);
PrevNode = ParentRegion->getBBNode(Flow);
return Flow;
}
BasicBlock *StructurizeCFG::needPostfix(BasicBlock *Flow,
bool ExitUseAllowed) {
if (!Order.empty() || !ExitUseAllowed)
return getNextFlow(Flow);
BasicBlock *Exit = ParentRegion->getExit();
DT->changeImmediateDominator(Exit, Flow);
addPhiValues(Flow, Exit);
return Exit;
}
void StructurizeCFG::setPrevNode(BasicBlock *BB) {
PrevNode = ParentRegion->contains(BB) ? ParentRegion->getBBNode(BB)
: nullptr;
}
bool StructurizeCFG::dominatesPredicates(BasicBlock *BB, RegionNode *Node) {
BBPredicates &Preds = Predicates[Node->getEntry()];
return llvm::all_of(Preds, [&](std::pair<BasicBlock *, Value *> Pred) {
return DT->dominates(BB, Pred.first);
});
}
bool StructurizeCFG::isPredictableTrue(RegionNode *Node) {
BBPredicates &Preds = Predicates[Node->getEntry()];
bool Dominated = false;
if (!PrevNode)
return true;
for (std::pair<BasicBlock*, Value*> Pred : Preds) {
BasicBlock *BB = Pred.first;
Value *V = Pred.second;
if (V != BoolTrue)
return false;
if (!Dominated && DT->dominates(BB, PrevNode->getEntry()))
Dominated = true;
}
return Dominated;
}
void StructurizeCFG::wireFlow(bool ExitUseAllowed,
BasicBlock *LoopEnd) {
RegionNode *Node = Order.pop_back_val();
Visited.insert(Node->getEntry());
if (isPredictableTrue(Node)) {
if (PrevNode) {
changeExit(PrevNode, Node->getEntry(), true);
}
PrevNode = Node;
} else {
BasicBlock *Flow = needPrefix(false);
BasicBlock *Entry = Node->getEntry();
BasicBlock *Next = needPostfix(Flow, ExitUseAllowed);
Conditions.push_back(BranchInst::Create(Entry, Next, BoolUndef, Flow));
addPhiValues(Flow, Entry);
DT->changeImmediateDominator(Entry, Flow);
PrevNode = Node;
while (!Order.empty() && !Visited.count(LoopEnd) &&
dominatesPredicates(Entry, Order.back())) {
handleLoops(false, LoopEnd);
}
changeExit(PrevNode, Next, false);
setPrevNode(Next);
}
}
void StructurizeCFG::handleLoops(bool ExitUseAllowed,
BasicBlock *LoopEnd) {
RegionNode *Node = Order.back();
BasicBlock *LoopStart = Node->getEntry();
if (!Loops.count(LoopStart)) {
wireFlow(ExitUseAllowed, LoopEnd);
return;
}
if (!isPredictableTrue(Node))
LoopStart = needPrefix(true);
LoopEnd = Loops[Node->getEntry()];
wireFlow(false, LoopEnd);
while (!Visited.count(LoopEnd)) {
handleLoops(false, LoopEnd);
}
Function *LoopFunc = LoopStart->getParent();
if (LoopStart == &LoopFunc->getEntryBlock()) {
LoopStart->setName("entry.orig");
BasicBlock *NewEntry =
BasicBlock::Create(LoopStart->getContext(),
"entry",
LoopFunc,
LoopStart);
BranchInst::Create(LoopStart, NewEntry);
DT->setNewRoot(NewEntry);
}
LoopEnd = needPrefix(false);
BasicBlock *Next = needPostfix(LoopEnd, ExitUseAllowed);
LoopConds.push_back(BranchInst::Create(Next, LoopStart,
BoolUndef, LoopEnd));
addPhiValues(LoopEnd, LoopStart);
setPrevNode(Next);
}
void StructurizeCFG::createFlow() {
BasicBlock *Exit = ParentRegion->getExit();
bool EntryDominatesExit = DT->dominates(ParentRegion->getEntry(), Exit);
AffectedPhis.clear();
DeletedPhis.clear();
AddedPhis.clear();
Conditions.clear();
LoopConds.clear();
PrevNode = nullptr;
Visited.clear();
while (!Order.empty()) {
handleLoops(EntryDominatesExit, nullptr);
}
if (PrevNode)
changeExit(PrevNode, Exit, EntryDominatesExit);
else
assert(EntryDominatesExit);
}
void StructurizeCFG::rebuildSSA() {
SSAUpdater Updater;
for (BasicBlock *BB : ParentRegion->blocks())
for (Instruction &I : *BB) {
bool Initialized = false;
for (Use &U : llvm::make_early_inc_range(I.uses())) {
Instruction *User = cast<Instruction>(U.getUser());
if (User->getParent() == BB) {
continue;
} else if (PHINode *UserPN = dyn_cast<PHINode>(User)) {
if (UserPN->getIncomingBlock(U) == BB)
continue;
}
if (DT->dominates(&I, User))
continue;
if (!Initialized) {
Value *Undef = UndefValue::get(I.getType());
Updater.Initialize(I.getType(), "");
Updater.AddAvailableValue(&Func->getEntryBlock(), Undef);
Updater.AddAvailableValue(BB, &I);
Initialized = true;
}
Updater.RewriteUseAfterInsertions(U);
}
}
}
static bool hasOnlyUniformBranches(Region *R, unsigned UniformMDKindID,
const LegacyDivergenceAnalysis &DA) {
bool SubRegionsAreUniform = true;
unsigned ConditionalDirectChildren = 0;
for (auto E : R->elements()) {
if (!E->isSubRegion()) {
auto Br = dyn_cast<BranchInst>(E->getEntry()->getTerminator());
if (!Br || !Br->isConditional())
continue;
if (!DA.isUniform(Br))
return false;
ConditionalDirectChildren++;
LLVM_DEBUG(dbgs() << "BB: " << Br->getParent()->getName()
<< " has uniform terminator\n");
} else {
for (auto BB : E->getNodeAs<Region>()->blocks()) {
auto Br = dyn_cast<BranchInst>(BB->getTerminator());
if (!Br || !Br->isConditional())
continue;
if (!Br->getMetadata(UniformMDKindID)) {
if (!RelaxedUniformRegions)
return false;
SubRegionsAreUniform = false;
break;
}
}
}
}
return SubRegionsAreUniform || (ConditionalDirectChildren <= 1);
}
void StructurizeCFG::init(Region *R) {
LLVMContext &Context = R->getEntry()->getContext();
Boolean = Type::getInt1Ty(Context);
BoolTrue = ConstantInt::getTrue(Context);
BoolFalse = ConstantInt::getFalse(Context);
BoolUndef = UndefValue::get(Boolean);
this->DA = nullptr;
}
bool StructurizeCFG::makeUniformRegion(Region *R,
LegacyDivergenceAnalysis *DA) {
if (R->isTopLevelRegion())
return false;
this->DA = DA;
unsigned UniformMDKindID =
R->getEntry()->getContext().getMDKindID("structurizecfg.uniform");
if (hasOnlyUniformBranches(R, UniformMDKindID, *DA)) {
LLVM_DEBUG(dbgs() << "Skipping region with uniform control flow: " << *R
<< '\n');
MDNode *MD = MDNode::get(R->getEntry()->getParent()->getContext(), {});
for (RegionNode *E : R->elements()) {
if (E->isSubRegion())
continue;
if (Instruction *Term = E->getEntry()->getTerminator())
Term->setMetadata(UniformMDKindID, MD);
}
return true;
}
return false;
}
bool StructurizeCFG::run(Region *R, DominatorTree *DT) {
if (R->isTopLevelRegion())
return false;
this->DT = DT;
Func = R->getEntry()->getParent();
ParentRegion = R;
orderNodes();
collectInfos();
createFlow();
insertConditions(false);
insertConditions(true);
setPhiValues();
simplifyConditions();
simplifyAffectedPhis();
rebuildSSA();
Order.clear();
Visited.clear();
DeletedPhis.clear();
AddedPhis.clear();
Predicates.clear();
Conditions.clear();
Loops.clear();
LoopPreds.clear();
LoopConds.clear();
return true;
}
Pass *llvm::createStructurizeCFGPass(bool SkipUniformRegions) {
return new StructurizeCFGLegacyPass(SkipUniformRegions);
}
static void addRegionIntoQueue(Region &R, std::vector<Region *> &Regions) {
Regions.push_back(&R);
for (const auto &E : R)
addRegionIntoQueue(*E, Regions);
}
PreservedAnalyses StructurizeCFGPass::run(Function &F,
FunctionAnalysisManager &AM) {
bool Changed = false;
DominatorTree *DT = &AM.getResult<DominatorTreeAnalysis>(F);
auto &RI = AM.getResult<RegionInfoAnalysis>(F);
std::vector<Region *> Regions;
addRegionIntoQueue(*RI.getTopLevelRegion(), Regions);
while (!Regions.empty()) {
Region *R = Regions.back();
StructurizeCFG SCFG;
SCFG.init(R);
Changed |= SCFG.run(R, DT);
Regions.pop_back();
}
if (!Changed)
return PreservedAnalyses::all();
PreservedAnalyses PA;
PA.preserve<DominatorTreeAnalysis>();
return PA;
}