1*0b57cec5SDimitry Andric //===- UnifyFunctionExitNodes.cpp - Make all functions have a single exit -===//
2*0b57cec5SDimitry Andric //
3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0b57cec5SDimitry Andric //
7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8*0b57cec5SDimitry Andric //
9e8d8bef9SDimitry Andric // This pass is used to ensure that functions have at most one return and one
10e8d8bef9SDimitry Andric // unreachable instruction in them.
11*0b57cec5SDimitry Andric //
12*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
13*0b57cec5SDimitry Andric
14*0b57cec5SDimitry Andric #include "llvm/Transforms/Utils/UnifyFunctionExitNodes.h"
15*0b57cec5SDimitry Andric #include "llvm/IR/BasicBlock.h"
16*0b57cec5SDimitry Andric #include "llvm/IR/Function.h"
17*0b57cec5SDimitry Andric #include "llvm/IR/Instructions.h"
18*0b57cec5SDimitry Andric #include "llvm/IR/Type.h"
19*0b57cec5SDimitry Andric #include "llvm/Transforms/Utils.h"
20*0b57cec5SDimitry Andric using namespace llvm;
21*0b57cec5SDimitry Andric
22e8d8bef9SDimitry Andric namespace {
23e8d8bef9SDimitry Andric
unifyUnreachableBlocks(Function & F)24e8d8bef9SDimitry Andric bool unifyUnreachableBlocks(Function &F) {
25*0b57cec5SDimitry Andric std::vector<BasicBlock *> UnreachableBlocks;
26e8d8bef9SDimitry Andric
27*0b57cec5SDimitry Andric for (BasicBlock &I : F)
28e8d8bef9SDimitry Andric if (isa<UnreachableInst>(I.getTerminator()))
29*0b57cec5SDimitry Andric UnreachableBlocks.push_back(&I);
30*0b57cec5SDimitry 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);
36*0b57cec5SDimitry Andric new UnreachableInst(F.getContext(), UnreachableBlock);
37*0b57cec5SDimitry Andric
38*0b57cec5SDimitry Andric for (BasicBlock *BB : UnreachableBlocks) {
39bdd1243dSDimitry Andric BB->back().eraseFromParent(); // Remove the unreachable inst.
40*0b57cec5SDimitry Andric BranchInst::Create(UnreachableBlock, BB);
41*0b57cec5SDimitry Andric }
42e8d8bef9SDimitry Andric
43e8d8bef9SDimitry Andric return true;
44*0b57cec5SDimitry Andric }
45*0b57cec5SDimitry 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)
54*0b57cec5SDimitry Andric return false;
55*0b57cec5SDimitry 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.
59*0b57cec5SDimitry Andric BasicBlock *NewRetBlock = BasicBlock::Create(F.getContext(),
60*0b57cec5SDimitry Andric "UnifiedReturnBlock", &F);
61*0b57cec5SDimitry Andric
62*0b57cec5SDimitry Andric PHINode *PN = nullptr;
63*0b57cec5SDimitry Andric if (F.getReturnType()->isVoidTy()) {
64*0b57cec5SDimitry Andric ReturnInst::Create(F.getContext(), nullptr, NewRetBlock);
65*0b57cec5SDimitry Andric } else {
66*0b57cec5SDimitry Andric // If the function doesn't return void... add a PHI node to the block...
67*0b57cec5SDimitry Andric PN = PHINode::Create(F.getReturnType(), ReturningBlocks.size(),
68*0b57cec5SDimitry Andric "UnifiedRetVal");
69bdd1243dSDimitry Andric PN->insertInto(NewRetBlock, NewRetBlock->end());
70*0b57cec5SDimitry Andric ReturnInst::Create(F.getContext(), PN, NewRetBlock);
71*0b57cec5SDimitry Andric }
72*0b57cec5SDimitry Andric
73*0b57cec5SDimitry Andric // Loop over all of the blocks, replacing the return instruction with an
74*0b57cec5SDimitry Andric // unconditional branch.
75*0b57cec5SDimitry Andric for (BasicBlock *BB : ReturningBlocks) {
76*0b57cec5SDimitry Andric // Add an incoming element to the PHI node for every return instruction that
77*0b57cec5SDimitry Andric // is merging into this new block...
78*0b57cec5SDimitry Andric if (PN)
79*0b57cec5SDimitry Andric PN->addIncoming(BB->getTerminator()->getOperand(0), BB);
80*0b57cec5SDimitry Andric
81bdd1243dSDimitry Andric BB->back().eraseFromParent(); // Remove the return insn
82*0b57cec5SDimitry Andric BranchInst::Create(NewRetBlock, BB);
83*0b57cec5SDimitry Andric }
84e8d8bef9SDimitry Andric
85*0b57cec5SDimitry Andric return true;
86*0b57cec5SDimitry 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