Index: llvm/include/llvm/Passes/StandardInstrumentations.h =================================================================== --- llvm/include/llvm/Passes/StandardInstrumentations.h +++ llvm/include/llvm/Passes/StandardInstrumentations.h @@ -153,6 +153,61 @@ FunctionAnalysisManager &FAM); }; +// Class that holds information for reconstructing essential parts of an IR +// when a pass is invalidated. The IR is not provided in the callback +// when a pass is invalidated because the IR may have been deleted. This +// class holds on to the module and the function names so that a representation +// can be built up for comparison purposes. +class DataForInvalidatedIR { +public: + DataForInvalidatedIR() {} + + // const Module *getModule() const { return SavedModule; } + const std::string getName() const { return Name; } + + // call HandleFunc on each of the saved functions to create the comparable + // after data when a pass has been invalidated. + void generateInvalidatedIRRep( + std::function HandleFunc) const; + + bool haveMultipleFunctionNames() const { + return SavedFunctionNames.size() > 1; + } + + // Build up the data needed to create a representation when a pass + // gets invalidated. + void saveRepForInvalidated(Any IR); + + // Is the data valid? + bool haveValidData() const { return SavedModule; } + +protected: + const Module *SavedModule = nullptr; + std::string Name; + SmallVector SavedFunctionNames; +}; + +// Class that holds the string representation of the IR and the +// data needed when the IR is invalidated. +class StringIR : public DataForInvalidatedIR { +public: + StringIR() {} + + bool operator==(const StringIR &that) const { return Str == that.str(); } + + // Accessors for the represenation of the IR. + std::string &str() { return Str; } + const std::string &str() const { return Str; } + +protected: + std::string Str; +}; + +inline raw_ostream &operator<<(raw_ostream &Out, const StringIR &S) { + Out << S.str(); + return Out; +} + // Base class for classes that report changes to the IR. // It presents an interface for such classes and provides calls // on various events as the new pass manager transforms the IR. @@ -181,7 +236,7 @@ // Compare the IR from before the pass after the pass. void handleIRAfterPass(Any IR, StringRef PassID, StringRef PassName); // Handle the situation where a pass is invalidated. - void handleInvalidatedPass(StringRef PassID); + void handleInvalidatedPass(StringRef PassID, StringRef PassName); protected: // Register required callbacks. @@ -190,16 +245,15 @@ // Called on the first IR processed. virtual void handleInitialIR(Any IR) = 0; // Called before and after a pass to get the representation of the IR. - virtual void generateIRRepresentation(Any IR, StringRef PassID, - IRUnitT &Output) = 0; + virtual void generateIRRepresentation(Any IR, IRUnitT &Output) = 0; // Called when the pass is not iteresting. virtual void omitAfter(StringRef PassID, std::string &Name) = 0; // Called when an interesting IR has changed. - virtual void handleAfter(StringRef PassID, std::string &Name, + virtual void handleAfter(StringRef PassID, const std::string &Name, const IRUnitT &Before, const IRUnitT &After, - Any) = 0; - // Called when an interesting pass is invalidated. - virtual void handleInvalidated(StringRef PassID) = 0; + bool compareMultiple) = 0; + // Called when an interesting pass is invalidated but no IR available. + virtual void handleInvalidatedUnavailableBanner(StringRef PassID) = 0; // Called when the IR or pass is not interesting. virtual void handleFiltered(StringRef PassID, std::string &Name) = 0; // Called when an ignored pass is encountered. @@ -225,8 +279,8 @@ void handleInitialIR(Any IR) override; // Report that the IR was omitted because it did not change. void omitAfter(StringRef PassID, std::string &Name) override; - // Report that the pass was invalidated. - void handleInvalidated(StringRef PassID) override; + // Called when an interesting pass is invalidated but no IR available. + void handleInvalidatedUnavailableBanner(StringRef PassID) override; // Report that the IR was filtered out. void handleFiltered(StringRef PassID, std::string &Name) override; // Report that the pass was ignored. @@ -241,21 +295,20 @@ // by unwrapAndPrint. The string representation is stored in a std::string // to preserve it as the IR changes in each pass. Note that the banner is // included in this representation but it is massaged before reporting. -class IRChangedPrinter : public TextChangeReporter { +class IRChangedPrinter : public TextChangeReporter { public: IRChangedPrinter(bool VerboseMode) - : TextChangeReporter(VerboseMode) {} + : TextChangeReporter(VerboseMode) {} ~IRChangedPrinter() override; void registerCallbacks(PassInstrumentationCallbacks &PIC); protected: // Called before and after a pass to get the representation of the IR. - void generateIRRepresentation(Any IR, StringRef PassID, - std::string &Output) override; + void generateIRRepresentation(Any IR, StringIR &Output) override; // Called when an interesting IR has changed. - void handleAfter(StringRef PassID, std::string &Name, - const std::string &Before, const std::string &After, - Any) override; + void handleAfter(StringRef PassID, const std::string &Name, + const StringIR &Before, const StringIR &After, + bool compareMultiple) override; }; // Information that needs to be saved for a basic block in order to compare @@ -335,7 +388,8 @@ // The data saved for comparing IRs. template -class IRDataT : public OrderedChangedData> {}; +class IRDataT : public DataForInvalidatedIR, + public OrderedChangedData> {}; // Abstract template base class for a class that compares two IRs. The // class is created with the 2 IRs to compare and then compare is called. @@ -349,7 +403,7 @@ // compare of a function. When \p InModule is set, // this function is being handled as part of comparing a module. void compare( - bool CompareModule, + bool CompareMultiple, std::function &Before, const FuncDataT &After)> CompareFunc); @@ -381,13 +435,14 @@ protected: // Create a representation of the IR. - void generateIRRepresentation(Any IR, StringRef PassID, - IRDataT &Output) override; + virtual void generateIRRepresentation(Any IR, + IRDataT &Output) override; // Called when an interesting IR has changed. - void handleAfter(StringRef PassID, std::string &Name, - const IRDataT &Before, - const IRDataT &After, Any) override; + virtual void handleAfter(StringRef PassID, const std::string &Name, + const IRDataT &Before, + const IRDataT &After, + bool compareMultiple) override; void handleFunctionCompare(StringRef Name, StringRef Prefix, StringRef PassID, StringRef Divider, bool InModule, unsigned Minor, @@ -451,16 +506,15 @@ // Called on the first IR processed. void handleInitialIR(Any IR) override; // Called before and after a pass to get the representation of the IR. - void generateIRRepresentation(Any IR, StringRef PassID, - IRDataT &Output) override; + void generateIRRepresentation(Any IR, IRDataT &Output) override; // Called when the pass is not iteresting. void omitAfter(StringRef PassID, std::string &Name) override; // Called when an interesting IR has changed. - void handleAfter(StringRef PassID, std::string &Name, + void handleAfter(StringRef PassID, const std::string &Name, const IRDataT &Before, const IRDataT &After, - Any) override; - // Called when an interesting pass is invalidated. - void handleInvalidated(StringRef PassID) override; + bool compareMultiple) override; + // Called when an interesting pass is invalidated but no IR available. + void handleInvalidatedUnavailableBanner(StringRef PassID) override; // Called when the IR or pass is not interesting. void handleFiltered(StringRef PassID, std::string &Name) override; // Called when an ignored pass is encountered. @@ -529,8 +583,8 @@ TimePassesHandler &getTimePasses() { return TimePasses; } }; -extern template class ChangeReporter; -extern template class TextChangeReporter; +extern template class ChangeReporter; +extern template class TextChangeReporter; extern template class BlockDataT; extern template class FuncDataT; Index: llvm/lib/Passes/StandardInstrumentations.cpp =================================================================== --- llvm/lib/Passes/StandardInstrumentations.cpp +++ llvm/lib/Passes/StandardInstrumentations.cpp @@ -51,7 +51,7 @@ #else cl::init(true) #endif - ); +); // An option that supports the -print-changed option. See // the description for -print-changed for an explanation of the use @@ -345,7 +345,83 @@ // Save the IR representation on the stack. T &Data = BeforeStack.back(); - generateIRRepresentation(IR, PassID, Data); + generateIRRepresentation(IR, Data); + Data.saveRepForInvalidated(IR); +} + +void DataForInvalidatedIR::generateInvalidatedIRRep( + std::function HandleFunc) const { + assert(haveValidData() && "Expected valid data"); + for (auto S : SavedFunctionNames) + if (const Function *F = SavedModule->getFunction(S)) { + assert(isInterestingFunction(*F) && "Expecting non-filtered function"); + HandleFunc(F); + } +} + +void DataForInvalidatedIR::saveRepForInvalidated(Any IR) { + if (any_isa(IR)) { + // Do not save module since we should not produce anything when + // a module becomes invalidated. + return; + } + + // Save the name for printing in banners + Name = getIRName(IR); + + auto saveModuleData = [&](const Module *M) { + if (forcePrintModuleIR()) { + SavedModule = M; + for (auto &F : *M) + // filtering + if (isInterestingFunction(F)) + SavedFunctionNames.emplace_back(F.getName().str()); + return true; + } + return false; + }; + + if (any_isa(IR)) { + // Save the function + const Function &F = *any_cast(IR); + if (!saveModuleData(F.getParent())) + // filtering + if (isInterestingFunction(F)) { + SavedModule = F.getParent(); + SavedFunctionNames.emplace_back(F.getName().str()); + } + return; + } + + if (any_isa(IR)) { + // Save all functions in SCC + const LazyCallGraph::SCC &C = *any_cast(IR); + if (!saveModuleData(C.begin()->getFunction().getParent())) + for (const LazyCallGraph::Node &N : C) { + const Function &F = N.getFunction(); + // filtering + if (!F.isDeclaration() && isInterestingFunction(F)) { + SavedModule = F.getParent(); + SavedFunctionNames.emplace_back(F.getName().str()); + } + } + return; + } + + if (any_isa(IR)) { + // Save function containing loop + const Loop &L = *any_cast(IR); + const Function &F = *L.getHeader()->getParent(); + if (!saveModuleData(F.getParent())) + // filtering + if (isInterestingFunction(F)) { + SavedModule = F.getParent(); + SavedFunctionNames.emplace_back(F.getName().str()); + } + return; + } + + llvm_unreachable("Unknown IR unit"); } template @@ -366,28 +442,49 @@ T &Before = BeforeStack.back(); // Create the after rep T After; - generateIRRepresentation(IR, PassID, After); + generateIRRepresentation(IR, After); // Was there a change in IR? if (Before == After) { if (VerboseMode) omitAfter(PassID, Name); } else - handleAfter(PassID, Name, Before, After, IR); + handleAfter(PassID, Name, Before, After, getModuleForComparison(IR)); } BeforeStack.pop_back(); } template -void ChangeReporter::handleInvalidatedPass(StringRef PassID) { +void ChangeReporter::handleInvalidatedPass(StringRef PassID, + StringRef PassName) { assert(!BeforeStack.empty() && "Unexpected empty stack encountered."); - // Always flag it as invalidated as we cannot determine when - // a pass for a filtered function is invalidated since we do not - // get the IR in the call. Also, the output is just alternate - // forms of the banner anyway. - if (VerboseMode) - handleInvalidated(PassID); + SmallString<60> IPassID = formatv("Invalidated {0}", PassID); + if (isIgnored(PassID)) { + if (VerboseMode) { + std::string Name = "unknown IR"; + handleIgnored(IPassID, Name); + } + } else if (!isInteresting(Any(), PassID, PassName)) { + if (VerboseMode) { + std::string Name = "unknown IR"; + handleFiltered(IPassID, Name); + } + } else { + T &Before = BeforeStack.back(); + if (!Before.haveValidData()) + handleInvalidatedUnavailableBanner(IPassID); + else { + // Create a representation from the saved before data to compare against. + T After; + auto Func = [&](const Function *F) { + generateIRRepresentation(Any(F), After); + }; + Before.generateInvalidatedIRRep(Func); + handleAfter(IPassID, Before.getName(), Before, After, + Before.haveMultipleFunctionNames()); + } + } BeforeStack.pop_back(); } @@ -403,8 +500,8 @@ handleIRAfterPass(IR, P, PIC.getPassNameForClassName(P)); }); PIC.registerAfterPassInvalidatedCallback( - [this](StringRef P, const PreservedAnalyses &) { - handleInvalidatedPass(P); + [&PIC, this](StringRef P, const PreservedAnalyses &) { + handleInvalidatedPass(P, PIC.getPassNameForClassName(P)); }); } @@ -427,9 +524,11 @@ PassID, Name); } -template -void TextChangeReporter::handleInvalidated(StringRef PassID) { - Out << formatv("*** IR Pass {0} invalidated ***\n", PassID); +template +void TextChangeReporter::handleInvalidatedUnavailableBanner( + StringRef PassID) { + Out << formatv( + "*** IR Dump After {0} but IR unavailable or filtered out ***\n", PassID); } template @@ -450,19 +549,19 @@ void IRChangedPrinter::registerCallbacks(PassInstrumentationCallbacks &PIC) { if (PrintChanged == ChangePrinter::Verbose || PrintChanged == ChangePrinter::Quiet) - TextChangeReporter::registerRequiredCallbacks(PIC); + TextChangeReporter::registerRequiredCallbacks(PIC); } -void IRChangedPrinter::generateIRRepresentation(Any IR, StringRef PassID, - std::string &Output) { - raw_string_ostream OS(Output); +void IRChangedPrinter::generateIRRepresentation(Any IR, StringIR &Output) { + raw_string_ostream OS(Output.str()); unwrapAndPrint(OS, IR); OS.str(); } -void IRChangedPrinter::handleAfter(StringRef PassID, std::string &Name, - const std::string &Before, - const std::string &After, Any) { +void IRChangedPrinter::handleAfter(StringRef PassID, const std::string &Name, + const StringIR &Before, + const StringIR &After, + bool compareMultiple) { // Report the IR before the changes when requested. if (PrintChangedBefore) Out << "*** IR Dump Before " << PassID << " on " << Name << " ***\n" @@ -470,7 +569,7 @@ // We might not get anything to print if we only want to print a specific // function but it gets deleted. - if (After.empty()) { + if (After.str().empty()) { Out << "*** IR Deleted After " << PassID << " on " << Name << " ***\n"; return; } @@ -552,11 +651,17 @@ template void IRComparer::compare( - bool CompareModule, + bool CompareMultiple, std::function &Before, const FuncDataT &After)> CompareFunc) { - if (!CompareModule) { + if (!CompareMultiple) { + if (!Before.getData().size()) { + assert(!After.getData().size() && "Inconsistent filtering of functions"); + // Function was filtered out + return; + } + // Just handle the single function. assert(Before.getData().size() == 1 && After.getData().size() == 1 && "Expected only one function."); @@ -1093,19 +1198,20 @@ InLineChangePrinter::~InLineChangePrinter() = default; -void InLineChangePrinter::generateIRRepresentation(Any IR, StringRef PassID, +void InLineChangePrinter::generateIRRepresentation(Any IR, IRDataT &D) { IRComparer::analyzeIR(IR, D); } -void InLineChangePrinter::handleAfter(StringRef PassID, std::string &Name, +void InLineChangePrinter::handleAfter(StringRef PassID, const std::string &Name, const IRDataT &Before, - const IRDataT &After, Any IR) { + const IRDataT &After, + bool compareMultiple) { SmallString<20> Banner = formatv("*** IR Dump After {0} on {1} ***\n", PassID, Name); Out << Banner; IRComparer(Before, After) - .compare(getModuleForComparison(IR), + .compare(compareMultiple, [&](bool InModule, unsigned Minor, const FuncDataT &Before, const FuncDataT &After) -> void { @@ -1794,7 +1900,7 @@ SmallString<200> Text; Text = formatv("{0}.{1}{2}{3}{4}", Number, Prefix, makeHTMLReady(PassID), - Divider, Name); + Divider, makeHTMLReady(Name)); DotCfgDiff Diff(Text, Before, After); std::string EntryBlockName = After.getEntryBlockName(); @@ -1837,13 +1943,15 @@ << "Initial IR (by function)\n" << "
\n" << "

\n"; - // Create representation of IR + // Create representation of full IR IRDataT Data; - IRComparer::analyzeIR(IR, Data); + auto *M = unwrapModule(IR, /*Force=*/true); + assert(M && "Expected module to be unwrapped when forced."); + IRComparer::analyzeIR(M, Data); // Now compare it against itself, which will have everything the // same and will generate the files. IRComparer(Data, Data) - .compare(getModuleForComparison(IR), + .compare(true, [&](bool InModule, unsigned Minor, const FuncDataT &Before, const FuncDataT &After) -> void { @@ -1855,7 +1963,7 @@ ++N; } -void DotCfgChangeReporter::generateIRRepresentation(Any IR, StringRef PassID, +void DotCfgChangeReporter::generateIRRepresentation(Any IR, IRDataT &Data) { IRComparer::analyzeIR(IR, Data); } @@ -1869,12 +1977,14 @@ ++N; } -void DotCfgChangeReporter::handleAfter(StringRef PassID, std::string &Name, +void DotCfgChangeReporter::handleAfter(StringRef PassID, + const std::string &Name, const IRDataT &Before, - const IRDataT &After, Any IR) { + const IRDataT &After, + bool compareMultiple) { assert(HTML && "Expected outstream to be set"); IRComparer(Before, After) - .compare(getModuleForComparison(IR), + .compare(compareMultiple, [&](bool InModule, unsigned Minor, const FuncDataT &Before, const FuncDataT &After) -> void { @@ -1885,11 +1995,12 @@ ++N; } -void DotCfgChangeReporter::handleInvalidated(StringRef PassID) { +void DotCfgChangeReporter::handleInvalidatedUnavailableBanner( + StringRef PassID) { assert(HTML && "Expected outstream to be set"); - SmallString<20> Banner = - formatv(" {0}. {1} invalidated
\n", N, makeHTMLReady(PassID)); - *HTML << Banner; + *HTML << formatv( + " {0}. {1} but IR unavailable or filtered out
\n", N, + makeHTMLReady(PassID)); ++N; } @@ -1974,7 +2085,7 @@ void DotCfgChangeReporter::registerCallbacks( PassInstrumentationCallbacks &PIC) { if (PrintChanged == ChangePrinter::DotCfgVerbose || - PrintChanged == ChangePrinter::DotCfgQuiet) { + PrintChanged == ChangePrinter::DotCfgQuiet) { SmallString<128> OutputDir; sys::fs::expand_tilde(DotCfgDir, OutputDir); sys::fs::make_absolute(OutputDir); @@ -2063,8 +2174,8 @@ PrintCrashIR.registerCallbacks(PIC); } -template class ChangeReporter; -template class TextChangeReporter; +template class ChangeReporter; +template class TextChangeReporter; template class BlockDataT; template class FuncDataT; Index: llvm/test/Other/ChangePrinters/DotCfg/print-changed-dot-cfg-invalidated.ll =================================================================== --- /dev/null +++ llvm/test/Other/ChangePrinters/DotCfg/print-changed-dot-cfg-invalidated.ll @@ -0,0 +1,65 @@ +; Deleting a loop invalidates the pass; test reporting of changes. +; +; Note that (mostly) only the banners are checked. +; +; Basic functionality +; RUN: rm -rf %t && mkdir -p %t +; RUN: opt -disable-verify -S -print-changed=dot-cfg -passes=loop-deletion -dot-cfg-dir=%t < %s -o /dev/null +; RUN: ls %t/*.pdf %t/passes.html | count 4 +; RUN: FileCheck %s -input-file=%t/passes.html --check-prefix=CHECK-DOT-CFG-INVALIDATE +; +; CHECK-DOT-CFG-INVALIDATE-FILES: passes.html diff_0_0.pdf diff_4.pdf +; CHECK-DOT-CFG-INVALIDATE: 4. Pass Invalidated LoopDeletionPass on loop
+; CHECK-DOT-CFG-INVALIDATE-NEXT:

+; CHECK-DOT-CFG-INVALIDATE-NEXT: 5. Invalidated PassManager{{.*}} on unknown IR ignored
+ +; module scope +; RUN: rm -rf %t && mkdir -p %t +; RUN: opt -disable-verify -S -print-changed=dot-cfg -print-module-scope -passes=loop-deletion -dot-cfg-dir=%t < %s -o /dev/null +; RUN: ls %t/*.pdf %t/passes.html | count 5 +; RUN: FileCheck %s -input-file=%t/passes.html --check-prefix=CHECK-DOT-CFG-INVALIDATE-MOD +; +; CHECK-DOT-CFG-INVALIDATE-MOD-FILES: passes.html diff_0.pdf diff_4_0.pdf diff_4_1.pdf +; CHECK-DOT-CFG-INVALIDATE-MOD: 4.0. Pass Invalidated LoopDeletionPass on loop
+; CHECK-DOT-CFG-INVALIDATE-MOD: 4.1. Pass Invalidated LoopDeletionPass on loop
+; CHECK-DOT-CFG-INVALIDATE-MOD-NEXT:

+; CHECK-DOT-CFG-INVALIDATE-MOD-NEXT: 5. Invalidated PassManager{{.*}} on unknown IR ignored
+ +; +; Filter out passes +; RUN: rm -rf %t && mkdir -p %t +; RUN: opt -disable-verify -S -print-changed=dot-cfg -passes=loop-deletion -dot-cfg-dir=%t -filter-passes="notThisPass" < %s -o /dev/null +; RUN: ls %t | count 1 +; +; Filter in passes +; RUN: rm -rf %t && mkdir -p %t +; RUN: opt -disable-verify -S -print-changed=dot-cfg -passes=loop-deletion -dot-cfg-dir=%t -filter-passes="loop-deletion" < %s -o /dev/null +; RUN: ls %t | count 4 +; +; Filter out functions +; RUN: rm -rf %t && mkdir -p %t +; RUN: opt -disable-verify -S -print-changed=dot-cfg -passes=loop-deletion -dot-cfg-dir=%t -filter-print-funcs="notThisFunc" < %s -o /dev/null +; RUN: ls %t | count 1 +; +; Filter in functions +; RUN: rm -rf %t && mkdir -p %t +; RUN: opt -disable-verify -S -print-changed=dot-cfg -passes=loop-deletion -dot-cfg-dir=%t -filter-print-funcs="deleteme" < %s -o /dev/null +; RUN: ls %t | count 3 +; + +define void @deleteme() { +entry: + br label %loop +loop: + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %iv.next = add i32 %iv, 1 + %check = icmp ult i32 %iv.next, 3 + br i1 %check, label %loop, label %exit +exit: + ret void +} + +define void @f() { +entry: + ret void +} Index: llvm/test/Other/ChangePrinters/DotCfg/print-changed-dot-cfg.ll =================================================================== --- llvm/test/Other/ChangePrinters/DotCfg/print-changed-dot-cfg.ll +++ llvm/test/Other/ChangePrinters/DotCfg/print-changed-dot-cfg.ll @@ -5,7 +5,7 @@ ; Simple functionality check. ; RUN: rm -rf %t && mkdir -p %t ; RUN: opt -disable-verify -S -print-changed=dot-cfg -passes=instsimplify -dot-cfg-dir=%t < %s -o /dev/null -; RUN: ls %t/*.pdf %t/passes.html | count 4 +; RUN: ls %t/*.pdf %t/passes.html | count 5 ; RUN: FileCheck %s -input-file=%t/passes.html --check-prefix=CHECK-DOT-CFG-SIMPLE ; ; Check that only the passes that change the IR are printed and that the @@ -19,25 +19,25 @@ ; -print-module-scope ; RUN: rm -rf %t && mkdir -p %t ; RUN: opt -disable-verify -S -print-changed=dot-cfg -passes=instsimplify -print-module-scope -dot-cfg-dir=%t < %s -o /dev/null -; RUN: ls %t/*.pdf %t/passes.html | count 4 +; RUN: ls %t/*.pdf %t/passes.html | count 5 ; RUN: FileCheck %s -input-file=%t/passes.html --check-prefix=CHECK-DOT-CFG-PRINT-MOD-SCOPE ; ; Check that reporting of multiple functions happens ; RUN: rm -rf %t && mkdir -p %t ; RUN: opt -disable-verify -S -print-changed=dot-cfg -passes=instsimplify -filter-print-funcs="f,g" -dot-cfg-dir=%t < %s -o /dev/null -; RUN: ls %t/*.pdf %t/passes.html | count 4 +; RUN: ls %t/*.pdf %t/passes.html | count 5 ; RUN: FileCheck %s -input-file=%t/passes.html --check-prefix=CHECK-DOT-CFG-FILTER-MULT-FUNC ; ; Check that the reporting of IRs respects -filter-passes ; RUN: rm -rf %t && mkdir -p %t ; RUN: opt -disable-verify -S -print-changed=dot-cfg -passes="instsimplify,no-op-function" -filter-passes="no-op-function" -dot-cfg-dir=%t < %s -o /dev/null -; RUN: ls %t/*.pdf %t/passes.html | count 2 +; RUN: ls %t/*.pdf %t/passes.html | count 3 ; RUN: FileCheck %s -input-file=%t/passes.html --check-prefix=CHECK-DOT-CFG-FILTER-PASSES ; ; Check that the reporting of IRs respects -filter-passes with multiple passes ; RUN: rm -rf %t && mkdir -p %t ; RUN: opt -disable-verify -S -print-changed=dot-cfg -passes="instsimplify,no-op-function" -filter-passes="no-op-function,instsimplify" -dot-cfg-dir=%t < %s -o /dev/null -; RUN: ls %t/*.pdf %t/passes.html | count 4 +; RUN: ls %t/*.pdf %t/passes.html | count 5 ; RUN: FileCheck %s -input-file=%t/passes.html --check-prefix=CHECK-DOT-CFG-FILTER-MULT-PASSES ; ; Check that the reporting of IRs respects both -filter-passes and -filter-print-funcs @@ -121,12 +121,13 @@ ret i32 %a } -; CHECK-DOT-CFG-SIMPLE-FILES: passes.html diff_0.pdf diff_1.pdf diff_3.pdf +; CHECK-DOT-CFG-SIMPLE-FILES: passes.html diff_0_0.pdf diff_0_1.pdf diff_1.pdf diff_3.pdf ; CHECK-DOT-CFG-SIMPLE: passes.html ; CHECK-DOT-CFG-SIMPLE-NEXT: ; CHECK-DOT-CFG-SIMPLE-NEXT:
; CHECK-DOT-CFG-SIMPLE-NEXT:

-; CHECK-DOT-CFG-SIMPLE-NEXT: 0. Initial IR
+; CHECK-DOT-CFG-SIMPLE-NEXT: 0.0. Initial IR
+; CHECK-DOT-CFG-SIMPLE-NEXT: 0.1. Initial IR
; CHECK-DOT-CFG-SIMPLE-NEXT:

; CHECK-DOT-CFG-SIMPLE-NEXT:

; CHECK-DOT-CFG-SIMPLE-NEXT: 1. Pass InstSimplifyPass on g
@@ -145,7 +146,7 @@ ; CHECK-DOT-CFG-FUNC-FILTER-NEXT: ; CHECK-DOT-CFG-FUNC-FILTER-NEXT:
; CHECK-DOT-CFG-FUNC-FILTER-NEXT:

-; CHECK-DOT-CFG-FUNC-FILTER-NEXT: 2. Initial IR
+; CHECK-DOT-CFG-FUNC-FILTER-NEXT: 2.0. Initial IR
; CHECK-DOT-CFG-FUNC-FILTER-NEXT:

; CHECK-DOT-CFG-FUNC-FILTER-NEXT:

; CHECK-DOT-CFG-FUNC-FILTER-NEXT: 3. Pass InstSimplifyPass on f
@@ -159,7 +160,8 @@ ; CHECK-DOT-CFG-PRINT-MOD-SCOPE-NEXT: ; CHECK-DOT-CFG-PRINT-MOD-SCOPE-NEXT:
; CHECK-DOT-CFG-PRINT-MOD-SCOPE-NEXT:

-; CHECK-DOT-CFG-PRINT-MOD-SCOPE-NEXT: 0. Initial IR
+; CHECK-DOT-CFG-PRINT-MOD-SCOPE-NEXT: 0.0. Initial IR
+; CHECK-DOT-CFG-PRINT-MOD-SCOPE-NEXT: 0.1. Initial IR
; CHECK-DOT-CFG-PRINT-MOD-SCOPE-NEXT:

; CHECK-DOT-CFG-PRINT-MOD-SCOPE-NEXT:

; CHECK-DOT-CFG-PRINT-MOD-SCOPE-NEXT: 1. Pass InstSimplifyPass on g
@@ -176,7 +178,8 @@ ; CHECK-DOT-CFG-FILTER-MULT-FUNC-NEXT: ; CHECK-DOT-CFG-FILTER-MULT-FUNC-NEXT:
; CHECK-DOT-CFG-FILTER-MULT-FUNC-NEXT:

-; CHECK-DOT-CFG-FILTER-MULT-FUNC-NEXT: 0. Initial IR
+; CHECK-DOT-CFG-FILTER-MULT-FUNC-NEXT: 0.0. Initial IR
+; CHECK-DOT-CFG-FILTER-MULT-FUNC-NEXT: 0.1. Initial IR
; CHECK-DOT-CFG-FILTER-MULT-FUNC-NEXT:

; CHECK-DOT-CFG-FILTER-MULT-FUNC-NEXT:

; CHECK-DOT-CFG-FILTER-MULT-FUNC-NEXT: 1. Pass InstSimplifyPass on g
@@ -194,7 +197,8 @@ ; CHECK-DOT-CFG-FILTER-PASSES-NEXT: ; CHECK-DOT-CFG-FILTER-PASSES-NEXT:
; CHECK-DOT-CFG-FILTER-PASSES-NEXT:

-; CHECK-DOT-CFG-FILTER-PASSES-NEXT: 1. Initial IR
+; CHECK-DOT-CFG-FILTER-PASSES-NEXT: 1.0. Initial IR
+; CHECK-DOT-CFG-FILTER-PASSES-NEXT: 1.1. Initial IR
; CHECK-DOT-CFG-FILTER-PASSES-NEXT:

; CHECK-DOT-CFG-FILTER-PASSES-NEXT:

; CHECK-DOT-CFG-FILTER-PASSES-NEXT: 2. Pass NoOpFunctionPass on g omitted because no change
@@ -212,7 +216,8 @@ ; CHECK-DOT-CFG-FILTER-MULT-PASSES-NEXT: ; CHECK-DOT-CFG-FILTER-MULT-PASSES-NEXT:
; CHECK-DOT-CFG-FILTER-MULT-PASSES-NEXT:

-; CHECK-DOT-CFG-FILTER-MULT-PASSES-NEXT: 0. Initial IR
+; CHECK-DOT-CFG-FILTER-MULT-PASSES-NEXT: 0.0. Initial IR
+; CHECK-DOT-CFG-FILTER-MULT-PASSES-NEXT: 0.1. Initial IR
; CHECK-DOT-CFG-FILTER-MULT-PASSES-NEXT:

; CHECK-DOT-CFG-FILTER-MULT-PASSES-NEXT:

; CHECK-DOT-CFG-FILTER-MULT-PASSES-NEXT: 1. Pass InstSimplifyPass on g
@@ -234,7 +239,7 @@ ; CHECK-DOT-CFG-FILTER-FUNC-PASSES-NEXT: ; CHECK-DOT-CFG-FILTER-FUNC-PASSES-NEXT:
; CHECK-DOT-CFG-FILTER-FUNC-PASSES-NEXT:

-; CHECK-DOT-CFG-FILTER-FUNC-PASSES-NEXT: 3. Initial IR
+; CHECK-DOT-CFG-FILTER-FUNC-PASSES-NEXT: 3.0. Initial IR
; CHECK-DOT-CFG-FILTER-FUNC-PASSES-NEXT:

; CHECK-DOT-CFG-FILTER-FUNC-PASSES-NEXT:

; CHECK-DOT-CFG-FILTER-FUNC-PASSES-NEXT: 4. Pass InstSimplifyPass on f
@@ -253,7 +258,7 @@ ; CHECK-DOT-CFG-MULT-PASSES-FILTER-FUNC-NEXT: ; CHECK-DOT-CFG-MULT-PASSES-FILTER-FUNC-NEXT:
; CHECK-DOT-CFG-MULT-PASSES-FILTER-FUNC-NEXT:

-; CHECK-DOT-CFG-MULT-PASSES-FILTER-FUNC-NEXT: 3. Initial IR
+; CHECK-DOT-CFG-MULT-PASSES-FILTER-FUNC-NEXT: 3.0. Initial IR
; CHECK-DOT-CFG-MULT-PASSES-FILTER-FUNC-NEXT:

; CHECK-DOT-CFG-MULT-PASSES-FILTER-FUNC-NEXT:

; CHECK-DOT-CFG-MULT-PASSES-FILTER-FUNC-NEXT: 4. Pass InstSimplifyPass on f
Index: llvm/test/Other/ChangePrinters/print-changed-invalidated.ll =================================================================== --- /dev/null +++ llvm/test/Other/ChangePrinters/print-changed-invalidated.ll @@ -0,0 +1,160 @@ +; Deleting a loop invalidates the pass; test reporting of changes. +; +; Basic functionality of print-changed=diff +; RUN: opt < %s -disable-output -passes=loop-deletion -print-changed=diff 2>&1 | FileCheck %s -check-prefix=DIFF +; +; DIFF-LABEL: entry: +; DIFF-LABEL: loop: +; DIFF-LABEL: exit: +; DIFF-LABEL: entry: +; DIFF: IR Dump After Invalidated LoopDeletionPass on loop +; DIFF-LABEL: entry: +; DIFF-NEXT: - br label %loop +; DIFF-NEXT: + br label %exit +; DIFF-LABEL: -loop: ; preds = %loop, %entry +; DIFF-NEXT: - %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] +; DIFF-NEXT: - %iv.next = add i32 %iv, 1 +; DIFF-NEXT: - %check = icmp ult i32 %iv.next, 3 +; DIFF-NEXT: - br i1 %check, label %loop, label %exit +; DIFF-LABEL: -exit: +; DIFF-LABEL: +exit: +; DIFF-NEXT: ret +; DIFF-NOT: IR DUMP +; DIFF: IR Pass Invalidated {{.*}} on unknown IR ignored +; +; module scope +; RUN: opt < %s -disable-output -passes=loop-deletion -print-changed=diff -print-module-scope 2>&1 | FileCheck %s -check-prefix=DIFF-MOD +; +; DIFF-MOD-LABEL: entry: +; DIFF-MOD-LABEL: loop: +; DIFF-MOD-LABEL: exit: +; DIFF-MOD-LABEL: entry: +; DIFF-MOD: IR Dump After Invalidated LoopDeletionPass on loop +; DIFF-MOD: IR for function {{.*}} +; DIFF-MOD-LABEL: entry: +; DIFF-MOD-NEXT: - br label %loop +; DIFF-MOD-NEXT: + br label %exit +; DIFF-MOD-LABEL: -loop: ; preds = %loop, %entry +; DIFF-MOD-NEXT: - %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] +; DIFF-MOD-NEXT: - %iv.next = add i32 %iv, 1 +; DIFF-MOD-NEXT: - %check = icmp ult i32 %iv.next, 3 +; DIFF-MOD-NEXT: - br i1 %check, label %loop, label %exit +; DIFF-MOD-LABEL: -exit: +; DIFF-MOD-LABEL: +exit: +; DIFF-MOD-NEXT: ret +; DIFF-MOD-NOT: IR DUMP +; DIFF-MOD: IR for function {{.*}} +; DIFF-MOD-LABEL: entry: +; DIFF-MOD-NOT: IR DUMP +; DIFF-MOD: IR Pass Invalidated {{.*}} on unknown IR ignored +; +; Filter out passes +; RUN: opt < %s -disable-output -passes=loop-deletion -print-changed=diff -filter-passes="notThisPass" 2>&1 | FileCheck %s -check-prefix=DIFF-FILTER-PASS-OUT +; +; DIFF-FILTER-PASS-OUT: IR Dump After Invalidated LoopDeletionPass on unknown IR filtered out +; +; Filter in passes +; RUN: opt < %s -disable-output -passes=loop-deletion -print-changed=diff-quiet -filter-passes="loop-deletion" 2>&1 | FileCheck %s -check-prefix=DIFF-FILTER-PASS-IN +; +; DIFF-FILTER-PASS-IN: IR Dump After Invalidated LoopDeletionPass on loop +; DIFF-FILTER-PASS-IN-LABEL: entry: +; +; Filter out functions +; RUN: opt < %s -disable-output -passes=loop-deletion -print-changed=diff -filter-print-funcs=notThisFunc 2>&1 | FileCheck %s -check-prefix=DIFF-FILTER-FUNC-OUT +; +; DIFF-FILTER-FUNC-OUT: IR Dump After Invalidated LoopDeletionPass but IR unavailable or filtered out +; +; Filter in functions +; RUN: opt < %s -disable-output -passes=loop-deletion -print-changed=diff-quiet -filter-print-funcs=deleteme 2>&1 | FileCheck %s -check-prefix=DIFF-FILTER-FUNC-IN +; +; DIFF-FILTER-FUNC-IN: IR Dump After Invalidated LoopDeletionPass on loop +; DIFF-FILTER-FUNC-IN-LABEL: entry: +; + +; Basic functionality of print-changed=cdiff +; RUN: opt < %s -disable-output -passes=loop-deletion -print-changed=cdiff 2>&1 | FileCheck %s -check-prefix=CDIFF +; +; CDIFF-LABEL: entry: +; CDIFF-LABEL: loop: +; CDIFF-LABEL: exit: +; CDIFF-LABEL: entry: +; CDIFF: IR Dump After Invalidated LoopDeletionPass on loop +; CDIFF-LABEL: entry: +; CDIFF-NEXT:{{.\[31m-}} br label %loop{{.\[0m}} +; CDIFF-NEXT:{{.\[32m\+}} br label %exit{{.\[0m}} +; CDIFF-LABEL:{{.\[31m-}}loop: ; preds = %loop, %entry{{.\[0m}} +; CDIFF-NEXT:{{.\[31m-}} %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]{{.\[0m}} +; CDIFF-NEXT:{{.\[31m-}} %iv.next = add i32 %iv, 1{{.\[0m}} +; CDIFF-NEXT:{{.\[31m-}} %check = icmp ult i32 %iv.next, 3{{.\[0m}} +; CDIFF-NEXT:{{.\[31m-}} br i1 %check, label %loop, label %exit{{.\[0m}} +; CDIFF-LABEL:{{.\[31m-}}exit: ; preds = %loop{{.\[0m}} +; CDIFF-LABEL:{{.\[32m\+}}exit: ; preds = %entry{{.\[0m}} +; CDIFF-NEXT: ret +; CDIFF-NOT: IR DUMP +; CDIFF: IR Pass Invalidated {{.*}} on unknown IR ignored +; +; module scope +; RUN: opt < %s -disable-output -passes=loop-deletion -print-changed=cdiff -print-module-scope 2>&1 | FileCheck %s -check-prefix=CDIFF-MOD +; +; CDIFF-MOD-LABEL: entry: +; CDIFF-MOD-LABEL: loop: +; CDIFF-MOD-LABEL: exit: +; CDIFF-MOD-LABEL: entry: +; CDIFF-MOD: IR Dump After Invalidated LoopDeletionPass on loop +; CDIFF-MOD: IR for function {{.*}} +; CDIFF-MOD-LABEL: entry: +; CDIFF-MOD-NEXT:{{.\[31m-}} br label %loop{{.\[0m}} +; CDIFF-MOD-NEXT:{{.\[32m\+}} br label %exit{{.\[0m}} +; CDIFF-MOD:{{.\[31m-}}loop: ; preds = %loop, %entry{{.\[0m}} +; CDIFF-MOD-NEXT:{{.\[31m-}} %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]{{.\[0m}} +; CDIFF-MOD-NEXT:{{.\[31m-}} %iv.next = add i32 %iv, 1{{.\[0m}} +; CDIFF-MOD-NEXT:{{.\[31m-}} %check = icmp ult i32 %iv.next, 3{{.\[0m}} +; CDIFF-MOD-NEXT:{{.\[31m-}} br i1 %check, label %loop, label %exit{{.\[0m}} +; CDIFF-MOD:{{.\[31m-}}exit: ; preds = %loop{{.\[0m}} +; CDIFF-MOD:{{.\[32m\+}}exit: ; preds = %entry{{.\[0m}} +; CDIFF-MOD-NEXT: ret +; CDIFF-MOD-NOT: IR DUMP +; CDIFF-MOD: IR for function {{.*}} +; CDIFF-MOD-LABEL: entry: +; CDIFF-MOD-NOT: IR DUMP +; CDIFF-MOD: IR Pass Invalidated {{.*}} on unknown IR ignored +; +; Filter out passes +; RUN: opt < %s -disable-output -passes=loop-deletion -print-changed=cdiff -filter-passes="notThisPass" 2>&1 | FileCheck %s -check-prefix=CDIFF-FILTER-PASS-OUT +; +; CDIFF-FILTER-PASS-OUT: IR Dump After Invalidated LoopDeletionPass on unknown IR filtered out +; +; Filter in passes +; RUN: opt < %s -disable-output -passes=loop-deletion -print-changed=cdiff-quiet -filter-passes="loop-deletion" 2>&1 | FileCheck %s -check-prefix=CDIFF-FILTER-PASS-IN +; +; CDIFF-FILTER-PASS-IN: IR Dump After Invalidated LoopDeletionPass on loop +; CDIFF-FILTER-PASS-IN-LABEL: entry: +; +; Filter out functions +; RUN: opt < %s -disable-output -passes=loop-deletion -print-changed=cdiff -filter-print-funcs=notThisFunc 2>&1 | FileCheck %s -check-prefix=CDIFF-FILTER-FUNC-OUT +; +; CDIFF-FILTER-FUNC-OUT: IR Dump After Invalidated LoopDeletionPass but IR unavailable or filtered out +; +; Filter in functions +; RUN: opt < %s -disable-output -passes=loop-deletion -print-changed=cdiff-quiet -filter-print-funcs=deleteme 2>&1 | FileCheck %s -check-prefix=CDIFF-FILTER-FUNC-IN +; +; CDIFF-FILTER-FUNC-IN: IR Dump After Invalidated LoopDeletionPass on loop +; CDIFF-FILTER-FUNC-IN-LABEL: entry: +; + +define void @deleteme() { +entry: + br label %loop +loop: + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %iv.next = add i32 %iv, 1 + %check = icmp ult i32 %iv.next, 3 + br i1 %check, label %loop, label %exit +exit: + ret void +} + +define void @f() { +entry: + ret void +} Index: llvm/test/Other/loop-deletion-printer.ll =================================================================== --- llvm/test/Other/loop-deletion-printer.ll +++ llvm/test/Other/loop-deletion-printer.ll @@ -1,6 +1,8 @@ ; Make sure that Loop which was invalidated by loop-deletion ; does not lead to problems for -print-after-all and is just skipped. ; +; Test that print-changed handles the invalidation well. +; ; RUN: opt < %s -disable-output \ ; RUN: -passes=loop-instsimplify -print-after-all 2>&1 | FileCheck %s -check-prefix=SIMPLIFY ; RUN: opt < %s -disable-output \ @@ -11,6 +13,19 @@ ; SIMPLIFY: IR Dump {{.*}} LoopInstSimplifyPass ; DELETED: IR Dump {{.*}}LoopDeletionPass {{.*}}(invalidated) ; DELETED-NOT: IR Dump {{.*}}LoopInstSimplifyPass +; +; RUN: opt < %s -disable-output -passes=loop-deletion -print-changed 2>&1 | FileCheck %s -check-prefix=DELETED_PRINT_CHANGED +; +; DELETED_PRINT_CHANGED-LABEL: entry: +; DELETED_PRINT_CHANGED-LABEL: loop: +; DELETED_PRINT_CHANGED-LABEL: exit: +; DELETED_PRINT_CHANGED: IR Dump After Invalidated LoopDeletionPass on loop +; DELETED_PRINT_CHANGED-LABEL: entry: +; DELETED_PRINT_CHANGED-NEXT: br +; DELETED_PRINT_CHANGED-LABEL: exit: +; DELETED_PRINT_CHANGED-NEXT: ret +; DELETED_PRINT_CHANGED-NOT: IR DUMP +; DELETED_PRINT_CHANGED: IR Pass Invalidated {{.*}} on unknown IR ignored define void @deleteme() { entry: