xref: /freebsd-src/contrib/llvm-project/llvm/lib/Transforms/Utils/UnifyFunctionExitNodes.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
10b57cec5SDimitry Andric //===- UnifyFunctionExitNodes.cpp - Make all functions have a single exit -===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
9e8d8bef9SDimitry Andric // This pass is used to ensure that functions have at most one return and one
10e8d8bef9SDimitry Andric // unreachable instruction in them.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #include "llvm/Transforms/Utils/UnifyFunctionExitNodes.h"
150b57cec5SDimitry Andric #include "llvm/IR/BasicBlock.h"
160b57cec5SDimitry Andric #include "llvm/IR/Function.h"
170b57cec5SDimitry Andric #include "llvm/IR/Instructions.h"
180b57cec5SDimitry Andric #include "llvm/IR/Type.h"
190b57cec5SDimitry Andric #include "llvm/Transforms/Utils.h"
200b57cec5SDimitry Andric using namespace llvm;
210b57cec5SDimitry Andric 
22e8d8bef9SDimitry Andric namespace {
23e8d8bef9SDimitry Andric 
unifyUnreachableBlocks(Function & F)24e8d8bef9SDimitry Andric bool unifyUnreachableBlocks(Function &F) {
250b57cec5SDimitry Andric   std::vector<BasicBlock *> UnreachableBlocks;
26e8d8bef9SDimitry Andric 
270b57cec5SDimitry Andric   for (BasicBlock &I : F)
28e8d8bef9SDimitry Andric     if (isa<UnreachableInst>(I.getTerminator()))
290b57cec5SDimitry Andric       UnreachableBlocks.push_back(&I);
300b57cec5SDimitry Andric 
31e8d8bef9SDimitry Andric   if (UnreachableBlocks.size() <= 1)
32e8d8bef9SDimitry Andric     return false;
33e8d8bef9SDimitry Andric 
34e8d8bef9SDimitry Andric   BasicBlock *UnreachableBlock =
35e8d8bef9SDimitry Andric       BasicBlock::Create(F.getContext(), "UnifiedUnreachableBlock", &F);
360b57cec5SDimitry Andric   new UnreachableInst(F.getContext(), UnreachableBlock);
370b57cec5SDimitry Andric 
380b57cec5SDimitry Andric   for (BasicBlock *BB : UnreachableBlocks) {
39*bdd1243dSDimitry Andric     BB->back().eraseFromParent(); // Remove the unreachable inst.
400b57cec5SDimitry Andric     BranchInst::Create(UnreachableBlock, BB);
410b57cec5SDimitry Andric   }
42e8d8bef9SDimitry Andric 
43e8d8bef9SDimitry Andric   return true;
440b57cec5SDimitry Andric }
450b57cec5SDimitry Andric 
unifyReturnBlocks(Function & F)46e8d8bef9SDimitry Andric bool unifyReturnBlocks(Function &F) {
47e8d8bef9SDimitry Andric   std::vector<BasicBlock *> ReturningBlocks;
48e8d8bef9SDimitry Andric 
49e8d8bef9SDimitry Andric   for (BasicBlock &I : F)
50e8d8bef9SDimitry Andric     if (isa<ReturnInst>(I.getTerminator()))
51e8d8bef9SDimitry Andric       ReturningBlocks.push_back(&I);
52e8d8bef9SDimitry Andric 
53e8d8bef9SDimitry Andric   if (ReturningBlocks.size() <= 1)
540b57cec5SDimitry Andric     return false;
550b57cec5SDimitry Andric 
56e8d8bef9SDimitry Andric   // Insert a new basic block into the function, add PHI nodes (if the function
57e8d8bef9SDimitry Andric   // returns values), and convert all of the return instructions into
58e8d8bef9SDimitry Andric   // unconditional branches.
590b57cec5SDimitry Andric   BasicBlock *NewRetBlock = BasicBlock::Create(F.getContext(),
600b57cec5SDimitry Andric                                                "UnifiedReturnBlock", &F);
610b57cec5SDimitry Andric 
620b57cec5SDimitry Andric   PHINode *PN = nullptr;
630b57cec5SDimitry Andric   if (F.getReturnType()->isVoidTy()) {
640b57cec5SDimitry Andric     ReturnInst::Create(F.getContext(), nullptr, NewRetBlock);
650b57cec5SDimitry Andric   } else {
660b57cec5SDimitry Andric     // If the function doesn't return void... add a PHI node to the block...
670b57cec5SDimitry Andric     PN = PHINode::Create(F.getReturnType(), ReturningBlocks.size(),
680b57cec5SDimitry Andric                          "UnifiedRetVal");
69*bdd1243dSDimitry Andric     PN->insertInto(NewRetBlock, NewRetBlock->end());
700b57cec5SDimitry Andric     ReturnInst::Create(F.getContext(), PN, NewRetBlock);
710b57cec5SDimitry Andric   }
720b57cec5SDimitry Andric 
730b57cec5SDimitry Andric   // Loop over all of the blocks, replacing the return instruction with an
740b57cec5SDimitry Andric   // unconditional branch.
750b57cec5SDimitry Andric   for (BasicBlock *BB : ReturningBlocks) {
760b57cec5SDimitry Andric     // Add an incoming element to the PHI node for every return instruction that
770b57cec5SDimitry Andric     // is merging into this new block...
780b57cec5SDimitry Andric     if (PN)
790b57cec5SDimitry Andric       PN->addIncoming(BB->getTerminator()->getOperand(0), BB);
800b57cec5SDimitry Andric 
81*bdd1243dSDimitry Andric     BB->back().eraseFromParent(); // Remove the return insn
820b57cec5SDimitry Andric     BranchInst::Create(NewRetBlock, BB);
830b57cec5SDimitry Andric   }
84e8d8bef9SDimitry Andric 
850b57cec5SDimitry Andric   return true;
860b57cec5SDimitry Andric }
87e8d8bef9SDimitry Andric } // namespace
88e8d8bef9SDimitry Andric 
run(Function & F,FunctionAnalysisManager & AM)89e8d8bef9SDimitry Andric PreservedAnalyses UnifyFunctionExitNodesPass::run(Function &F,
90e8d8bef9SDimitry Andric                                                   FunctionAnalysisManager &AM) {
91e8d8bef9SDimitry Andric   bool Changed = false;
92e8d8bef9SDimitry Andric   Changed |= unifyUnreachableBlocks(F);
93e8d8bef9SDimitry Andric   Changed |= unifyReturnBlocks(F);
94e8d8bef9SDimitry Andric   return Changed ? PreservedAnalyses() : PreservedAnalyses::all();
95e8d8bef9SDimitry Andric }
96