1b8f8dbc2SMatt Arsenault //===- AMDGPUUnifyDivergentExitNodes.cpp ----------------------------------===//
2b8f8dbc2SMatt Arsenault //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6b8f8dbc2SMatt Arsenault //
7b8f8dbc2SMatt Arsenault //===----------------------------------------------------------------------===//
8b8f8dbc2SMatt Arsenault //
9092a3ce5SJay Foad // This is a variant of the UnifyFunctionExitNodes pass. Rather than ensuring
10b8f8dbc2SMatt Arsenault // there is at most one ret and one unreachable instruction, it ensures there is
11b8f8dbc2SMatt Arsenault // at most one divergent exiting block.
12b8f8dbc2SMatt Arsenault //
13b8f8dbc2SMatt Arsenault // StructurizeCFG can't deal with multi-exit regions formed by branches to
14b8f8dbc2SMatt Arsenault // multiple return nodes. It is not desirable to structurize regions with
15b8f8dbc2SMatt Arsenault // uniform branches, so unifying those to the same return block as divergent
16b8f8dbc2SMatt Arsenault // branches inhibits use of scalar branching. It still can't deal with the case
17b8f8dbc2SMatt Arsenault // where one branch goes to return, and one unreachable. Replace unreachable in
18b8f8dbc2SMatt Arsenault // this case with a return.
19b8f8dbc2SMatt Arsenault //
20b8f8dbc2SMatt Arsenault //===----------------------------------------------------------------------===//
21b8f8dbc2SMatt Arsenault
22b8f8dbc2SMatt Arsenault #include "AMDGPU.h"
23ad3ec089SJay Foad #include "SIDefines.h"
246cadde7fSEugene Zelenko #include "llvm/ADT/ArrayRef.h"
256cadde7fSEugene Zelenko #include "llvm/ADT/SmallPtrSet.h"
266cadde7fSEugene Zelenko #include "llvm/ADT/SmallVector.h"
276cadde7fSEugene Zelenko #include "llvm/ADT/StringRef.h"
284b806473SRoman Lebedev #include "llvm/Analysis/DomTreeUpdater.h"
2935617ed4SNicolai Haehnle #include "llvm/Analysis/LegacyDivergenceAnalysis.h"
30b8f8dbc2SMatt Arsenault #include "llvm/Analysis/PostDominators.h"
31b8f8dbc2SMatt Arsenault #include "llvm/Analysis/TargetTransformInfo.h"
32b8f8dbc2SMatt Arsenault #include "llvm/IR/BasicBlock.h"
33b8f8dbc2SMatt Arsenault #include "llvm/IR/CFG.h"
346cadde7fSEugene Zelenko #include "llvm/IR/Constants.h"
354b806473SRoman Lebedev #include "llvm/IR/Dominators.h"
36b8f8dbc2SMatt Arsenault #include "llvm/IR/Function.h"
374b806473SRoman Lebedev #include "llvm/IR/IRBuilder.h"
386cadde7fSEugene Zelenko #include "llvm/IR/InstrTypes.h"
39b8f8dbc2SMatt Arsenault #include "llvm/IR/Instructions.h"
406cadde7fSEugene Zelenko #include "llvm/IR/Intrinsics.h"
416a87e9b0Sdfukalov #include "llvm/IR/IntrinsicsAMDGPU.h"
42b8f8dbc2SMatt Arsenault #include "llvm/IR/Type.h"
4305da2fe5SReid Kleckner #include "llvm/InitializePasses.h"
446cadde7fSEugene Zelenko #include "llvm/Pass.h"
456cadde7fSEugene Zelenko #include "llvm/Support/Casting.h"
46b8f8dbc2SMatt Arsenault #include "llvm/Transforms/Scalar.h"
47a373d18eSDavid Blaikie #include "llvm/Transforms/Utils.h"
4805da2fe5SReid Kleckner #include "llvm/Transforms/Utils/Local.h"
496cadde7fSEugene Zelenko
50b8f8dbc2SMatt Arsenault using namespace llvm;
51b8f8dbc2SMatt Arsenault
52b8f8dbc2SMatt Arsenault #define DEBUG_TYPE "amdgpu-unify-divergent-exit-nodes"
53b8f8dbc2SMatt Arsenault
54b8f8dbc2SMatt Arsenault namespace {
55b8f8dbc2SMatt Arsenault
56b8f8dbc2SMatt Arsenault class AMDGPUUnifyDivergentExitNodes : public FunctionPass {
57dc2f6bf5SJay Foad private:
58dc2f6bf5SJay Foad const TargetTransformInfo *TTI = nullptr;
59dc2f6bf5SJay Foad
60b8f8dbc2SMatt Arsenault public:
61b8f8dbc2SMatt Arsenault static char ID; // Pass identification, replacement for typeid
626cadde7fSEugene Zelenko
AMDGPUUnifyDivergentExitNodes()63b8f8dbc2SMatt Arsenault AMDGPUUnifyDivergentExitNodes() : FunctionPass(ID) {
64b8f8dbc2SMatt Arsenault initializeAMDGPUUnifyDivergentExitNodesPass(*PassRegistry::getPassRegistry());
65b8f8dbc2SMatt Arsenault }
66b8f8dbc2SMatt Arsenault
67b8f8dbc2SMatt Arsenault // We can preserve non-critical-edgeness when we unify function exit nodes
68b8f8dbc2SMatt Arsenault void getAnalysisUsage(AnalysisUsage &AU) const override;
69dc2f6bf5SJay Foad BasicBlock *unifyReturnBlockSet(Function &F, DomTreeUpdater &DTU,
70dc2f6bf5SJay Foad ArrayRef<BasicBlock *> ReturningBlocks,
71d9b9fdd9SRuiling Song StringRef Name);
72b8f8dbc2SMatt Arsenault bool runOnFunction(Function &F) override;
73b8f8dbc2SMatt Arsenault };
74b8f8dbc2SMatt Arsenault
756cadde7fSEugene Zelenko } // end anonymous namespace
76b8f8dbc2SMatt Arsenault
77b8f8dbc2SMatt Arsenault char AMDGPUUnifyDivergentExitNodes::ID = 0;
786cadde7fSEugene Zelenko
796cadde7fSEugene Zelenko char &llvm::AMDGPUUnifyDivergentExitNodesID = AMDGPUUnifyDivergentExitNodes::ID;
806cadde7fSEugene Zelenko
81b8f8dbc2SMatt Arsenault INITIALIZE_PASS_BEGIN(AMDGPUUnifyDivergentExitNodes, DEBUG_TYPE,
82b8f8dbc2SMatt Arsenault "Unify divergent function exit nodes", false, false)
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)834b806473SRoman Lebedev INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
84b8f8dbc2SMatt Arsenault INITIALIZE_PASS_DEPENDENCY(PostDominatorTreeWrapperPass)
8535617ed4SNicolai Haehnle INITIALIZE_PASS_DEPENDENCY(LegacyDivergenceAnalysis)
86b8f8dbc2SMatt Arsenault INITIALIZE_PASS_END(AMDGPUUnifyDivergentExitNodes, DEBUG_TYPE,
87b8f8dbc2SMatt Arsenault "Unify divergent function exit nodes", false, false)
88b8f8dbc2SMatt Arsenault
89b8f8dbc2SMatt Arsenault void AMDGPUUnifyDivergentExitNodes::getAnalysisUsage(AnalysisUsage &AU) const{
904b806473SRoman Lebedev if (RequireAndPreserveDomTree)
914b806473SRoman Lebedev AU.addRequired<DominatorTreeWrapperPass>();
924b806473SRoman Lebedev
93b8f8dbc2SMatt Arsenault AU.addRequired<PostDominatorTreeWrapperPass>();
94b8f8dbc2SMatt Arsenault
9535617ed4SNicolai Haehnle AU.addRequired<LegacyDivergenceAnalysis>();
96b8f8dbc2SMatt Arsenault
974b806473SRoman Lebedev if (RequireAndPreserveDomTree) {
984b806473SRoman Lebedev AU.addPreserved<DominatorTreeWrapperPass>();
997c8b8063SRoman Lebedev // FIXME: preserve PostDominatorTreeWrapperPass
1004b806473SRoman Lebedev }
1014b806473SRoman Lebedev
102b8f8dbc2SMatt Arsenault // No divergent values are changed, only blocks and branch edges.
10335617ed4SNicolai Haehnle AU.addPreserved<LegacyDivergenceAnalysis>();
104b8f8dbc2SMatt Arsenault
105b8f8dbc2SMatt Arsenault // We preserve the non-critical-edgeness property
106b8f8dbc2SMatt Arsenault AU.addPreservedID(BreakCriticalEdgesID);
107b8f8dbc2SMatt Arsenault
108b8f8dbc2SMatt Arsenault // This is a cluster of orthogonal Transforms
109b8f8dbc2SMatt Arsenault AU.addPreservedID(LowerSwitchID);
110b8f8dbc2SMatt Arsenault FunctionPass::getAnalysisUsage(AU);
111b8f8dbc2SMatt Arsenault
112b8f8dbc2SMatt Arsenault AU.addRequired<TargetTransformInfoWrapperPass>();
113b8f8dbc2SMatt Arsenault }
114b8f8dbc2SMatt Arsenault
115b8f8dbc2SMatt Arsenault /// \returns true if \p BB is reachable through only uniform branches.
116b8f8dbc2SMatt Arsenault /// XXX - Is there a more efficient way to find this?
isUniformlyReached(const LegacyDivergenceAnalysis & DA,BasicBlock & BB)11735617ed4SNicolai Haehnle static bool isUniformlyReached(const LegacyDivergenceAnalysis &DA,
118b8f8dbc2SMatt Arsenault BasicBlock &BB) {
1197925aa09SKazu Hirata SmallVector<BasicBlock *, 8> Stack(predecessors(&BB));
120b8f8dbc2SMatt Arsenault SmallPtrSet<BasicBlock *, 8> Visited;
121b8f8dbc2SMatt Arsenault
122b8f8dbc2SMatt Arsenault while (!Stack.empty()) {
123b8f8dbc2SMatt Arsenault BasicBlock *Top = Stack.pop_back_val();
124b8f8dbc2SMatt Arsenault if (!DA.isUniform(Top->getTerminator()))
125b8f8dbc2SMatt Arsenault return false;
126b8f8dbc2SMatt Arsenault
127b8f8dbc2SMatt Arsenault for (BasicBlock *Pred : predecessors(Top)) {
128b8f8dbc2SMatt Arsenault if (Visited.insert(Pred).second)
129b8f8dbc2SMatt Arsenault Stack.push_back(Pred);
130b8f8dbc2SMatt Arsenault }
131b8f8dbc2SMatt Arsenault }
132b8f8dbc2SMatt Arsenault
133b8f8dbc2SMatt Arsenault return true;
134b8f8dbc2SMatt Arsenault }
135b8f8dbc2SMatt Arsenault
unifyReturnBlockSet(Function & F,DomTreeUpdater & DTU,ArrayRef<BasicBlock * > ReturningBlocks,StringRef Name)136dc2f6bf5SJay Foad BasicBlock *AMDGPUUnifyDivergentExitNodes::unifyReturnBlockSet(
137dc2f6bf5SJay Foad Function &F, DomTreeUpdater &DTU, ArrayRef<BasicBlock *> ReturningBlocks,
138d9b9fdd9SRuiling Song StringRef Name) {
139b8f8dbc2SMatt Arsenault // Otherwise, we need to insert a new basic block into the function, add a PHI
140b8f8dbc2SMatt Arsenault // nodes (if the function returns values), and convert all of the return
141b8f8dbc2SMatt Arsenault // instructions into unconditional branches.
142b8f8dbc2SMatt Arsenault BasicBlock *NewRetBlock = BasicBlock::Create(F.getContext(), Name, &F);
14387d98c14SConnor Abbott IRBuilder<> B(NewRetBlock);
14487d98c14SConnor Abbott
145b8f8dbc2SMatt Arsenault PHINode *PN = nullptr;
146b8f8dbc2SMatt Arsenault if (F.getReturnType()->isVoidTy()) {
14787d98c14SConnor Abbott B.CreateRetVoid();
148b8f8dbc2SMatt Arsenault } else {
149b8f8dbc2SMatt Arsenault // If the function doesn't return void... add a PHI node to the block...
15087d98c14SConnor Abbott PN = B.CreatePHI(F.getReturnType(), ReturningBlocks.size(),
151b8f8dbc2SMatt Arsenault "UnifiedRetVal");
15287d98c14SConnor Abbott B.CreateRet(PN);
153b8f8dbc2SMatt Arsenault }
154b8f8dbc2SMatt Arsenault
155b8f8dbc2SMatt Arsenault // Loop over all of the blocks, replacing the return instruction with an
156b8f8dbc2SMatt Arsenault // unconditional branch.
1574b806473SRoman Lebedev std::vector<DominatorTree::UpdateType> Updates;
1584b806473SRoman Lebedev Updates.reserve(ReturningBlocks.size());
159b8f8dbc2SMatt Arsenault for (BasicBlock *BB : ReturningBlocks) {
160b8f8dbc2SMatt Arsenault // Add an incoming element to the PHI node for every return instruction that
161b8f8dbc2SMatt Arsenault // is merging into this new block...
162b8f8dbc2SMatt Arsenault if (PN)
163b8f8dbc2SMatt Arsenault PN->addIncoming(BB->getTerminator()->getOperand(0), BB);
164b8f8dbc2SMatt Arsenault
165d049da37SChangpeng Fang // Remove and delete the return inst.
166d049da37SChangpeng Fang BB->getTerminator()->eraseFromParent();
167b8f8dbc2SMatt Arsenault BranchInst::Create(NewRetBlock, BB);
1684b806473SRoman Lebedev Updates.push_back({DominatorTree::Insert, BB, NewRetBlock});
169b8f8dbc2SMatt Arsenault }
170b8f8dbc2SMatt Arsenault
1714b806473SRoman Lebedev if (RequireAndPreserveDomTree)
1724b806473SRoman Lebedev DTU.applyUpdates(Updates);
1734b806473SRoman Lebedev Updates.clear();
1744b806473SRoman Lebedev
175b8f8dbc2SMatt Arsenault for (BasicBlock *BB : ReturningBlocks) {
176b8f8dbc2SMatt Arsenault // Cleanup possible branch to unconditional branch to the return.
177dc2f6bf5SJay Foad simplifyCFG(BB, *TTI, RequireAndPreserveDomTree ? &DTU : nullptr,
17849dac4acSRoman Lebedev SimplifyCFGOptions().bonusInstThreshold(2));
179b8f8dbc2SMatt Arsenault }
180b8f8dbc2SMatt Arsenault
181b8f8dbc2SMatt Arsenault return NewRetBlock;
182b8f8dbc2SMatt Arsenault }
183b8f8dbc2SMatt Arsenault
runOnFunction(Function & F)184b8f8dbc2SMatt Arsenault bool AMDGPUUnifyDivergentExitNodes::runOnFunction(Function &F) {
1854b806473SRoman Lebedev DominatorTree *DT = nullptr;
1864b806473SRoman Lebedev if (RequireAndPreserveDomTree)
1874b806473SRoman Lebedev DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
1884b806473SRoman Lebedev
189b8f8dbc2SMatt Arsenault auto &PDT = getAnalysis<PostDominatorTreeWrapperPass>().getPostDomTree();
190ce06d507SConnor Abbott
191d9b9fdd9SRuiling Song // If there's only one exit, we don't need to do anything.
192d9b9fdd9SRuiling Song if (PDT.root_size() <= 1)
19313ab22abSConnor Abbott return false;
194b8f8dbc2SMatt Arsenault
19535617ed4SNicolai Haehnle LegacyDivergenceAnalysis &DA = getAnalysis<LegacyDivergenceAnalysis>();
196dc2f6bf5SJay Foad TTI = &getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F);
197b8f8dbc2SMatt Arsenault
198b8f8dbc2SMatt Arsenault // Loop over all of the blocks in a function, tracking all of the blocks that
199b8f8dbc2SMatt Arsenault // return.
200b8f8dbc2SMatt Arsenault SmallVector<BasicBlock *, 4> ReturningBlocks;
201b8f8dbc2SMatt Arsenault SmallVector<BasicBlock *, 4> UnreachableBlocks;
202b8f8dbc2SMatt Arsenault
203391bcf88SChangpeng Fang // Dummy return block for infinite loop.
204391bcf88SChangpeng Fang BasicBlock *DummyReturnBB = nullptr;
205391bcf88SChangpeng Fang
20672e4da45SJay Foad bool Changed = false;
2074b806473SRoman Lebedev std::vector<DominatorTree::UpdateType> Updates;
2084b806473SRoman Lebedev
209dfcc68c5SNicolai Hähnle for (BasicBlock *BB : PDT.roots()) {
210b8f8dbc2SMatt Arsenault if (isa<ReturnInst>(BB->getTerminator())) {
211b8f8dbc2SMatt Arsenault if (!isUniformlyReached(DA, *BB))
212b8f8dbc2SMatt Arsenault ReturningBlocks.push_back(BB);
213b8f8dbc2SMatt Arsenault } else if (isa<UnreachableInst>(BB->getTerminator())) {
214b8f8dbc2SMatt Arsenault if (!isUniformlyReached(DA, *BB))
215b8f8dbc2SMatt Arsenault UnreachableBlocks.push_back(BB);
216391bcf88SChangpeng Fang } else if (BranchInst *BI = dyn_cast<BranchInst>(BB->getTerminator())) {
217391bcf88SChangpeng Fang
218391bcf88SChangpeng Fang ConstantInt *BoolTrue = ConstantInt::getTrue(F.getContext());
219391bcf88SChangpeng Fang if (DummyReturnBB == nullptr) {
220391bcf88SChangpeng Fang DummyReturnBB = BasicBlock::Create(F.getContext(),
221391bcf88SChangpeng Fang "DummyReturnBlock", &F);
222391bcf88SChangpeng Fang Type *RetTy = F.getReturnType();
223391bcf88SChangpeng Fang Value *RetVal = RetTy->isVoidTy() ? nullptr : UndefValue::get(RetTy);
224391bcf88SChangpeng Fang ReturnInst::Create(F.getContext(), RetVal, DummyReturnBB);
225391bcf88SChangpeng Fang ReturningBlocks.push_back(DummyReturnBB);
226391bcf88SChangpeng Fang }
227391bcf88SChangpeng Fang
228391bcf88SChangpeng Fang if (BI->isUnconditional()) {
229391bcf88SChangpeng Fang BasicBlock *LoopHeaderBB = BI->getSuccessor(0);
230391bcf88SChangpeng Fang BI->eraseFromParent(); // Delete the unconditional branch.
231391bcf88SChangpeng Fang // Add a new conditional branch with a dummy edge to the return block.
232391bcf88SChangpeng Fang BranchInst::Create(LoopHeaderBB, DummyReturnBB, BoolTrue, BB);
2334b806473SRoman Lebedev Updates.push_back({DominatorTree::Insert, BB, DummyReturnBB});
234391bcf88SChangpeng Fang } else { // Conditional branch.
235*5648f717SKazu Hirata SmallVector<BasicBlock *, 2> Successors(successors(BB));
2364b806473SRoman Lebedev
237391bcf88SChangpeng Fang // Create a new transition block to hold the conditional branch.
238688afeb8SDiego Novillo BasicBlock *TransitionBB = BB->splitBasicBlock(BI, "TransitionBlock");
239391bcf88SChangpeng Fang
2404b806473SRoman Lebedev Updates.reserve(Updates.size() + 2 * Successors.size() + 2);
2414b806473SRoman Lebedev
2424b806473SRoman Lebedev // 'Successors' become successors of TransitionBB instead of BB,
2434b806473SRoman Lebedev // and TransitionBB becomes a single successor of BB.
2444b806473SRoman Lebedev Updates.push_back({DominatorTree::Insert, BB, TransitionBB});
2454b806473SRoman Lebedev for (BasicBlock *Successor : Successors) {
2464b806473SRoman Lebedev Updates.push_back({DominatorTree::Insert, TransitionBB, Successor});
2474b806473SRoman Lebedev Updates.push_back({DominatorTree::Delete, BB, Successor});
2484b806473SRoman Lebedev }
2494b806473SRoman Lebedev
250688afeb8SDiego Novillo // Create a branch that will always branch to the transition block and
251688afeb8SDiego Novillo // references DummyReturnBB.
252688afeb8SDiego Novillo BB->getTerminator()->eraseFromParent();
253391bcf88SChangpeng Fang BranchInst::Create(TransitionBB, DummyReturnBB, BoolTrue, BB);
2544b806473SRoman Lebedev Updates.push_back({DominatorTree::Insert, BB, DummyReturnBB});
255391bcf88SChangpeng Fang }
25672e4da45SJay Foad Changed = true;
257b8f8dbc2SMatt Arsenault }
258b8f8dbc2SMatt Arsenault }
259b8f8dbc2SMatt Arsenault
260b8f8dbc2SMatt Arsenault if (!UnreachableBlocks.empty()) {
261b8f8dbc2SMatt Arsenault BasicBlock *UnreachableBlock = nullptr;
262b8f8dbc2SMatt Arsenault
263b8f8dbc2SMatt Arsenault if (UnreachableBlocks.size() == 1) {
264b8f8dbc2SMatt Arsenault UnreachableBlock = UnreachableBlocks.front();
265b8f8dbc2SMatt Arsenault } else {
266b8f8dbc2SMatt Arsenault UnreachableBlock = BasicBlock::Create(F.getContext(),
267b8f8dbc2SMatt Arsenault "UnifiedUnreachableBlock", &F);
268b8f8dbc2SMatt Arsenault new UnreachableInst(F.getContext(), UnreachableBlock);
269b8f8dbc2SMatt Arsenault
2704b806473SRoman Lebedev Updates.reserve(Updates.size() + UnreachableBlocks.size());
271b8f8dbc2SMatt Arsenault for (BasicBlock *BB : UnreachableBlocks) {
272d049da37SChangpeng Fang // Remove and delete the unreachable inst.
273d049da37SChangpeng Fang BB->getTerminator()->eraseFromParent();
274b8f8dbc2SMatt Arsenault BranchInst::Create(UnreachableBlock, BB);
2754b806473SRoman Lebedev Updates.push_back({DominatorTree::Insert, BB, UnreachableBlock});
276b8f8dbc2SMatt Arsenault }
27772e4da45SJay Foad Changed = true;
278b8f8dbc2SMatt Arsenault }
279b8f8dbc2SMatt Arsenault
280b8f8dbc2SMatt Arsenault if (!ReturningBlocks.empty()) {
281b8f8dbc2SMatt Arsenault // Don't create a new unreachable inst if we have a return. The
282b8f8dbc2SMatt Arsenault // structurizer/annotator can't handle the multiple exits
283b8f8dbc2SMatt Arsenault
284b8f8dbc2SMatt Arsenault Type *RetTy = F.getReturnType();
285b8f8dbc2SMatt Arsenault Value *RetVal = RetTy->isVoidTy() ? nullptr : UndefValue::get(RetTy);
286d049da37SChangpeng Fang // Remove and delete the unreachable inst.
287d049da37SChangpeng Fang UnreachableBlock->getTerminator()->eraseFromParent();
288b8f8dbc2SMatt Arsenault
289b8f8dbc2SMatt Arsenault Function *UnreachableIntrin =
290b8f8dbc2SMatt Arsenault Intrinsic::getDeclaration(F.getParent(), Intrinsic::amdgcn_unreachable);
291b8f8dbc2SMatt Arsenault
292b8f8dbc2SMatt Arsenault // Insert a call to an intrinsic tracking that this is an unreachable
293b8f8dbc2SMatt Arsenault // point, in case we want to kill the active lanes or something later.
294b8f8dbc2SMatt Arsenault CallInst::Create(UnreachableIntrin, {}, "", UnreachableBlock);
295b8f8dbc2SMatt Arsenault
296b8f8dbc2SMatt Arsenault // Don't create a scalar trap. We would only want to trap if this code was
297b8f8dbc2SMatt Arsenault // really reached, but a scalar trap would happen even if no lanes
298b8f8dbc2SMatt Arsenault // actually reached here.
299b8f8dbc2SMatt Arsenault ReturnInst::Create(F.getContext(), RetVal, UnreachableBlock);
300b8f8dbc2SMatt Arsenault ReturningBlocks.push_back(UnreachableBlock);
30172e4da45SJay Foad Changed = true;
302b8f8dbc2SMatt Arsenault }
303b8f8dbc2SMatt Arsenault }
304b8f8dbc2SMatt Arsenault
3057c8b8063SRoman Lebedev // FIXME: add PDT here once simplifycfg is ready.
3067c8b8063SRoman Lebedev DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Eager);
3074b806473SRoman Lebedev if (RequireAndPreserveDomTree)
3084b806473SRoman Lebedev DTU.applyUpdates(Updates);
3094b806473SRoman Lebedev Updates.clear();
3104b806473SRoman Lebedev
311b8f8dbc2SMatt Arsenault // Now handle return blocks.
312b8f8dbc2SMatt Arsenault if (ReturningBlocks.empty())
31372e4da45SJay Foad return Changed; // No blocks return
314b8f8dbc2SMatt Arsenault
315d9b9fdd9SRuiling Song if (ReturningBlocks.size() == 1)
31672e4da45SJay Foad return Changed; // Already has a single return block
317b8f8dbc2SMatt Arsenault
318d9b9fdd9SRuiling Song unifyReturnBlockSet(F, DTU, ReturningBlocks, "UnifiedReturnBlock");
319b8f8dbc2SMatt Arsenault return true;
320b8f8dbc2SMatt Arsenault }
321