mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-26 04:32:44 +01:00
a150110be1
SimplifyCFG should not merge empty return blocks and leave a CallBr behind with a duplicated destination since the verifier will then trigger an assert. This patch checks for this case and avoids the transformation. CodeGenPrepare has a similar check which also has a FIXME comment about why this is needed. It seems perhaps better if these two passes would eventually instead update the CallBr instruction instead of just checking and avoiding. This fixes https://bugs.llvm.org/show_bug.cgi?id=45062. Review: Craig Topper Differential Revision: https://reviews.llvm.org/D75620
312 lines
12 KiB
C++
312 lines
12 KiB
C++
//===- SimplifyCFGPass.cpp - CFG Simplification Pass ----------------------===//
|
|
//
|
|
// 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 file implements dead code elimination and basic block merging, along
|
|
// with a collection of other peephole control flow optimizations. For example:
|
|
//
|
|
// * Removes basic blocks with no predecessors.
|
|
// * Merges a basic block into its predecessor if there is only one and the
|
|
// predecessor only has one successor.
|
|
// * Eliminates PHI nodes for basic blocks with a single predecessor.
|
|
// * Eliminates a basic block that only contains an unconditional branch.
|
|
// * Changes invoke instructions to nounwind functions to be calls.
|
|
// * Change things like "if (x) if (y)" into "if (x&y)".
|
|
// * etc..
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/ADT/SmallPtrSet.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/Statistic.h"
|
|
#include "llvm/Analysis/AssumptionCache.h"
|
|
#include "llvm/Analysis/CFG.h"
|
|
#include "llvm/Analysis/GlobalsModRef.h"
|
|
#include "llvm/Analysis/TargetTransformInfo.h"
|
|
#include "llvm/IR/Attributes.h"
|
|
#include "llvm/IR/CFG.h"
|
|
#include "llvm/IR/Constants.h"
|
|
#include "llvm/IR/DataLayout.h"
|
|
#include "llvm/IR/Instructions.h"
|
|
#include "llvm/IR/IntrinsicInst.h"
|
|
#include "llvm/IR/Module.h"
|
|
#include "llvm/InitializePasses.h"
|
|
#include "llvm/Pass.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Transforms/Scalar.h"
|
|
#include "llvm/Transforms/Scalar/SimplifyCFG.h"
|
|
#include "llvm/Transforms/Utils/Local.h"
|
|
#include <utility>
|
|
using namespace llvm;
|
|
|
|
#define DEBUG_TYPE "simplifycfg"
|
|
|
|
static cl::opt<unsigned> UserBonusInstThreshold(
|
|
"bonus-inst-threshold", cl::Hidden, cl::init(1),
|
|
cl::desc("Control the number of bonus instructions (default = 1)"));
|
|
|
|
static cl::opt<bool> UserKeepLoops(
|
|
"keep-loops", cl::Hidden, cl::init(true),
|
|
cl::desc("Preserve canonical loop structure (default = true)"));
|
|
|
|
static cl::opt<bool> UserSwitchToLookup(
|
|
"switch-to-lookup", cl::Hidden, cl::init(false),
|
|
cl::desc("Convert switches to lookup tables (default = false)"));
|
|
|
|
static cl::opt<bool> UserForwardSwitchCond(
|
|
"forward-switch-cond", cl::Hidden, cl::init(false),
|
|
cl::desc("Forward switch condition to phi ops (default = false)"));
|
|
|
|
static cl::opt<bool> UserSinkCommonInsts(
|
|
"sink-common-insts", cl::Hidden, cl::init(false),
|
|
cl::desc("Sink common instructions (default = false)"));
|
|
|
|
|
|
STATISTIC(NumSimpl, "Number of blocks simplified");
|
|
|
|
/// If we have more than one empty (other than phi node) return blocks,
|
|
/// merge them together to promote recursive block merging.
|
|
static bool mergeEmptyReturnBlocks(Function &F) {
|
|
bool Changed = false;
|
|
|
|
BasicBlock *RetBlock = nullptr;
|
|
|
|
// Scan all the blocks in the function, looking for empty return blocks.
|
|
for (Function::iterator BBI = F.begin(), E = F.end(); BBI != E; ) {
|
|
BasicBlock &BB = *BBI++;
|
|
|
|
// Only look at return blocks.
|
|
ReturnInst *Ret = dyn_cast<ReturnInst>(BB.getTerminator());
|
|
if (!Ret) continue;
|
|
|
|
// Only look at the block if it is empty or the only other thing in it is a
|
|
// single PHI node that is the operand to the return.
|
|
if (Ret != &BB.front()) {
|
|
// Check for something else in the block.
|
|
BasicBlock::iterator I(Ret);
|
|
--I;
|
|
// Skip over debug info.
|
|
while (isa<DbgInfoIntrinsic>(I) && I != BB.begin())
|
|
--I;
|
|
if (!isa<DbgInfoIntrinsic>(I) &&
|
|
(!isa<PHINode>(I) || I != BB.begin() || Ret->getNumOperands() == 0 ||
|
|
Ret->getOperand(0) != &*I))
|
|
continue;
|
|
}
|
|
|
|
// If this is the first returning block, remember it and keep going.
|
|
if (!RetBlock) {
|
|
RetBlock = &BB;
|
|
continue;
|
|
}
|
|
|
|
// Skip merging if this would result in a CallBr instruction with a
|
|
// duplicate destination. FIXME: See note in CodeGenPrepare.cpp.
|
|
bool SkipCallBr = false;
|
|
for (pred_iterator PI = pred_begin(&BB), E = pred_end(&BB);
|
|
PI != E && !SkipCallBr; ++PI) {
|
|
if (auto *CBI = dyn_cast<CallBrInst>((*PI)->getTerminator()))
|
|
for (unsigned i = 0, e = CBI->getNumSuccessors(); i != e; ++i)
|
|
if (RetBlock == CBI->getSuccessor(i)) {
|
|
SkipCallBr = true;
|
|
break;
|
|
}
|
|
}
|
|
if (SkipCallBr)
|
|
continue;
|
|
|
|
// Otherwise, we found a duplicate return block. Merge the two.
|
|
Changed = true;
|
|
|
|
// Case when there is no input to the return or when the returned values
|
|
// agree is trivial. Note that they can't agree if there are phis in the
|
|
// blocks.
|
|
if (Ret->getNumOperands() == 0 ||
|
|
Ret->getOperand(0) ==
|
|
cast<ReturnInst>(RetBlock->getTerminator())->getOperand(0)) {
|
|
BB.replaceAllUsesWith(RetBlock);
|
|
BB.eraseFromParent();
|
|
continue;
|
|
}
|
|
|
|
// If the canonical return block has no PHI node, create one now.
|
|
PHINode *RetBlockPHI = dyn_cast<PHINode>(RetBlock->begin());
|
|
if (!RetBlockPHI) {
|
|
Value *InVal = cast<ReturnInst>(RetBlock->getTerminator())->getOperand(0);
|
|
pred_iterator PB = pred_begin(RetBlock), PE = pred_end(RetBlock);
|
|
RetBlockPHI = PHINode::Create(Ret->getOperand(0)->getType(),
|
|
std::distance(PB, PE), "merge",
|
|
&RetBlock->front());
|
|
|
|
for (pred_iterator PI = PB; PI != PE; ++PI)
|
|
RetBlockPHI->addIncoming(InVal, *PI);
|
|
RetBlock->getTerminator()->setOperand(0, RetBlockPHI);
|
|
}
|
|
|
|
// Turn BB into a block that just unconditionally branches to the return
|
|
// block. This handles the case when the two return blocks have a common
|
|
// predecessor but that return different things.
|
|
RetBlockPHI->addIncoming(Ret->getOperand(0), &BB);
|
|
BB.getTerminator()->eraseFromParent();
|
|
BranchInst::Create(RetBlock, &BB);
|
|
}
|
|
|
|
return Changed;
|
|
}
|
|
|
|
/// Call SimplifyCFG on all the blocks in the function,
|
|
/// iterating until no more changes are made.
|
|
static bool iterativelySimplifyCFG(Function &F, const TargetTransformInfo &TTI,
|
|
const SimplifyCFGOptions &Options) {
|
|
bool Changed = false;
|
|
bool LocalChange = true;
|
|
|
|
SmallVector<std::pair<const BasicBlock *, const BasicBlock *>, 32> Edges;
|
|
FindFunctionBackedges(F, Edges);
|
|
SmallPtrSet<BasicBlock *, 16> LoopHeaders;
|
|
for (unsigned i = 0, e = Edges.size(); i != e; ++i)
|
|
LoopHeaders.insert(const_cast<BasicBlock *>(Edges[i].second));
|
|
|
|
while (LocalChange) {
|
|
LocalChange = false;
|
|
|
|
// Loop over all of the basic blocks and remove them if they are unneeded.
|
|
for (Function::iterator BBIt = F.begin(); BBIt != F.end(); ) {
|
|
if (simplifyCFG(&*BBIt++, TTI, Options, &LoopHeaders)) {
|
|
LocalChange = true;
|
|
++NumSimpl;
|
|
}
|
|
}
|
|
Changed |= LocalChange;
|
|
}
|
|
return Changed;
|
|
}
|
|
|
|
static bool simplifyFunctionCFG(Function &F, const TargetTransformInfo &TTI,
|
|
const SimplifyCFGOptions &Options) {
|
|
bool EverChanged = removeUnreachableBlocks(F);
|
|
EverChanged |= mergeEmptyReturnBlocks(F);
|
|
EverChanged |= iterativelySimplifyCFG(F, TTI, Options);
|
|
|
|
// If neither pass changed anything, we're done.
|
|
if (!EverChanged) return false;
|
|
|
|
// iterativelySimplifyCFG can (rarely) make some loops dead. If this happens,
|
|
// removeUnreachableBlocks is needed to nuke them, which means we should
|
|
// iterate between the two optimizations. We structure the code like this to
|
|
// avoid rerunning iterativelySimplifyCFG if the second pass of
|
|
// removeUnreachableBlocks doesn't do anything.
|
|
if (!removeUnreachableBlocks(F))
|
|
return true;
|
|
|
|
do {
|
|
EverChanged = iterativelySimplifyCFG(F, TTI, Options);
|
|
EverChanged |= removeUnreachableBlocks(F);
|
|
} while (EverChanged);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Command-line settings override compile-time settings.
|
|
SimplifyCFGPass::SimplifyCFGPass(const SimplifyCFGOptions &Opts) {
|
|
Options.BonusInstThreshold = UserBonusInstThreshold.getNumOccurrences()
|
|
? UserBonusInstThreshold
|
|
: Opts.BonusInstThreshold;
|
|
Options.ForwardSwitchCondToPhi = UserForwardSwitchCond.getNumOccurrences()
|
|
? UserForwardSwitchCond
|
|
: Opts.ForwardSwitchCondToPhi;
|
|
Options.ConvertSwitchToLookupTable = UserSwitchToLookup.getNumOccurrences()
|
|
? UserSwitchToLookup
|
|
: Opts.ConvertSwitchToLookupTable;
|
|
Options.NeedCanonicalLoop = UserKeepLoops.getNumOccurrences()
|
|
? UserKeepLoops
|
|
: Opts.NeedCanonicalLoop;
|
|
Options.SinkCommonInsts = UserSinkCommonInsts.getNumOccurrences()
|
|
? UserSinkCommonInsts
|
|
: Opts.SinkCommonInsts;
|
|
}
|
|
|
|
PreservedAnalyses SimplifyCFGPass::run(Function &F,
|
|
FunctionAnalysisManager &AM) {
|
|
auto &TTI = AM.getResult<TargetIRAnalysis>(F);
|
|
Options.AC = &AM.getResult<AssumptionAnalysis>(F);
|
|
if (!simplifyFunctionCFG(F, TTI, Options))
|
|
return PreservedAnalyses::all();
|
|
PreservedAnalyses PA;
|
|
PA.preserve<GlobalsAA>();
|
|
return PA;
|
|
}
|
|
|
|
namespace {
|
|
struct CFGSimplifyPass : public FunctionPass {
|
|
static char ID;
|
|
SimplifyCFGOptions Options;
|
|
std::function<bool(const Function &)> PredicateFtor;
|
|
|
|
CFGSimplifyPass(unsigned Threshold = 1, bool ForwardSwitchCond = false,
|
|
bool ConvertSwitch = false, bool KeepLoops = true,
|
|
bool SinkCommon = false,
|
|
std::function<bool(const Function &)> Ftor = nullptr)
|
|
: FunctionPass(ID), PredicateFtor(std::move(Ftor)) {
|
|
|
|
initializeCFGSimplifyPassPass(*PassRegistry::getPassRegistry());
|
|
|
|
// Check for command-line overrides of options for debug/customization.
|
|
Options.BonusInstThreshold = UserBonusInstThreshold.getNumOccurrences()
|
|
? UserBonusInstThreshold
|
|
: Threshold;
|
|
|
|
Options.ForwardSwitchCondToPhi = UserForwardSwitchCond.getNumOccurrences()
|
|
? UserForwardSwitchCond
|
|
: ForwardSwitchCond;
|
|
|
|
Options.ConvertSwitchToLookupTable = UserSwitchToLookup.getNumOccurrences()
|
|
? UserSwitchToLookup
|
|
: ConvertSwitch;
|
|
|
|
Options.NeedCanonicalLoop =
|
|
UserKeepLoops.getNumOccurrences() ? UserKeepLoops : KeepLoops;
|
|
|
|
Options.SinkCommonInsts = UserSinkCommonInsts.getNumOccurrences()
|
|
? UserSinkCommonInsts
|
|
: SinkCommon;
|
|
}
|
|
|
|
bool runOnFunction(Function &F) override {
|
|
if (skipFunction(F) || (PredicateFtor && !PredicateFtor(F)))
|
|
return false;
|
|
|
|
Options.AC = &getAnalysis<AssumptionCacheTracker>().getAssumptionCache(F);
|
|
auto &TTI = getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F);
|
|
return simplifyFunctionCFG(F, TTI, Options);
|
|
}
|
|
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
|
AU.addRequired<AssumptionCacheTracker>();
|
|
AU.addRequired<TargetTransformInfoWrapperPass>();
|
|
AU.addPreserved<GlobalsAAWrapperPass>();
|
|
}
|
|
};
|
|
}
|
|
|
|
char CFGSimplifyPass::ID = 0;
|
|
INITIALIZE_PASS_BEGIN(CFGSimplifyPass, "simplifycfg", "Simplify the CFG", false,
|
|
false)
|
|
INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass)
|
|
INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker)
|
|
INITIALIZE_PASS_END(CFGSimplifyPass, "simplifycfg", "Simplify the CFG", false,
|
|
false)
|
|
|
|
// Public interface to the CFGSimplification pass
|
|
FunctionPass *
|
|
llvm::createCFGSimplificationPass(unsigned Threshold, bool ForwardSwitchCond,
|
|
bool ConvertSwitch, bool KeepLoops,
|
|
bool SinkCommon,
|
|
std::function<bool(const Function &)> Ftor) {
|
|
return new CFGSimplifyPass(Threshold, ForwardSwitchCond, ConvertSwitch,
|
|
KeepLoops, SinkCommon, std::move(Ftor));
|
|
}
|