Index: llvm/include/llvm/Analysis/CGSCCPassManager.h =================================================================== --- llvm/include/llvm/Analysis/CGSCCPassManager.h +++ llvm/include/llvm/Analysis/CGSCCPassManager.h @@ -508,7 +508,7 @@ PassPA = Pass.run(F, FAM); } - PI.runAfterPass(Pass, F); + PI.runAfterPass(Pass, F, PassPA); // We know that the function pass couldn't have invalidated any other // function's analyses (that's the contract of a function pass), so @@ -639,9 +639,9 @@ PreservedAnalyses PassPA = Pass.run(*C, AM, CG, UR); if (UR.InvalidatedSCCs.count(C)) - PI.runAfterPassInvalidated(Pass); + PI.runAfterPassInvalidated(Pass, PassPA); else - PI.runAfterPass(Pass, *C); + PI.runAfterPass(Pass, *C, PassPA); // If the SCC structure has changed, bail immediately and let the outer // CGSCC layer handle any iteration to reflect the refined structure. @@ -903,9 +903,9 @@ } if (UR.InvalidatedSCCs.count(C)) - PI.runAfterPassInvalidated(Pass); + PI.runAfterPassInvalidated(Pass, PassPA); else - PI.runAfterPass(Pass, *C); + PI.runAfterPass(Pass, *C, PassPA); // Update the SCC and RefSCC if necessary. C = UR.UpdatedC ? UR.UpdatedC : C; Index: llvm/include/llvm/IR/PassInstrumentation.h =================================================================== --- llvm/include/llvm/IR/PassInstrumentation.h +++ llvm/include/llvm/IR/PassInstrumentation.h @@ -75,8 +75,8 @@ // useful instrumentation that needs it. using BeforePassFunc = bool(StringRef, Any); using BeforeNonSkippedPassFunc = void(StringRef, Any); - using AfterPassFunc = void(StringRef, Any); - using AfterPassInvalidatedFunc = void(StringRef); + using AfterPassFunc = void(StringRef, Any, const PreservedAnalyses &); + using AfterPassInvalidatedFunc = void(StringRef, const PreservedAnalyses &); using BeforeAnalysisFunc = void(StringRef, Any); using AfterAnalysisFunc = void(StringRef, Any); @@ -188,20 +188,22 @@ /// just been executed and constant reference to \p IR it operates on. /// \p IR is guaranteed to be valid at this point. template - void runAfterPass(const PassT &Pass, const IRUnitT &IR) const { + void runAfterPass(const PassT &Pass, const IRUnitT &IR, + const PreservedAnalyses &PA) const { if (Callbacks) for (auto &C : Callbacks->AfterPassCallbacks) - C(Pass.name(), llvm::Any(&IR)); + C(Pass.name(), llvm::Any(&IR), PA); } /// AfterPassInvalidated instrumentation point - takes \p Pass instance /// that has just been executed. For use when IR has been invalidated /// by \p Pass execution. template - void runAfterPassInvalidated(const PassT &Pass) const { + void runAfterPassInvalidated(const PassT &Pass, + const PreservedAnalyses &PA) const { if (Callbacks) for (auto &C : Callbacks->AfterPassInvalidatedCallbacks) - C(Pass.name()); + C(Pass.name(), PA); } /// BeforeAnalysis instrumentation point - takes \p Analysis instance Index: llvm/include/llvm/IR/PassManager.h =================================================================== --- llvm/include/llvm/IR/PassManager.h +++ llvm/include/llvm/IR/PassManager.h @@ -522,7 +522,7 @@ // Call onto PassInstrumentation's AfterPass callbacks immediately after // running the pass. - PI.runAfterPass(*P, IR); + PI.runAfterPass(*P, IR, PassPA); // Update the analysis manager as each pass runs and potentially // invalidates analyses. @@ -1249,7 +1249,7 @@ PassPA = Pass.run(F, FAM); } - PI.runAfterPass(Pass, F); + PI.runAfterPass(Pass, F, PassPA); // We know that the function pass couldn't have invalidated any other // function's analyses (that's the contract of a function pass), so @@ -1373,8 +1373,9 @@ // false). if (!PI.runBeforePass(P, IR)) continue; - PA.intersect(P.run(IR, AM, std::forward(Args)...)); - PI.runAfterPass(P, IR); + PreservedAnalyses IterPA = P.run(IR, AM, std::forward(Args)...); + PA.intersect(IterPA); + PI.runAfterPass(P, IR, IterPA); } return PA; } Index: llvm/include/llvm/Transforms/Scalar/LoopPassManager.h =================================================================== --- llvm/include/llvm/Transforms/Scalar/LoopPassManager.h +++ llvm/include/llvm/Transforms/Scalar/LoopPassManager.h @@ -252,7 +252,7 @@ // canonicalization pipeline. if (PI.runBeforePass(LoopCanonicalizationFPM, F)) { PA = LoopCanonicalizationFPM.run(F, AM); - PI.runAfterPass(LoopCanonicalizationFPM, F); + PI.runAfterPass(LoopCanonicalizationFPM, F, PA); } // Get the loop structure for this function @@ -326,9 +326,9 @@ // Do not pass deleted Loop into the instrumentation. if (Updater.skipCurrentLoop()) - PI.runAfterPassInvalidated(Pass); + PI.runAfterPassInvalidated(Pass, PassPA); else - PI.runAfterPass(Pass, *L); + PI.runAfterPass(Pass, *L, PassPA); // FIXME: We should verify the set of analyses relevant to Loop passes // are preserved. Index: llvm/lib/Analysis/CGSCCPassManager.cpp =================================================================== --- llvm/lib/Analysis/CGSCCPassManager.cpp +++ llvm/lib/Analysis/CGSCCPassManager.cpp @@ -88,9 +88,9 @@ } if (UR.InvalidatedSCCs.count(C)) - PI.runAfterPassInvalidated(*Pass); + PI.runAfterPassInvalidated(*Pass, PassPA); else - PI.runAfterPass(*Pass, *C); + PI.runAfterPass(*Pass, *C, PassPA); // Update the SCC if necessary. C = UR.UpdatedC ? UR.UpdatedC : C; Index: llvm/lib/IR/PassTimingInfo.cpp =================================================================== --- llvm/lib/IR/PassTimingInfo.cpp +++ llvm/lib/IR/PassTimingInfo.cpp @@ -267,9 +267,13 @@ PIC.registerBeforeNonSkippedPassCallback( [this](StringRef P, Any) { this->runBeforePass(P); }); PIC.registerAfterPassCallback( - [this](StringRef P, Any) { this->runAfterPass(P); }); + [this](StringRef P, Any, const PreservedAnalyses &) { + this->runAfterPass(P); + }); PIC.registerAfterPassInvalidatedCallback( - [this](StringRef P) { this->runAfterPass(P); }); + [this](StringRef P, const PreservedAnalyses &) { + this->runAfterPass(P); + }); PIC.registerBeforeAnalysisCallback( [this](StringRef P, Any) { this->runBeforePass(P); }); PIC.registerAfterAnalysisCallback( Index: llvm/lib/Passes/StandardInstrumentations.cpp =================================================================== --- llvm/lib/Passes/StandardInstrumentations.cpp +++ llvm/lib/Passes/StandardInstrumentations.cpp @@ -245,9 +245,13 @@ if (llvm::shouldPrintAfterPass()) { PIC.registerAfterPassCallback( - [this](StringRef P, Any IR) { this->printAfterPass(P, IR); }); + [this](StringRef P, Any IR, const PreservedAnalyses &) { + this->printAfterPass(P, IR); + }); PIC.registerAfterPassInvalidatedCallback( - [this](StringRef P) { this->printAfterPassInvalidated(P); }); + [this](StringRef P, const PreservedAnalyses &) { + this->printAfterPassInvalidated(P); + }); } } Index: llvm/lib/Transforms/Scalar/LoopPassManager.cpp =================================================================== --- llvm/lib/Transforms/Scalar/LoopPassManager.cpp +++ llvm/lib/Transforms/Scalar/LoopPassManager.cpp @@ -50,9 +50,9 @@ // do not pass deleted Loop into the instrumentation if (U.skipCurrentLoop()) - PI.runAfterPassInvalidated(*Pass); + PI.runAfterPassInvalidated(*Pass, PassPA); else - PI.runAfterPass(*Pass, L); + PI.runAfterPass(*Pass, L, PassPA); // If the loop was deleted, abort the run and return to the outer walk. if (U.skipCurrentLoop()) { Index: llvm/unittests/IR/PassBuilderCallbacksTest.cpp =================================================================== --- llvm/unittests/IR/PassBuilderCallbacksTest.cpp +++ llvm/unittests/IR/PassBuilderCallbacksTest.cpp @@ -1,1101 +1,286 @@ -//===- unittests/IR/PassBuilderCallbacksTest.cpp - PB Callback Tests --===// +//===- Standard pass instrumentations handling ----------------*- C++ -*--===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// - -#include "llvm/Testing/Support/Error.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace llvm; - -namespace { -using testing::AnyNumber; -using testing::AtLeast; -using testing::DoDefault; -using testing::Not; -using testing::Return; -using testing::Expectation; -using testing::Invoke; -using testing::WithArgs; -using testing::_; - -/// A CRTP base for analysis mock handles +/// \file /// -/// This class reconciles mocking with the value semantics implementation of the -/// AnalysisManager. Analysis mock handles should derive from this class and -/// call \c setDefault() in their constroctur for wiring up the defaults defined -/// by this base with their mock run() and invalidate() implementations. -template , - typename... ExtraArgTs> -class MockAnalysisHandleBase { -public: - class Analysis : public AnalysisInfoMixin { - friend AnalysisInfoMixin; - friend MockAnalysisHandleBase; - static AnalysisKey Key; - - DerivedT *Handle; - - Analysis(DerivedT &Handle) : Handle(&Handle) { - static_assert(std::is_base_of::value, - "Must pass the derived type to this template!"); - } - - public: - class Result { - friend MockAnalysisHandleBase; - - DerivedT *Handle; - - Result(DerivedT &Handle) : Handle(&Handle) {} - - public: - // Forward invalidation events to the mock handle. - bool invalidate(IRUnitT &IR, const PreservedAnalyses &PA, - typename AnalysisManagerT::Invalidator &Inv) { - return Handle->invalidate(IR, PA, Inv); - } - }; - - Result run(IRUnitT &IR, AnalysisManagerT &AM, ExtraArgTs... ExtraArgs) { - return Handle->run(IR, AM, ExtraArgs...); - } - }; - - Analysis getAnalysis() { return Analysis(static_cast(*this)); } - typename Analysis::Result getResult() { - return typename Analysis::Result(static_cast(*this)); - } - static StringRef getName() { return llvm::getTypeName(); } - -protected: - // FIXME: MSVC seems unable to handle a lambda argument to Invoke from within - // the template, so we use a boring static function. - static bool invalidateCallback(IRUnitT &IR, const PreservedAnalyses &PA, - typename AnalysisManagerT::Invalidator &Inv) { - auto PAC = PA.template getChecker(); - return !PAC.preserved() && - !PAC.template preservedSet>(); - } - - /// Derived classes should call this in their constructor to set up default - /// mock actions. (We can't do this in our constructor because this has to - /// run after the DerivedT is constructed.) - void setDefaults() { - ON_CALL(static_cast(*this), - run(_, _, testing::Matcher(_)...)) - .WillByDefault(Return(this->getResult())); - ON_CALL(static_cast(*this), invalidate(_, _, _)) - .WillByDefault(Invoke(&invalidateCallback)); - } -}; - -/// A CRTP base for pass mock handles +/// This file defines IR-printing pass instrumentation callbacks as well as +/// StandardInstrumentations class that manages standard pass instrumentations. /// -/// This class reconciles mocking with the value semantics implementation of the -/// PassManager. Pass mock handles should derive from this class and -/// call \c setDefault() in their constroctur for wiring up the defaults defined -/// by this base with their mock run() and invalidate() implementations. -template -AnalysisKey MockAnalysisHandleBase::Analysis::Key; - -template , - typename... ExtraArgTs> -class MockPassHandleBase { -public: - class Pass : public PassInfoMixin { - friend MockPassHandleBase; - - DerivedT *Handle; +//===----------------------------------------------------------------------===// - Pass(DerivedT &Handle) : Handle(&Handle) { - static_assert(std::is_base_of::value, - "Must pass the derived type to this template!"); - } +#include "llvm/ADT/Any.h" +#include "llvm/ADT/Optional.h" +#include "llvm/Analysis/CallGraphSCCPass.h" +#include "llvm/Analysis/LazyCallGraph.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/IRPrintingPasses.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/PassInstrumentation.h" +#include "llvm/Passes/StandardInstrumentations.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/raw_ostream.h" - public: - PreservedAnalyses run(IRUnitT &IR, AnalysisManagerT &AM, - ExtraArgTs... ExtraArgs) { - return Handle->run(IR, AM, ExtraArgs...); - } - }; +using namespace llvm; - static StringRef getName() { return llvm::getTypeName(); } +// TODO: remove once all required passes are marked as such. +static cl::opt + EnableOptnone("enable-npm-optnone", cl::init(false), + cl::desc("Enable skipping optional passes optnone functions " + "under new pass manager")); - Pass getPass() { return Pass(static_cast(*this)); } +namespace { -protected: - /// Derived classes should call this in their constructor to set up default - /// mock actions. (We can't do this in our constructor because this has to - /// run after the DerivedT is constructed.) - void setDefaults() { - ON_CALL(static_cast(*this), - run(_, _, testing::Matcher(_)...)) - .WillByDefault(Return(PreservedAnalyses::all())); +/// Extracting Module out of \p IR unit. Also fills a textual description +/// of \p IR for use in header when printing. +Optional> unwrapModule(Any IR) { + if (any_isa(IR)) + return std::make_pair(any_cast(IR), std::string()); + + if (any_isa(IR)) { + const Function *F = any_cast(IR); + if (!llvm::isFunctionInPrintList(F->getName())) + return None; + const Module *M = F->getParent(); + return std::make_pair(M, formatv(" (function: {0})", F->getName()).str()); } -}; -/// Mock handles for passes for the IRUnits Module, CGSCC, Function, Loop. -/// These handles define the appropriate run() mock interface for the respective -/// IRUnit type. -template struct MockPassHandle; -template <> -struct MockPassHandle - : MockPassHandleBase, Loop, LoopAnalysisManager, - LoopStandardAnalysisResults &, LPMUpdater &> { - MOCK_METHOD4(run, - PreservedAnalyses(Loop &, LoopAnalysisManager &, - LoopStandardAnalysisResults &, LPMUpdater &)); - static void invalidateLoop(Loop &L, LoopAnalysisManager &, - LoopStandardAnalysisResults &, - LPMUpdater &Updater) { - Updater.markLoopAsDeleted(L, L.getName()); + if (any_isa(IR)) { + const LazyCallGraph::SCC *C = any_cast(IR); + for (const LazyCallGraph::Node &N : *C) { + const Function &F = N.getFunction(); + if (!F.isDeclaration() && isFunctionInPrintList(F.getName())) { + const Module *M = F.getParent(); + return std::make_pair(M, formatv(" (scc: {0})", C->getName()).str()); + } + } + return None; } - MockPassHandle() { setDefaults(); } -}; - -template <> -struct MockPassHandle - : MockPassHandleBase, Function> { - MOCK_METHOD2(run, PreservedAnalyses(Function &, FunctionAnalysisManager &)); - - MockPassHandle() { setDefaults(); } -}; - -template <> -struct MockPassHandle - : MockPassHandleBase, LazyCallGraph::SCC, - CGSCCAnalysisManager, LazyCallGraph &, - CGSCCUpdateResult &> { - MOCK_METHOD4(run, - PreservedAnalyses(LazyCallGraph::SCC &, CGSCCAnalysisManager &, - LazyCallGraph &G, CGSCCUpdateResult &UR)); - static void invalidateSCC(LazyCallGraph::SCC &C, CGSCCAnalysisManager &, - LazyCallGraph &, CGSCCUpdateResult &UR) { - UR.InvalidatedSCCs.insert(&C); + if (any_isa(IR)) { + const Loop *L = any_cast(IR); + const Function *F = L->getHeader()->getParent(); + if (!isFunctionInPrintList(F->getName())) + return None; + const Module *M = F->getParent(); + std::string LoopName; + raw_string_ostream ss(LoopName); + L->getHeader()->printAsOperand(ss, false); + return std::make_pair(M, formatv(" (loop: {0})", ss.str()).str()); } - MockPassHandle() { setDefaults(); } -}; - -template <> -struct MockPassHandle - : MockPassHandleBase, Module> { - MOCK_METHOD2(run, PreservedAnalyses(Module &, ModuleAnalysisManager &)); - - MockPassHandle() { setDefaults(); } -}; - -/// Mock handles for analyses for the IRUnits Module, CGSCC, Function, Loop. -/// These handles define the appropriate run() and invalidate() mock interfaces -/// for the respective IRUnit type. -template struct MockAnalysisHandle; -template <> -struct MockAnalysisHandle - : MockAnalysisHandleBase, Loop, - LoopAnalysisManager, - LoopStandardAnalysisResults &> { - - MOCK_METHOD3_T(run, typename Analysis::Result(Loop &, LoopAnalysisManager &, - LoopStandardAnalysisResults &)); - - MOCK_METHOD3_T(invalidate, bool(Loop &, const PreservedAnalyses &, - LoopAnalysisManager::Invalidator &)); - - MockAnalysisHandle() { this->setDefaults(); } -}; - -template <> -struct MockAnalysisHandle - : MockAnalysisHandleBase, Function> { - MOCK_METHOD2(run, Analysis::Result(Function &, FunctionAnalysisManager &)); - - MOCK_METHOD3(invalidate, bool(Function &, const PreservedAnalyses &, - FunctionAnalysisManager::Invalidator &)); - - MockAnalysisHandle() { setDefaults(); } -}; - -template <> -struct MockAnalysisHandle - : MockAnalysisHandleBase, - LazyCallGraph::SCC, CGSCCAnalysisManager, - LazyCallGraph &> { - MOCK_METHOD3(run, Analysis::Result(LazyCallGraph::SCC &, - CGSCCAnalysisManager &, LazyCallGraph &)); - - MOCK_METHOD3(invalidate, bool(LazyCallGraph::SCC &, const PreservedAnalyses &, - CGSCCAnalysisManager::Invalidator &)); - - MockAnalysisHandle() { setDefaults(); } -}; - -template <> -struct MockAnalysisHandle - : MockAnalysisHandleBase, Module> { - MOCK_METHOD2(run, Analysis::Result(Module &, ModuleAnalysisManager &)); - - MOCK_METHOD3(invalidate, bool(Module &, const PreservedAnalyses &, - ModuleAnalysisManager::Invalidator &)); - - MockAnalysisHandle() { setDefaults(); } -}; - -static std::unique_ptr parseIR(LLVMContext &C, const char *IR) { - SMDiagnostic Err; - return parseAssemblyString(IR, Err, C); + llvm_unreachable("Unknown IR unit"); } -/// Helper for HasName matcher that returns getName both for IRUnit and -/// for IRUnit pointer wrapper into llvm::Any (wrapped by PassInstrumentation). -template std::string getName(const IRUnitT &IR) { - return std::string(IR.getName()); +void printIR(const Function *F, StringRef Banner, + StringRef Extra = StringRef()) { + if (!llvm::isFunctionInPrintList(F->getName())) + return; + dbgs() << Banner << Extra << "\n" << static_cast(*F); } -template <> std::string getName(const StringRef &name) { - return std::string(name); +void printIR(const Module *M, StringRef Banner, StringRef Extra = StringRef()) { + if (llvm::isFunctionInPrintList("*") || llvm::forcePrintModuleIR()) { + dbgs() << Banner << Extra << "\n"; + M->print(dbgs(), nullptr, false); + } else { + for (const auto &F : M->functions()) { + printIR(&F, Banner, Extra); + } + } } -template <> std::string getName(const llvm::Any &WrappedIR) { - if (any_isa(WrappedIR)) - return any_cast(WrappedIR)->getName().str(); - if (any_isa(WrappedIR)) - return any_cast(WrappedIR)->getName().str(); - if (any_isa(WrappedIR)) - return any_cast(WrappedIR)->getName().str(); - if (any_isa(WrappedIR)) - return any_cast(WrappedIR)->getName(); - return ""; -} -/// Define a custom matcher for objects which support a 'getName' method. -/// -/// LLVM often has IR objects or analysis objects which expose a name -/// and in tests it is convenient to match these by name for readability. -/// Usually, this name is either a StringRef or a plain std::string. This -/// matcher supports any type exposing a getName() method of this form whose -/// return value is compatible with an std::ostream. For StringRef, this uses -/// the shift operator defined above. -/// -/// It should be used as: -/// -/// HasName("my_function") -/// -/// No namespace or other qualification is required. -MATCHER_P(HasName, Name, "") { - *result_listener << "has name '" << getName(arg) << "'"; - return Name == getName(arg); +void printIR(const LazyCallGraph::SCC *C, StringRef Banner, + StringRef Extra = StringRef()) { + bool BannerPrinted = false; + for (const LazyCallGraph::Node &N : *C) { + const Function &F = N.getFunction(); + if (!F.isDeclaration() && llvm::isFunctionInPrintList(F.getName())) { + if (!BannerPrinted) { + dbgs() << Banner << Extra << "\n"; + BannerPrinted = true; + } + F.print(dbgs()); + } + } } - -MATCHER_P(HasNameRegex, Name, "") { - *result_listener << "has name '" << getName(arg) << "'"; - llvm::Regex r(Name); - return r.match(getName(arg)); +void printIR(const Loop *L, StringRef Banner) { + const Function *F = L->getHeader()->getParent(); + if (!llvm::isFunctionInPrintList(F->getName())) + return; + llvm::printLoop(const_cast(*L), dbgs(), std::string(Banner)); } -struct MockPassInstrumentationCallbacks { - PassInstrumentationCallbacks Callbacks; - - MockPassInstrumentationCallbacks() { - ON_CALL(*this, runBeforePass(_, _)).WillByDefault(Return(true)); +/// Generic IR-printing helper that unpacks a pointer to IRUnit wrapped into +/// llvm::Any and does actual print job. +void unwrapAndPrint(Any IR, StringRef Banner, bool ForceModule = false) { + if (ForceModule) { + if (auto UnwrappedModule = unwrapModule(IR)) + printIR(UnwrappedModule->first, Banner, UnwrappedModule->second); + return; } - MOCK_METHOD2(runBeforePass, bool(StringRef PassID, llvm::Any)); - MOCK_METHOD2(runBeforeNonSkippedPass, void(StringRef PassID, llvm::Any)); - MOCK_METHOD2(runAfterPass, void(StringRef PassID, llvm::Any)); - MOCK_METHOD1(runAfterPassInvalidated, void(StringRef PassID)); - MOCK_METHOD2(runBeforeAnalysis, void(StringRef PassID, llvm::Any)); - MOCK_METHOD2(runAfterAnalysis, void(StringRef PassID, llvm::Any)); - void registerPassInstrumentation() { - Callbacks.registerBeforePassCallback([this](StringRef P, llvm::Any IR) { - return this->runBeforePass(P, IR); - }); - Callbacks.registerBeforeNonSkippedPassCallback( - [this](StringRef P, llvm::Any IR) { - this->runBeforeNonSkippedPass(P, IR); - }); - Callbacks.registerAfterPassCallback( - [this](StringRef P, llvm::Any IR) { this->runAfterPass(P, IR); }); - Callbacks.registerAfterPassInvalidatedCallback( - [this](StringRef P) { this->runAfterPassInvalidated(P); }); - Callbacks.registerBeforeAnalysisCallback([this](StringRef P, llvm::Any IR) { - return this->runBeforeAnalysis(P, IR); - }); - Callbacks.registerAfterAnalysisCallback( - [this](StringRef P, llvm::Any IR) { this->runAfterAnalysis(P, IR); }); + if (any_isa(IR)) { + const Module *M = any_cast(IR); + assert(M && "module should be valid for printing"); + printIR(M, Banner); + return; } - void ignoreNonMockPassInstrumentation(StringRef IRName) { - // Generic EXPECT_CALLs are needed to match instrumentation on unimportant - // parts of a pipeline that we do not care about (e.g. various passes added - // by default by PassBuilder - Verifier pass etc). - // Make sure to avoid ignoring Mock passes/analysis, we definitely want - // to check these explicitly. - EXPECT_CALL(*this, - runBeforePass(Not(HasNameRegex("Mock")), HasName(IRName))) - .Times(AnyNumber()); - EXPECT_CALL(*this, runBeforeNonSkippedPass(Not(HasNameRegex("Mock")), - HasName(IRName))) - .Times(AnyNumber()); - EXPECT_CALL(*this, runAfterPass(Not(HasNameRegex("Mock")), HasName(IRName))) - .Times(AnyNumber()); - EXPECT_CALL(*this, - runBeforeAnalysis(Not(HasNameRegex("Mock")), HasName(IRName))) - .Times(AnyNumber()); - EXPECT_CALL(*this, - runAfterAnalysis(Not(HasNameRegex("Mock")), HasName(IRName))) - .Times(AnyNumber()); + if (any_isa(IR)) { + const Function *F = any_cast(IR); + assert(F && "function should be valid for printing"); + printIR(F, Banner); + return; } -}; - -template class PassBuilderCallbacksTest; - -/// This test fixture is shared between all the actual tests below and -/// takes care of setting up appropriate defaults. -/// -/// The template specialization serves to extract the IRUnit and AM types from -/// the given PassManagerT. -template -class PassBuilderCallbacksTest, - ExtraPassArgTs...>> : public testing::Test { -protected: - using IRUnitT = TestIRUnitT; - using AnalysisManagerT = AnalysisManager; - using PassManagerT = - PassManager; - using AnalysisT = typename MockAnalysisHandle::Analysis; - - LLVMContext Context; - std::unique_ptr M; - - MockPassInstrumentationCallbacks CallbacksHandle; - - PassBuilder PB; - ModulePassManager PM; - LoopAnalysisManager LAM; - FunctionAnalysisManager FAM; - CGSCCAnalysisManager CGAM; - ModuleAnalysisManager AM; - MockPassHandle PassHandle; - MockAnalysisHandle AnalysisHandle; - - static PreservedAnalyses getAnalysisResult(IRUnitT &U, AnalysisManagerT &AM, - ExtraAnalysisArgTs &&... Args) { - (void)AM.template getResult( - U, std::forward(Args)...); - return PreservedAnalyses::all(); + if (any_isa(IR)) { + const LazyCallGraph::SCC *C = any_cast(IR); + assert(C && "scc should be valid for printing"); + std::string Extra = std::string(formatv(" (scc: {0})", C->getName())); + printIR(C, Banner, Extra); + return; } - PassBuilderCallbacksTest() - : M(parseIR(Context, - "declare void @bar()\n" - "define void @foo(i32 %n) {\n" - "entry:\n" - " br label %loop\n" - "loop:\n" - " %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]\n" - " %iv.next = add i32 %iv, 1\n" - " tail call void @bar()\n" - " %cmp = icmp eq i32 %iv, %n\n" - " br i1 %cmp, label %exit, label %loop\n" - "exit:\n" - " ret void\n" - "}\n")), - CallbacksHandle(), - PB(nullptr, PipelineTuningOptions(), None, &CallbacksHandle.Callbacks), - PM(true), LAM(true), FAM(true), CGAM(true), AM(true) { - - EXPECT_TRUE(&CallbacksHandle.Callbacks == - PB.getPassInstrumentationCallbacks()); - - /// Register a callback for analysis registration. - /// - /// The callback is a function taking a reference to an AnalyisManager - /// object. When called, the callee gets to register its own analyses with - /// this PassBuilder instance. - PB.registerAnalysisRegistrationCallback([this](AnalysisManagerT &AM) { - // Register our mock analysis - AM.registerPass([this] { return AnalysisHandle.getAnalysis(); }); - }); - - /// Register a callback for pipeline parsing. - /// - /// During parsing of a textual pipeline, the PassBuilder will call these - /// callbacks for each encountered pass name that it does not know. This - /// includes both simple pass names as well as names of sub-pipelines. In - /// the latter case, the InnerPipeline is not empty. - PB.registerPipelineParsingCallback( - [this](StringRef Name, PassManagerT &PM, - ArrayRef InnerPipeline) { - /// Handle parsing of the names of analysis utilities such as - /// require and invalidate for our - /// analysis mock handle - if (parseAnalysisUtilityPasses("test-analysis", Name, PM)) - return true; - - /// Parse the name of our pass mock handle - if (Name == "test-transform") { - PM.addPass(PassHandle.getPass()); - return true; - } - return false; - }); - - /// Register builtin analyses and cross-register the analysis proxies - PB.registerModuleAnalyses(AM); - PB.registerCGSCCAnalyses(CGAM); - PB.registerFunctionAnalyses(FAM); - PB.registerLoopAnalyses(LAM); - PB.crossRegisterProxies(LAM, FAM, CGAM, AM); + if (any_isa(IR)) { + const Loop *L = any_cast(IR); + assert(L && "Loop should be valid for printing"); + printIR(L, Banner); + return; } -}; - -using ModuleCallbacksTest = PassBuilderCallbacksTest; -using CGSCCCallbacksTest = PassBuilderCallbacksTest; -using FunctionCallbacksTest = PassBuilderCallbacksTest; -using LoopCallbacksTest = PassBuilderCallbacksTest; - -/// Test parsing of the name of our mock pass for all IRUnits. -/// -/// The pass should by default run our mock analysis and then preserve it. -TEST_F(ModuleCallbacksTest, Passes) { - EXPECT_CALL(AnalysisHandle, run(HasName(""), _)); - EXPECT_CALL(PassHandle, run(HasName(""), _)) - .WillOnce(Invoke(getAnalysisResult)); - - StringRef PipelineText = "test-transform"; - ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Succeeded()) - << "Pipeline was: " << PipelineText; - - PM.run(*M, AM); -} - -TEST_F(ModuleCallbacksTest, InstrumentedPasses) { - EXPECT_CALL(AnalysisHandle, run(HasName(""), _)); - EXPECT_CALL(PassHandle, run(HasName(""), _)) - .WillOnce(Invoke(getAnalysisResult)); - - CallbacksHandle.registerPassInstrumentation(); - // Non-mock instrumentation not specifically mentioned below can be ignored. - CallbacksHandle.ignoreNonMockPassInstrumentation(""); - - // PassInstrumentation calls should happen in-sequence, in the same order - // as passes/analyses are scheduled. - ::testing::Sequence PISequence; - EXPECT_CALL(CallbacksHandle, runBeforePass(HasNameRegex("MockPassHandle"), - HasName(""))) - .InSequence(PISequence); - EXPECT_CALL(CallbacksHandle, - runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), - HasName(""))) - .InSequence(PISequence); - EXPECT_CALL(CallbacksHandle, - runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), - HasName(""))) - .InSequence(PISequence); - EXPECT_CALL( - CallbacksHandle, - runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), HasName(""))) - .InSequence(PISequence); - EXPECT_CALL(CallbacksHandle, - runAfterPass(HasNameRegex("MockPassHandle"), HasName(""))) - .InSequence(PISequence); - - StringRef PipelineText = "test-transform"; - ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Succeeded()) - << "Pipeline was: " << PipelineText; - - PM.run(*M, AM); -} - -TEST_F(ModuleCallbacksTest, InstrumentedSkippedPasses) { - CallbacksHandle.registerPassInstrumentation(); - // Non-mock instrumentation run here can safely be ignored. - CallbacksHandle.ignoreNonMockPassInstrumentation(""); - - // Skip all passes by returning false. Pass managers and adaptor passes are - // also passes that observed by the callbacks. - EXPECT_CALL(CallbacksHandle, runBeforePass(_, _)) - .WillRepeatedly(Return(false)); - - EXPECT_CALL(AnalysisHandle, run(HasName(""), _)).Times(0); - EXPECT_CALL(PassHandle, run(HasName(""), _)).Times(0); - - // As the pass is skipped there is no nonskippedpass/afterPass, - // beforeAnalysis/afterAnalysis as well. - EXPECT_CALL(CallbacksHandle, - runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), _)) - .Times(0); - EXPECT_CALL(CallbacksHandle, runAfterPass(HasNameRegex("MockPassHandle"), _)) - .Times(0); - EXPECT_CALL(CallbacksHandle, - runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), _)) - .Times(0); - EXPECT_CALL(CallbacksHandle, - runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), _)) - .Times(0); - - // Order is important here. `Adaptor` expectations should be checked first - // because the its argument contains 'PassManager' (for example: - // ModuleToFunctionPassAdaptor{{.*}}PassManager{{.*}}). Check - // `runBeforeNonSkippedPass` and `runAfterPass` to show that they are not - // skipped. - // - // Pass managers are not ignored. - // 5 = (1) ModulePassManager + (2) FunctionPassMangers + (1) LoopPassManager + - // (1) CGSCCPassManager - EXPECT_CALL(CallbacksHandle, - runBeforeNonSkippedPass(HasNameRegex("PassManager"), _)) - .Times(5); - EXPECT_CALL( - CallbacksHandle, - runBeforeNonSkippedPass(HasNameRegex("ModuleToFunctionPassAdaptor"), _)) - .Times(1); - EXPECT_CALL(CallbacksHandle, - runBeforeNonSkippedPass( - HasNameRegex("ModuleToPostOrderCGSCCPassAdaptor"), _)) - .Times(1); - EXPECT_CALL( - CallbacksHandle, - runBeforeNonSkippedPass(HasNameRegex("CGSCCToFunctionPassAdaptor"), _)) - .Times(1); - EXPECT_CALL( - CallbacksHandle, - runBeforeNonSkippedPass(HasNameRegex("FunctionToLoopPassAdaptor"), _)) - .Times(1); - - // The `runAfterPass` checks are the same as these of - // `runBeforeNonSkippedPass`. - EXPECT_CALL(CallbacksHandle, runAfterPass(HasNameRegex("PassManager"), _)) - .Times(5); - EXPECT_CALL(CallbacksHandle, - runAfterPass(HasNameRegex("ModuleToFunctionPassAdaptor"), _)) - .Times(1); - EXPECT_CALL( - CallbacksHandle, - runAfterPass(HasNameRegex("ModuleToPostOrderCGSCCPassAdaptor"), _)) - .Times(1); - EXPECT_CALL(CallbacksHandle, - runAfterPass(HasNameRegex("CGSCCToFunctionPassAdaptor"), _)) - .Times(1); - EXPECT_CALL(CallbacksHandle, - runAfterPass(HasNameRegex("FunctionToLoopPassAdaptor"), _)) - .Times(1); - - // Ignore analyses introduced by adaptor passes. - EXPECT_CALL(CallbacksHandle, - runBeforeAnalysis(Not(HasNameRegex("MockAnalysisHandle")), _)) - .Times(AnyNumber()); - EXPECT_CALL(CallbacksHandle, - runAfterAnalysis(Not(HasNameRegex("MockAnalysisHandle")), _)) - .Times(AnyNumber()); - - // Register Funtion and Loop version of "test-transform" for testing - PB.registerPipelineParsingCallback( - [](StringRef Name, FunctionPassManager &FPM, - ArrayRef) { - if (Name == "test-transform") { - FPM.addPass(MockPassHandle().getPass()); - return true; - } - return false; - }); - PB.registerPipelineParsingCallback( - [](StringRef Name, LoopPassManager &LPM, - ArrayRef) { - if (Name == "test-transform") { - LPM.addPass(MockPassHandle().getPass()); - return true; - } - return false; - }); - - StringRef PipelineText = "test-transform,function(test-transform),cgscc(" - "function(loop(test-transform)))"; - ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Succeeded()) - << "Pipeline was: " << PipelineText; - - PM.run(*M, AM); -} - -TEST_F(FunctionCallbacksTest, Passes) { - EXPECT_CALL(AnalysisHandle, run(HasName("foo"), _)); - EXPECT_CALL(PassHandle, run(HasName("foo"), _)) - .WillOnce(Invoke(getAnalysisResult)); - - StringRef PipelineText = "test-transform"; - ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Succeeded()) - << "Pipeline was: " << PipelineText; - PM.run(*M, AM); -} - -TEST_F(FunctionCallbacksTest, InstrumentedPasses) { - CallbacksHandle.registerPassInstrumentation(); - // Non-mock instrumentation not specifically mentioned below can be ignored. - CallbacksHandle.ignoreNonMockPassInstrumentation(""); - CallbacksHandle.ignoreNonMockPassInstrumentation("foo"); - - EXPECT_CALL(AnalysisHandle, run(HasName("foo"), _)); - EXPECT_CALL(PassHandle, run(HasName("foo"), _)) - .WillOnce(Invoke(getAnalysisResult)); - - // PassInstrumentation calls should happen in-sequence, in the same order - // as passes/analyses are scheduled. - ::testing::Sequence PISequence; - EXPECT_CALL(CallbacksHandle, - runBeforePass(HasNameRegex("MockPassHandle"), HasName("foo"))) - .InSequence(PISequence); - EXPECT_CALL( - CallbacksHandle, - runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), HasName("foo"))) - .InSequence(PISequence); - EXPECT_CALL( - CallbacksHandle, - runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("foo"))) - .InSequence(PISequence); - EXPECT_CALL( - CallbacksHandle, - runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("foo"))) - .InSequence(PISequence); - EXPECT_CALL(CallbacksHandle, - runAfterPass(HasNameRegex("MockPassHandle"), HasName("foo"))) - .InSequence(PISequence); - - // Our mock pass does not invalidate IR. - EXPECT_CALL(CallbacksHandle, - runAfterPassInvalidated(HasNameRegex("MockPassHandle"))) - .Times(0); - - StringRef PipelineText = "test-transform"; - ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Succeeded()) - << "Pipeline was: " << PipelineText; - PM.run(*M, AM); + llvm_unreachable("Unknown wrapped IR type"); } -TEST_F(FunctionCallbacksTest, InstrumentedSkippedPasses) { - CallbacksHandle.registerPassInstrumentation(); - // Non-mock instrumentation run here can safely be ignored. - CallbacksHandle.ignoreNonMockPassInstrumentation(""); - CallbacksHandle.ignoreNonMockPassInstrumentation("foo"); - - // Skip the pass by returning false. - EXPECT_CALL(CallbacksHandle, - runBeforePass(HasNameRegex("MockPassHandle"), HasName("foo"))) - .WillOnce(Return(false)); - - EXPECT_CALL(AnalysisHandle, run(HasName("foo"), _)).Times(0); - EXPECT_CALL(PassHandle, run(HasName("foo"), _)).Times(0); +} // namespace - // As the pass is skipped there is no afterPass, beforeAnalysis/afterAnalysis - // as well. - EXPECT_CALL(CallbacksHandle, runAfterPass(HasNameRegex("MockPassHandle"), _)) - .Times(0); - EXPECT_CALL(CallbacksHandle, - runAfterPassInvalidated(HasNameRegex("MockPassHandle"))) - .Times(0); - EXPECT_CALL(CallbacksHandle, runAfterPass(HasNameRegex("MockPassHandle"), _)) - .Times(0); - EXPECT_CALL(CallbacksHandle, - runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), _)) - .Times(0); - EXPECT_CALL(CallbacksHandle, - runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), _)) - .Times(0); - - StringRef PipelineText = "test-transform"; - ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Succeeded()) - << "Pipeline was: " << PipelineText; - PM.run(*M, AM); +PrintIRInstrumentation::~PrintIRInstrumentation() { + assert(ModuleDescStack.empty() && "ModuleDescStack is not empty at exit"); } -TEST_F(LoopCallbacksTest, Passes) { - EXPECT_CALL(AnalysisHandle, run(HasName("loop"), _, _)); - EXPECT_CALL(PassHandle, run(HasName("loop"), _, _, _)) - .WillOnce(WithArgs<0, 1, 2>(Invoke(getAnalysisResult))); - - StringRef PipelineText = "test-transform"; - ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Succeeded()) - << "Pipeline was: " << PipelineText; - PM.run(*M, AM); +void PrintIRInstrumentation::pushModuleDesc(StringRef PassID, Any IR) { + assert(StoreModuleDesc); + const Module *M = nullptr; + std::string Extra; + if (auto UnwrappedModule = unwrapModule(IR)) + std::tie(M, Extra) = UnwrappedModule.getValue(); + ModuleDescStack.emplace_back(M, Extra, PassID); } -TEST_F(LoopCallbacksTest, InstrumentedPasses) { - CallbacksHandle.registerPassInstrumentation(); - // Non-mock instrumentation not specifically mentioned below can be ignored. - CallbacksHandle.ignoreNonMockPassInstrumentation(""); - CallbacksHandle.ignoreNonMockPassInstrumentation("foo"); - CallbacksHandle.ignoreNonMockPassInstrumentation("loop"); - - EXPECT_CALL(AnalysisHandle, run(HasName("loop"), _, _)); - EXPECT_CALL(PassHandle, run(HasName("loop"), _, _, _)) - .WillOnce(WithArgs<0, 1, 2>(Invoke(getAnalysisResult))); - - // PassInstrumentation calls should happen in-sequence, in the same order - // as passes/analyses are scheduled. - ::testing::Sequence PISequence; - EXPECT_CALL(CallbacksHandle, - runBeforePass(HasNameRegex("MockPassHandle"), HasName("loop"))) - .InSequence(PISequence); - EXPECT_CALL( - CallbacksHandle, - runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), HasName("loop"))) - .InSequence(PISequence); - EXPECT_CALL( - CallbacksHandle, - runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("loop"))) - .InSequence(PISequence); - EXPECT_CALL( - CallbacksHandle, - runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("loop"))) - .InSequence(PISequence); - EXPECT_CALL(CallbacksHandle, - runAfterPass(HasNameRegex("MockPassHandle"), HasName("loop"))) - .InSequence(PISequence); - - // Our mock pass does not invalidate IR. - EXPECT_CALL(CallbacksHandle, - runAfterPassInvalidated(HasNameRegex("MockPassHandle"))) - .Times(0); - - StringRef PipelineText = "test-transform"; - ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Succeeded()) - << "Pipeline was: " << PipelineText; - PM.run(*M, AM); +PrintIRInstrumentation::PrintModuleDesc +PrintIRInstrumentation::popModuleDesc(StringRef PassID) { + assert(!ModuleDescStack.empty() && "empty ModuleDescStack"); + PrintModuleDesc ModuleDesc = ModuleDescStack.pop_back_val(); + assert(std::get<2>(ModuleDesc).equals(PassID) && "malformed ModuleDescStack"); + return ModuleDesc; } -TEST_F(LoopCallbacksTest, InstrumentedInvalidatingPasses) { - CallbacksHandle.registerPassInstrumentation(); - // Non-mock instrumentation not specifically mentioned below can be ignored. - CallbacksHandle.ignoreNonMockPassInstrumentation(""); - CallbacksHandle.ignoreNonMockPassInstrumentation("foo"); - CallbacksHandle.ignoreNonMockPassInstrumentation("loop"); - - EXPECT_CALL(AnalysisHandle, run(HasName("loop"), _, _)); - EXPECT_CALL(PassHandle, run(HasName("loop"), _, _, _)) - .WillOnce(DoAll(WithArgs<0, 1, 2, 3>(Invoke(PassHandle.invalidateLoop)), - WithArgs<0, 1, 2>(Invoke(getAnalysisResult)))); +void PrintIRInstrumentation::printBeforePass(StringRef PassID, Any IR) { + if (PassID.startswith("PassManager<") || PassID.contains("PassAdaptor<")) + return; - // PassInstrumentation calls should happen in-sequence, in the same order - // as passes/analyses are scheduled. - ::testing::Sequence PISequence; - EXPECT_CALL(CallbacksHandle, - runBeforePass(HasNameRegex("MockPassHandle"), HasName("loop"))) - .InSequence(PISequence); - EXPECT_CALL( - CallbacksHandle, - runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), HasName("loop"))) - .InSequence(PISequence); - EXPECT_CALL( - CallbacksHandle, - runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("loop"))) - .InSequence(PISequence); - EXPECT_CALL( - CallbacksHandle, - runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("loop"))) - .InSequence(PISequence); - EXPECT_CALL(CallbacksHandle, - runAfterPassInvalidated(HasNameRegex("MockPassHandle"))) - .InSequence(PISequence); - EXPECT_CALL(CallbacksHandle, - runAfterPassInvalidated(HasNameRegex("^PassManager"))) - .InSequence(PISequence); + // Saving Module for AfterPassInvalidated operations. + // Note: here we rely on a fact that we do not change modules while + // traversing the pipeline, so the latest captured module is good + // for all print operations that has not happen yet. + if (StoreModuleDesc && llvm::shouldPrintAfterPass(PassID)) + pushModuleDesc(PassID, IR); - // Our mock pass invalidates IR, thus normal runAfterPass is never called. - EXPECT_CALL(CallbacksHandle, - runAfterPass(HasNameRegex("MockPassHandle"), HasName("loop"))) - .Times(0); + if (!llvm::shouldPrintBeforePass(PassID)) + return; - StringRef PipelineText = "test-transform"; - ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Succeeded()) - << "Pipeline was: " << PipelineText; - PM.run(*M, AM); + SmallString<20> Banner = formatv("*** IR Dump Before {0} ***", PassID); + unwrapAndPrint(IR, Banner, llvm::forcePrintModuleIR()); + return; } -TEST_F(LoopCallbacksTest, InstrumentedSkippedPasses) { - CallbacksHandle.registerPassInstrumentation(); - // Non-mock instrumentation run here can safely be ignored. - CallbacksHandle.ignoreNonMockPassInstrumentation(""); - CallbacksHandle.ignoreNonMockPassInstrumentation("foo"); - CallbacksHandle.ignoreNonMockPassInstrumentation("loop"); - - // Skip the pass by returning false. - EXPECT_CALL(CallbacksHandle, - runBeforePass(HasNameRegex("MockPassHandle"), HasName("loop"))) - .WillOnce(Return(false)); +void PrintIRInstrumentation::printAfterPass(StringRef PassID, Any IR) { + if (PassID.startswith("PassManager<") || PassID.contains("PassAdaptor<")) + return; - EXPECT_CALL(AnalysisHandle, run(HasName("loop"), _, _)).Times(0); - EXPECT_CALL(PassHandle, run(HasName("loop"), _, _, _)).Times(0); + if (!llvm::shouldPrintAfterPass(PassID)) + return; - // As the pass is skipped there is no afterPass, beforeAnalysis/afterAnalysis - // as well. - EXPECT_CALL(CallbacksHandle, runAfterPass(HasNameRegex("MockPassHandle"), _)) - .Times(0); - EXPECT_CALL(CallbacksHandle, - runAfterPassInvalidated(HasNameRegex("MockPassHandle"))) - .Times(0); - EXPECT_CALL(CallbacksHandle, - runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), _)) - .Times(0); - EXPECT_CALL(CallbacksHandle, - runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), _)) - .Times(0); + if (StoreModuleDesc) + popModuleDesc(PassID); - StringRef PipelineText = "test-transform"; - ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Succeeded()) - << "Pipeline was: " << PipelineText; - PM.run(*M, AM); + SmallString<20> Banner = formatv("*** IR Dump After {0} ***", PassID); + unwrapAndPrint(IR, Banner, llvm::forcePrintModuleIR()); } -TEST_F(CGSCCCallbacksTest, Passes) { - EXPECT_CALL(AnalysisHandle, run(HasName("(foo)"), _, _)); - EXPECT_CALL(PassHandle, run(HasName("(foo)"), _, _, _)) - .WillOnce(WithArgs<0, 1, 2>(Invoke(getAnalysisResult))); - - StringRef PipelineText = "test-transform"; - ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Succeeded()) - << "Pipeline was: " << PipelineText; - PM.run(*M, AM); +void PrintIRInstrumentation::printAfterPassInvalidated(StringRef PassID) { + if (!StoreModuleDesc || !llvm::shouldPrintAfterPass(PassID)) + return; + + if (PassID.startswith("PassManager<") || PassID.contains("PassAdaptor<")) + return; + + const Module *M; + std::string Extra; + StringRef StoredPassID; + std::tie(M, Extra, StoredPassID) = popModuleDesc(PassID); + // Additional filtering (e.g. -filter-print-func) can lead to module + // printing being skipped. + if (!M) + return; + + SmallString<20> Banner = + formatv("*** IR Dump After {0} *** invalidated: ", PassID); + printIR(M, Banner, Extra); } -TEST_F(CGSCCCallbacksTest, InstrumentedPasses) { - CallbacksHandle.registerPassInstrumentation(); - // Non-mock instrumentation not specifically mentioned below can be ignored. - CallbacksHandle.ignoreNonMockPassInstrumentation(""); - CallbacksHandle.ignoreNonMockPassInstrumentation("foo"); - CallbacksHandle.ignoreNonMockPassInstrumentation("(foo)"); - - EXPECT_CALL(AnalysisHandle, run(HasName("(foo)"), _, _)); - EXPECT_CALL(PassHandle, run(HasName("(foo)"), _, _, _)) - .WillOnce(WithArgs<0, 1, 2>(Invoke(getAnalysisResult))); - - // PassInstrumentation calls should happen in-sequence, in the same order - // as passes/analyses are scheduled. - ::testing::Sequence PISequence; - EXPECT_CALL(CallbacksHandle, - runBeforePass(HasNameRegex("MockPassHandle"), HasName("(foo)"))) - .InSequence(PISequence); - EXPECT_CALL( - CallbacksHandle, - runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), HasName("(foo)"))) - .InSequence(PISequence); - EXPECT_CALL( - CallbacksHandle, - runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("(foo)"))) - .InSequence(PISequence); - EXPECT_CALL( - CallbacksHandle, - runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("(foo)"))) - .InSequence(PISequence); - EXPECT_CALL(CallbacksHandle, - runAfterPass(HasNameRegex("MockPassHandle"), HasName("(foo)"))) - .InSequence(PISequence); - - // Our mock pass does not invalidate IR. - EXPECT_CALL(CallbacksHandle, - runAfterPassInvalidated(HasNameRegex("MockPassHandle"))) - .Times(0); - - StringRef PipelineText = "test-transform"; - ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Succeeded()) - << "Pipeline was: " << PipelineText; - PM.run(*M, AM); -} - -TEST_F(CGSCCCallbacksTest, InstrumentedInvalidatingPasses) { - CallbacksHandle.registerPassInstrumentation(); - // Non-mock instrumentation not specifically mentioned below can be ignored. - CallbacksHandle.ignoreNonMockPassInstrumentation(""); - CallbacksHandle.ignoreNonMockPassInstrumentation("foo"); - CallbacksHandle.ignoreNonMockPassInstrumentation("(foo)"); - - EXPECT_CALL(AnalysisHandle, run(HasName("(foo)"), _, _)); - EXPECT_CALL(PassHandle, run(HasName("(foo)"), _, _, _)) - .WillOnce(DoAll(WithArgs<0, 1, 2, 3>(Invoke(PassHandle.invalidateSCC)), - WithArgs<0, 1, 2>(Invoke(getAnalysisResult)))); - - // PassInstrumentation calls should happen in-sequence, in the same order - // as passes/analyses are scheduled. - ::testing::Sequence PISequence; - EXPECT_CALL(CallbacksHandle, - runBeforePass(HasNameRegex("MockPassHandle"), HasName("(foo)"))) - .InSequence(PISequence); - EXPECT_CALL( - CallbacksHandle, - runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), HasName("(foo)"))) - .InSequence(PISequence); - EXPECT_CALL( - CallbacksHandle, - runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("(foo)"))) - .InSequence(PISequence); - EXPECT_CALL( - CallbacksHandle, - runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("(foo)"))) - .InSequence(PISequence); - EXPECT_CALL(CallbacksHandle, - runAfterPassInvalidated(HasNameRegex("MockPassHandle"))) - .InSequence(PISequence); - EXPECT_CALL(CallbacksHandle, - runAfterPassInvalidated(HasNameRegex("^PassManager"))) - .InSequence(PISequence); - - // Our mock pass does invalidate IR, thus normal runAfterPass is never called. - EXPECT_CALL(CallbacksHandle, - runAfterPass(HasNameRegex("MockPassHandle"), HasName("(foo)"))) - .Times(0); - - StringRef PipelineText = "test-transform"; - ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Succeeded()) - << "Pipeline was: " << PipelineText; - PM.run(*M, AM); -} - -TEST_F(CGSCCCallbacksTest, InstrumentedSkippedPasses) { - CallbacksHandle.registerPassInstrumentation(); - // Non-mock instrumentation run here can safely be ignored. - CallbacksHandle.ignoreNonMockPassInstrumentation(""); - CallbacksHandle.ignoreNonMockPassInstrumentation("foo"); - CallbacksHandle.ignoreNonMockPassInstrumentation("(foo)"); - - // Skip the pass by returning false. - EXPECT_CALL(CallbacksHandle, - runBeforePass(HasNameRegex("MockPassHandle"), HasName("(foo)"))) - .WillOnce(Return(false)); - - // neither Analysis nor Pass are called. - EXPECT_CALL(AnalysisHandle, run(HasName("(foo)"), _, _)).Times(0); - EXPECT_CALL(PassHandle, run(HasName("(foo)"), _, _, _)).Times(0); - - // As the pass is skipped there is no afterPass, beforeAnalysis/afterAnalysis - // as well. - EXPECT_CALL(CallbacksHandle, runAfterPass(HasNameRegex("MockPassHandle"), _)) - .Times(0); - EXPECT_CALL(CallbacksHandle, - runAfterPassInvalidated(HasNameRegex("MockPassHandle"))) - .Times(0); - EXPECT_CALL(CallbacksHandle, - runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), _)) - .Times(0); - EXPECT_CALL(CallbacksHandle, - runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), _)) - .Times(0); - - StringRef PipelineText = "test-transform"; - ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Succeeded()) - << "Pipeline was: " << PipelineText; - PM.run(*M, AM); -} - -/// Test parsing of the names of analysis utilities for our mock analysis -/// for all IRUnits. -/// -/// We first require<>, then invalidate<> it, expecting the analysis to be run -/// once and subsequently invalidated. -TEST_F(ModuleCallbacksTest, AnalysisUtilities) { - EXPECT_CALL(AnalysisHandle, run(HasName(""), _)); - EXPECT_CALL(AnalysisHandle, invalidate(HasName(""), _, _)); - - StringRef PipelineText = "require,invalidate"; - ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Succeeded()) - << "Pipeline was: " << PipelineText; - PM.run(*M, AM); -} - -TEST_F(CGSCCCallbacksTest, PassUtilities) { - EXPECT_CALL(AnalysisHandle, run(HasName("(foo)"), _, _)); - EXPECT_CALL(AnalysisHandle, invalidate(HasName("(foo)"), _, _)); - - StringRef PipelineText = "require,invalidate"; - ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Succeeded()) - << "Pipeline was: " << PipelineText; - PM.run(*M, AM); -} - -TEST_F(FunctionCallbacksTest, AnalysisUtilities) { - EXPECT_CALL(AnalysisHandle, run(HasName("foo"), _)); - EXPECT_CALL(AnalysisHandle, invalidate(HasName("foo"), _, _)); - - StringRef PipelineText = "require,invalidate"; - ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Succeeded()) - << "Pipeline was: " << PipelineText; - PM.run(*M, AM); +void PrintIRInstrumentation::registerCallbacks( + PassInstrumentationCallbacks &PIC) { + // BeforePass callback is not just for printing, it also saves a Module + // for later use in AfterPassInvalidated. + StoreModuleDesc = llvm::forcePrintModuleIR() && llvm::shouldPrintAfterPass(); + if (llvm::shouldPrintBeforePass() || StoreModuleDesc) + PIC.registerBeforeNonSkippedPassCallback( + [this](StringRef P, Any IR) { this->printBeforePass(P, IR); }); + + if (llvm::shouldPrintAfterPass()) { + PIC.registerAfterPassCallback( + [this](StringRef P, Any IR, const PreservedAnalyses &) { + this->printAfterPass(P, IR); + }); + PIC.registerAfterPassInvalidatedCallback( + [this](StringRef P, const PreservedAnalyses &) { + this->printAfterPassInvalidated(P); + }); + } } -TEST_F(LoopCallbacksTest, PassUtilities) { - EXPECT_CALL(AnalysisHandle, run(HasName("loop"), _, _)); - EXPECT_CALL(AnalysisHandle, invalidate(HasName("loop"), _, _)); - - StringRef PipelineText = "require,invalidate"; - - ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Succeeded()) - << "Pipeline was: " << PipelineText; - PM.run(*M, AM); +void OptNoneInstrumentation::registerCallbacks( + PassInstrumentationCallbacks &PIC) { + PIC.registerBeforePassCallback( + [this](StringRef P, Any IR) { return this->skip(P, IR); }); } -/// Test parsing of the top-level pipeline. -/// -/// The ParseTopLevelPipeline callback takes over parsing of the entire pipeline -/// from PassBuilder if it encounters an unknown pipeline entry at the top level -/// (i.e., the first entry on the pipeline). -/// This test parses a pipeline named 'another-pipeline', whose only elements -/// may be the test-transform pass or the analysis utilities -TEST_F(ModuleCallbacksTest, ParseTopLevelPipeline) { - PB.registerParseTopLevelPipelineCallback([this]( - ModulePassManager &MPM, ArrayRef Pipeline, - bool VerifyEachPass, bool DebugLogging) { - auto &FirstName = Pipeline.front().Name; - auto &InnerPipeline = Pipeline.front().InnerPipeline; - if (FirstName == "another-pipeline") { - for (auto &E : InnerPipeline) { - if (parseAnalysisUtilityPasses("test-analysis", E.Name, PM)) - continue; - - if (E.Name == "test-transform") { - PM.addPass(PassHandle.getPass()); - continue; - } - return false; - } - } +bool OptNoneInstrumentation::skip(StringRef PassID, Any IR) { + if (!EnableOptnone) return true; - }); - - EXPECT_CALL(AnalysisHandle, run(HasName(""), _)); - EXPECT_CALL(PassHandle, run(HasName(""), _)) - .WillOnce(Invoke(getAnalysisResult)); - EXPECT_CALL(AnalysisHandle, invalidate(HasName(""), _, _)); - - StringRef PipelineText = - "another-pipeline(test-transform,invalidate)"; - ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Succeeded()) - << "Pipeline was: " << PipelineText; - PM.run(*M, AM); + const Function *F = nullptr; + if (any_isa(IR)) { + F = any_cast(IR); + } else if (any_isa(IR)) { + F = any_cast(IR)->getHeader()->getParent(); + } + if (F && F->hasOptNone()) { + if (DebugLogging) + dbgs() << "Skipping pass: " << PassID << " (optnone)\n"; + return false; + } + return true; +} - /// Test the negative case - PipelineText = "another-pipeline(instcombine)"; - ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Failed()) - << "Pipeline was: " << PipelineText; +void StandardInstrumentations::registerCallbacks( + PassInstrumentationCallbacks &PIC) { + PrintIR.registerCallbacks(PIC); + TimePasses.registerCallbacks(PIC); + OptNone.registerCallbacks(PIC); } -} // end anonymous namespace Index: llvm/unittests/IR/TimePassesTest.cpp =================================================================== --- llvm/unittests/IR/TimePassesTest.cpp +++ llvm/unittests/IR/TimePassesTest.cpp @@ -134,8 +134,8 @@ // Pretending that passes are running to trigger the timers. PI.runBeforePass(Pass1, M); PI.runBeforePass(Pass2, M); - PI.runAfterPass(Pass2, M); - PI.runAfterPass(Pass1, M); + PI.runAfterPass(Pass2, M, PreservedAnalyses::all()); + PI.runAfterPass(Pass1, M, PreservedAnalyses::all()); // Generating report. TimePasses->print(); @@ -154,7 +154,7 @@ // Now trigger just a single pass to populate timers again. PI.runBeforePass(Pass2, M); - PI.runAfterPass(Pass2, M); + PI.runAfterPass(Pass2, M, PreservedAnalyses::all()); // Generate report by deleting the handler. TimePasses.reset();