/// the call graph. If the derived class implements this method, it should
/// always explicitly call the implementation here.
void getAnalysisUsage(AnalysisUsage &Info) const override;
+
+protected:
+ /// Optional passes call this function to check whether the pass should be
+ /// skipped. This is the case when optimization bisect is over the limit.
+ bool skipSCC(CallGraphSCC &SCC) const;
};
/// CallGraphSCC - This is a single SCC that a CallGraphSCCPass is run on.
class CallGraphSCC {
+ const CallGraph &CG; // The call graph for this SCC.
void *Context; // The CGPassManager object that is vending this.
std::vector<CallGraphNode*> Nodes;
public:
- CallGraphSCC(void *context) : Context(context) {}
+ CallGraphSCC(CallGraph &cg, void *context) : CG(cg), Context(context) {}
void initialize(CallGraphNode *const *I, CallGraphNode *const *E) {
Nodes.assign(I, E);
typedef std::vector<CallGraphNode *>::const_iterator iterator;
iterator begin() const { return Nodes.begin(); }
iterator end() const { return Nodes.end(); }
+
+ const CallGraph &getCallGraph() { return CG; }
};
} // End llvm namespace
virtual void deleteAnalysisLoop(Loop *L) {}
protected:
- /// skipOptnoneFunction - Containing function has Attribute::OptimizeNone
- /// and most transformation passes should skip it.
- bool skipOptnoneFunction(const Loop *L) const;
+ /// Optional passes call this function to check whether the pass should be
+ /// skipped. This is the case when Attribute::OptimizeNone is set or when
+ /// optimization bisect is over the limit.
+ bool skipLoop(const Loop *L) const;
};
class LPPassManager : public FunctionPass, public PMDataManager {
template <typename T> class SmallVectorImpl;
class Function;
class DebugLoc;
+class OptBisect;
/// This is an important class for using LLVM in a threaded context. It
/// (opaquely) owns and manages the core "global" data of LLVM's core
return OptionRegistry::instance().template get<ValT, Base, Mem>();
}
+ /// \brief Access the object which manages optimization bisection for failure
+ /// analysis.
+ OptBisect &getOptBisect();
private:
LLVMContext(LLVMContext&) = delete;
void operator=(LLVMContext&) = delete;
--- /dev/null
+//===----------- llvm/IR/OptBisect.h - LLVM Bisect support -------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file declares the interface for bisecting optimizations.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_IR_OPTBISECT_H
+#define LLVM_IR_OPTBISECT_H
+
+namespace llvm {
+
+class Pass;
+class StringRef;
+class Twine;
+
+/// This class implements a mechanism to disable passes and individual
+/// optimizations at compile time based on a command line option
+/// (-opt-bisect-limit) in order to perform a bisecting search for
+/// optimization-related problems.
+class OptBisect {
+public:
+ /// \brief Default constructor, initializes the OptBisect state based on the
+ /// -opt-bisect-limit command line argument.
+ ///
+ /// By default, bisection is disabled.
+ ///
+ /// Clients should not instantiate this class directly. All access should go
+ /// through LLVMContext.
+ OptBisect();
+
+ /// Checks the bisect limit to determine if the specified pass should run.
+ ///
+ /// This function will immediate return true if bisection is disabled. If the
+ /// bisect limit is set to -1, the function will print a message describing
+ /// the pass and the bisect number assigned to it and return true. Otherwise,
+ /// the function will print a message with the bisect number assigned to the
+ /// pass and indicating whether or not the pass will be run and return true if
+ /// the bisect limit has not yet been exceded or false if it has.
+ ///
+ /// Most passes should not call this routine directly. Instead, it is called
+ /// through a helper routine provided by the pass base class. For instance,
+ /// function passes should call FunctionPass::skipFunction().
+ template <class UnitT>
+ bool shouldRunPass(const Pass *P, const UnitT &U);
+
+ /// Checks the bisect limit to determine if the optimization described by the
+ /// /p Desc argument should run.
+ ///
+ /// This function will immediate return true if bisection is disabled. If the
+ /// bisect limit is set to -1, the function will print a message with the
+ /// bisect number assigned to the optimization along with the /p Desc
+ /// description and return true. Otherwise, the function will print a message
+ /// with the bisect number assigned to the optimization and indicating whether
+ /// or not the pass will be run and return true if the bisect limit has not
+ /// yet been exceded or false if it has.
+ ///
+ /// Passes may call this function to provide more fine grained control over
+ /// individual optimizations performed by the pass. Passes which cannot be
+ /// skipped entirely (such as non-optional code generation passes) may still
+ /// call this function to control whether or not individual optional
+ /// transformations are performed.
+ bool shouldRunCase(const Twine &Desc);
+
+private:
+ bool checkPass(const StringRef PassName, const StringRef TargetDesc);
+
+ bool BisectEnabled = false;
+ unsigned LastBisectNum = 0;
+};
+
+} // end namespace llvm
+
+#endif // LLVM_IR_OPTBISECT_H
explicit ModulePass(char &pid) : Pass(PT_Module, pid) {}
// Force out-of-line virtual method.
~ModulePass() override;
+
+protected:
+ /// Optional passes call this function to check whether the pass should be
+ /// skipped. This is the case when optimization bisect is over the limit.
+ bool skipModule(Module &M) const;
};
PassManagerType getPotentialPassManagerType() const override;
protected:
- /// skipOptnoneFunction - This function has Attribute::OptimizeNone
- /// and most transformation passes should skip it.
- bool skipOptnoneFunction(const Function &F) const;
+ /// Optional passes call this function to check whether the pass should be
+ /// skipped. This is the case when Attribute::OptimizeNone is set or when
+ /// optimization bisect is over the limit.
+ bool skipFunction(const Function &F) const;
};
PassManagerType getPotentialPassManagerType() const override;
protected:
- /// skipOptnoneFunction - Containing function has Attribute::OptimizeNone
- /// and most transformation passes should skip it.
- bool skipOptnoneFunction(const BasicBlock &BB) const;
+ /// Optional passes call this function to check whether the pass should be
+ /// skipped. This is the case when Attribute::OptimizeNone is set or when
+ /// optimization bisect is over the limit.
+ bool skipBasicBlock(const BasicBlock &BB) const;
};
/// If the user specifies the -time-passes argument on an LLVM tool command line
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/LegacyPassManagers.h"
+#include "llvm/IR/OptBisect.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Timer.h"
// Walk the callgraph in bottom-up SCC order.
scc_iterator<CallGraph*> CGI = scc_begin(&CG);
- CallGraphSCC CurSCC(&CGI);
+ CallGraphSCC CurSCC(CG, &CGI);
while (!CGI.isAtEnd()) {
// Copy the current SCC and increment past it so that the pass can hack
// on the SCC if it wants to without invalidating our iterator.
return new PrintCallGraphPass(Banner, O);
}
+bool CallGraphSCCPass::skipSCC(CallGraphSCC &SCC) const {
+ return !SCC.getCallGraph().getModule()
+ .getContext()
+ .getOptBisect()
+ .shouldRunPass(this, SCC);
+}
#include "llvm/Analysis/LoopPass.h"
#include "llvm/IR/IRPrintingPasses.h"
#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/OptBisect.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Timer.h"
LPPM->add(this);
}
-// Containing function has Attribute::OptimizeNone and transformation
-// passes should skip it.
-bool LoopPass::skipOptnoneFunction(const Loop *L) const {
+bool LoopPass::skipLoop(const Loop *L) const {
const Function *F = L->getHeader()->getParent();
- if (F && F->hasFnAttribute(Attribute::OptimizeNone)) {
+ if (!F)
+ return false;
+ // Check the opt bisect limit.
+ LLVMContext &Context = F->getContext();
+ if (!Context.getOptBisect().shouldRunPass(this, *L))
+ return true;
+ // Check for the OptimizeNone attribute.
+ if (F->hasFnAttribute(Attribute::OptimizeNone)) {
// FIXME: Report this to dbgs() only once per function.
DEBUG(dbgs() << "Skipping pass '" << getPassName()
<< "' in function " << F->getName() << "\n");
"Control Flow Optimizer", false, false)
bool BranchFolderPass::runOnMachineFunction(MachineFunction &MF) {
- if (skipOptnoneFunction(*MF.getFunction()))
+ if (skipFunction(*MF.getFunction()))
return false;
TargetPassConfig *PassConfig = &getAnalysis<TargetPassConfig>();
}
bool CodeGenPrepare::runOnFunction(Function &F) {
- if (skipOptnoneFunction(F))
+ if (skipFunction(F))
return false;
DL = &F.getParent()->getDataLayout();
}
bool DeadMachineInstructionElim::runOnMachineFunction(MachineFunction &MF) {
- if (skipOptnoneFunction(*MF.getFunction()))
+ if (skipFunction(*MF.getFunction()))
return false;
bool AnyChanges = false;
}
bool LowerEmuTLS::runOnModule(Module &M) {
+ if (skipModule(M))
+ return false;
+
if (!TM || !TM->Options.EmulatedTLS)
return false;
if (std::next(F.begin()) == F.end())
return false;
- if (skipOptnoneFunction(*F.getFunction()))
+ if (skipFunction(*F.getFunction()))
return false;
MBPI = &getAnalysis<MachineBranchProbabilityInfo>();
}
bool MachineCSE::runOnMachineFunction(MachineFunction &MF) {
- if (skipOptnoneFunction(*MF.getFunction()))
+ if (skipFunction(*MF.getFunction()))
return false;
TII = MF.getSubtarget().getInstrInfo();
}
bool MachineCopyPropagation::runOnMachineFunction(MachineFunction &MF) {
- if (skipOptnoneFunction(*MF.getFunction()))
+ if (skipFunction(*MF.getFunction()))
return false;
Changed = false;
}
bool MachineLICM::runOnMachineFunction(MachineFunction &MF) {
- if (skipOptnoneFunction(*MF.getFunction()))
+ if (skipFunction(*MF.getFunction()))
return false;
Changed = FirstInLoop = false;
/// design would be to split blocks at scheduling boundaries, but LLVM has a
/// general bias against block splitting purely for implementation simplicity.
bool MachineScheduler::runOnMachineFunction(MachineFunction &mf) {
- if (skipOptnoneFunction(*mf.getFunction()))
+ if (skipFunction(*mf.getFunction()))
return false;
if (EnableMachineSched.getNumOccurrences()) {
}
bool PostMachineScheduler::runOnMachineFunction(MachineFunction &mf) {
- if (skipOptnoneFunction(*mf.getFunction()))
+ if (skipFunction(*mf.getFunction()))
return false;
if (EnablePostRAMachineSched.getNumOccurrences()) {
}
bool MachineSinking::runOnMachineFunction(MachineFunction &MF) {
- if (skipOptnoneFunction(*MF.getFunction()))
+ if (skipFunction(*MF.getFunction()))
return false;
DEBUG(dbgs() << "******** Machine Sinking ********\n");
"Optimize machine instruction PHIs", false, false)
bool OptimizePHIs::runOnMachineFunction(MachineFunction &Fn) {
- if (skipOptnoneFunction(*Fn.getFunction()))
+ if (skipFunction(*Fn.getFunction()))
return false;
MRI = &Fn.getRegInfo();
}
bool PeepholeOptimizer::runOnMachineFunction(MachineFunction &MF) {
- if (skipOptnoneFunction(*MF.getFunction()))
+ if (skipFunction(*MF.getFunction()))
return false;
DEBUG(dbgs() << "********** PEEPHOLE OPTIMIZER **********\n");
}
bool PostRAScheduler::runOnMachineFunction(MachineFunction &Fn) {
- if (skipOptnoneFunction(*Fn.getFunction()))
+ if (skipFunction(*Fn.getFunction()))
return false;
TII = Fn.getSubtarget().getInstrInfo();
}
bool StackColoring::runOnMachineFunction(MachineFunction &Func) {
- if (skipOptnoneFunction(*Func.getFunction()))
+ if (skipFunction(*Func.getFunction()))
return false;
DEBUG(dbgs() << "********** Stack Coloring **********\n"
false)
bool TailDuplicatePass::runOnMachineFunction(MachineFunction &MF) {
- if (skipOptnoneFunction(*MF.getFunction()))
+ if (skipFunction(*MF.getFunction()))
return false;
auto MMI = getAnalysisIfAvailable<MachineModuleInfo>();
Module.cpp
ModuleSummaryIndex.cpp
Operator.cpp
+ OptBisect.cpp
Pass.cpp
PassManager.cpp
PassRegistry.cpp
void LLVMContext::setDiscardValueNames(bool Discard) {
pImpl->DiscardValueNames = Discard;
}
+
+OptBisect &LLVMContext::getOptBisect() {
+ return pImpl->getOptBisect();
+}
#include "llvm/IR/Attributes.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/Module.h"
+#include "llvm/IR/OptBisect.h"
+#include "llvm/Support/ManagedStatic.h"
#include <algorithm>
using namespace llvm;
void CompareConstantExpr::anchor() { }
+/// Singleton instance of the OptBisect class.
+///
+/// This singleton is accessed via the LLVMContext::getOptBisect() function. It
+/// provides a mechanism to disable passes and individual optimizations at
+/// compile time based on a command line option (-opt-bisect-limit) in order to
+/// perform a bisecting search for optimization-related problems.
+///
+/// Even if multiple LLVMContext objects are created, they will all return the
+/// same instance of OptBisect in order to provide a single bisect count. Any
+/// code that uses the OptBisect object should be serialized when bisection is
+/// enabled in order to enable a consistent bisect count.
+static ManagedStatic<OptBisect> OptBisector;
+
+OptBisect &LLVMContextImpl::getOptBisect() {
+ return *OptBisector;
+}
/// Destroy the ConstantArrays if they are not used.
void dropTriviallyDeadConstantArrays();
+
+ /// \brief Access the object which manages optimization bisection for failure
+ /// analysis.
+ OptBisect &getOptBisect();
};
}
--- /dev/null
+//===------- llvm/IR/OptBisect/Bisect.cpp - LLVM Bisect support --------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file implements support for a bisecting optimizations based on a
+/// command line option.
+///
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Analysis/CallGraphSCCPass.h"
+#include "llvm/Analysis/LazyCallGraph.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/OptBisect.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+
+static cl::opt<int> OptBisectLimit("opt-bisect-limit", cl::Hidden,
+ cl::init(INT_MAX), cl::Optional,
+ cl::desc("Maximum optimization to perform"));
+
+OptBisect::OptBisect() {
+ BisectEnabled = OptBisectLimit != INT_MAX;
+}
+
+static void printPassMessage(const StringRef &Name, int PassNum,
+ StringRef TargetDesc, bool Running) {
+ StringRef Status = Running ? "" : "NOT ";
+ errs() << "BISECT: " << Status << "running pass "
+ << "(" << PassNum << ") " << Name << " on " << TargetDesc << "\n";
+}
+
+static void printCaseMessage(int CaseNum, StringRef Msg, bool Running) {
+ if (Running)
+ errs() << "BISECT: running case (";
+ else
+ errs() << "BISECT: NOT running case (";
+ errs() << CaseNum << "): " << Msg << "\n";
+}
+
+static std::string getDescription(const Module &M) {
+ return "module (" + M.getName().str() + ")";
+}
+
+static std::string getDescription(const Function &F) {
+ return "function (" + F.getName().str() + ")";
+}
+
+static std::string getDescription(const BasicBlock &BB) {
+ return "basic block (" + BB.getName().str() + ") in function (" +
+ BB.getParent()->getName().str() + ")";
+}
+
+static std::string getDescription(const Loop &L) {
+ // FIXME: I'd like to be able to provide a better description here, but
+ // calling L->getHeader() would introduce a new dependency on the
+ // LLVMCore library.
+ return "loop";
+}
+
+static std::string getDescription(const CallGraphSCC &SCC) {
+ std::string Desc = "SCC (";
+ bool First = true;
+ for (CallGraphNode *CGN : SCC) {
+ if (First)
+ First = false;
+ else
+ Desc += ", ";
+ Function *F = CGN->getFunction();
+ if (F)
+ Desc += F->getName();
+ else
+ Desc += "<<null function>>";
+ }
+ Desc += ")";
+ return Desc;
+}
+
+static std::string getDescription(const LazyCallGraph::SCC &SCC) {
+ std::string Desc = "SCC (";
+ bool First = true;
+ for (LazyCallGraph::Node &CGN : SCC) {
+ if (First)
+ First = false;
+ else
+ Desc += ", ";
+ Function &F = CGN.getFunction();
+ Desc += F.getName();
+ }
+ Desc += ")";
+ return Desc;
+}
+
+// Force instantiations.
+template bool OptBisect::shouldRunPass(const Pass *, const Module &);
+template bool OptBisect::shouldRunPass(const Pass *, const Function &);
+template bool OptBisect::shouldRunPass(const Pass *, const BasicBlock &);
+template bool OptBisect::shouldRunPass(const Pass *, const Loop &);
+template bool OptBisect::shouldRunPass(const Pass *, const CallGraphSCC &);
+
+template <class UnitT>
+bool OptBisect::shouldRunPass(const Pass *P, const UnitT &U) {
+ if (!BisectEnabled)
+ return true;
+ return checkPass(P->getPassName(), getDescription(U));
+}
+
+bool OptBisect::checkPass(const StringRef PassName,
+ const StringRef TargetDesc) {
+ assert(BisectEnabled);
+
+ int CurBisectNum = ++LastBisectNum;
+ bool ShouldRun = (OptBisectLimit == -1 || CurBisectNum <= OptBisectLimit);
+ printPassMessage(PassName, CurBisectNum, TargetDesc, ShouldRun);
+ return ShouldRun;
+}
+
+bool OptBisect::shouldRunCase(const Twine &Msg) {
+ if (!BisectEnabled)
+ return true;
+ int CurFuelNum = ++LastBisectNum;
+ bool ShouldRun = (OptBisectLimit == -1 || CurFuelNum <= OptBisectLimit);
+ printCaseMessage(CurFuelNum, Msg.str(), ShouldRun);
+ return ShouldRun;
+}
+
#include "llvm/IR/Function.h"
#include "llvm/IR/IRPrintingPasses.h"
#include "llvm/IR/LegacyPassNameParser.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/OptBisect.h"
#include "llvm/PassRegistry.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
return PMT_ModulePassManager;
}
+bool ModulePass::skipModule(Module &M) const {
+ return !M.getContext().getOptBisect().shouldRunPass(this, M);
+}
+
bool Pass::mustPreserveAnalysisID(char &AID) const {
return Resolver->getAnalysisIfAvailable(&AID, true) != nullptr;
}
return PMT_FunctionPassManager;
}
-bool FunctionPass::skipOptnoneFunction(const Function &F) const {
+bool FunctionPass::skipFunction(const Function &F) const {
+ if (!F.getContext().getOptBisect().shouldRunPass(this, F))
+ return true;
+
if (F.hasFnAttribute(Attribute::OptimizeNone)) {
- DEBUG(dbgs() << "Skipping pass '" << getPassName()
- << "' on function " << F.getName() << "\n");
+ DEBUG(dbgs() << "Skipping pass '" << getPassName() << "' on function "
+ << F.getName() << "\n");
return true;
}
return false;
return false;
}
-bool BasicBlockPass::skipOptnoneFunction(const BasicBlock &BB) const {
+bool BasicBlockPass::skipBasicBlock(const BasicBlock &BB) const {
const Function *F = BB.getParent();
- if (F && F->hasFnAttribute(Attribute::OptimizeNone)) {
+ if (!F)
+ return false;
+ if (!F->getContext().getOptBisect().shouldRunPass(this, BB))
+ return true;
+ if (F->hasFnAttribute(Attribute::OptimizeNone)) {
// Report this only once per function.
if (&BB == &F->getEntryBlock())
DEBUG(dbgs() << "Skipping pass '" << getPassName()
}
bool ArgPromotion::runOnSCC(CallGraphSCC &SCC) {
+ if (skipSCC(SCC))
+ return false;
+
bool Changed = false, LocalChange;
do { // Iterate until we stop promoting from this SCC.
}
bool ConstantMerge::runOnModule(Module &M) {
+ if (skipModule(M))
+ return false;
// Find all the globals that are marked "used". These cannot be merged.
SmallPtrSet<const GlobalValue*, 8> UsedGlobals;
}
bool CrossDSOCFI::runOnModule(Module &M) {
+ if (skipModule(M))
+ return false;
+
if (M.getModuleFlag("Cross-DSO CFI") == nullptr)
return false;
buildCFICheck();
}
bool DAE::runOnModule(Module &M) {
+ if (skipModule(M))
+ return false;
+
bool Changed = false;
// First pass: Do a simple check to see if any functions can have their "..."
}
bool EliminateAvailableExternally::runOnModule(Module &M) {
+ if (skipModule(M))
+ return false;
+
bool Changed = false;
// Drop initializers of available externally global variables.
: ModulePass(ID), Named(GVs.begin(), GVs.end()), deleteStuff(deleteS) {}
bool runOnModule(Module &M) override {
+ if (skipModule(M))
+ return false;
+
// Visit the global inline asm.
if (!deleteStuff)
M.setModuleInlineAsm("");
Pass *llvm::createPostOrderFunctionAttrsLegacyPass() { return new PostOrderFunctionAttrsLegacyPass(); }
bool PostOrderFunctionAttrsLegacyPass::runOnSCC(CallGraphSCC &SCC) {
+ if (skipSCC(SCC))
+ return false;
+
TLI = &getAnalysis<TargetLibraryInfoWrapperPass>().getTLI();
bool Changed = false;
}
bool ReversePostOrderFunctionAttrs::runOnModule(Module &M) {
+ if (skipModule(M))
+ return false;
+
// We only have a post-order SCC traversal (because SCCs are inherently
// discovered in post-order), so we accumulate them in a vector and then walk
// it in reverse. This is simpler than using the RPO iterator infrastructure
: ModulePass(ID), Index(Index) {}
bool runOnModule(Module &M) override {
+ if (skipModule(M))
+ return false;
+
if (SummaryFile.empty() && !Index)
report_fatal_error("error: -function-import requires -summary-file or "
"file from frontend\n");
ModulePass *llvm::createGlobalDCEPass() { return new GlobalDCE(); }
bool GlobalDCE::runOnModule(Module &M) {
+ if (skipModule(M))
+ return false;
+
bool Changed = false;
// Remove empty functions from the global ctors list.
}
bool GlobalOpt::runOnModule(Module &M) {
+ if (skipModule(M))
+ return false;
+
bool Changed = false;
auto &DL = M.getDataLayout();
ModulePass *llvm::createIPConstantPropagationPass() { return new IPCP(); }
bool IPCP::runOnModule(Module &M) {
+ if (skipModule(M))
+ return false;
+
bool Changed = false;
bool LocalChange = true;
}
bool runOnModule(Module &M) override {
+ if (skipModule(M))
+ return false;
+
auto &TLI = getAnalysis<TargetLibraryInfoWrapperPass>().getTLI();
return inferAllPrototypeAttributes(M, TLI);
}
}
bool Inliner::runOnSCC(CallGraphSCC &SCC) {
+ if (skipSCC(SCC))
+ return false;
+
CallGraph &CG = getAnalysis<CallGraphWrapperPass>().getCallGraph();
ACT = &getAnalysis<AssumptionCacheTracker>();
auto &TLI = getAnalysis<TargetLibraryInfoWrapperPass>().getTLI();
}
bool runOnModule(Module &M) override {
+ if (skipModule(M))
+ return false;
+
CallGraphWrapperPass *CGPass =
getAnalysisIfAvailable<CallGraphWrapperPass>();
CallGraph *CG = CGPass ? &CGPass->getCallGraph() : nullptr;
Pass *llvm::createLoopExtractorPass() { return new LoopExtractor(); }
bool LoopExtractor::runOnLoop(Loop *L, LPPassManager &) {
- if (skipOptnoneFunction(L))
+ if (skipLoop(L))
return false;
// Only visit top-level loops.
}
bool BlockExtractorPass::runOnModule(Module &M) {
+ if (skipModule(M))
+ return false;
+
std::set<BasicBlock*> TranslatedBlocksToNotExtract;
for (unsigned i = 0, e = BlocksToNotExtract.size(); i != e; ++i) {
BasicBlock *BB = BlocksToNotExtract[i];
}
bool LowerBitSets::runOnModule(Module &M) {
+ if (skipModule(M))
+ return false;
+
bool Changed = buildBitSets();
Changed |= eraseBitSetMetadata();
return Changed;
}
bool MergeFunctions::runOnModule(Module &M) {
+ if (skipModule(M))
+ return false;
+
bool Changed = false;
// All functions in the module, ordered by hash. Functions with a unique
}
bool PartialInliner::runOnModule(Module& M) {
+ if (skipModule(M))
+ return false;
+
std::vector<Function*> worklist;
worklist.reserve(M.size());
for (Module::iterator FI = M.begin(), FE = M.end(); FI != FE; ++FI)
bool PruneEH::runOnSCC(CallGraphSCC &SCC) {
+ if (skipSCC(SCC))
+ return false;
+
SmallPtrSet<CallGraphNode *, 8> SCCNodes;
CallGraph &CG = getAnalysis<CallGraphWrapperPass>().getCallGraph();
bool MadeChange = false;
*PassRegistry::getPassRegistry());
}
bool runOnModule(Module &M) override {
+ if (skipModule(M))
+ return false;
+
return stripDeadPrototypes(M);
}
};
}
bool StripSymbols::runOnModule(Module &M) {
+ if (skipModule(M))
+ return false;
+
bool Changed = false;
Changed |= StripDebugInfo(M);
if (!OnlyDebugInfo)
}
bool StripNonDebugSymbols::runOnModule(Module &M) {
+ if (skipModule(M))
+ return false;
+
return StripSymbolNames(M, true);
}
bool StripDebugDeclare::runOnModule(Module &M) {
+ if (skipModule(M))
+ return false;
Function *Declare = M.getFunction("llvm.dbg.declare");
std::vector<Constant*> DeadConstants;
/// optimized away by the optimizer. This special pass removes debug info for
/// such symbols.
bool StripDeadDebugInfo::runOnModule(Module &M) {
+ if (skipModule(M))
+ return false;
+
bool Changed = false;
LLVMContext &C = M.getContext();
WholeProgramDevirt() : ModulePass(ID) {
initializeWholeProgramDevirtPass(*PassRegistry::getPassRegistry());
}
- bool runOnModule(Module &M) { return DevirtModule(M).run(); }
+ bool runOnModule(Module &M) {
+ if (skipModule(M))
+ return false;
+
+ return DevirtModule(M).run();
+ }
};
} // anonymous namespace
}
bool InstructionCombiningPass::runOnFunction(Function &F) {
- if (skipOptnoneFunction(F))
+ if (skipFunction(F))
return false;
// Required analyses.
}
bool PGOInstrumentationGen::runOnModule(Module &M) {
+ if (skipModule(M))
+ return false;
+
createIRLevelProfileFlagVariable(M);
for (auto &F : M) {
if (F.isDeclaration())
}
bool PGOInstrumentationUse::runOnModule(Module &M) {
+ if (skipModule(M))
+ return false;
+
DEBUG(dbgs() << "Read in profile counters: ");
auto &Ctx = M.getContext();
// Read the counter array from file.
if (!ModuleHasARC(M))
return false;
+ if (skipModule(M))
+ return false;
+
// Find the llvm.global_ctors variable, as the first step in
// identifying the global constructors. In theory, unnecessary autorelease
// pools could occur anywhere, but in practice it's pretty rare. Global
}
bool runOnFunction(Function& F) override {
- if (skipOptnoneFunction(F))
+ if (skipFunction(F))
return false;
return aggressiveDCE(F);
}
false, false)
bool BDCE::runOnFunction(Function& F) {
- if (skipOptnoneFunction(F))
+ if (skipFunction(F))
return false;
auto &DB = getAnalysis<DemandedBitsWrapperPass>().getDemandedBits();
/// \brief Perform the constant hoisting optimization for the given function.
bool ConstantHoisting::runOnFunction(Function &Fn) {
- if (skipOptnoneFunction(Fn))
+ if (skipFunction(Fn))
return false;
DEBUG(dbgs() << "********** Begin Constant Hoisting **********\n");
}
bool CorrelatedValuePropagation::runOnFunction(Function &F) {
- if (skipOptnoneFunction(F))
+ if (skipFunction(F))
return false;
LVI = &getAnalysis<LazyValueInfo>();
initializeDeadInstEliminationPass(*PassRegistry::getPassRegistry());
}
bool runOnBasicBlock(BasicBlock &BB) override {
- if (skipOptnoneFunction(BB))
+ if (skipBasicBlock(BB))
return false;
auto *TLIP = getAnalysisIfAvailable<TargetLibraryInfoWrapperPass>();
TargetLibraryInfo *TLI = TLIP ? &TLIP->getTLI() : nullptr;
}
bool runOnFunction(Function &F) override {
- if (skipOptnoneFunction(F))
+ if (skipFunction(F))
return false;
auto *TLIP = getAnalysisIfAvailable<TargetLibraryInfoWrapperPass>();
}
bool runOnFunction(Function &F) override {
- if (skipOptnoneFunction(F))
+ if (skipFunction(F))
return false;
AA = &getAnalysis<AAResultsWrapperPass>().getAAResults();
}
bool runOnFunction(Function &F) override {
- if (skipOptnoneFunction(F))
+ if (skipFunction(F))
return false;
auto &TLI = getAnalysis<TargetLibraryInfoWrapperPass>().getTLI();
}
bool Float2Int::runOnFunction(Function &F) {
- if (skipOptnoneFunction(F))
+ if (skipFunction(F))
return false;
DEBUG(dbgs() << "F2I: Looking at function " << F.getName() << "\n");
}
bool runOnFunction(Function &F) override {
- if (skipOptnoneFunction(F))
+ if (skipFunction(F))
return false;
return Impl.runImpl(
//===----------------------------------------------------------------------===//
bool IndVarSimplify::runOnLoop(Loop *L, LPPassManager &LPM) {
- if (skipOptnoneFunction(L))
+ if (skipLoop(L))
return false;
// If LoopSimplify form is not available, stay out of trouble. Some notes:
/// runOnFunction - Top level algorithm.
///
bool JumpThreading::runOnFunction(Function &F) {
- if (skipOptnoneFunction(F))
+ if (skipFunction(F))
return false;
DEBUG(dbgs() << "Jump threading on function '" << F.getName() << "'\n");
/// times on one loop.
///
bool LICM::runOnLoop(Loop *L, LPPassManager &LPM) {
- if (skipOptnoneFunction(L))
+ if (skipLoop(L))
return false;
Changed = false;
}
bool LoadCombine::runOnBasicBlock(BasicBlock &BB) {
- if (skipOptnoneFunction(BB))
+ if (skipBasicBlock(BB))
return false;
AA = &getAnalysis<AAResultsWrapperPass>().getAAResults();
/// NOTE: This entire process relies pretty heavily on LoopSimplify and LCSSA
/// in order to make various safety checks work.
bool LoopDeletion::runOnLoop(Loop *L, LPPassManager &) {
- if (skipOptnoneFunction(L))
+ if (skipLoop(L))
return false;
DominatorTree &DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree();
//===----------------------------------------------------------------------===//
bool LoopIdiomRecognize::runOnLoop(Loop *L, LPPassManager &LPM) {
- if (skipOptnoneFunction(L))
+ if (skipLoop(L))
return false;
CurLoop = L;
}
bool LoopInstSimplify::runOnLoop(Loop *L, LPPassManager &LPM) {
- if (skipOptnoneFunction(L))
+ if (skipLoop(L))
return false;
DominatorTreeWrapperPass *DTWP =
}
bool runOnFunction(Function &F) override {
+ if (skipFunction(F))
+ return false;
+
auto *LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
auto *LAA = &getAnalysis<LoopAccessAnalysis>();
auto *DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
}
bool LoopReroll::runOnLoop(Loop *L, LPPassManager &LPM) {
- if (skipOptnoneFunction(L))
+ if (skipLoop(L))
return false;
AA = &getAnalysis<AAResultsWrapperPass>().getAAResults();
}
bool runOnLoop(Loop *L, LPPassManager &LPM) override {
- if (skipOptnoneFunction(L))
+ if (skipLoop(L))
return false;
Function &F = *L->getHeader()->getParent();
/// runOnLoop - Perform basic CFG simplifications to assist other loop passes.
/// For now, this only attempts to merge blocks in the trivial case.
bool LoopSimplifyCFG::runOnLoop(Loop *L, LPPassManager &) {
- if (skipOptnoneFunction(L))
+ if (skipLoop(L))
return false;
DominatorTree *DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
}
bool LoopStrengthReduce::runOnLoop(Loop *L, LPPassManager & /*LPM*/) {
- if (skipOptnoneFunction(L))
+ if (skipLoop(L))
return false;
auto &IU = getAnalysis<IVUsers>();
Optional<bool> ProvidedRuntime;
bool runOnLoop(Loop *L, LPPassManager &) override {
- if (skipOptnoneFunction(L))
+ if (skipLoop(L))
return false;
Function &F = *L->getHeader()->getParent();
}
bool LoopUnswitch::runOnLoop(Loop *L, LPPassManager &LPM_Ref) {
- if (skipOptnoneFunction(L))
+ if (skipLoop(L))
return false;
AC = &getAnalysis<AssumptionCacheTracker>().getAssumptionCache(
}
bool LoopVersioningLICM::runOnLoop(Loop *L, LPPassManager &LPM) {
- if (skipOptnoneFunction(L))
+ if (skipLoop(L))
return false;
Changed = false;
// Get Analysis information.
initializeLowerAtomicPass(*PassRegistry::getPassRegistry());
}
bool runOnBasicBlock(BasicBlock &BB) override {
- if (skipOptnoneFunction(BB))
+ if (skipBasicBlock(BB))
return false;
bool Changed = false;
for (BasicBlock::iterator DI = BB.begin(), DE = BB.end(); DI != DE; ) {
/// This is the main transformation entry point for a function.
bool MemCpyOpt::runOnFunction(Function &F) {
- if (skipOptnoneFunction(F))
+ if (skipFunction(F))
return false;
bool MadeChange = false;
/// \brief Run the transformation for each function
///
bool MergedLoadStoreMotion::runOnFunction(Function &F) {
+ if (skipFunction(F))
+ return false;
+
auto *MDWP = getAnalysisIfAvailable<MemoryDependenceWrapperPass>();
MD = MDWP ? &MDWP->getMemDep() : nullptr;
AA = &getAnalysis<AAResultsWrapperPass>().getAAResults();
}
bool NaryReassociate::runOnFunction(Function &F) {
- if (skipOptnoneFunction(F))
+ if (skipFunction(F))
return false;
AC = &getAnalysis<AssumptionCacheTracker>().getAssumptionCache(F);
}
bool Reassociate::runOnFunction(Function &F) {
- if (skipOptnoneFunction(F))
+ if (skipFunction(F))
return false;
// Reassociate needs for each instruction to have its operands already
// and return true if the function was modified.
//
bool SCCP::runOnFunction(Function &F) {
- if (skipOptnoneFunction(F))
+ if (skipFunction(F))
return false;
DEBUG(dbgs() << "SCCP on function '" << F.getName() << "'\n");
}
bool IPSCCP::runOnModule(Module &M) {
+ if (skipModule(M))
+ return false;
+
const DataLayout &DL = M.getDataLayout();
const TargetLibraryInfo *TLI =
&getAnalysis<TargetLibraryInfoWrapperPass>().getTLI();
initializeSROALegacyPassPass(*PassRegistry::getPassRegistry());
}
bool runOnFunction(Function &F) override {
- if (skipOptnoneFunction(F))
+ if (skipFunction(F))
return false;
auto PA = Impl.runImpl(
bool SROA::runOnFunction(Function &F) {
- if (skipOptnoneFunction(F))
+ if (skipFunction(F))
return false;
bool Changed = performPromotion(F);
}
bool SeparateConstOffsetFromGEP::runOnFunction(Function &F) {
- if (skipOptnoneFunction(F))
+ if (skipFunction(F))
return false;
if (DisableSeparateConstOffsetFromGEP)
if (PredicateFtor && !PredicateFtor(F))
return false;
- if (skipOptnoneFunction(F))
+ if (skipFunction(F))
return false;
AssumptionCache *AC =
}
bool SpeculativeExecution::runOnFunction(Function &F) {
- if (skipOptnoneFunction(F))
+ if (skipFunction(F))
return false;
TTI = &getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F);
}
bool StraightLineStrengthReduce::runOnFunction(Function &F) {
- if (skipOptnoneFunction(F))
+ if (skipFunction(F))
return false;
TTI = &getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F);
}
bool TailCallElim::runOnFunction(Function &F) {
- if (skipOptnoneFunction(F))
+ if (skipFunction(F))
return false;
if (F.getFnAttribute("disable-tail-calls").getValueAsString() == "true")
false, false)
bool PromotePass::runOnFunction(Function &F) {
+ if (skipFunction(F))
+ return false;
+
std::vector<AllocaInst*> Allocas;
BasicBlock &BB = F.getEntryBlock(); // Get the entry node for the function
- if (F.hasFnAttribute(Attribute::OptimizeNone))
- return false;
-
bool Changed = false;
DominatorTree &DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree();
Instruction *I, Instruction *J);
bool vectorizeBB(BasicBlock &BB) {
- if (skipOptnoneFunction(BB))
+ if (skipBasicBlock(BB))
return false;
if (!DT->isReachableFromEntry(&BB)) {
DEBUG(dbgs() << "BBV: skipping unreachable " << BB.getName() <<
BlockFrequency ColdEntryFreq;
bool runOnFunction(Function &F) override {
+ if (skipFunction(F))
+ return false;
+
SE = &getAnalysis<ScalarEvolutionWrapperPass>().getSE();
LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
TTI = &getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F);
}
bool runOnFunction(Function &F) override {
- if (skipOptnoneFunction(F))
+ if (skipFunction(F))
return false;
SE = &getAnalysis<ScalarEvolutionWrapperPass>().getSE();
--- /dev/null
+#!/usr/bin/env python
+
+import os
+import sys
+import argparse
+import subprocess
+
+parser = argparse.ArgumentParser()
+
+parser.add_argument('--start', type=int, default=0)
+parser.add_argument('--end', type=int, default=(1 << 32))
+parser.add_argument('--optcmd', default=("opt"))
+parser.add_argument('--filecheckcmd', default=("FileCheck"))
+parser.add_argument('--prefix', default=("CHECK-BISECT"))
+parser.add_argument('--test', default=(""))
+
+args = parser.parse_args()
+
+start = args.start
+end = args.end
+
+opt_command = [args.optcmd, "-O2", "-opt-bisect-limit=%(count)s", "-S", args.test]
+check_command = [args.filecheckcmd, args.test, "--check-prefix=%s" % args.prefix]
+last = None
+while start != end and start != end-1:
+ count = int(round(start + (end - start)/2))
+ cmd = [x % {'count':count} for x in opt_command]
+ print("opt: " + str(cmd))
+ opt_result = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ filecheck_result = subprocess.Popen(check_command, stdin=opt_result.stdout)
+ opt_result.stdout.close()
+ opt_result.stderr.close()
+ filecheck_result.wait()
+ if filecheck_result.returncode == 0:
+ start = count
+ else:
+ end = count
+
+print("Last good count: %d" % start)
--- /dev/null
+; This file verifies the behavior of the OptBisect class, which is used to
+; diagnose optimization related failures. The tests check various
+; invocations that result in different sets of optimization passes that
+; are run in different ways.
+;
+; This set of tests exercises the legacy pass manager interface to the OptBisect
+; class. Because the exact set of optimizations that will be run may
+; change over time, these tests are written in a more general manner than the
+; corresponding tests for the new pass manager.
+;
+; Don't use NEXT checks or hard-code pass numbering so that this won't fail if
+; new passes are inserted.
+
+
+; Verify that the file can be compiled to an object file at -O3 with all
+; skippable passes skipped.
+
+; RUN: opt -O3 -opt-bisect-limit=0 < %s | llc -O3 -opt-bisect-limit=0
+
+
+; Verify that no skippable passes are run with -opt-bisect-limit=0.
+
+; RUN: opt -disable-output -disable-verify -O3 -opt-bisect-limit=0 %s 2>&1 \
+; RUN: | FileCheck %s --check-prefix=CHECK-SKIP-ALL
+; CHECK-SKIP-ALL: BISECT: NOT running pass ({{[0-9]+}})
+; CHECK-SKIP-ALL-NOT: BISECT: running pass ({{[0-9]+}})
+
+
+; Verify that we can use the opt-bisect-helper.py script (derived from
+; utils/bisect) to locate the optimization that inlines the call to
+; f2() in f3().
+
+; RUN: %python %S/opt-bisect-helper.py --start=0 --end=256 --optcmd=opt \
+; RUN: --filecheckcmd=FileCheck --test=%s \
+; RUN: --prefix=CHECK-BISECT-INLINE-HELPER \
+; RUN: | FileCheck %s --check-prefix=CHECK-BISECT-INLINE-RESULT
+; The helper script uses this to find the optimization that inlines the call.
+; CHECK-BISECT-INLINE-HELPER: call i32 @f2()
+; These checks verifies that the optimization was found.
+; CHECK-BISECT-INLINE-RESULT-NOT: Last good count: 0
+; CHECK-BISECT-INLINE-RESULT: Last good count: {{[0-9]+}}
+
+
+; Test a module pass.
+
+; RUN: opt -disable-output -disable-verify -deadargelim -opt-bisect-limit=-1 %s \
+; RUN: 2>&1 | FileCheck %s --check-prefix=CHECK-DEADARG
+; CHECK-DEADARG: BISECT: running pass ({{[0-9]+}}) Dead Argument Elimination on module
+
+; RUN: opt -disable-output -disable-verify -deadargelim -opt-bisect-limit=0 %s \
+; RUN: 2>&1 | FileCheck %s --check-prefix=CHECK-NOT-DEADARG
+; CHECK-NOT-DEADARG: BISECT: NOT running pass ({{[0-9]+}}) Dead Argument Elimination on module
+
+
+; Test an SCC pass.
+
+; RUN: opt -disable-output -disable-verify -inline -opt-bisect-limit=-1 %s \
+; RUN: 2>&1 | FileCheck %s --check-prefix=CHECK-INLINE
+; CHECK-INLINE: BISECT: running pass ({{[0-9]+}}) Function Integration/Inlining on SCC (<<null function>>)
+; CHECK-INLINE: BISECT: running pass ({{[0-9]+}}) Function Integration/Inlining on SCC (g)
+; CHECK-INLINE: BISECT: running pass ({{[0-9]+}}) Function Integration/Inlining on SCC (f1)
+; CHECK-INLINE: BISECT: running pass ({{[0-9]+}}) Function Integration/Inlining on SCC (f2)
+; CHECK-INLINE: BISECT: running pass ({{[0-9]+}}) Function Integration/Inlining on SCC (f3)
+; CHECK-INLINE: BISECT: running pass ({{[0-9]+}}) Function Integration/Inlining on SCC (<<null function>>)
+
+; RUN: opt -disable-output -disable-verify -inline -opt-bisect-limit=0 %s \
+; RUN: 2>&1 | FileCheck %s --check-prefix=CHECK-NOT-INLINE
+; CHECK-NOT-INLINE: BISECT: NOT running pass ({{[0-9]+}}) Function Integration/Inlining on SCC (<<null function>>)
+; CHECK-NOT-INLINE: BISECT: NOT running pass ({{[0-9]+}}) Function Integration/Inlining on SCC (g)
+; CHECK-NOT-INLINE: BISECT: NOT running pass ({{[0-9]+}}) Function Integration/Inlining on SCC (f1)
+; CHECK-NOT-INLINE: BISECT: NOT running pass ({{[0-9]+}}) Function Integration/Inlining on SCC (f2)
+; CHECK-NOT-INLINE: BISECT: NOT running pass ({{[0-9]+}}) Function Integration/Inlining on SCC (f3)
+; CHECK-NOT-INLINE: BISECT: NOT running pass ({{[0-9]+}}) Function Integration/Inlining on SCC (<<null function>>)
+
+
+; Test a function pass.
+
+; RUN: opt -disable-output -disable-verify -early-cse -opt-bisect-limit=-1 \
+; RUN: %s 2>&1 | FileCheck %s --check-prefix=CHECK-EARLY-CSE
+; CHECK-EARLY-CSE: BISECT: running pass ({{[0-9]+}}) Early CSE on function (f1)
+; CHECK-EARLY-CSE: BISECT: running pass ({{[0-9]+}}) Early CSE on function (f2)
+; CHECK-EARLY-CSE: BISECT: running pass ({{[0-9]+}}) Early CSE on function (f3)
+
+; RUN: opt -disable-output -disable-verify -early-cse -opt-bisect-limit=0 %s \
+; RUN: 2>&1 | FileCheck %s --check-prefix=CHECK-NOT-EARLY-CSE
+; CHECK-NOT-EARLY-CSE: BISECT: NOT running pass ({{[0-9]+}}) Early CSE on function (f1)
+; CHECK-NOT-EARLY-CSE: BISECT: NOT running pass ({{[0-9]+}}) Early CSE on function (f2)
+; CHECK-NOT-EARLY-CSE: BISECT: NOT running pass ({{[0-9]+}}) Early CSE on function (f3)
+
+
+; Test a loop pass.
+
+; RUN: opt -disable-output -disable-verify -loop-reduce -opt-bisect-limit=-1 \
+; RUN: %s 2>&1 | FileCheck %s --check-prefix=CHECK-LOOP-REDUCE
+; CHECK-LOOP-REDUCE: BISECT: running pass ({{[0-9]+}}) Loop Strength Reduction on loop
+; CHECK-LOOP-REDUCE: BISECT: running pass ({{[0-9]+}}) Loop Strength Reduction on loop
+; CHECK-LOOP-REDUCE: BISECT: running pass ({{[0-9]+}}) Loop Strength Reduction on loop
+; CHECK-LOOP-REDUCE: BISECT: running pass ({{[0-9]+}}) Loop Strength Reduction on loop
+; CHECK-LOOP-REDUCE: BISECT: running pass ({{[0-9]+}}) Loop Strength Reduction on loop
+
+; RUN: opt -disable-output -disable-verify -loop-reduce -opt-bisect-limit=0 \
+; RUN: %s 2>&1 | FileCheck %s --check-prefix=CHECK-NOT-LOOP-REDUCE
+; CHECK-NOT-LOOP-REDUCE: BISECT: NOT running pass ({{[0-9]+}}) Loop Strength Reduction on loop
+; CHECK-NOT-LOOP-REDUCE: BISECT: NOT running pass ({{[0-9]+}}) Loop Strength Reduction on loop
+; CHECK-NOT-LOOP-REDUCE: BISECT: NOT running pass ({{[0-9]+}}) Loop Strength Reduction on loop
+; CHECK-NOT-LOOP-REDUCE: BISECT: NOT running pass ({{[0-9]+}}) Loop Strength Reduction on loop
+; CHECK-NOT-LOOP-REDUCE: BISECT: NOT running pass ({{[0-9]+}}) Loop Strength Reduction on loop
+
+
+declare i32 @g()
+
+define void @f1() {
+entry:
+ br label %loop.0
+loop.0:
+ br i1 undef, label %loop.0.0, label %loop.1
+loop.0.0:
+ br i1 undef, label %loop.0.0, label %loop.0.1
+loop.0.1:
+ br i1 undef, label %loop.0.1, label %loop.0
+loop.1:
+ br i1 undef, label %loop.1, label %loop.1.bb1
+loop.1.bb1:
+ br i1 undef, label %loop.1, label %loop.1.bb2
+loop.1.bb2:
+ br i1 undef, label %end, label %loop.1.0
+loop.1.0:
+ br i1 undef, label %loop.1.0, label %loop.1
+end:
+ ret void
+}
+
+define i32 @f2() {
+entry:
+ ret i32 0
+}
+
+define i32 @f3() {
+entry:
+ %temp = call i32 @g()
+ %icmp = icmp ugt i32 %temp, 2
+ br i1 %icmp, label %bb.true, label %bb.false
+bb.true:
+ %temp2 = call i32 @f2()
+ ret i32 %temp2
+bb.false:
+ ret i32 0
+}