diff --git a/llvm/include/llvm/Passes/StandardInstrumentations.h b/llvm/include/llvm/Passes/StandardInstrumentations.h --- a/llvm/include/llvm/Passes/StandardInstrumentations.h +++ b/llvm/include/llvm/Passes/StandardInstrumentations.h @@ -15,6 +15,7 @@ #ifndef LLVM_PASSES_STANDARDINSTRUMENTATIONS_H #define LLVM_PASSES_STANDARDINSTRUMENTATIONS_H +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/IR/BasicBlock.h" @@ -256,6 +257,131 @@ bool same(const std::string &Before, const std::string &After) override; }; +// The following classes hold a representation of the IR for a change +// reporter that uses string comparisons of the basic blocks +// that are created using print (ie, similar to dump()). +// These classes respect the filtering of passes and functions using +// -filter-passes and -filter-print-funcs. +// +// Information that needs to be saved for a basic block in order to compare +// before and after the pass to determine if it was changed by a pass. +class ChangedBlockData { +public: + ChangedBlockData(const BasicBlock &B); + + bool operator==(const ChangedBlockData &That) const { + return Body == That.Body; + } + bool operator!=(const ChangedBlockData &That) const { + return Body != That.Body; + } + + // Return the label of the represented basic block. + StringRef getLabel() const { return Label; } + // Return the string representation of the basic block. + StringRef getBody() const { return Body; } + +protected: + std::string Label; + std::string Body; +}; + +template class OrderedChangedData { +public: + // Return the names in the order they were saved + std::vector &getOrder() { return Order; } + const std::vector &getOrder() const { return Order; } + + // Return a map of names to saved representations + StringMap &getData() { return Data; } + const StringMap &getData() const { return Data; } + + bool operator==(const OrderedChangedData &That) const { + return Data == That.getData(); + } + + // Call the lambda \p HandlePair on each corresponding pair of data from + // \p Before and \p After. The order is based on the order in \p After + // with ones that are only in \p Before interspersed based on where they + // occur in \p Before. This is used to present the output in an order + // based on how the data is ordered in LLVM. + static void + report(const OrderedChangedData &Before, const OrderedChangedData &After, + function_ref HandlePair); + +protected: + std::vector Order; + StringMap Data; +}; + +// The data saved for comparing functions. +using ChangedFuncData = OrderedChangedData; + +// A map of names to the saved data. +using ChangedIRData = OrderedChangedData; + +// A class that compares two IRs and does a diff between them. The +// added lines are prefixed with a '+', the removed lines are prefixed +// with a '-' and unchanged lines are prefixed with a space (to have +// things line up). +class ChangedIRComparer { +public: + ChangedIRComparer(raw_ostream &OS, const ChangedIRData &Before, + const ChangedIRData &After) + : Before(Before), After(After), Out(OS) {} + + // Compare the 2 IRs. + void compare(Any IR, StringRef Prefix, StringRef PassID, StringRef Name); + + // Analyze \p IR and build the IR representation in \p Data. + static void analyzeIR(Any IR, ChangedIRData &Data); + +protected: + // Return the module when that is the appropriate level of + // comparison for \p IR. + static const Module *getModuleForComparison(Any IR); + + // Generate the data for \p F into \p Data. + static bool generateFunctionData(ChangedIRData &Data, const Function &F); + + // Called to handle the compare of a function. When \p InModule is set, + // this function is being handled as part of comparing a module. + void handleFunctionCompare(StringRef Name, StringRef Prefix, StringRef PassID, + bool InModule, const ChangedFuncData &Before, + const ChangedFuncData &After); + + const ChangedIRData &Before; + const ChangedIRData &After; + raw_ostream &Out; +}; + +// A change printer that prints out in-line differences in the basic +// blocks. It uses an InlineComparer to do the comparison so it shows +// the differences prefixed with '-' and '+' for code that is removed +// and added, respectively. Changes to the IR that do not affect basic +// blocks are not reported as having changed the IR. The option +// -print-module-scope does not affect this change reporter. +class InLineChangePrinter : public TextChangeReporter { +public: + InLineChangePrinter(bool VerboseMode) + : TextChangeReporter(VerboseMode) {} + ~InLineChangePrinter() override; + void registerCallbacks(PassInstrumentationCallbacks &PIC); + +protected: + // Create a representation of the IR. + virtual void generateIRRepresentation(Any IR, StringRef PassID, + ChangedIRData &Output) override; + + // Called when an interesting IR has changed. + virtual void handleAfter(StringRef PassID, std::string &Name, + const ChangedIRData &Before, + const ChangedIRData &After, Any) override; + // Called to compare the before and after representations of the IR. + virtual bool same(const ChangedIRData &Before, + const ChangedIRData &After) override; +}; + class VerifyInstrumentation { bool DebugLogging; @@ -275,6 +401,7 @@ PreservedCFGCheckerInstrumentation PreservedCFGChecker; IRChangedPrinter PrintChangedIR; PseudoProbeVerifier PseudoProbeVerification; + InLineChangePrinter PrintChangedDiff; VerifyInstrumentation Verify; bool VerifyEach; @@ -290,6 +417,9 @@ extern template class ChangeReporter; extern template class TextChangeReporter; +extern template class ChangeReporter; +extern template class TextChangeReporter; + } // namespace llvm #endif diff --git a/llvm/lib/Passes/StandardInstrumentations.cpp b/llvm/lib/Passes/StandardInstrumentations.cpp --- a/llvm/lib/Passes/StandardInstrumentations.cpp +++ b/llvm/lib/Passes/StandardInstrumentations.cpp @@ -27,6 +27,8 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Program.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -66,14 +68,33 @@ // reported as filtered out. The -print-before-changed option will print // the IR as it was before each pass that changed it. The optional // value of quiet will only report when the IR changes, suppressing -// all other messages, including the initial IR. -enum ChangePrinter { NoChangePrinter, PrintChangedVerbose, PrintChangedQuiet }; +// all other messages, including the initial IR. The values "diff" and +// "diff-quiet" will present the changes in a form similar to a patch, in +// either verbose or quiet mode, respectively. The lines that are removed +// and added are prefixed with '-' and '+', respectively. The +// -filter-print-funcs and -filter-passes can be used to filter the output. +// This reporter relies on the linux diff utility to do comparisons and +// insert the prefixes. For systems that do not have the necessary +// facilities, the error message will be shown in place of the expected output. +// +enum class ChangePrinter { + NoChangePrinter, + PrintChangedVerbose, + PrintChangedQuiet, + PrintChangedDiffVerbose, + PrintChangedDiffQuiet +}; static cl::opt PrintChanged( "print-changed", cl::desc("Print changed IRs"), cl::Hidden, - cl::ValueOptional, cl::init(NoChangePrinter), - cl::values(clEnumValN(PrintChangedQuiet, "quiet", "Run in quiet mode"), + cl::ValueOptional, cl::init(ChangePrinter::NoChangePrinter), + cl::values(clEnumValN(ChangePrinter::PrintChangedQuiet, "quiet", + "Run in quiet mode"), + clEnumValN(ChangePrinter::PrintChangedDiffVerbose, "diff", + "Display patch-like changes"), + clEnumValN(ChangePrinter::PrintChangedDiffQuiet, "diff-quiet", + "Display patch-like changes in quiet mode"), // Sentinel value for unspecified option. - clEnumValN(PrintChangedVerbose, "", ""))); + clEnumValN(ChangePrinter::PrintChangedVerbose, "", ""))); // An option that supports the -print-changed option. See // the description for -print-changed for an explanation of the use @@ -91,8 +112,79 @@ cl::desc("Print before passes that change them"), cl::init(false), cl::Hidden); +// An option for specifying the diff used by print-changed=[diff | diff-quiet] +static cl::opt + DiffBinary("print-changed-diff-path", cl::Hidden, cl::init("diff"), + cl::desc("system diff used by change reporters")); + namespace { +// Perform a system based diff between \p Before and \p After, using +// \p OldLineFormat, \p NewLineFormat, and \p UnchangedLineFormat +// to control the formatting of the output. Return an error message +// for any failures instead of the diff. +std::string doSystemDiff(StringRef Before, StringRef After, + StringRef OldLineFormat, StringRef NewLineFormat, + StringRef UnchangedLineFormat) { + StringRef SR[2]{Before, After}; + // Store the 2 bodies into temporary files and call diff on them + // to get the body of the node. + const unsigned NumFiles = 3; + std::string FileName[NumFiles]; + int FD[NumFiles]{-1, -1, -1}; + for (unsigned I = 0; I < NumFiles; ++I) { + if (FD[I] == -1) { + SmallVector SV; + std::error_code EC = + sys::fs::createTemporaryFile("tmpdiff", "txt", FD[I], SV); + if (EC) + return "Unable to create temporary file."; + FileName[I] = Twine(SV).str(); + } + // The third file is used as the result of the diff. + if (I == NumFiles - 1) + break; + + std::error_code EC = sys::fs::openFileForWrite(FileName[I], FD[I]); + if (EC) + return "Unable to open temporary file for writing."; + + raw_fd_ostream OutStream(FD[I], /*shouldClose=*/true); + if (FD[I] == -1) + return "Error opening file for writing."; + OutStream << SR[I]; + } + + static ErrorOr DiffExe = sys::findProgramByName(DiffBinary); + if (!DiffExe) + return "Unable to find diff executable."; + + SmallString<128> OLF = formatv("--old-line-format={0}", OldLineFormat); + SmallString<128> NLF = formatv("--new-line-format={0}", NewLineFormat); + SmallString<128> ULF = + formatv("--unchanged-line-format={0}", UnchangedLineFormat); + + StringRef Args[] = {"-w", "-d", OLF, NLF, ULF, FileName[0], FileName[1]}; + Optional Redirects[] = {None, StringRef(FileName[2]), None}; + int Result = sys::ExecuteAndWait(*DiffExe, Args, None, Redirects); + if (Result < 0) + return "Error executing system diff."; + std::string Diff; + auto B = MemoryBuffer::getFile(FileName[2]); + if (B && *B) + Diff = (*B)->getBuffer().str(); + else + return "Unable to read result."; + + // Clean up. + for (unsigned I = 0; I < NumFiles; ++I) { + std::error_code EC = sys::fs::remove(FileName[I]); + if (EC) + return "Unable to remove temporary file."; + } + return Diff; +} + /// Extracting Module out of \p IR unit. Also fills a textual description /// of \p IR for use in header when printing. Optional> @@ -371,6 +463,12 @@ }); } +ChangedBlockData::ChangedBlockData(const BasicBlock &B) + : Label(B.getName().str()) { + raw_string_ostream SS(Body); + B.print(SS, nullptr, true, true); +} + template TextChangeReporter::TextChangeReporter(bool Verbose) : ChangeReporter(Verbose), Out(dbgs()) {} @@ -415,7 +513,8 @@ IRChangedPrinter::~IRChangedPrinter() {} void IRChangedPrinter::registerCallbacks(PassInstrumentationCallbacks &PIC) { - if (PrintChanged != NoChangePrinter) + if (PrintChanged == ChangePrinter::PrintChangedVerbose || + PrintChanged == ChangePrinter::PrintChangedQuiet) TextChangeReporter::registerRequiredCallbacks(PIC); } @@ -464,6 +563,145 @@ return S1 == S2; } +template +void OrderedChangedData::report( + const OrderedChangedData &Before, const OrderedChangedData &After, + function_ref HandlePair) { + const auto &BFD = Before.getData(); + const auto &AFD = After.getData(); + std::vector::const_iterator BI = Before.getOrder().begin(); + std::vector::const_iterator BE = Before.getOrder().end(); + std::vector::const_iterator AI = After.getOrder().begin(); + std::vector::const_iterator AE = After.getOrder().end(); + + auto handlePotentiallyRemovedIRData = [&](std::string S) { + // The order in LLVM may have changed so check if still exists. + if (!AFD.count(S)) { + // This has been removed. + HandlePair(&BFD.find(*BI)->getValue(), nullptr); + } + }; + auto handleNewIRData = [&](std::vector &Q) { + // Print out any queued up new sections + for (const IRData *NBI : Q) + HandlePair(nullptr, NBI); + Q.clear(); + }; + + // Print out the IRData in the after order, with before ones interspersed + // appropriately (ie, somewhere near where they were in the before list). + // Start at the beginning of both lists. Loop through the + // after list. If an element is common, then advance in the before list + // reporting the removed ones until the common one is reached. Report any + // queued up new ones and then report the common one. If an element is not + // common, then enqueue it for reporting. When the after list is exhausted, + // loop through the before list, reporting any removed ones. Finally, + // report the rest of the enqueued new ones. + std::vector NewIRDataQueue; + while (AI != AE) { + if (!BFD.count(*AI)) { + // This section is new so place it in the queue. This will cause it + // to be reported after deleted sections. + NewIRDataQueue.emplace_back(&AFD.find(*AI)->getValue()); + ++AI; + continue; + } + // This section is in both; advance and print out any before-only + // until we get to it. + while (*BI != *AI) { + handlePotentiallyRemovedIRData(*BI); + ++BI; + } + // Report any new sections that were queued up and waiting. + handleNewIRData(NewIRDataQueue); + + const IRData &AData = AFD.find(*AI)->getValue(); + const IRData &BData = BFD.find(*AI)->getValue(); + HandlePair(&BData, &AData); + ++BI; + ++AI; + } + + // Check any remaining before sections to see if they have been removed + while (BI != BE) { + handlePotentiallyRemovedIRData(*BI); + ++BI; + } + + handleNewIRData(NewIRDataQueue); +} + +void ChangedIRComparer::compare(Any IR, StringRef Prefix, StringRef PassID, + StringRef Name) { + if (!getModuleForComparison(IR)) { + // Not a module so just handle the single function. + assert(Before.getData().size() == 1 && "Expected only one function."); + assert(After.getData().size() == 1 && "Expected only one function."); + handleFunctionCompare(Name, Prefix, PassID, false, + Before.getData().begin()->getValue(), + After.getData().begin()->getValue()); + return; + } + + ChangedIRData::report( + Before, After, [&](const ChangedFuncData *B, const ChangedFuncData *A) { + ChangedFuncData Missing; + if (!B) + B = &Missing; + else if (!A) + A = &Missing; + assert(B != &Missing && A != &Missing && + "Both functions cannot be missing."); + handleFunctionCompare(Name, Prefix, PassID, true, *B, *A); + }); +} + +void ChangedIRComparer::analyzeIR(Any IR, ChangedIRData &Data) { + if (const Module *M = getModuleForComparison(IR)) { + // Create data for each existing/interesting function in the module. + for (const Function &F : *M) + generateFunctionData(Data, F); + return; + } + + const Function *F = nullptr; + if (any_isa(IR)) + F = any_cast(IR); + else { + assert(any_isa(IR) && "Unknown IR unit."); + const Loop *L = any_cast(IR); + F = L->getHeader()->getParent(); + } + assert(F && "Unknown IR unit."); + generateFunctionData(Data, *F); +} + +const Module *ChangedIRComparer::getModuleForComparison(Any IR) { + if (any_isa(IR)) + return any_cast(IR); + if (any_isa(IR)) + return any_cast(IR) + ->begin() + ->getFunction() + .getParent(); + return nullptr; +} + +bool ChangedIRComparer::generateFunctionData(ChangedIRData &Data, + const Function &F) { + if (!F.isDeclaration() && isFunctionInPrintList(F.getName())) { + ChangedFuncData CFD; + for (const auto &B : F) { + CFD.getOrder().emplace_back(B.getName()); + CFD.getData().insert({B.getName(), B}); + } + Data.getOrder().emplace_back(F.getName()); + Data.getData().insert({F.getName(), CFD}); + return true; + } + return false; +} + PrintIRInstrumentation::~PrintIRInstrumentation() { assert(ModuleDescStack.empty() && "ModuleDescStack is not empty at exit"); } @@ -867,11 +1105,61 @@ }); } +InLineChangePrinter::~InLineChangePrinter() {} + +void InLineChangePrinter::generateIRRepresentation(Any IR, StringRef PassID, + ChangedIRData &D) { + ChangedIRComparer::analyzeIR(IR, D); +} + +void InLineChangePrinter::handleAfter(StringRef PassID, std::string &Name, + const ChangedIRData &Before, + const ChangedIRData &After, Any IR) { + if (Name == "") + Name = " (module)"; + SmallString<20> Banner = + formatv("*** IR Dump After {0} ***{1}\n", PassID, Name); + Out << Banner; + ChangedIRComparer(Out, Before, After).compare(IR, "", PassID, Name); + Out << "\n"; +} + +bool InLineChangePrinter::same(const ChangedIRData &D1, + const ChangedIRData &D2) { + return D1 == D2; +} + +void ChangedIRComparer::handleFunctionCompare(StringRef Name, StringRef Prefix, + StringRef PassID, bool InModule, + const ChangedFuncData &Before, + const ChangedFuncData &After) { + // Print a banner when this is being shown in the context of a module + if (InModule) + Out << "\n*** IR for function " << Name << " ***\n"; + + ChangedFuncData::report( + Before, After, [&](const ChangedBlockData *B, const ChangedBlockData *A) { + StringRef BStr = B ? B->getBody() : "\n"; + StringRef AStr = A ? A->getBody() : "\n"; + const std::string Removed = "\033[31m-%l\033[0m\n"; + const std::string Added = "\033[32m+%l\033[0m\n"; + const std::string NoChange = " %l\n"; + Out << doSystemDiff(BStr, AStr, Removed, Added, NoChange); + }); +} + +void InLineChangePrinter::registerCallbacks(PassInstrumentationCallbacks &PIC) { + if (PrintChanged == ChangePrinter::PrintChangedDiffVerbose || + PrintChanged == ChangePrinter::PrintChangedDiffQuiet) + TextChangeReporter::registerRequiredCallbacks(PIC); +} + StandardInstrumentations::StandardInstrumentations(bool DebugLogging, bool VerifyEach) : PrintPass(DebugLogging), OptNone(DebugLogging), - PrintChangedIR(PrintChanged != PrintChangedQuiet), Verify(DebugLogging), - VerifyEach(VerifyEach) {} + PrintChangedIR(PrintChanged == ChangePrinter::PrintChangedVerbose), + PrintChangedDiff(PrintChanged == ChangePrinter::PrintChangedDiffVerbose), + Verify(DebugLogging), VerifyEach(VerifyEach) {} void StandardInstrumentations::registerCallbacks( PassInstrumentationCallbacks &PIC) { @@ -885,6 +1173,7 @@ PseudoProbeVerification.registerCallbacks(PIC); if (VerifyEach) Verify.registerCallbacks(PIC); + PrintChangedDiff.registerCallbacks(PIC); } namespace llvm { @@ -892,4 +1181,7 @@ template class ChangeReporter; template class TextChangeReporter; +template class ChangeReporter; +template class TextChangeReporter; + } // namespace llvm diff --git a/llvm/test/Other/ChangePrinters/lit.local.cfg b/llvm/test/Other/ChangePrinters/lit.local.cfg new file mode 100644 --- /dev/null +++ b/llvm/test/Other/ChangePrinters/lit.local.cfg @@ -0,0 +1,16 @@ +import os +import subprocess + +def have_needed_diff_support(): + if not os.path.exists('/usr/bin/diff'): + return False + + ld_cmd = subprocess.Popen( + ['/usr/bin/diff', '--help'], stdout=subprocess.PIPE, env={'LANG': 'C'}) + ld_out = ld_cmd.stdout.read().decode() + ld_cmd.wait() + + return '-line-format' in ld_out + +if not have_needed_diff_support(): + config.unsupported = True diff --git a/llvm/test/Other/ChangePrinters/print-changed-diff.ll b/llvm/test/Other/ChangePrinters/print-changed-diff.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Other/ChangePrinters/print-changed-diff.ll @@ -0,0 +1,288 @@ +; Simple checks of -print-changed=diff +; +; Note that (mostly) only the banners are checked. +; +; Simple functionality check. +; RUN: opt -S -print-changed=diff -passes=instsimplify 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK-DIFF-SIMPLE +; +; Check that only the passes that change the IR are printed and that the +; others (including g) are filtered out. +; RUN: opt -S -print-changed=diff -passes=instsimplify -filter-print-funcs=f 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK-DIFF-FUNC-FILTER +; +; Check that the reporting of IRs respects is not affected by +; -print-module-scope +; RUN: opt -S -print-changed=diff -passes=instsimplify -print-module-scope 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK-DIFF-PRINT-MOD-SCOPE +; +; Check that reporting of multiple functions happens +; RUN: opt -S -print-changed=diff -passes=instsimplify -filter-print-funcs="f,g" 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK-DIFF-FILTER-MULT-FUNC +; +; Check that the reporting of IRs respects -filter-passes +; RUN: opt -S -print-changed=diff -passes="instsimplify,no-op-function" -filter-passes="NoOpFunctionPass" 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK-DIFF-FILTER-PASSES +; +; Check that the reporting of IRs respects -filter-passes with multiple passes +; RUN: opt -S -print-changed=diff -passes="instsimplify,no-op-function" -filter-passes="NoOpFunctionPass,InstSimplifyPass" 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK-DIFF-FILTER-MULT-PASSES +; +; Check that the reporting of IRs respects both -filter-passes and -filter-print-funcs +; RUN: opt -S -print-changed=diff -passes="instsimplify,no-op-function" -filter-passes="NoOpFunctionPass,InstSimplifyPass" -filter-print-funcs=f 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK-DIFF-FILTER-FUNC-PASSES +; +; Check that repeated passes that change the IR are printed and that the +; others (including g) are filtered out. Note that only the first time +; instsimplify is run on f will result in changes +; RUN: opt -S -print-changed=diff -passes="instsimplify,instsimplify" -filter-print-funcs=f 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK-DIFF-MULT-PASSES-FILTER-FUNC +; +; Simple checks of -print-changed=diff-quiet +; +; Note that (mostly) only the banners are checked. +; +; Simple functionality check. +; RUN: opt -S -print-changed=diff-quiet -passes=instsimplify 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK-DIFF-QUIET-SIMPLE --allow-empty +; +; Check that only the passes that change the IR are printed and that the +; others (including g) are filtered out. +; RUN: opt -S -print-changed=diff-quiet -passes=instsimplify -filter-print-funcs=f 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK-DIFF-QUIET-FUNC-FILTER +; +; Check that the reporting of IRs respects is not affected by +; -print-module-scope +; RUN: opt -S -print-changed=diff-quiet -passes=instsimplify -print-module-scope 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK-DIFF-QUIET-PRINT-MOD-SCOPE +; +; Check that reporting of multiple functions happens +; RUN: opt -S -print-changed=diff-quiet -passes=instsimplify -filter-print-funcs="f,g" 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK-DIFF-QUIET-FILTER-MULT-FUNC +; +; Check that the reporting of IRs respects -filter-passes +; RUN: opt -S -print-changed=diff-quiet -passes="instsimplify,no-op-function" -filter-passes="NoOpFunctionPass" 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK-DIFF-QUIET-FILTER-PASSES-NONE --allow-empty +; +; Check that the reporting of IRs respects -filter-passes with multiple passes +; RUN: opt -S -print-changed=diff-quiet -passes="instsimplify,no-op-function" -filter-passes="NoOpFunctionPass,InstSimplifyPass" 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK-DIFF-QUIET-FILTER-MULT-PASSES +; +; Check that the reporting of IRs respects both -filter-passes and -filter-print-funcs +; RUN: opt -S -print-changed=diff-quiet -passes="instsimplify,no-op-function" -filter-passes="NoOpFunctionPass,InstSimplifyPass" -filter-print-funcs=f 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK-DIFF-QUIET-FILTER-FUNC-PASSES +; +; Check that repeated passes that change the IR are printed and that the +; others (including g) are filtered out. Note that only the first time +; instsimplify is run on f will result in changes +; RUN: opt -S -print-changed=diff-quiet -passes="instsimplify,instsimplify" -filter-print-funcs=f 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK-DIFF-QUIET-MULT-PASSES-FILTER-FUNC + +define i32 @g() { +entry: + %a = add i32 2, 3 + ret i32 %a +} + +define i32 @f() { +entry: + %a = add i32 2, 3 + ret i32 %a +} + +; CHECK-DIFF-SIMPLE: *** IR Dump At Start: *** +; CHECK-DIFF-SIMPLE: ModuleID = {{.+}} +; CHECK-DIFF-SIMPLE: *** IR Dump After VerifierPass (module) omitted because no change *** +; CHECK-DIFF-SIMPLE: *** IR Dump After InstSimplifyPass *** (function: g) +; CHECK-DIFF-SIMPLE-NOT: ModuleID = {{.+}} +; CHECK-DIFF-SIMPLE-NOT: *** IR{{.*}} +; CHECK-DIFF-SIMPLE: entry: +; CHECK-DIFF-SIMPLE-NEXT:- %a = add i32 2, 3 +; CHECK-DIFF-SIMPLE-NEXT:- ret i32 %a +; CHECK-DIFF-SIMPLE-NEXT:+ ret i32 5 +; CHECK-DIFF-SIMPLE: *** IR Pass PassManager{{.*}} (function: g) ignored *** +; CHECK-DIFF-SIMPLE: *** IR Dump After InstSimplifyPass *** (function: f) +; CHECK-DIFF-SIMPLE-NOT: ModuleID = {{.+}} +; CHECK-DIFF-SIMPLE-NOT: *** IR{{.*}} +; CHECK-DIFF-SIMPLE: entry: +; CHECK-DIFF-SIMPLE-NEXT:- %a = add i32 2, 3 +; CHECK-DIFF-SIMPLE-NEXT:- ret i32 %a +; CHECK-DIFF-SIMPLE-NEXT:+ ret i32 5 +; CHECK-DIFF-SIMPLE: *** IR Pass PassManager{{.*}} (function: f) ignored *** +; CHECK-DIFF-SIMPLE: *** IR Pass ModuleToFunctionPassAdaptor (module) ignored *** +; CHECK-DIFF-SIMPLE: *** IR Dump After VerifierPass (module) omitted because no change *** +; CHECK-DIFF-SIMPLE: *** IR Dump After PrintModulePass (module) omitted because no change *** + +; CHECK-DIFF-FUNC-FILTER: *** IR Dump At Start: *** +; CHECK-DIFF-FUNC-FILTER-NEXT: ; ModuleID = {{.+}} +; CHECK-DIFF-FUNC-FILTER: *** IR Dump After InstSimplifyPass (function: g) filtered out *** +; CHECK-DIFF-FUNC-FILTER: *** IR Dump After InstSimplifyPass *** (function: f) +; CHECK-DIFF-FUNC-FILTER-NOT: ModuleID = {{.+}} +; CHECK-DIFF-FUNC-FILTER: entry: +; CHECK-DIFF-FUNC-FILTER:- %a = add i32 2, 3 +; CHECK-DIFF-FUNC-FILTER:- ret i32 %a +; CHECK-DIFF-FUNC-FILTER:+ ret i32 5 +; CHECK-DIFF-FUNC-FILTER: *** IR Pass PassManager{{.*}} (function: f) ignored *** +; CHECK-DIFF-FUNC-FILTER: *** IR Pass ModuleToFunctionPassAdaptor (module) ignored *** +; CHECK-DIFF-FUNC-FILTER: *** IR Dump After VerifierPass (module) omitted because no change *** +; CHECK-DIFF-FUNC-FILTER: *** IR Dump After PrintModulePass (module) omitted because no change *** + +; CHECK-DIFF-PRINT-MOD-SCOPE: *** IR Dump At Start: *** +; CHECK-DIFF-PRINT-MOD-SCOPE: ModuleID = {{.+}} +; CHECK-DIFF-PRINT-MOD-SCOPE: *** IR Dump After InstSimplifyPass *** (function: g) +; CHECK-DIFF-PRINT-MOD-SCOPE-NOT: ModuleID = {{.+}} +; CHECK-DIFF-PRINT-MOD-SCOPE: entry: +; CHECK-DIFF-PRINT-MOD-SCOPE:- %a = add i32 2, 3 +; CHECK-DIFF-PRINT-MOD-SCOPE:- ret i32 %a +; CHECK-DIFF-PRINT-MOD-SCOPE:+ ret i32 5 +; CHECK-DIFF-PRINT-MOD-SCOPE: *** IR Dump After InstSimplifyPass *** (function: f) +; CHECK-DIFF-PRINT-MOD-SCOPE-NOT: ModuleID = {{.+}} +; CHECK-DIFF-PRINT-MOD-SCOPE: entry: +; CHECK-DIFF-PRINT-MOD-SCOPE:- %a = add i32 2, 3 +; CHECK-DIFF-PRINT-MOD-SCOPE:- ret i32 %a +; CHECK-DIFF-PRINT-MOD-SCOPE:+ ret i32 5 +; CHECK-DIFF-PRINT-MOD-SCOPE: *** IR Pass PassManager{{.*}} (function: f) ignored *** +; CHECK-DIFF-PRINT-MOD-SCOPE: *** IR Pass ModuleToFunctionPassAdaptor (module) ignored *** +; CHECK-DIFF-PRINT-MOD-SCOPE: *** IR Dump After VerifierPass (module) omitted because no change *** +; CHECK-DIFF-PRINT-MOD-SCOPE: *** IR Dump After PrintModulePass (module) omitted because no change *** + +; CHECK-DIFF-FILTER-MULT-FUNC: *** IR Dump At Start: *** +; CHECK-DIFF-FILTER-MULT-FUNC: *** IR Dump After InstSimplifyPass *** (function: g) +; CHECK-DIFF-FILTER-MULT-FUNC-NOT: ModuleID = {{.+}} +; CHECK-DIFF-FILTER-MULT-FUNC: entry: +; CHECK-DIFF-FILTER-MULT-FUNC:- %a = add i32 2, 3 +; CHECK-DIFF-FILTER-MULT-FUNC:- ret i32 %a +; CHECK-DIFF-FILTER-MULT-FUNC:+ ret i32 5 +; CHECK-DIFF-FILTER-MULT-FUNC: *** IR Dump After InstSimplifyPass *** (function: f) +; CHECK-DIFF-FILTER-MULT-FUNC-NOT: ModuleID = {{.+}} +; CHECK-DIFF-FILTER-MULT-FUNC: entry: +; CHECK-DIFF-FILTER-MULT-FUNC:- %a = add i32 2, 3 +; CHECK-DIFF-FILTER-MULT-FUNC:- ret i32 %a +; CHECK-DIFF-FILTER-MULT-FUNC:+ ret i32 5 +; CHECK-DIFF-FILTER-MULT-FUNC: *** IR Pass PassManager{{.*}} (function: f) ignored *** +; CHECK-DIFF-FILTER-MULT-FUNC: *** IR Pass ModuleToFunctionPassAdaptor (module) ignored *** +; CHECK-DIFF-FILTER-MULT-FUNC: *** IR Dump After VerifierPass (module) omitted because no change *** +; CHECK-DIFF-FILTER-MULT-FUNC: *** IR Dump After PrintModulePass (module) omitted because no change *** + +; CHECK-DIFF-FILTER-PASSES: *** IR Dump After InstSimplifyPass (function: g) filtered out *** +; CHECK-DIFF-FILTER-PASSES: *** IR Dump At Start: *** (function: g) +; CHECK-DIFF-FILTER-PASSES: *** IR Dump After NoOpFunctionPass (function: g) omitted because no change *** +; CHECK-DIFF-FILTER-PASSES: *** IR Dump After InstSimplifyPass (function: f) filtered out *** +; CHECK-DIFF-FILTER-PASSES: *** IR Dump After NoOpFunctionPass (function: f) omitted because no change *** + +; CHECK-DIFF-FILTER-MULT-PASSES: *** IR Dump At Start: *** (function: g) +; CHECK-DIFF-FILTER-MULT-PASSES: *** IR Dump After InstSimplifyPass *** (function: g) +; CHECK-DIFF-FILTER-MULT-PASSES-NOT: ModuleID = {{.+}} +; CHECK-DIFF-FILTER-MULT-PASSES: entry: +; CHECK-DIFF-FILTER-MULT-PASSES:- %a = add i32 2, 3 +; CHECK-DIFF-FILTER-MULT-PASSES:- ret i32 %a +; CHECK-DIFF-FILTER-MULT-PASSES:+ ret i32 5 +; CHECK-DIFF-FILTER-MULT-PASSES: *** IR Dump After NoOpFunctionPass (function: g) omitted because no change *** +; CHECK-DIFF-FILTER-MULT-PASSES: *** IR Dump After InstSimplifyPass *** (function: f) +; CHECK-DIFF-FILTER-MULT-PASSES-NOT: ModuleID = {{.+}} +; CHECK-DIFF-FILTER-MULT-PASSES: entry: +; CHECK-DIFF-FILTER-MULT-PASSES:- %a = add i32 2, 3 +; CHECK-DIFF-FILTER-MULT-PASSES:- ret i32 %a +; CHECK-DIFF-FILTER-MULT-PASSES:+ ret i32 5 +; CHECK-DIFF-FILTER-MULT-PASSES: *** IR Dump After NoOpFunctionPass (function: f) omitted because no change *** + +; CHECK-DIFF-FILTER-FUNC-PASSES: *** IR Dump After InstSimplifyPass (function: g) filtered out *** +; CHECK-DIFF-FILTER-FUNC-PASSES: *** IR Dump After NoOpFunctionPass (function: g) filtered out *** +; CHECK-DIFF-FILTER-FUNC-PASSES: *** IR Dump At Start: *** (function: f) +; CHECK-DIFF-FILTER-FUNC-PASSES: *** IR Dump After InstSimplifyPass *** (function: f) +; CHECK-DIFF-FILTER-FUNC-PASSES-NOT: ModuleID = {{.+}} +; CHECK-DIFF-FILTER-FUNC-PASSES: entry: +; CHECK-DIFF-FILTER-FUNC-PASSES:- %a = add i32 2, 3 +; CHECK-DIFF-FILTER-FUNC-PASSES:- ret i32 %a +; CHECK-DIFF-FILTER-FUNC-PASSES:+ ret i32 5 +; CHECK-DIFF-FILTER-FUNC-PASSES: *** IR Dump After NoOpFunctionPass (function: f) omitted because no change *** + +; CHECK-DIFF-MULT-PASSES-FILTER-FUNC: *** IR Dump At Start: *** +; CHECK-DIFF-MULT-PASSES-FILTER-FUNC: *** IR Dump After InstSimplifyPass (function: g) filtered out *** +; CHECK-DIFF-MULT-PASSES-FILTER-FUNC: *** IR Dump After InstSimplifyPass (function: g) filtered out *** +; CHECK-DIFF-MULT-PASSES-FILTER-FUNC: *** IR Dump After InstSimplifyPass *** (function: f) +; CHECK-DIFF-MULT-PASSES-FILTER-FUNC-NOT: ModuleID = {{.+}} +; CHECK-DIFF-MULT-PASSES-FILTER-FUNC: entry: +; CHECK-DIFF-MULT-PASSES-FILTER-FUNC:- %a = add i32 2, 3 +; CHECK-DIFF-MULT-PASSES-FILTER-FUNC:- ret i32 %a +; CHECK-DIFF-MULT-PASSES-FILTER-FUNC:+ ret i32 5 +; CHECK-DIFF-MULT-PASSES-FILTER-FUNC: *** IR Dump After InstSimplifyPass (function: f) omitted because no change *** + +; CHECK-DIFF-QUIET-SIMPLE-NOT: *** IR Dump {{.*(At Start:|no change|ignored|filtered out)}} *** +; CHECK-DIFF-QUIET-SIMPLE: *** IR Dump After InstSimplifyPass *** (function: g) +; CHECK-DIFF-QUIET-SIMPLE-NOT: ModuleID = {{.+}} +; CHECK-DIFF-QUIET-SIMPLE-NOT: *** IR{{.*}} +; CHECK-DIFF-QUIET-SIMPLE: entry: +; CHECK-DIFF-QUIET-SIMPLE-NEXT:- %a = add i32 2, 3 +; CHECK-DIFF-QUIET-SIMPLE-NEXT:- ret i32 %a +; CHECK-DIFF-QUIET-SIMPLE-NEXT:+ ret i32 5 +; CHECK-DIFF-QUIET-SIMPLE-EMPTY: +; CHECK-DIFF-QUIET-SIMPLE-NEXT: *** IR Dump After InstSimplifyPass *** (function: f) +; CHECK-DIFF-QUIET-SIMPLE-NOT: ModuleID = {{.+}} +; CHECK-DIFF-QUIET-SIMPLE-NOT: *** IR{{.*}} +; CHECK-DIFF-QUIET-SIMPLE: entry: +; CHECK-DIFF-QUIET-SIMPLE-NEXT:- %a = add i32 2, 3 +; CHECK-DIFF-QUIET-SIMPLE-NEXT:- ret i32 %a +; CHECK-DIFF-QUIET-SIMPLE-NEXT:+ ret i32 5 +; CHECK-DIFF-QUIET-SIMPLE-NOT: *** IR{{.*}} + +; CHECK-DIFF-QUIET-FUNC-FILTER-NOT: *** IR Dump {{.*(At Start:|no change|ignored|filtered out)}} *** +; CHECK-DIFF-QUIET-FUNC-FILTER: *** IR Dump After InstSimplifyPass *** (function: f) +; CHECK-DIFF-QUIET-FUNC-FILTER-NOT: ModuleID = {{.+}} +; CHECK-DIFF-QUIET-FUNC-FILTER: entry: +; CHECK-DIFF-QUIET-FUNC-FILTER:- %a = add i32 2, 3 +; CHECK-DIFF-QUIET-FUNC-FILTER:- ret i32 %a +; CHECK-DIFF-QUIET-FUNC-FILTER:+ ret i32 5 +; CHECK-DIFF-QUIET-FUNC-FILTER-NOT: *** IR{{.*}} + +; CHECK-DIFF-QUIET-PRINT-MOD-SCOPE-NOT: *** IR Dump {{.*(At Start:|no change|ignored|filtered out)}} *** +; CHECK-DIFF-QUIET-PRINT-MOD-SCOPE: *** IR Dump After InstSimplifyPass *** (function: g) +; CHECK-DIFF-QUIET-PRINT-MOD-SCOPE-NOT: ModuleID = {{.+}} +; CHECK-DIFF-QUIET-PRINT-MOD-SCOPE: entry: +; CHECK-DIFF-QUIET-PRINT-MOD-SCOPE:- %a = add i32 2, 3 +; CHECK-DIFF-QUIET-PRINT-MOD-SCOPE:- ret i32 %a +; CHECK-DIFF-QUIET-PRINT-MOD-SCOPE:+ ret i32 5 +; CHECK-DIFF-QUIET-PRINT-MOD-SCOPE-EMPTY: +; CHECK-DIFF-QUIET-PRINT-MOD-SCOPE-NEXT: *** IR Dump After InstSimplifyPass *** (function: f) +; CHECK-DIFF-QUIET-PRINT-MOD-SCOPE-NOT: ModuleID = {{.+}} +; CHECK-DIFF-QUIET-PRINT-MOD-SCOPE: entry: +; CHECK-DIFF-QUIET-PRINT-MOD-SCOPE:- %a = add i32 2, 3 +; CHECK-DIFF-QUIET-PRINT-MOD-SCOPE:- ret i32 %a +; CHECK-DIFF-QUIET-PRINT-MOD-SCOPE:+ ret i32 5 +; CHECK-DIFF-QUIET-PRINT-MOD-SCOPE-NOT: *** IR{{.*}} + +; CHECK-DIFF-QUIET-FILTER-MULT-FUNC-NOT: *** IR Dump {{.*(At Start:|no change|ignored|filtered out)}} *** +; CHECK-DIFF-QUIET-FILTER-MULT-FUNC: *** IR Dump After InstSimplifyPass *** (function: g) +; CHECK-DIFF-QUIET-FILTER-MULT-FUNC-NOT: ModuleID = {{.+}} +; CHECK-DIFF-QUIET-FILTER-MULT-FUNC: entry: +; CHECK-DIFF-QUIET-FILTER-MULT-FUNC:- %a = add i32 2, 3 +; CHECK-DIFF-QUIET-FILTER-MULT-FUNC:- ret i32 %a +; CHECK-DIFF-QUIET-FILTER-MULT-FUNC:+ ret i32 5 +; CHECK-DIFF-QUIET-FILTER-MULT-FUNC-EMPTY: +; CHECK-DIFF-QUIET-FILTER-MULT-FUNC-NEXT: *** IR Dump After InstSimplifyPass *** (function: f) +; CHECK-DIFF-QUIET-FILTER-MULT-FUNC-NOT: ModuleID = {{.+}} +; CHECK-DIFF-QUIET-FILTER-MULT-FUNC: entry: +; CHECK-DIFF-QUIET-FILTER-MULT-FUNC:- %a = add i32 2, 3 +; CHECK-DIFF-QUIET-FILTER-MULT-FUNC:- ret i32 %a +; CHECK-DIFF-QUIET-FILTER-MULT-FUNC:+ ret i32 5 +; CHECK-DIFF-QUIET-FILTER-MULT-FUNC-NOT: *** IR{{.*}} + +; CHECK-DIFF-QUIET-FILTER-PASSES-NONE-NOT: *** IR + +; CHECK-DIFF-QUIET-FILTER-MULT-PASSES-NOT: *** IR Dump {{.*(At Start:|no change|ignored|filtered out)}} *** +; CHECK-DIFF-QUIET-FILTER-MULT-PASSES: *** IR Dump After InstSimplifyPass *** (function: g) +; CHECK-DIFF-QUIET-FILTER-MULT-PASSES-NOT: ModuleID = {{.+}} +; CHECK-DIFF-QUIET-FILTER-MULT-PASSES: entry: +; CHECK-DIFF-QUIET-FILTER-MULT-PASSES:- %a = add i32 2, 3 +; CHECK-DIFF-QUIET-FILTER-MULT-PASSES:- ret i32 %a +; CHECK-DIFF-QUIET-FILTER-MULT-PASSES:+ ret i32 5 +; CHECK-DIFF-QUIET-FILTER-MULT-PASSES-EMPTY: +; CHECK-DIFF-QUIET-FILTER-MULT-PASSES: *** IR Dump After InstSimplifyPass *** (function: f) +; CHECK-DIFF-QUIET-FILTER-MULT-PASSES-NOT: ModuleID = {{.+}} +; CHECK-DIFF-QUIET-FILTER-MULT-PASSES: entry: +; CHECK-DIFF-QUIET-FILTER-MULT-PASSES:- %a = add i32 2, 3 +; CHECK-DIFF-QUIET-FILTER-MULT-PASSES:- ret i32 %a +; CHECK-DIFF-QUIET-FILTER-MULT-PASSES:+ ret i32 5 +; CHECK-DIFF-QUIET-FILTER-MULT-PASSES-NOT: *** IR + +; CHECK-DIFF-QUIET-FILTER-FUNC-PASSES-NOT: *** IR Dump {{.*(At Start:|no change|ignored|filtered out)}} *** +; CHECK-DIFF-QUIET-FILTER-FUNC-PASSES: *** IR Dump After InstSimplifyPass *** (function: f) +; CHECK-DIFF-QUIET-FILTER-FUNC-PASSES-NOT: ModuleID = {{.+}} +; CHECK-DIFF-QUIET-FILTER-FUNC-PASSES: entry: +; CHECK-DIFF-QUIET-FILTER-FUNC-PASSES:- %a = add i32 2, 3 +; CHECK-DIFF-QUIET-FILTER-FUNC-PASSES:- ret i32 %a +; CHECK-DIFF-QUIET-FILTER-FUNC-PASSES:+ ret i32 5 +; CHECK-DIFF-QUIET-FILTER-FUNC-PASSES-NOT: *** IR + +; CHECK-DIFF-QUIET-MULT-PASSES-FILTER-FUNC-NOT: *** IR Dump {{.*(At Start:|no change|ignored|filtered out)}} *** +; CHECK-DIFF-QUIET-MULT-PASSES-FILTER-FUNC: *** IR Dump After InstSimplifyPass *** (function: f) +; CHECK-DIFF-QUIET-MULT-PASSES-FILTER-FUNC-NOT: ModuleID = {{.+}} +; CHECK-DIFF-QUIET-MULT-PASSES-FILTER-FUNC: entry: +; CHECK-DIFF-QUIET-MULT-PASSES-FILTER-FUNC:- %a = add i32 2, 3 +; CHECK-DIFF-QUIET-MULT-PASSES-FILTER-FUNC:- ret i32 %a +; CHECK-DIFF-QUIET-MULT-PASSES-FILTER-FUNC:+ ret i32 5 +; CHECK-DIFF-QUIET-MULT-PASSES-FILTER-FUNC-NOT: *** IR