Index: include/llvm/Analysis/CallGraphSCCPass.h =================================================================== --- include/llvm/Analysis/CallGraphSCCPass.h +++ include/llvm/Analysis/CallGraphSCCPass.h @@ -77,15 +77,21 @@ /// 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 { + CallGraph &CG; // The call graph for this SCC. void *Context; // The CGPassManager object that is vending this. std::vector 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); @@ -101,6 +107,8 @@ typedef std::vector::const_iterator iterator; iterator begin() const { return Nodes.begin(); } iterator end() const { return Nodes.end(); } + + LLVMContext &getContext() { return CG.getModule().getContext(); } }; } // End llvm namespace Index: include/llvm/Analysis/LazyCallGraph.h =================================================================== --- include/llvm/Analysis/LazyCallGraph.h +++ include/llvm/Analysis/LazyCallGraph.h @@ -923,6 +923,10 @@ PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); }; +// This function is implemented in OptBisect.cpp but must be declared +// here to avoid include file dependency problems. +bool skipPassForSCC(const StringRef PassName, const LazyCallGraph::SCC &SCC); + } #endif Index: include/llvm/Analysis/LoopPass.h =================================================================== --- include/llvm/Analysis/LoopPass.h +++ include/llvm/Analysis/LoopPass.h @@ -88,9 +88,10 @@ 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 { Index: include/llvm/IR/LLVMContext.h =================================================================== --- include/llvm/IR/LLVMContext.h +++ include/llvm/IR/LLVMContext.h @@ -15,6 +15,7 @@ #ifndef LLVM_IR_LLVMCONTEXT_H #define LLVM_IR_LLVMCONTEXT_H +#include "llvm/IR/OptBisect.h" #include "llvm/Support/CBindingWrapping.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Options.h" @@ -220,6 +221,9 @@ return OptionRegistry::instance().template get(); } + /// \brief Access the object which manages optimization bisection for failure + /// analysis. + OptBisect &getOptBisect(); private: LLVMContext(LLVMContext&) = delete; void operator=(LLVMContext&) = delete; Index: include/llvm/IR/OptBisect.h =================================================================== --- include/llvm/IR/OptBisect.h +++ include/llvm/IR/OptBisect.h @@ -0,0 +1,67 @@ +//===----------- 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. +// +//===----------------------------------------------------------------------===// +// +// 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; + +class OptBisect { +public: + // Do not instantiate directly. All access should go through LLVMContext. + OptBisect(); + + // Interface function for the legacy pass manager. + template + bool shouldRunPass(const Pass *P, const UnitT &U); + + // Interface function for the new pass manager. + template + bool shouldRunPass(const StringRef PassName, const UnitT &U); + + // Opt-in function for individual optimization cases. + bool shouldRunCase(const Twine &Msg); + +private: + bool checkPass(const StringRef PassName, const StringRef TargetDesc); + + bool BisectEnabled = false; + int LastBisectNum = 0; +}; + +// Access to OptBisect should go through LLVMContext, but for the +// new pass manager there is no single base class from which a +// helper function to abstract the messy details can be provided. +// Instead, we provide standalone helper functions for each IR +// type that must be handled. + +class Module; +class Function; +class BasicBlock; +class Loop; + +bool skipPassForModule(const StringRef PassName, const Module &M); +bool skipPassForFunction(const StringRef PassName, const Function &F); +bool skipPassForBasicBlock(const StringRef PassName, const BasicBlock &BB); +bool skipPassForLoop(const StringRef PassName, const Loop &L); + +// skiPassForSCC is declared in LazyCallGraph.h because of include file +// dependency issues related to LazyCallGraph::SCC being nested. + +} // namespace llvm + +#endif // LLVM_IR_OPTBISECT_H Index: include/llvm/Pass.h =================================================================== --- include/llvm/Pass.h +++ include/llvm/Pass.h @@ -251,6 +251,11 @@ 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; }; @@ -310,9 +315,10 @@ 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; }; @@ -359,9 +365,10 @@ 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 Index: lib/Analysis/CallGraphSCCPass.cpp =================================================================== --- lib/Analysis/CallGraphSCCPass.cpp +++ lib/Analysis/CallGraphSCCPass.cpp @@ -444,7 +444,7 @@ // Walk the callgraph in bottom-up SCC order. scc_iterator 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. @@ -631,3 +631,6 @@ return new PrintCallGraphPass(Banner, O); } +bool CallGraphSCCPass::skipSCC(CallGraphSCC &SCC) const { + return !SCC.getContext().getOptBisect().shouldRunPass(this, SCC); +} Index: lib/Analysis/LoopPass.cpp =================================================================== --- lib/Analysis/LoopPass.cpp +++ lib/Analysis/LoopPass.cpp @@ -335,11 +335,16 @@ 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"); Index: lib/CodeGen/BranchFolding.cpp =================================================================== --- lib/CodeGen/BranchFolding.cpp +++ lib/CodeGen/BranchFolding.cpp @@ -90,7 +90,7 @@ "Control Flow Optimizer", false, false) bool BranchFolderPass::runOnMachineFunction(MachineFunction &MF) { - if (skipOptnoneFunction(*MF.getFunction())) + if (skipFunction(*MF.getFunction())) return false; TargetPassConfig *PassConfig = &getAnalysis(); Index: lib/CodeGen/CodeGenPrepare.cpp =================================================================== --- lib/CodeGen/CodeGenPrepare.cpp +++ lib/CodeGen/CodeGenPrepare.cpp @@ -210,7 +210,7 @@ } bool CodeGenPrepare::runOnFunction(Function &F) { - if (skipOptnoneFunction(F)) + if (skipFunction(F)) return false; DL = &F.getParent()->getDataLayout(); Index: lib/CodeGen/DeadMachineInstructionElim.cpp =================================================================== --- lib/CodeGen/DeadMachineInstructionElim.cpp +++ lib/CodeGen/DeadMachineInstructionElim.cpp @@ -90,7 +90,7 @@ } bool DeadMachineInstructionElim::runOnMachineFunction(MachineFunction &MF) { - if (skipOptnoneFunction(*MF.getFunction())) + if (skipFunction(*MF.getFunction())) return false; bool AnyChanges = false; Index: lib/CodeGen/LowerEmuTLS.cpp =================================================================== --- lib/CodeGen/LowerEmuTLS.cpp +++ lib/CodeGen/LowerEmuTLS.cpp @@ -63,6 +63,9 @@ } bool LowerEmuTLS::runOnModule(Module &M) { + if (skipModule(M)) + return false; + if (!TM || !TM->Options.EmulatedTLS) return false; Index: lib/CodeGen/MachineBlockPlacement.cpp =================================================================== --- lib/CodeGen/MachineBlockPlacement.cpp +++ lib/CodeGen/MachineBlockPlacement.cpp @@ -1430,7 +1430,7 @@ if (std::next(F.begin()) == F.end()) return false; - if (skipOptnoneFunction(*F.getFunction())) + if (skipFunction(*F.getFunction())) return false; MBPI = &getAnalysis(); Index: lib/CodeGen/MachineCSE.cpp =================================================================== --- lib/CodeGen/MachineCSE.cpp +++ lib/CodeGen/MachineCSE.cpp @@ -698,7 +698,7 @@ } bool MachineCSE::runOnMachineFunction(MachineFunction &MF) { - if (skipOptnoneFunction(*MF.getFunction())) + if (skipFunction(*MF.getFunction())) return false; TII = MF.getSubtarget().getInstrInfo(); Index: lib/CodeGen/MachineCopyPropagation.cpp =================================================================== --- lib/CodeGen/MachineCopyPropagation.cpp +++ lib/CodeGen/MachineCopyPropagation.cpp @@ -349,7 +349,7 @@ } bool MachineCopyPropagation::runOnMachineFunction(MachineFunction &MF) { - if (skipOptnoneFunction(*MF.getFunction())) + if (skipFunction(*MF.getFunction())) return false; Changed = false; Index: lib/CodeGen/MachineLICM.cpp =================================================================== --- lib/CodeGen/MachineLICM.cpp +++ lib/CodeGen/MachineLICM.cpp @@ -260,7 +260,7 @@ } bool MachineLICM::runOnMachineFunction(MachineFunction &MF) { - if (skipOptnoneFunction(*MF.getFunction())) + if (skipFunction(*MF.getFunction())) return false; Changed = FirstInLoop = false; Index: lib/CodeGen/MachineScheduler.cpp =================================================================== --- lib/CodeGen/MachineScheduler.cpp +++ lib/CodeGen/MachineScheduler.cpp @@ -319,7 +319,7 @@ /// 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()) { @@ -357,7 +357,7 @@ } bool PostMachineScheduler::runOnMachineFunction(MachineFunction &mf) { - if (skipOptnoneFunction(*mf.getFunction())) + if (skipFunction(*mf.getFunction())) return false; if (EnablePostRAMachineSched.getNumOccurrences()) { Index: lib/CodeGen/MachineSink.cpp =================================================================== --- lib/CodeGen/MachineSink.cpp +++ lib/CodeGen/MachineSink.cpp @@ -257,7 +257,7 @@ } bool MachineSinking::runOnMachineFunction(MachineFunction &MF) { - if (skipOptnoneFunction(*MF.getFunction())) + if (skipFunction(*MF.getFunction())) return false; DEBUG(dbgs() << "******** Machine Sinking ********\n"); Index: lib/CodeGen/OptimizePHIs.cpp =================================================================== --- lib/CodeGen/OptimizePHIs.cpp +++ lib/CodeGen/OptimizePHIs.cpp @@ -63,7 +63,7 @@ "Optimize machine instruction PHIs", false, false) bool OptimizePHIs::runOnMachineFunction(MachineFunction &Fn) { - if (skipOptnoneFunction(*Fn.getFunction())) + if (skipFunction(*Fn.getFunction())) return false; MRI = &Fn.getRegInfo(); Index: lib/CodeGen/PeepholeOptimizer.cpp =================================================================== --- lib/CodeGen/PeepholeOptimizer.cpp +++ lib/CodeGen/PeepholeOptimizer.cpp @@ -1471,7 +1471,7 @@ } bool PeepholeOptimizer::runOnMachineFunction(MachineFunction &MF) { - if (skipOptnoneFunction(*MF.getFunction())) + if (skipFunction(*MF.getFunction())) return false; DEBUG(dbgs() << "********** PEEPHOLE OPTIMIZER **********\n"); Index: lib/CodeGen/PostRASchedulerList.cpp =================================================================== --- lib/CodeGen/PostRASchedulerList.cpp +++ lib/CodeGen/PostRASchedulerList.cpp @@ -274,7 +274,7 @@ } bool PostRAScheduler::runOnMachineFunction(MachineFunction &Fn) { - if (skipOptnoneFunction(*Fn.getFunction())) + if (skipFunction(*Fn.getFunction())) return false; TII = Fn.getSubtarget().getInstrInfo(); Index: lib/CodeGen/StackColoring.cpp =================================================================== --- lib/CodeGen/StackColoring.cpp +++ lib/CodeGen/StackColoring.cpp @@ -658,7 +658,7 @@ } bool StackColoring::runOnMachineFunction(MachineFunction &Func) { - if (skipOptnoneFunction(*Func.getFunction())) + if (skipFunction(*Func.getFunction())) return false; DEBUG(dbgs() << "********** Stack Coloring **********\n" Index: lib/CodeGen/TailDuplication.cpp =================================================================== --- lib/CodeGen/TailDuplication.cpp +++ lib/CodeGen/TailDuplication.cpp @@ -44,7 +44,7 @@ false) bool TailDuplicatePass::runOnMachineFunction(MachineFunction &MF) { - if (skipOptnoneFunction(*MF.getFunction())) + if (skipFunction(*MF.getFunction())) return false; auto MMI = getAnalysisIfAvailable(); Index: lib/IR/CMakeLists.txt =================================================================== --- lib/IR/CMakeLists.txt +++ lib/IR/CMakeLists.txt @@ -39,6 +39,7 @@ Module.cpp ModuleSummaryIndex.cpp Operator.cpp + OptBisect.cpp Pass.cpp PassManager.cpp PassRegistry.cpp Index: lib/IR/LLVMContext.cpp =================================================================== --- lib/IR/LLVMContext.cpp +++ lib/IR/LLVMContext.cpp @@ -320,3 +320,7 @@ void LLVMContext::setDiscardValueNames(bool Discard) { pImpl->DiscardValueNames = Discard; } + +OptBisect &LLVMContext::getOptBisect() { + return pImpl->getOptBisect(); +} Index: lib/IR/LLVMContextImpl.h =================================================================== --- lib/IR/LLVMContextImpl.h +++ lib/IR/LLVMContextImpl.h @@ -1049,6 +1049,10 @@ /// Destroy the ConstantArrays if they are not used. void dropTriviallyDeadConstantArrays(); + + /// \brief Access the object which manages optimization bisection for failure + /// analysis. + OptBisect &getOptBisect(); }; } Index: lib/IR/LLVMContextImpl.cpp =================================================================== --- lib/IR/LLVMContextImpl.cpp +++ lib/IR/LLVMContextImpl.cpp @@ -16,6 +16,7 @@ #include "llvm/IR/Attributes.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/Module.h" +#include "llvm/Support/ManagedStatic.h" #include using namespace llvm; @@ -232,3 +233,12 @@ void CompareConstantExpr::anchor() { } +// The OptBisector object needs to be a singleton so that a single bisect count +// can be used everywhere, even if multiple LLVMContext objects are created. +// Compilation needs to be serialized in order for bisecting to be meaningful, +// so access from multiple threads should never be an issue. +static ManagedStatic OptBisector; + +OptBisect &LLVMContextImpl::getOptBisect() { + return *OptBisector; +} Index: lib/IR/OptBisect.cpp =================================================================== --- lib/IR/OptBisect.cpp +++ lib/IR/OptBisect.cpp @@ -0,0 +1,208 @@ +//===------- 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. +// +//===----------------------------------------------------------------------===// +// +// This file implements support for a bisecting optimizations based on a +// command line option. +// +// A singleton is used so that consistent numbering is maintained across the +// runs of multiple pass managers. A typical invocation of clang (for instance) +// will run three pass managers: one for function level optimizations, one for +// module level optimizations (which also runs function passes) and one for code +// generation (which also runs module and function passes). For more details +// see clang/lib/CodeGen/BackendUtil.cpp, particularly the functions +// EmitAssemblyHelper::CreatePasses() and EmitAssemblyHelper::EmitAssembly(). +// +//===----------------------------------------------------------------------===// + +#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/Debug.h" + +using namespace llvm; + +//////////////////////////////////////////////////////////////////////////////// +// Command line options +//////////////////////////////////////////////////////////////////////////////// + +static cl::opt OptBisectLimit("opt-bisect-limit", cl::Hidden, + cl::init(INT_MAX), cl::Optional, + cl::desc("Maximum optimization to perform")); + +//////////////////////////////////////////////////////////////////////////////// +// Constructor +//////////////////////////////////////////////////////////////////////////////// + +OptBisect::OptBisect() { + BisectEnabled = OptBisectLimit != INT_MAX; +} + +//////////////////////////////////////////////////////////////////////////////// +// Output formatters +//////////////////////////////////////////////////////////////////////////////// + +static void printPassMessage(const StringRef &Name, int PassNum, + StringRef TargetDesc, bool Running) { + StringRef Status = Running ? "" : "NOT "; + dbgs() << "BISECT: " << Status << "running pass " + << "(" << PassNum << ") " << Name << " on " << TargetDesc << "\n"; +} + +static void printCaseMessage(int CaseNum, StringRef Msg, bool Running) { + if (Running) + dbgs() << "BISECT: running case ("; + else + dbgs() << "BISECT: NOT running case ("; + dbgs() << CaseNum << "): " << Msg << "\n"; +} + +//////////////////////////////////////////////////////////////////////////////// +// Target description helpers +//////////////////////////////////////////////////////////////////////////////// + +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 += ", "; + auto *F = CGN->getFunction(); + if (F) + Desc += F->getName(); + else + Desc += "<>"; + } + 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 += ", "; + auto &F = CGN.getFunction(); + Desc += F.getName(); + } + Desc += ")"; + return Desc; +} + +//////////////////////////////////////////////////////////////////////////////// +// Criteria functions +//////////////////////////////////////////////////////////////////////////////// + +// 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 bool OptBisect::shouldRunPass(const StringRef PassName, + const Module &); +template bool OptBisect::shouldRunPass(const StringRef PassName, + const Function &); +template bool OptBisect::shouldRunPass(const StringRef PassName, + const BasicBlock &); +template bool OptBisect::shouldRunPass(const StringRef PassName, const Loop &); +template bool OptBisect::shouldRunPass(const StringRef PassName, + const LazyCallGraph::SCC &); + +template +bool OptBisect::shouldRunPass(const Pass *P, const UnitT &U) { + if (!BisectEnabled) + return true; + return checkPass(P->getPassName(), getDescription(U)); +} + +// Interface function for the new pass manager. +template +bool OptBisect::shouldRunPass(const StringRef PassName, const UnitT &U) { + if (!BisectEnabled) + return true; + // FIXME: Check for analysis passes. + return checkPass(PassName, 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; +} + +//////////////////////////////////////////////////////////////////////////////// +// Helpers for access via LLVMContext. +//////////////////////////////////////////////////////////////////////////////// + +bool llvm::skipPassForModule(const StringRef PassName, const Module &M) { + return !M.getContext().getOptBisect().shouldRunPass(PassName, M); +} + +bool llvm::skipPassForFunction(const StringRef PassName, const Function &F) { + return !F.getContext().getOptBisect().shouldRunPass(PassName, F); +} + +bool llvm::skipPassForBasicBlock(const StringRef PassName, const BasicBlock &BB) { + return !BB.getContext().getOptBisect().shouldRunPass(PassName, BB); +} + +bool llvm::skipPassForLoop(const StringRef PassName, const Loop &L) { + const Function *F = L.getHeader()->getParent(); + if (!F) + return false; + return !F->getContext().getOptBisect().shouldRunPass(PassName, L); +} + +bool llvm::skipPassForSCC(const StringRef PassName, const LazyCallGraph::SCC &SCC) { + LLVMContext &Context = SCC.begin()->getFunction().getContext(); + return !Context.getOptBisect().shouldRunPass(PassName, SCC); +} + Index: lib/IR/Pass.cpp =================================================================== --- lib/IR/Pass.cpp +++ lib/IR/Pass.cpp @@ -17,6 +17,7 @@ #include "llvm/IR/Function.h" #include "llvm/IR/IRPrintingPasses.h" #include "llvm/IR/LegacyPassNameParser.h" +#include "llvm/IR/Module.h" #include "llvm/PassRegistry.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" @@ -45,6 +46,10 @@ 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; } @@ -140,10 +145,13 @@ 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; @@ -168,9 +176,13 @@ 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() Index: lib/Passes/PassBuilder.cpp =================================================================== --- lib/Passes/PassBuilder.cpp +++ lib/Passes/PassBuilder.cpp @@ -39,6 +39,7 @@ #include "llvm/Analysis/TypeBasedAliasAnalysis.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/IRPrintingPasses.h" +#include "llvm/IR/OptBisect.h" #include "llvm/IR/PassManager.h" #include "llvm/IR/Verifier.h" #include "llvm/Support/Debug.h" @@ -65,7 +66,11 @@ /// \brief No-op module pass which does nothing. struct NoOpModulePass { - PreservedAnalyses run(Module &M) { return PreservedAnalyses::all(); } + PreservedAnalyses run(Module &M) { + // This call is here for testing purposes. + skipPassForModule(name(), M); + return PreservedAnalyses::all(); + } static StringRef name() { return "NoOpModulePass"; } }; @@ -83,6 +88,8 @@ /// \brief No-op CGSCC pass which does nothing. struct NoOpCGSCCPass { PreservedAnalyses run(LazyCallGraph::SCC &C) { + // This call is here for testing purposes. + skipPassForSCC(name(), C); return PreservedAnalyses::all(); } static StringRef name() { return "NoOpCGSCCPass"; } @@ -101,7 +108,11 @@ /// \brief No-op function pass which does nothing. struct NoOpFunctionPass { - PreservedAnalyses run(Function &F) { return PreservedAnalyses::all(); } + PreservedAnalyses run(Function &F) { + // This call is here for testing purposes. + skipPassForFunction(name(), F); + return PreservedAnalyses::all(); + } static StringRef name() { return "NoOpFunctionPass"; } }; @@ -118,7 +129,11 @@ /// \brief No-op loop pass which does nothing. struct NoOpLoopPass { - PreservedAnalyses run(Loop &L) { return PreservedAnalyses::all(); } + PreservedAnalyses run(Loop &L) { + // This call is here for testing purposes. + skipPassForLoop(name(), L); + return PreservedAnalyses::all(); + } static StringRef name() { return "NoOpLoopPass"; } }; Index: lib/Target/AArch64/AArch64PromoteConstant.cpp =================================================================== --- lib/Target/AArch64/AArch64PromoteConstant.cpp +++ lib/Target/AArch64/AArch64PromoteConstant.cpp @@ -109,6 +109,8 @@ /// global variables with module scope. bool runOnModule(Module &M) override { DEBUG(dbgs() << getPassName() << '\n'); + if (skipModule(M)) + return false; bool Changed = false; PromotionCacheTy PromotionCache; for (auto &MF : M) { Index: lib/Target/AMDGPU/AMDGPUAlwaysInlinePass.cpp =================================================================== --- lib/Target/AMDGPU/AMDGPUAlwaysInlinePass.cpp +++ lib/Target/AMDGPU/AMDGPUAlwaysInlinePass.cpp @@ -35,6 +35,9 @@ char AMDGPUAlwaysInline::ID = 0; bool AMDGPUAlwaysInline::runOnModule(Module &M) { + if (skipModule(M)) + return false; + std::vector FuncsToClone; for (Function &F : M) { Index: lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp =================================================================== --- lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp +++ lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp @@ -97,7 +97,7 @@ } bool AMDGPUPromoteAlloca::runOnFunction(Function &F) { - if (!TM || F.hasFnAttribute(Attribute::OptimizeNone)) + if (!TM || skipFunction(F)) return false; FunctionType *FTy = F.getFunctionType(); Index: lib/Target/Mips/Mips16HardFloat.cpp =================================================================== --- lib/Target/Mips/Mips16HardFloat.cpp +++ lib/Target/Mips/Mips16HardFloat.cpp @@ -523,6 +523,9 @@ // during call lowering but it should be moved here in the future. // bool Mips16HardFloat::runOnModule(Module &M) { + if (skipModule(M)) + return false; + DEBUG(errs() << "Run on Module Mips16HardFloat\n"); bool Modified = false; for (Module::iterator F = M.begin(), E = M.end(); F != E; ++F) { Index: lib/Target/Mips/MipsOs16.cpp =================================================================== --- lib/Target/Mips/MipsOs16.cpp +++ lib/Target/Mips/MipsOs16.cpp @@ -109,6 +109,9 @@ bool MipsOs16::runOnModule(Module &M) { + if (skipModule(M)) + return false; + bool usingMask = Mips32FunctionMask.length() > 0; bool doneUsingMask = false; // this will make it stop repeating Index: lib/Target/NVPTX/NVPTXGenericToNVVM.cpp =================================================================== --- lib/Target/NVPTX/NVPTXGenericToNVVM.cpp +++ lib/Target/NVPTX/NVPTXGenericToNVVM.cpp @@ -74,6 +74,9 @@ false) bool GenericToNVVM::runOnModule(Module &M) { + if (skipModule(M)) + return false; + // Create a clone of each global variable that has the default address space. // The clone is created with the global address space specifier, and the pair // of original global variable and its clone is placed in the GVMap for later Index: lib/Target/XCore/XCoreLowerThreadLocal.cpp =================================================================== --- lib/Target/XCore/XCoreLowerThreadLocal.cpp +++ lib/Target/XCore/XCoreLowerThreadLocal.cpp @@ -225,6 +225,9 @@ } bool XCoreLowerThreadLocal::runOnModule(Module &M) { + if (skipModule(M)) + return false; + // Find thread local globals. bool MadeChange = false; SmallVector ThreadLocalGlobals; Index: lib/Transforms/IPO/ArgumentPromotion.cpp =================================================================== --- lib/Transforms/IPO/ArgumentPromotion.cpp +++ lib/Transforms/IPO/ArgumentPromotion.cpp @@ -114,6 +114,9 @@ } bool ArgPromotion::runOnSCC(CallGraphSCC &SCC) { + if (skipSCC(SCC)) + return false; + bool Changed = false, LocalChange; do { // Iterate until we stop promoting from this SCC. Index: lib/Transforms/IPO/ConstantMerge.cpp =================================================================== --- lib/Transforms/IPO/ConstantMerge.cpp +++ lib/Transforms/IPO/ConstantMerge.cpp @@ -96,6 +96,8 @@ } bool ConstantMerge::runOnModule(Module &M) { + if (skipModule(M)) + return false; // Find all the globals that are marked "used". These cannot be merged. SmallPtrSet UsedGlobals; Index: lib/Transforms/IPO/CrossDSOCFI.cpp =================================================================== --- lib/Transforms/IPO/CrossDSOCFI.cpp +++ lib/Transforms/IPO/CrossDSOCFI.cpp @@ -158,6 +158,9 @@ } bool CrossDSOCFI::runOnModule(Module &M) { + if (skipModule(M)) + return false; + if (M.getModuleFlag("Cross-DSO CFI") == nullptr) return false; buildCFICheck(); Index: lib/Transforms/IPO/DeadArgumentElimination.cpp =================================================================== --- lib/Transforms/IPO/DeadArgumentElimination.cpp +++ lib/Transforms/IPO/DeadArgumentElimination.cpp @@ -1094,6 +1094,9 @@ } 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 "..." Index: lib/Transforms/IPO/ElimAvailExtern.cpp =================================================================== --- lib/Transforms/IPO/ElimAvailExtern.cpp +++ lib/Transforms/IPO/ElimAvailExtern.cpp @@ -50,6 +50,9 @@ } bool EliminateAvailableExternally::runOnModule(Module &M) { + if (skipModule(M)) + return false; + bool Changed = false; // Drop initializers of available externally global variables. Index: lib/Transforms/IPO/ExtractGV.cpp =================================================================== --- lib/Transforms/IPO/ExtractGV.cpp +++ lib/Transforms/IPO/ExtractGV.cpp @@ -68,6 +68,9 @@ : 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(""); Index: lib/Transforms/IPO/FunctionAttrs.cpp =================================================================== --- lib/Transforms/IPO/FunctionAttrs.cpp +++ lib/Transforms/IPO/FunctionAttrs.cpp @@ -987,6 +987,9 @@ PreservedAnalyses PostOrderFunctionAttrsPass::run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM) { + if (skipPassForSCC(name(), C)) + return PreservedAnalyses::all(); + Module &M = *C.begin()->getFunction().getParent(); const ModuleAnalysisManager &MAM = AM.getResult(C).getManager(); @@ -1081,6 +1084,9 @@ Pass *llvm::createPostOrderFunctionAttrsLegacyPass() { return new PostOrderFunctionAttrsLegacyPass(); } bool PostOrderFunctionAttrsLegacyPass::runOnSCC(CallGraphSCC &SCC) { + if (skipSCC(SCC)) + return false; + TLI = &getAnalysis().getTLI(); bool Changed = false; @@ -1195,6 +1201,9 @@ } 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 Index: lib/Transforms/IPO/FunctionImport.cpp =================================================================== --- lib/Transforms/IPO/FunctionImport.cpp +++ lib/Transforms/IPO/FunctionImport.cpp @@ -445,6 +445,9 @@ : 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"); Index: lib/Transforms/IPO/GlobalDCE.cpp =================================================================== --- lib/Transforms/IPO/GlobalDCE.cpp +++ lib/Transforms/IPO/GlobalDCE.cpp @@ -75,6 +75,9 @@ 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. Index: lib/Transforms/IPO/GlobalOpt.cpp =================================================================== --- lib/Transforms/IPO/GlobalOpt.cpp +++ lib/Transforms/IPO/GlobalOpt.cpp @@ -2530,6 +2530,9 @@ } bool GlobalOpt::runOnModule(Module &M) { + if (skipModule(M)) + return false; + bool Changed = false; auto &DL = M.getDataLayout(); Index: lib/Transforms/IPO/IPConstantPropagation.cpp =================================================================== --- lib/Transforms/IPO/IPConstantPropagation.cpp +++ lib/Transforms/IPO/IPConstantPropagation.cpp @@ -54,6 +54,9 @@ ModulePass *llvm::createIPConstantPropagationPass() { return new IPCP(); } bool IPCP::runOnModule(Module &M) { + if (skipModule(M)) + return false; + bool Changed = false; bool LocalChange = true; Index: lib/Transforms/IPO/InferFunctionAttrs.cpp =================================================================== --- lib/Transforms/IPO/InferFunctionAttrs.cpp +++ lib/Transforms/IPO/InferFunctionAttrs.cpp @@ -955,6 +955,9 @@ PreservedAnalyses InferFunctionAttrsPass::run(Module &M, AnalysisManager &AM) { + if (skipPassForModule(name(), M)) + return PreservedAnalyses::all(); + auto &TLI = AM.getResult(M); if (!inferAllPrototypeAttributes(M, TLI)) @@ -979,6 +982,9 @@ } bool runOnModule(Module &M) override { + if (skipModule(M)) + return false; + auto &TLI = getAnalysis().getTLI(); return inferAllPrototypeAttributes(M, TLI); } Index: lib/Transforms/IPO/Inliner.cpp =================================================================== --- lib/Transforms/IPO/Inliner.cpp +++ lib/Transforms/IPO/Inliner.cpp @@ -357,6 +357,9 @@ } bool Inliner::runOnSCC(CallGraphSCC &SCC) { + if (skipSCC(SCC)) + return false; + CallGraph &CG = getAnalysis().getCallGraph(); ACT = &getAnalysis(); auto &TLI = getAnalysis().getTLI(); Index: lib/Transforms/IPO/Internalize.cpp =================================================================== --- lib/Transforms/IPO/Internalize.cpp +++ lib/Transforms/IPO/Internalize.cpp @@ -176,6 +176,9 @@ } bool InternalizePass::runOnModule(Module &M) { + if (skipModule(M)) + return false; + CallGraphWrapperPass *CGPass = getAnalysisIfAvailable(); CallGraph *CG = CGPass ? &CGPass->getCallGraph() : nullptr; CallGraphNode *ExternalNode = CG ? CG->getExternalCallingNode() : nullptr; Index: lib/Transforms/IPO/LoopExtractor.cpp =================================================================== --- lib/Transforms/IPO/LoopExtractor.cpp +++ lib/Transforms/IPO/LoopExtractor.cpp @@ -81,7 +81,7 @@ 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. @@ -249,6 +249,9 @@ } bool BlockExtractorPass::runOnModule(Module &M) { + if (skipModule(M)) + return false; + std::set TranslatedBlocksToNotExtract; for (unsigned i = 0, e = BlocksToNotExtract.size(); i != e; ++i) { BasicBlock *BB = BlocksToNotExtract[i]; Index: lib/Transforms/IPO/LowerBitSets.cpp =================================================================== --- lib/Transforms/IPO/LowerBitSets.cpp +++ lib/Transforms/IPO/LowerBitSets.cpp @@ -1050,6 +1050,9 @@ } bool LowerBitSets::runOnModule(Module &M) { + if (skipModule(M)) + return false; + bool Changed = buildBitSets(); Changed |= eraseBitSetMetadata(); return Changed; Index: lib/Transforms/IPO/MergeFunctions.cpp =================================================================== --- lib/Transforms/IPO/MergeFunctions.cpp +++ lib/Transforms/IPO/MergeFunctions.cpp @@ -1524,6 +1524,9 @@ } 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 Index: lib/Transforms/IPO/PartialInlining.cpp =================================================================== --- lib/Transforms/IPO/PartialInlining.cpp +++ lib/Transforms/IPO/PartialInlining.cpp @@ -149,6 +149,9 @@ } bool PartialInliner::runOnModule(Module& M) { + if (skipModule(M)) + return false; + std::vector worklist; worklist.reserve(M.size()); for (Module::iterator FI = M.begin(), FE = M.end(); FI != FE; ++FI) Index: lib/Transforms/IPO/PruneEH.cpp =================================================================== --- lib/Transforms/IPO/PruneEH.cpp +++ lib/Transforms/IPO/PruneEH.cpp @@ -64,6 +64,9 @@ bool PruneEH::runOnSCC(CallGraphSCC &SCC) { + if (skipSCC(SCC)) + return false; + SmallPtrSet SCCNodes; CallGraph &CG = getAnalysis().getCallGraph(); bool MadeChange = false; Index: lib/Transforms/IPO/StripDeadPrototypes.cpp =================================================================== --- lib/Transforms/IPO/StripDeadPrototypes.cpp +++ lib/Transforms/IPO/StripDeadPrototypes.cpp @@ -17,6 +17,7 @@ #include "llvm/Transforms/IPO/StripDeadPrototypes.h" #include "llvm/ADT/Statistic.h" #include "llvm/IR/Module.h" +#include "llvm/IR/OptBisect.h" #include "llvm/Pass.h" #include "llvm/Transforms/IPO.h" @@ -54,6 +55,9 @@ } PreservedAnalyses StripDeadPrototypesPass::run(Module &M) { + if (skipPassForModule(name(), M)) + return PreservedAnalyses::all(); + if (stripDeadPrototypes(M)) return PreservedAnalyses::none(); return PreservedAnalyses::all(); @@ -69,6 +73,9 @@ *PassRegistry::getPassRegistry()); } bool runOnModule(Module &M) override { + if (skipModule(M)) + return false; + return stripDeadPrototypes(M); } }; Index: lib/Transforms/IPO/StripSymbols.cpp =================================================================== --- lib/Transforms/IPO/StripSymbols.cpp +++ lib/Transforms/IPO/StripSymbols.cpp @@ -230,6 +230,9 @@ } bool StripSymbols::runOnModule(Module &M) { + if (skipModule(M)) + return false; + bool Changed = false; Changed |= StripDebugInfo(M); if (!OnlyDebugInfo) @@ -238,10 +241,15 @@ } 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 DeadConstants; @@ -287,6 +295,9 @@ /// 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(); Index: lib/Transforms/IPO/WholeProgramDevirt.cpp =================================================================== --- lib/Transforms/IPO/WholeProgramDevirt.cpp +++ lib/Transforms/IPO/WholeProgramDevirt.cpp @@ -264,7 +264,12 @@ 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 Index: lib/Transforms/InstCombine/InstructionCombining.cpp =================================================================== --- lib/Transforms/InstCombine/InstructionCombining.cpp +++ lib/Transforms/InstCombine/InstructionCombining.cpp @@ -3088,6 +3088,9 @@ PreservedAnalyses InstCombinePass::run(Function &F, AnalysisManager &AM) { + if (skipPassForFunction(name(), F)) + return PreservedAnalyses::all(); + auto &AC = AM.getResult(F); auto &DT = AM.getResult(F); auto &TLI = AM.getResult(F); @@ -3120,7 +3123,7 @@ } bool InstructionCombiningPass::runOnFunction(Function &F) { - if (skipOptnoneFunction(F)) + if (skipFunction(F)) return false; // Required analyses. Index: lib/Transforms/Instrumentation/PGOInstrumentation.cpp =================================================================== --- lib/Transforms/Instrumentation/PGOInstrumentation.cpp +++ lib/Transforms/Instrumentation/PGOInstrumentation.cpp @@ -779,6 +779,9 @@ } bool PGOInstrumentationGen::runOnModule(Module &M) { + if (skipModule(M)) + return false; + createIRLevelProfileFlagVariable(M); for (auto &F : M) { if (F.isDeclaration()) @@ -802,6 +805,9 @@ } 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. Index: lib/Transforms/ObjCARC/ObjCARCAPElim.cpp =================================================================== --- lib/Transforms/ObjCARC/ObjCARCAPElim.cpp +++ lib/Transforms/ObjCARC/ObjCARCAPElim.cpp @@ -132,6 +132,9 @@ 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 Index: lib/Transforms/Scalar/ADCE.cpp =================================================================== --- lib/Transforms/Scalar/ADCE.cpp +++ lib/Transforms/Scalar/ADCE.cpp @@ -130,6 +130,9 @@ } PreservedAnalyses ADCEPass::run(Function &F) { + if (skipPassForFunction(name(), F)) + return PreservedAnalyses::all(); + if (aggressiveDCE(F)) return PreservedAnalyses::none(); return PreservedAnalyses::all(); @@ -143,7 +146,7 @@ } bool runOnFunction(Function& F) override { - if (skipOptnoneFunction(F)) + if (skipFunction(F)) return false; return aggressiveDCE(F); } Index: lib/Transforms/Scalar/BDCE.cpp =================================================================== --- lib/Transforms/Scalar/BDCE.cpp +++ lib/Transforms/Scalar/BDCE.cpp @@ -59,7 +59,7 @@ false, false) bool BDCE::runOnFunction(Function& F) { - if (skipOptnoneFunction(F)) + if (skipFunction(F)) return false; DemandedBits &DB = getAnalysis(); Index: lib/Transforms/Scalar/ConstantHoisting.cpp =================================================================== --- lib/Transforms/Scalar/ConstantHoisting.cpp +++ lib/Transforms/Scalar/ConstantHoisting.cpp @@ -187,7 +187,7 @@ /// \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"); Index: lib/Transforms/Scalar/CorrelatedValuePropagation.cpp =================================================================== --- lib/Transforms/Scalar/CorrelatedValuePropagation.cpp +++ lib/Transforms/Scalar/CorrelatedValuePropagation.cpp @@ -399,7 +399,7 @@ } bool CorrelatedValuePropagation::runOnFunction(Function &F) { - if (skipOptnoneFunction(F)) + if (skipFunction(F)) return false; LVI = &getAnalysis(); Index: lib/Transforms/Scalar/DCE.cpp =================================================================== --- lib/Transforms/Scalar/DCE.cpp +++ lib/Transforms/Scalar/DCE.cpp @@ -41,7 +41,7 @@ initializeDeadInstEliminationPass(*PassRegistry::getPassRegistry()); } bool runOnBasicBlock(BasicBlock &BB) override { - if (skipOptnoneFunction(BB)) + if (skipBasicBlock(BB)) return false; auto *TLIP = getAnalysisIfAvailable(); TargetLibraryInfo *TLI = TLIP ? &TLIP->getTLI() : nullptr; @@ -122,7 +122,7 @@ } bool DCE::runOnFunction(Function &F) { - if (skipOptnoneFunction(F)) + if (skipFunction(F)) return false; auto *TLIP = getAnalysisIfAvailable(); Index: lib/Transforms/Scalar/DeadStoreElimination.cpp =================================================================== --- lib/Transforms/Scalar/DeadStoreElimination.cpp +++ lib/Transforms/Scalar/DeadStoreElimination.cpp @@ -58,7 +58,7 @@ } bool runOnFunction(Function &F) override { - if (skipOptnoneFunction(F)) + if (skipFunction(F)) return false; AA = &getAnalysis().getAAResults(); Index: lib/Transforms/Scalar/EarlyCSE.cpp =================================================================== --- lib/Transforms/Scalar/EarlyCSE.cpp +++ lib/Transforms/Scalar/EarlyCSE.cpp @@ -819,6 +819,9 @@ PreservedAnalyses EarlyCSEPass::run(Function &F, AnalysisManager &AM) { + if (skipPassForFunction(name(), F)) + return PreservedAnalyses::all(); + auto &TLI = AM.getResult(F); auto &TTI = AM.getResult(F); auto &DT = AM.getResult(F); @@ -853,7 +856,7 @@ } bool runOnFunction(Function &F) override { - if (skipOptnoneFunction(F)) + if (skipFunction(F)) return false; auto &TLI = getAnalysis().getTLI(); Index: lib/Transforms/Scalar/Float2Int.cpp =================================================================== --- lib/Transforms/Scalar/Float2Int.cpp +++ lib/Transforms/Scalar/Float2Int.cpp @@ -517,7 +517,7 @@ } bool Float2Int::runOnFunction(Function &F) { - if (skipOptnoneFunction(F)) + if (skipFunction(F)) return false; DEBUG(dbgs() << "F2I: Looking at function " << F.getName() << "\n"); Index: lib/Transforms/Scalar/GVN.cpp =================================================================== --- lib/Transforms/Scalar/GVN.cpp +++ lib/Transforms/Scalar/GVN.cpp @@ -585,6 +585,9 @@ //===----------------------------------------------------------------------===// PreservedAnalyses GVN::run(Function &F, AnalysisManager &AM) { + if (skipPassForFunction(name(), F)) + return PreservedAnalyses::all(); + // FIXME: The order of evaluation of these 'getResult' calls is very // significant! Re-ordering these variables will cause GVN when run alone to // be less effective! We should fix memdep and basic-aa to not exhibit this @@ -2676,7 +2679,7 @@ } bool runOnFunction(Function &F) override { - if (skipOptnoneFunction(F)) + if (skipFunction(F)) return false; return Impl.runImpl( Index: lib/Transforms/Scalar/IndVarSimplify.cpp =================================================================== --- lib/Transforms/Scalar/IndVarSimplify.cpp +++ lib/Transforms/Scalar/IndVarSimplify.cpp @@ -2123,7 +2123,7 @@ //===----------------------------------------------------------------------===// 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: Index: lib/Transforms/Scalar/JumpThreading.cpp =================================================================== --- lib/Transforms/Scalar/JumpThreading.cpp +++ lib/Transforms/Scalar/JumpThreading.cpp @@ -187,7 +187,7 @@ /// 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"); Index: lib/Transforms/Scalar/LICM.cpp =================================================================== --- lib/Transforms/Scalar/LICM.cpp +++ lib/Transforms/Scalar/LICM.cpp @@ -174,7 +174,7 @@ /// times on one loop. /// bool LICM::runOnLoop(Loop *L, LPPassManager &LPM) { - if (skipOptnoneFunction(L)) + if (skipLoop(L)) return false; Changed = false; Index: lib/Transforms/Scalar/LoadCombine.cpp =================================================================== --- lib/Transforms/Scalar/LoadCombine.cpp +++ lib/Transforms/Scalar/LoadCombine.cpp @@ -221,7 +221,7 @@ } bool LoadCombine::runOnBasicBlock(BasicBlock &BB) { - if (skipOptnoneFunction(BB)) + if (skipBasicBlock(BB)) return false; AA = &getAnalysis().getAAResults(); Index: lib/Transforms/Scalar/LoopDeletion.cpp =================================================================== --- lib/Transforms/Scalar/LoopDeletion.cpp +++ lib/Transforms/Scalar/LoopDeletion.cpp @@ -119,7 +119,7 @@ /// 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().getDomTree(); Index: lib/Transforms/Scalar/LoopIdiomRecognize.cpp =================================================================== --- lib/Transforms/Scalar/LoopIdiomRecognize.cpp +++ lib/Transforms/Scalar/LoopIdiomRecognize.cpp @@ -169,7 +169,7 @@ //===----------------------------------------------------------------------===// bool LoopIdiomRecognize::runOnLoop(Loop *L, LPPassManager &LPM) { - if (skipOptnoneFunction(L)) + if (skipLoop(L)) return false; CurLoop = L; Index: lib/Transforms/Scalar/LoopInstSimplify.cpp =================================================================== --- lib/Transforms/Scalar/LoopInstSimplify.cpp +++ lib/Transforms/Scalar/LoopInstSimplify.cpp @@ -65,7 +65,7 @@ } bool LoopInstSimplify::runOnLoop(Loop *L, LPPassManager &LPM) { - if (skipOptnoneFunction(L)) + if (skipLoop(L)) return false; DominatorTreeWrapperPass *DTWP = Index: lib/Transforms/Scalar/LoopLoadElimination.cpp =================================================================== --- lib/Transforms/Scalar/LoopLoadElimination.cpp +++ lib/Transforms/Scalar/LoopLoadElimination.cpp @@ -531,6 +531,9 @@ } bool runOnFunction(Function &F) override { + if (skipFunction(F)) + return false; + auto *LI = &getAnalysis().getLoopInfo(); auto *LAA = &getAnalysis(); auto *DT = &getAnalysis().getDomTree(); Index: lib/Transforms/Scalar/LoopRerollPass.cpp =================================================================== --- lib/Transforms/Scalar/LoopRerollPass.cpp +++ lib/Transforms/Scalar/LoopRerollPass.cpp @@ -1536,7 +1536,7 @@ } bool LoopReroll::runOnLoop(Loop *L, LPPassManager &LPM) { - if (skipOptnoneFunction(L)) + if (skipLoop(L)) return false; AA = &getAnalysis().getAAResults(); Index: lib/Transforms/Scalar/LoopRotation.cpp =================================================================== --- lib/Transforms/Scalar/LoopRotation.cpp +++ lib/Transforms/Scalar/LoopRotation.cpp @@ -585,7 +585,7 @@ } bool runOnLoop(Loop *L, LPPassManager &LPM) override { - if (skipOptnoneFunction(L)) + if (skipLoop(L)) return false; Function &F = *L->getHeader()->getParent(); Index: lib/Transforms/Scalar/LoopSimplifyCFG.cpp =================================================================== --- lib/Transforms/Scalar/LoopSimplifyCFG.cpp +++ lib/Transforms/Scalar/LoopSimplifyCFG.cpp @@ -91,7 +91,7 @@ /// 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().getDomTree(); Index: lib/Transforms/Scalar/LoopStrengthReduce.cpp =================================================================== --- lib/Transforms/Scalar/LoopStrengthReduce.cpp +++ lib/Transforms/Scalar/LoopStrengthReduce.cpp @@ -5002,7 +5002,7 @@ } bool LoopStrengthReduce::runOnLoop(Loop *L, LPPassManager & /*LPM*/) { - if (skipOptnoneFunction(L)) + if (skipLoop(L)) return false; auto &IU = getAnalysis(); Index: lib/Transforms/Scalar/LoopUnrollPass.cpp =================================================================== --- lib/Transforms/Scalar/LoopUnrollPass.cpp +++ lib/Transforms/Scalar/LoopUnrollPass.cpp @@ -787,7 +787,7 @@ Optional ProvidedRuntime; bool runOnLoop(Loop *L, LPPassManager &) override { - if (skipOptnoneFunction(L)) + if (skipLoop(L)) return false; Function &F = *L->getHeader()->getParent(); Index: lib/Transforms/Scalar/LoopUnswitch.cpp =================================================================== --- lib/Transforms/Scalar/LoopUnswitch.cpp +++ lib/Transforms/Scalar/LoopUnswitch.cpp @@ -420,7 +420,7 @@ } bool LoopUnswitch::runOnLoop(Loop *L, LPPassManager &LPM_Ref) { - if (skipOptnoneFunction(L)) + if (skipLoop(L)) return false; AC = &getAnalysis().getAssumptionCache( Index: lib/Transforms/Scalar/LoopVersioningLICM.cpp =================================================================== --- lib/Transforms/Scalar/LoopVersioningLICM.cpp +++ lib/Transforms/Scalar/LoopVersioningLICM.cpp @@ -553,7 +553,7 @@ } bool LoopVersioningLICM::runOnLoop(Loop *L, LPPassManager &LPM) { - if (skipOptnoneFunction(L)) + if (skipLoop(L)) return false; Changed = false; // Get Analysis information. Index: lib/Transforms/Scalar/LowerAtomic.cpp =================================================================== --- lib/Transforms/Scalar/LowerAtomic.cpp +++ lib/Transforms/Scalar/LowerAtomic.cpp @@ -116,7 +116,7 @@ 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; ) { Index: lib/Transforms/Scalar/MemCpyOptimizer.cpp =================================================================== --- lib/Transforms/Scalar/MemCpyOptimizer.cpp +++ lib/Transforms/Scalar/MemCpyOptimizer.cpp @@ -1361,7 +1361,7 @@ /// 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; Index: lib/Transforms/Scalar/MergedLoadStoreMotion.cpp =================================================================== --- lib/Transforms/Scalar/MergedLoadStoreMotion.cpp +++ lib/Transforms/Scalar/MergedLoadStoreMotion.cpp @@ -565,6 +565,9 @@ /// \brief Run the transformation for each function /// bool MergedLoadStoreMotion::runOnFunction(Function &F) { + if (skipFunction(F)) + return false; + auto *MDWP = getAnalysisIfAvailable(); MD = MDWP ? &MDWP->getMemDep() : nullptr; AA = &getAnalysis().getAAResults(); Index: lib/Transforms/Scalar/NaryReassociate.cpp =================================================================== --- lib/Transforms/Scalar/NaryReassociate.cpp +++ lib/Transforms/Scalar/NaryReassociate.cpp @@ -208,7 +208,7 @@ } bool NaryReassociate::runOnFunction(Function &F) { - if (skipOptnoneFunction(F)) + if (skipFunction(F)) return false; AC = &getAnalysis().getAssumptionCache(F); Index: lib/Transforms/Scalar/Reassociate.cpp =================================================================== --- lib/Transforms/Scalar/Reassociate.cpp +++ lib/Transforms/Scalar/Reassociate.cpp @@ -2257,7 +2257,7 @@ } bool Reassociate::runOnFunction(Function &F) { - if (skipOptnoneFunction(F)) + if (skipFunction(F)) return false; // Reassociate needs for each instruction to have its operands already Index: lib/Transforms/Scalar/SCCP.cpp =================================================================== --- lib/Transforms/Scalar/SCCP.cpp +++ lib/Transforms/Scalar/SCCP.cpp @@ -1568,7 +1568,7 @@ // 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"); @@ -1705,6 +1705,9 @@ } bool IPSCCP::runOnModule(Module &M) { + if (skipModule(M)) + return false; + const DataLayout &DL = M.getDataLayout(); const TargetLibraryInfo *TLI = &getAnalysis().getTLI(); Index: lib/Transforms/Scalar/SROA.cpp =================================================================== --- lib/Transforms/Scalar/SROA.cpp +++ lib/Transforms/Scalar/SROA.cpp @@ -4229,6 +4229,9 @@ } PreservedAnalyses SROA::run(Function &F, AnalysisManager &AM) { + if (skipPassForFunction(name(), F)) + return PreservedAnalyses::all(); + return runImpl(F, AM.getResult(F), AM.getResult(F)); } @@ -4246,7 +4249,7 @@ initializeSROALegacyPassPass(*PassRegistry::getPassRegistry()); } bool runOnFunction(Function &F) override { - if (skipOptnoneFunction(F)) + if (skipFunction(F)) return false; auto PA = Impl.runImpl( Index: lib/Transforms/Scalar/ScalarReplAggregates.cpp =================================================================== --- lib/Transforms/Scalar/ScalarReplAggregates.cpp +++ lib/Transforms/Scalar/ScalarReplAggregates.cpp @@ -1026,7 +1026,7 @@ bool SROA::runOnFunction(Function &F) { - if (skipOptnoneFunction(F)) + if (skipFunction(F)) return false; bool Changed = performPromotion(F); Index: lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp =================================================================== --- lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp +++ lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp @@ -1064,7 +1064,7 @@ } bool SeparateConstOffsetFromGEP::runOnFunction(Function &F) { - if (skipOptnoneFunction(F)) + if (skipFunction(F)) return false; if (DisableSeparateConstOffsetFromGEP) Index: lib/Transforms/Scalar/SimplifyCFGPass.cpp =================================================================== --- lib/Transforms/Scalar/SimplifyCFGPass.cpp +++ lib/Transforms/Scalar/SimplifyCFGPass.cpp @@ -187,6 +187,9 @@ PreservedAnalyses SimplifyCFGPass::run(Function &F, AnalysisManager &AM) { + if (skipPassForFunction(name(), F)) + return PreservedAnalyses::all(); + auto &TTI = AM.getResult(F); auto &AC = AM.getResult(F); @@ -212,7 +215,7 @@ if (PredicateFtor && !PredicateFtor(F)) return false; - if (skipOptnoneFunction(F)) + if (skipFunction(F)) return false; AssumptionCache *AC = Index: lib/Transforms/Scalar/SpeculativeExecution.cpp =================================================================== --- lib/Transforms/Scalar/SpeculativeExecution.cpp +++ lib/Transforms/Scalar/SpeculativeExecution.cpp @@ -112,7 +112,7 @@ } bool SpeculativeExecution::runOnFunction(Function &F) { - if (skipOptnoneFunction(F)) + if (skipFunction(F)) return false; TTI = &getAnalysis().getTTI(F); Index: lib/Transforms/Scalar/StraightLineStrengthReduce.cpp =================================================================== --- lib/Transforms/Scalar/StraightLineStrengthReduce.cpp +++ lib/Transforms/Scalar/StraightLineStrengthReduce.cpp @@ -684,7 +684,7 @@ } bool StraightLineStrengthReduce::runOnFunction(Function &F) { - if (skipOptnoneFunction(F)) + if (skipFunction(F)) return false; TTI = &getAnalysis().getTTI(F); Index: lib/Transforms/Scalar/TailRecursionElimination.cpp =================================================================== --- lib/Transforms/Scalar/TailRecursionElimination.cpp +++ lib/Transforms/Scalar/TailRecursionElimination.cpp @@ -157,7 +157,7 @@ } bool TailCallElim::runOnFunction(Function &F) { - if (skipOptnoneFunction(F)) + if (skipFunction(F)) return false; if (F.getFnAttribute("disable-tail-calls").getValueAsString() == "true") Index: lib/Transforms/Utils/Mem2Reg.cpp =================================================================== --- lib/Transforms/Utils/Mem2Reg.cpp +++ lib/Transforms/Utils/Mem2Reg.cpp @@ -59,13 +59,13 @@ false, false) bool PromotePass::runOnFunction(Function &F) { + if (skipFunction(F)) + return false; + std::vector 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().getDomTree(); Index: lib/Transforms/Vectorize/BBVectorize.cpp =================================================================== --- lib/Transforms/Vectorize/BBVectorize.cpp +++ lib/Transforms/Vectorize/BBVectorize.cpp @@ -397,7 +397,7 @@ 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() << Index: lib/Transforms/Vectorize/LoopVectorize.cpp =================================================================== --- lib/Transforms/Vectorize/LoopVectorize.cpp +++ lib/Transforms/Vectorize/LoopVectorize.cpp @@ -1691,6 +1691,9 @@ BlockFrequency ColdEntryFreq; bool runOnFunction(Function &F) override { + if (skipFunction(F)) + return false; + SE = &getAnalysis().getSE(); LI = &getAnalysis().getLoopInfo(); TTI = &getAnalysis().getTTI(F); Index: lib/Transforms/Vectorize/SLPVectorizer.cpp =================================================================== --- lib/Transforms/Vectorize/SLPVectorizer.cpp +++ lib/Transforms/Vectorize/SLPVectorizer.cpp @@ -3393,7 +3393,7 @@ } bool runOnFunction(Function &F) override { - if (skipOptnoneFunction(F)) + if (skipFunction(F)) return false; SE = &getAnalysis().getSE(); Index: test/Other/opt-bisect-helper.py =================================================================== --- test/Other/opt-bisect-helper.py +++ test/Other/opt-bisect-helper.py @@ -0,0 +1,39 @@ +#!/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) Index: test/Other/opt-bisect-legacy-pass-manager.ll =================================================================== --- test/Other/opt-bisect-legacy-pass-manager.ll +++ test/Other/opt-bisect-legacy-pass-manager.ll @@ -0,0 +1,148 @@ +; 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 (<>) +; 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 (<>) + +; 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 (<>) +; 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 (<>) + + +; 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 +} Index: test/Other/opt-bisect-new-pass-manager.ll =================================================================== --- test/Other/opt-bisect-new-pass-manager.ll +++ test/Other/opt-bisect-new-pass-manager.ll @@ -0,0 +1,137 @@ +; 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. +; +; Because the exact set of optimizations that will be run is expected to +; change over time, the checks for disabling passes are written in a +; conservative way that avoids assumptions about which specific passes +; will be disabled. + +; RUN: opt -disable-output -disable-verify \ +; RUN: -passes=no-op-module -opt-bisect-limit=-1 %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-MODULE-PASS +; CHECK-MODULE-PASS: BISECT: running pass (1) NoOpModulePass on module + +; RUN: opt -disable-output -disable-verify \ +; RUN: -passes=no-op-module -opt-bisect-limit=0 %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-LIMIT-MODULE-PASS +; CHECK-LIMIT-MODULE-PASS: BISECT: NOT running pass (1) NoOpModulePass on module + +; RUN: opt -disable-output -disable-verify \ +; RUN: -passes=no-op-function -opt-bisect-limit=-1 %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-FUNCTION-PASS +; CHECK-FUNCTION-PASS: BISECT: running pass (1) NoOpFunctionPass on function (f1) +; CHECK-FUNCTION-PASS: BISECT: running pass (2) NoOpFunctionPass on function (f2) +; CHECK-FUNCTION-PASS: BISECT: running pass (3) NoOpFunctionPass on function (f3) + +; RUN: opt -disable-output -disable-verify \ +; RUN: -passes=no-op-function -opt-bisect-limit=2 %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-LIMIT-FUNCTION-PASS +; CHECK-LIMIT-FUNCTION-PASS: BISECT: running pass (1) NoOpFunctionPass on function (f1) +; CHECK-LIMIT-FUNCTION-PASS: BISECT: running pass (2) NoOpFunctionPass on function (f2) +; CHECK-LIMIT-FUNCTION-PASS: BISECT: NOT running pass (3) NoOpFunctionPass on function (f3) + +; RUN: opt -disable-output -disable-verify \ +; RUN: -passes=no-op-cgscc -opt-bisect-limit=-1 %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-CGSCC-PASS +; CHECK-CGSCC-PASS: BISECT: running pass (1) NoOpCGSCCPass on SCC (f2) +; CHECK-CGSCC-PASS: BISECT: running pass (2) NoOpCGSCCPass on SCC (f3) +; CHECK-CGSCC-PASS: BISECT: running pass (3) NoOpCGSCCPass on SCC (f1) + +; RUN: opt -disable-output -disable-verify \ +; RUN: -passes=no-op-cgscc -opt-bisect-limit=2 %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-LIMIT-CGSCC-PASS +; CHECK-LIMIT-CGSCC-PASS: BISECT: running pass (1) NoOpCGSCCPass on SCC (f2) +; CHECK-LIMIT-CGSCC-PASS: BISECT: running pass (2) NoOpCGSCCPass on SCC (f3) +; CHECK-LIMIT-CGSCC-PASS: BISECT: NOT running pass (3) NoOpCGSCCPass on SCC (f1) + +; RUN: opt -disable-output -disable-verify \ +; RUN: -passes=no-op-loop -opt-bisect-limit=-1 %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-LOOP-PASS +; CHECK-LOOP-PASS: BISECT: running pass (1) NoOpLoopPass on loop +; CHECK-LOOP-PASS: BISECT: running pass (2) NoOpLoopPass on loop +; CHECK-LOOP-PASS: BISECT: running pass (3) NoOpLoopPass on loop +; CHECK-LOOP-PASS: BISECT: running pass (4) NoOpLoopPass on loop +; CHECK-LOOP-PASS: BISECT: running pass (5) NoOpLoopPass on loop + +; RUN: opt -disable-output -disable-verify \ +; RUN: -passes=no-op-loop -opt-bisect-limit=2 %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-LIMIT-LOOP-PASS +; CHECK-LIMIT-LOOP-PASS: BISECT: running pass (1) NoOpLoopPass on loop +; CHECK-LIMIT-LOOP-PASS: BISECT: running pass (2) NoOpLoopPass on loop +; CHECK-LIMIT-LOOP-PASS: BISECT: NOT running pass (3) NoOpLoopPass on loop +; CHECK-LIMIT-LOOP-PASS: BISECT: NOT running pass (4) NoOpLoopPass on loop +; CHECK-LIMIT-LOOP-PASS: BISECT: NOT running pass (5) NoOpLoopPass on loop + +; RUN: opt -disable-output -disable-verify -opt-bisect-limit=-1 \ +; RUN: -passes='no-op-module,cgscc(no-op-cgscc,function(no-op-function,loop(no-op-loop)))' %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-MULTI-PASS +; CHECK-MULTI-PASS: BISECT: running pass (1) NoOpModulePass on module +; CHECK-MULTI-PASS: BISECT: running pass (2) NoOpCGSCCPass on SCC (f2) +; CHECK-MULTI-PASS: BISECT: running pass (3) NoOpFunctionPass on function (f2) +; CHECK-MULTI-PASS: BISECT: running pass (4) NoOpCGSCCPass on SCC (f3) +; CHECK-MULTI-PASS: BISECT: running pass (5) NoOpFunctionPass on function (f3) +; CHECK-MULTI-PASS: BISECT: running pass (6) NoOpCGSCCPass on SCC (f1) +; CHECK-MULTI-PASS: BISECT: running pass (7) NoOpFunctionPass on function (f1) +; CHECK-MULTI-PASS: BISECT: running pass (8) NoOpLoopPass on loop +; CHECK-MULTI-PASS: BISECT: running pass (9) NoOpLoopPass on loop +; CHECK-MULTI-PASS: BISECT: running pass (10) NoOpLoopPass on loop +; CHECK-MULTI-PASS: BISECT: running pass (11) NoOpLoopPass on loop +; CHECK-MULTI-PASS: BISECT: running pass (12) NoOpLoopPass on loop + +; RUN: opt -disable-output -disable-verify -opt-bisect-limit=8 \ +; RUN: -passes='no-op-module,cgscc(no-op-cgscc,function(no-op-function,loop(no-op-loop)))' %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-LIMIT-MULTI-PASS +; CHECK-LIMIT-MULTI-PASS: BISECT: running pass (1) NoOpModulePass on module +; CHECK-LIMIT-MULTI-PASS: BISECT: running pass (2) NoOpCGSCCPass on SCC (f2) +; CHECK-LIMIT-MULTI-PASS: BISECT: running pass (3) NoOpFunctionPass on function (f2) +; CHECK-LIMIT-MULTI-PASS: BISECT: running pass (4) NoOpCGSCCPass on SCC (f3) +; CHECK-LIMIT-MULTI-PASS: BISECT: running pass (5) NoOpFunctionPass on function (f3) +; CHECK-LIMIT-MULTI-PASS: BISECT: running pass (6) NoOpCGSCCPass on SCC (f1) +; CHECK-LIMIT-MULTI-PASS: BISECT: running pass (7) NoOpFunctionPass on function (f1) +; CHECK-LIMIT-MULTI-PASS: BISECT: running pass (8) NoOpLoopPass on loop +; CHECK-LIMIT-MULTI-PASS: BISECT: NOT running pass (9) NoOpLoopPass on loop +; CHECK-LIMIT-MULTI-PASS: BISECT: NOT running pass (10) NoOpLoopPass on loop +; CHECK-LIMIT-MULTI-PASS: BISECT: NOT running pass (11) NoOpLoopPass on loop +; CHECK-LIMIT-MULTI-PASS: BISECT: NOT running pass (12) NoOpLoopPass 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 +}