Index: include/llvm/Analysis/CGSCCPassManager.h =================================================================== --- include/llvm/Analysis/CGSCCPassManager.h +++ include/llvm/Analysis/CGSCCPassManager.h @@ -364,6 +364,9 @@ InvalidSCCSet, nullptr, nullptr, InlinedInternalEdges}; + // Get pass instrumentation handler + PassInstrumentation PI = AM.getPassInstrumentation(M); + PreservedAnalyses PA = PreservedAnalyses::all(); CG.buildRefSCCs(); for (auto RCI = CG.postorder_ref_scc_begin(), @@ -428,8 +431,13 @@ UR.UpdatedRC = nullptr; UR.UpdatedC = nullptr; + if (!PI.runBeforePass(Pass.name(), *C)) + continue; + PreservedAnalyses PassPA = Pass.run(*C, CGAM, CG, UR); + PI.runAfterPass(Pass.name(), *C); + // Update the SCC and RefSCC if necessary. C = UR.UpdatedC ? UR.UpdatedC : C; RC = UR.UpdatedRC ? UR.UpdatedRC : RC; @@ -615,12 +623,20 @@ if (CG.lookupSCC(*N) != CurrentC) continue; - PreservedAnalyses PassPA = Pass.run(N->getFunction(), FAM); + Function& F = N->getFunction(); + + PassInstrumentation PI = FAM.getPassInstrumentation(F); + if (!PI.runBeforePass(Pass.name(), F)) + continue; + + PreservedAnalyses PassPA = Pass.run(F, FAM); + + PI.runAfterPass(Pass.name(), F); // We know that the function pass couldn't have invalidated any other // function's analyses (that's the contract of a function pass), so // directly handle the function analysis manager's invalidation here. - FAM.invalidate(N->getFunction(), PassPA); + FAM.invalidate(F, PassPA); // Then intersect the preserved set so that invalidation of module // analyses will eventually occur when the module pass completes. @@ -691,6 +707,11 @@ LazyCallGraph &CG, CGSCCUpdateResult &UR) { PreservedAnalyses PA = PreservedAnalyses::all(); + const ModuleAnalysisManager &MAM = + AM.getResult(InitialC, CG).getManager(); + Module &M = *InitialC.begin()->getFunction().getParent(); + PassInstrumentation PI = MAM.getPassInstrumentation(M); + // The SCC may be refined while we are running passes over it, so set up // a pointer that we can update. LazyCallGraph::SCC *C = &InitialC; @@ -733,8 +754,14 @@ auto CallCounts = ScanSCC(*C, CallHandles); for (int Iteration = 0;; ++Iteration) { + + if (!PI.runBeforePass(Pass.name(), *C)) + continue; + PreservedAnalyses PassPA = Pass.run(*C, AM, CG, UR); + PI.runAfterPass(Pass.name(), *C); + // If the SCC structure has changed, bail immediately and let the outer // CGSCC layer handle any iteration to reflect the refined structure. if (UR.UpdatedC && UR.UpdatedC != C) { Index: include/llvm/IR/LLVMContext.h =================================================================== --- include/llvm/IR/LLVMContext.h +++ include/llvm/IR/LLVMContext.h @@ -32,6 +32,7 @@ class LLVMContextImpl; class Module; class OptPassGate; +class PassInstrumentation; template class SmallVectorImpl; class SMDiagnostic; class StringRef; @@ -326,6 +327,9 @@ /// LLVMContext is used by compilation. void setOptPassGate(OptPassGate&); + /// Access the object that handles instrumentation of pass execution + PassInstrumentation &getPassInstrumentation() const; + private: // Module needs access to the add/removeModule methods. friend class Module; Index: include/llvm/IR/PassInstrumentation.h =================================================================== --- /dev/null +++ include/llvm/IR/PassInstrumentation.h @@ -0,0 +1,182 @@ +//===- llvm/IR/PassInstrumentation.h ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the PassInstrumentation class that is used to provide +// instrumentation points into the pass execution by PassManager. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_IR_PASSINSTRUMENTATION_H +#define LLVM_IR_PASSINSTRUMENTATION_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/Pass.h" + +namespace llvm { + +class PreservedAnalyses; + +class PassExecutionCounter { + unsigned count = 0; + +public: + PassExecutionCounter() = default; + PassExecutionCounter(const PassExecutionCounter &PC) = default; + PassExecutionCounter(PassExecutionCounter &&PC) = default; + + PassExecutionCounter& operator=(const PassExecutionCounter& PC) { count = PC.count; return *this; } + bool zero() const { return count == 0; } + // preincrement, advance the counter + void operator++() { count++; } +}; + +//===----------------------------------------------------------------------===// +/// This class provides the main interface for pass instrumentation. +/// It handles both registration of callbacks and exposes instrumentation calls +/// to be invoked by PassManagers. + +class PassInstrumentationImpl { +public: + struct IRUnitWrapper; + + using BeforePassFunc = bool(StringRef, IRUnitWrapper&, PassExecutionCounter); + using AfterPassFunc = void(StringRef, IRUnitWrapper&, PassExecutionCounter); + +public: + PassInstrumentationImpl() {} + + template + bool runBeforePass(StringRef PassID, IRUnitT&) const; + + template + void runAfterPass(StringRef PassID, IRUnitT&) const; + void runStartPipeline() const; + void runEndPipeline() const; + + void registerBeforePassCallback(const std::function &C) { + BeforePassCallbacks.push_back(C); + } + + void registerAfterPassCallback(const std::function &C) { + AfterPassCallbacks.push_back(C); + } + + void registerStartPipelineCallback(const std::function &C) { + StartPipelineCallbacks.push_back(C); + } + void registerEndPipelineCallback(const std::function &C) { + EndPipelineCallbacks.push_back(C); + } + + PassExecutionCounter& getPC() { return PC; } + +private: + mutable PassExecutionCounter PC; + + // instrumentation callbacks + SmallVector, 2> BeforePassCallbacks; + SmallVector, 2> AfterPassCallbacks; + SmallVector, 2> StartPipelineCallbacks; + SmallVector, 2> EndPipelineCallbacks; +}; + +class PassInstrumentation { + PassInstrumentationImpl* Impl; + +public: + PassInstrumentation() : Impl(nullptr) { } + + explicit PassInstrumentation(PassInstrumentationImpl &Impl) : Impl(&Impl) {} + + // Provide value semantics. + PassInstrumentation(const PassInstrumentation &PI) : Impl(PI.Impl) {} + PassInstrumentation(PassInstrumentation &&PI) : Impl(PI.Impl) {} + + PassInstrumentation &operator=(const PassInstrumentation &PI) { + Impl = PI.Impl; + return *this; + } + PassInstrumentation &operator=(PassInstrumentation &&PI) { + Impl = PI.Impl; + return *this; + } + + // pass-instrumentation calls + template + bool runBeforePass(StringRef PassID, IRUnitT& IR) { + return (Impl ? Impl->runBeforePass(PassID, IR) : true); + } + + template + void runAfterPass(StringRef PassID, IRUnitT& IR) { + if (Impl) Impl->runAfterPass(PassID, IR); + } + + void runStartPipeline() { + if (Impl) Impl->runStartPipeline(); + } + + void runEndPipeline() { + if (Impl) Impl->runEndPipeline(); + } + + void registerBeforePassCallback(const std::function &C) { + assert(Impl); + Impl->registerBeforePassCallback(C); + } + + void registerAfterPassCallback(const std::function &C) { + assert(Impl); + Impl->registerAfterPassCallback(C); + } + + void registerStartPipelineCallback(const std::function &C) { + assert(Impl); + Impl->registerStartPipelineCallback(C); + } + + void registerEndPpelineCallback(const std::function &C) { + assert(Impl); + Impl->registerEndPipelineCallback(C); + } + + PassExecutionCounter& getPC() { + assert(Impl); + return Impl->getPC(); + } + + /// Handle invalidation from the pass manager. + /// + /// On attempt to invalidate just return false. There is nothing to become invalid here. + bool invalidate(Module &, const class llvm::PreservedAnalyses&, ...) { + return false; + } + bool invalidate(Function &, const class llvm::PreservedAnalyses&, ...) { + return false; + } + +}; + +class PassInstrumentationWrapperPass : public ImmutablePass { + PassInstrumentationImpl PIImpl; + + virtual void anchor(); + +public: + static char ID; + + PassInstrumentationWrapperPass(); + explicit PassInstrumentationWrapperPass(const PassInstrumentationImpl &TLI); + + PassInstrumentation getPI() { return PassInstrumentation(PIImpl); } +}; + +} // End llvm namespace + +#endif Index: include/llvm/IR/PassManager.h =================================================================== --- include/llvm/IR/PassManager.h +++ include/llvm/IR/PassManager.h @@ -44,6 +44,7 @@ #include "llvm/ADT/TinyPtrVector.h" #include "llvm/IR/Function.h" #include "llvm/IR/Module.h" +#include "llvm/IR/PassInstrumentation.h" #include "llvm/IR/PassManagerInternal.h" #include "llvm/Support/Debug.h" #include "llvm/Support/TypeName.h" @@ -402,6 +403,8 @@ } }; +class PassInstrumentationAnalysis; + /// Manages a sequence of passes over a particular unit of IR. /// /// A pass manager contains a sequence of passes to run over a particular unit @@ -445,6 +448,8 @@ ExtraArgTs... ExtraArgs) { PreservedAnalyses PA = PreservedAnalyses::all(); + PassInstrumentation PI = AM.getPassInstrumentation(IR, std::forward(ExtraArgs)...); + if (DebugLogging) dbgs() << "Starting " << getTypeName() << " pass manager run.\n"; @@ -452,9 +457,13 @@ if (DebugLogging) dbgs() << "Running pass: " << Passes[Idx]->name() << " on " << IR.getName() << "\n"; + if (!PI.runBeforePass(Passes[Idx]->name(), IR)) + continue; PreservedAnalyses PassPA = Passes[Idx]->run(IR, AM, ExtraArgs...); + PI.runAfterPass(Passes[Idx]->name(), IR); + // Update the analysis manager as each pass runs and potentially // invalidates analyses. AM.invalidate(IR, PassPA); @@ -680,6 +689,28 @@ AnalysisResultLists.clear(); } + /// Get the result of a PassInstrumentationAnalysis for a given IR unit. + PassInstrumentation getPassInstrumentation(IRUnitT &IR, ExtraArgTs... ExtraArgs, ...) { + if (!isRegisteredAnalysis()) + return PassInstrumentation(); + return getResult(IR, std::forward(ExtraArgs)...); + } + + /// Get the result of a PassInstrumentationAnalysis for a given IR unit. + PassInstrumentation getPassInstrumentation(IRUnitT &IR) const { + if (!isRegisteredAnalysis()) + return PassInstrumentation(); + PassInstrumentation* PI = + getCachedResult(IR); + assert(PI && "PassInstrumentationAnalysis is required to be already present"); + return *PI; + } + + template + bool isRegisteredAnalysis() const { + return AnalysisPasses.count(PassT::ID()) != 0; + } + /// Get the result of an analysis pass for a given IR unit. /// /// Runs the analysis if a cached result is not available. @@ -1147,6 +1178,28 @@ const AnalysisManagerT *AM; }; +/// Pseudo-analysis pass that exposes the \c PassInstrumentation for a module being processed. +class PassInstrumentationAnalysis : public AnalysisInfoMixin { + friend AnalysisInfoMixin; + static AnalysisKey Key; + + PassInstrumentationImpl &PIImpl; + +public: + PassInstrumentationAnalysis(PassInstrumentationImpl&_PIImpl) + : PIImpl(_PIImpl) { } + + typedef PassInstrumentation Result; + + PassInstrumentation run(Module &, ModuleAnalysisManager &) { + return PassInstrumentation(PIImpl); + } + + PassInstrumentation run(Function &, FunctionAnalysisManager &) { + return PassInstrumentation(PIImpl); + } +}; + template AnalysisKey OuterAnalysisManagerProxy::Key; @@ -1192,13 +1245,19 @@ FunctionAnalysisManager &FAM = AM.getResult(M).getManager(); + PassInstrumentation PI = AM.getPassInstrumentation(M); + PreservedAnalyses PA = PreservedAnalyses::all(); for (Function &F : M) { if (F.isDeclaration()) continue; + if (!PI.runBeforePass(Pass.name(), F)) + continue; PreservedAnalyses PassPA = Pass.run(F, FAM); + PI.runAfterPass(Pass.name(), F); + // We know that the function pass couldn't have invalidated any other // function's analyses (that's the contract of a function pass), so // directly handle the function analysis manager's invalidation here. @@ -1303,9 +1362,15 @@ template PreservedAnalyses run(IRUnitT &Arg, AnalysisManagerT &AM, Ts &&... Args) { + PassInstrumentation PI = AM.getPassInstrumentation(Arg, std::forward(Args)...); + auto PA = PreservedAnalyses::all(); - for (int i = 0; i < Count; ++i) + for (int i = 0; i < Count; ++i) { + if (!PI.runBeforePass(P.name(), Arg)) + continue; PA.intersect(P.run(Arg, AM, std::forward(Args)...)); + PI.runAfterPass(P.name(), Arg); + } return PA; } Index: include/llvm/Passes/PassBuilder.h =================================================================== --- include/llvm/Passes/PassBuilder.h +++ include/llvm/Passes/PassBuilder.h @@ -57,6 +57,9 @@ class PassBuilder { TargetMachine *TM; Optional PGOOpt; + std::unique_ptr PIImpl; + + PassInstrumentationImpl* getPassInst() const { return PIImpl.get(); } public: /// A struct to capture parsed pass pipeline names. Index: include/llvm/Transforms/Scalar/LoopPassManager.h =================================================================== --- include/llvm/Transforms/Scalar/LoopPassManager.h +++ include/llvm/Transforms/Scalar/LoopPassManager.h @@ -321,6 +321,9 @@ // FIXME: Consider changing the order in LoopInfo. internal::appendLoopsToWorklist(reverse(LI), Worklist); + // Get pass instrumentation handler + PassInstrumentation PI = AM.getPassInstrumentation(F); + do { Loop *L = Worklist.pop_back_val(); @@ -337,8 +340,12 @@ assert(L->isRecursivelyLCSSAForm(LAR.DT, LI) && "Loops must remain in LCSSA form!"); #endif - + if (!PI.runBeforePass(Pass.name(), *L)) + continue; PreservedAnalyses PassPA = Pass.run(*L, LAM, LAR, Updater); + + PI.runAfterPass(Pass.name(), *L); + // FIXME: We should verify the set of analyses relevant to Loop passes // are preserved. Index: lib/Analysis/CGSCCPassManager.cpp =================================================================== --- lib/Analysis/CGSCCPassManager.cpp +++ lib/Analysis/CGSCCPassManager.cpp @@ -54,6 +54,12 @@ CGSCCUpdateResult &>::run(LazyCallGraph::SCC &InitialC, CGSCCAnalysisManager &AM, LazyCallGraph &G, CGSCCUpdateResult &UR) { + // Get pass instrumentation handler + const ModuleAnalysisManager &MAM = + AM.getResult(InitialC, G).getManager(); + Module &M = *InitialC.begin()->getFunction().getParent(); + PassInstrumentation PI = MAM.getPassInstrumentation(M); + PreservedAnalyses PA = PreservedAnalyses::all(); if (DebugLogging) @@ -67,8 +73,13 @@ if (DebugLogging) dbgs() << "Running pass: " << Pass->name() << " on " << *C << "\n"; + if (!PI.runBeforePass(Pass->name(), *C)) + continue; + PreservedAnalyses PassPA = Pass->run(*C, AM, G, UR); + PI.runAfterPass(Pass->name(), *C); + // Update the SCC if necessary. C = UR.UpdatedC ? UR.UpdatedC : C; Index: lib/IR/CMakeLists.txt =================================================================== --- lib/IR/CMakeLists.txt +++ lib/IR/CMakeLists.txt @@ -41,6 +41,7 @@ Operator.cpp OptBisect.cpp Pass.cpp + PassInstrumentation.cpp PassManager.cpp PassRegistry.cpp SafepointIRVerifier.cpp Index: lib/IR/PassInstrumentation.cpp =================================================================== --- /dev/null +++ lib/IR/PassInstrumentation.cpp @@ -0,0 +1,54 @@ +//===- PassInstrumentation.h - Pass Instrumentation interface ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/PassInstrumentation.h" +#include "llvm/Analysis/LazyCallGraph.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/IR/PassManager.h" + + +namespace llvm { + +AnalysisKey PassInstrumentationAnalysis::Key; + +char PassInstrumentationWrapperPass::ID = 0; + +void PassInstrumentationWrapperPass::anchor() {} + +template +bool PassInstrumentationImpl::runBeforePass(StringRef PassID, IRUnitT& IR) const { + ++PC; + bool shouldRun = true; + for (auto& C : BeforePassCallbacks) + shouldRun &= C(PassID, &IR, PC); + return shouldRun; +} + +template +void PassInstrumentationImpl::runAfterPass(StringRef PassID, IRUnitT& IR) const { + for (auto& C : AfterPassCallbacks) + C(PassID, &IR, PC); +} + +void PassInstrumentationImpl::runStartPipeline() const { + for (auto& C : StartPipelineCallbacks) + C(); +} + +void PassInstrumentationImpl::runEndPipeline() const { + for (auto& C : EndPipelineCallbacks) + C(); +} + +template <> bool PassInstrumentationImpl::runBeforePass(StringRef PassID, Module& IR) const; +template <> bool PassInstrumentationImpl::runBeforePass(StringRef PassID, Function& IR) const; +template <> bool PassInstrumentationImpl::runBeforePass(StringRef PassID, Loop& IR) const; +template <> bool PassInstrumentationImpl::runBeforePass(StringRef PassID, LazyCallGraph::SCC& IR) const; + +} Index: lib/Passes/PassBuilder.cpp =================================================================== --- lib/Passes/PassBuilder.cpp +++ lib/Passes/PassBuilder.cpp @@ -303,6 +303,8 @@ MAM.registerPass([&] { return CREATE_PASS; }); #include "PassRegistry.def" + MAM.registerPass([&] { return PassInstrumentationAnalysis(*this->getPassInst()); }); + for (auto &C : ModuleAnalysisRegistrationCallbacks) C(MAM); } @@ -321,6 +323,8 @@ FAM.registerPass([&] { return CREATE_PASS; }); #include "PassRegistry.def" + FAM.registerPass([&] { return PassInstrumentationAnalysis(*this->getPassInst()); }); + for (auto &C : FunctionAnalysisRegistrationCallbacks) C(FAM); }