| //===- UnifyFunctionExitNodes.cpp - Make all functions have a single exit -===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This pass is used to ensure that functions have at most one return and one | |
| // unreachable instruction in them. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/Transforms/Utils/UnifyFunctionExitNodes.h" | |
| #include "llvm/IR/BasicBlock.h" | |
| #include "llvm/IR/Function.h" | |
| #include "llvm/IR/Instructions.h" | |
| #include "llvm/IR/Type.h" | |
| using namespace llvm; | |
| namespace { | |
| bool unifyUnreachableBlocks(Function &F) { | |
| std::vector<BasicBlock *> UnreachableBlocks; | |
| for (BasicBlock &I : F) | |
| if (isa<UnreachableInst>(I.getTerminator())) | |
| UnreachableBlocks.push_back(&I); | |
| if (UnreachableBlocks.size() <= 1) | |
| return false; | |
| BasicBlock *UnreachableBlock = | |
| BasicBlock::Create(F.getContext(), "UnifiedUnreachableBlock", &F); | |
| new UnreachableInst(F.getContext(), UnreachableBlock); | |
| for (BasicBlock *BB : UnreachableBlocks) { | |
| BB->back().eraseFromParent(); // Remove the unreachable inst. | |
| UncondBrInst::Create(UnreachableBlock, BB); | |
| } | |
| return true; | |
| } | |
| bool unifyReturnBlocks(Function &F) { | |
| std::vector<BasicBlock *> ReturningBlocks; | |
| for (BasicBlock &I : F) | |
| if (isa<ReturnInst>(I.getTerminator())) | |
| ReturningBlocks.push_back(&I); | |
| if (ReturningBlocks.size() <= 1) | |
| return false; | |
| // Insert a new basic block into the function, add PHI nodes (if the function | |
| // returns values), and convert all of the return instructions into | |
| // unconditional branches. | |
| BasicBlock *NewRetBlock = BasicBlock::Create(F.getContext(), | |
| "UnifiedReturnBlock", &F); | |
| PHINode *PN = nullptr; | |
| if (F.getReturnType()->isVoidTy()) { | |
| ReturnInst::Create(F.getContext(), nullptr, NewRetBlock); | |
| } else { | |
| // If the function doesn't return void... add a PHI node to the block... | |
| PN = PHINode::Create(F.getReturnType(), ReturningBlocks.size(), | |
| "UnifiedRetVal"); | |
| PN->insertInto(NewRetBlock, NewRetBlock->end()); | |
| ReturnInst::Create(F.getContext(), PN, NewRetBlock); | |
| } | |
| // Loop over all of the blocks, replacing the return instruction with an | |
| // unconditional branch. | |
| for (BasicBlock *BB : ReturningBlocks) { | |
| // Add an incoming element to the PHI node for every return instruction that | |
| // is merging into this new block... | |
| if (PN) | |
| PN->addIncoming(BB->getTerminator()->getOperand(0), BB); | |
| BB->back().eraseFromParent(); // Remove the return insn | |
| UncondBrInst::Create(NewRetBlock, BB); | |
| } | |
| return true; | |
| } | |
| } // namespace | |
| PreservedAnalyses UnifyFunctionExitNodesPass::run(Function &F, | |
| FunctionAnalysisManager &AM) { | |
| bool Changed = false; | |
| Changed |= unifyUnreachableBlocks(F); | |
| Changed |= unifyReturnBlocks(F); | |
| return Changed ? PreservedAnalyses() : PreservedAnalyses::all(); | |
| } |