#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"
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 <typename IRData> class OrderedChangedData {
+public:
+ // Return the names in the order they were saved
+ std::vector<std::string> &getOrder() { return Order; }
+ const std::vector<std::string> &getOrder() const { return Order; }
+
+ // Return a map of names to saved representations
+ StringMap<IRData> &getData() { return Data; }
+ const StringMap<IRData> &getData() const { return Data; }
+
+ bool operator==(const OrderedChangedData<IRData> &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<void(const IRData *, const IRData *)> HandlePair);
+
+protected:
+ std::vector<std::string> Order;
+ StringMap<IRData> Data;
+};
+
+// The data saved for comparing functions.
+using ChangedFuncData = OrderedChangedData<ChangedBlockData>;
+
+// A map of names to the saved data.
+using ChangedIRData = OrderedChangedData<ChangedFuncData>;
+
+// 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<ChangedIRData> {
+public:
+ InLineChangePrinter(bool VerboseMode)
+ : TextChangeReporter<ChangedIRData>(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;
PreservedCFGCheckerInstrumentation PreservedCFGChecker;
IRChangedPrinter PrintChangedIR;
PseudoProbeVerifier PseudoProbeVerification;
+ InLineChangePrinter PrintChangedDiff;
VerifyInstrumentation Verify;
bool VerifyEach;
extern template class ChangeReporter<std::string>;
extern template class TextChangeReporter<std::string>;
+extern template class ChangeReporter<ChangedIRData>;
+extern template class TextChangeReporter<ChangedIRData>;
+
} // namespace llvm
#endif
#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 <unordered_set>
#include <vector>
// 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<ChangePrinter> 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
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<std::string>
+ 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<char, 200> 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<std::string> 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<StringRef> 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<std::pair<const Module *, std::string>>
});
}
+ChangedBlockData::ChangedBlockData(const BasicBlock &B)
+ : Label(B.getName().str()) {
+ raw_string_ostream SS(Body);
+ B.print(SS, nullptr, true, true);
+}
+
template <typename IRUnitT>
TextChangeReporter<IRUnitT>::TextChangeReporter(bool Verbose)
: ChangeReporter<IRUnitT>(Verbose), Out(dbgs()) {}
IRChangedPrinter::~IRChangedPrinter() {}
void IRChangedPrinter::registerCallbacks(PassInstrumentationCallbacks &PIC) {
- if (PrintChanged != NoChangePrinter)
+ if (PrintChanged == ChangePrinter::PrintChangedVerbose ||
+ PrintChanged == ChangePrinter::PrintChangedQuiet)
TextChangeReporter<std::string>::registerRequiredCallbacks(PIC);
}
return S1 == S2;
}
+template <typename IRData>
+void OrderedChangedData<IRData>::report(
+ const OrderedChangedData &Before, const OrderedChangedData &After,
+ function_ref<void(const IRData *, const IRData *)> HandlePair) {
+ const auto &BFD = Before.getData();
+ const auto &AFD = After.getData();
+ std::vector<std::string>::const_iterator BI = Before.getOrder().begin();
+ std::vector<std::string>::const_iterator BE = Before.getOrder().end();
+ std::vector<std::string>::const_iterator AI = After.getOrder().begin();
+ std::vector<std::string>::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<const IRData *> &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<const IRData *> 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<const Function *>(IR))
+ F = any_cast<const Function *>(IR);
+ else {
+ assert(any_isa<const Loop *>(IR) && "Unknown IR unit.");
+ const Loop *L = any_cast<const Loop *>(IR);
+ F = L->getHeader()->getParent();
+ }
+ assert(F && "Unknown IR unit.");
+ generateFunctionData(Data, *F);
+}
+
+const Module *ChangedIRComparer::getModuleForComparison(Any IR) {
+ if (any_isa<const Module *>(IR))
+ return any_cast<const Module *>(IR);
+ if (any_isa<const LazyCallGraph::SCC *>(IR))
+ return any_cast<const LazyCallGraph::SCC *>(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");
}
});
}
+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<ChangedIRData>::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) {
PseudoProbeVerification.registerCallbacks(PIC);
if (VerifyEach)
Verify.registerCallbacks(PIC);
+ PrintChangedDiff.registerCallbacks(PIC);
}
namespace llvm {
template class ChangeReporter<std::string>;
template class TextChangeReporter<std::string>;
+template class ChangeReporter<ChangedIRData>;
+template class TextChangeReporter<ChangedIRData>;
+
} // namespace llvm
--- /dev/null
+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
--- /dev/null
+; 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