void registerCallbacks(PassInstrumentationCallbacks &PIC);
};
+// 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.
+// It also provides filtering of information based on hidden options
+// specifying which functions are interesting.
+// Calls are made for the following events/queries:
+// 1. The initial IR processed.
+// 2. To get the representation of the IR (of type \p T).
+// 3. When a pass does not change the IR.
+// 4. When a pass changes the IR (given both before and after representations
+// of type \p T).
+// 5. When an IR is invalidated.
+// 6. When a pass is run on an IR that is not interesting (based on options).
+// 7. When a pass is ignored (pass manager or adapter pass).
+// 8. To compare two IR representations (of type \p T).
+template <typename IRUnitT> class ChangePrinter {
+protected:
+ ChangePrinter() : InitialIR(true) {}
+
+public:
+ virtual ~ChangePrinter();
+
+ // Determine if this pass/IR is interesting and if so, save the IR
+ // otherwise it is left on the stack without data
+ void saveIRBeforePass(Any IR, StringRef PassID);
+ // Compare the IR from before the pass after the pass.
+ void handleIRAfterPass(Any IR, StringRef PassID);
+ // Handle the situation where a pass is invalidated.
+ void handleInvalidatedPass(StringRef PassID);
+
+protected:
+ // 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;
+ // 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,
+ const IRUnitT &Before, const IRUnitT &After,
+ Any) = 0;
+ // called when an interesting pass is invalidated
+ virtual void handleInvalidated(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
+ virtual void handleIgnored(StringRef PassID, std::string &Name) = 0;
+ // called to compare the before and after representations of the IR
+ virtual bool same(const IRUnitT &Before, const IRUnitT &After) = 0;
+
+ // stack of IRs before passes
+ std::vector<IRUnitT> BeforeStack;
+ // Is this the first IR seen?
+ bool InitialIR;
+};
+
+// A change printer based on the string representation of the IR as created
+// 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 IRChangePrinter : public ChangePrinter<std::string> {
+public:
+ IRChangePrinter();
+ ~IRChangePrinter() override;
+ void registerCallbacks(PassInstrumentationCallbacks &PIC);
+
+protected:
+ // 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,
+ std::string &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,
+ const std::string &Before, const std::string &After,
+ Any) override;
+ // called when an interesting pass is invalidated
+ void handleInvalidated(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
+ void handleIgnored(StringRef PassID, std::string &Name) override;
+ // called to compare the before and after representations of the IR
+ bool same(const std::string &Before, const std::string &After) override;
+
+ raw_ostream &Out;
+};
+
/// This class provides an interface to register all the standard pass
/// instrumentations and manages their state (if any).
class StandardInstrumentations {
TimePassesHandler TimePasses;
OptNoneInstrumentation OptNone;
PreservedCFGCheckerInstrumentation PreservedCFGChecker;
+ IRChangePrinter PrintChangedIR;
public:
StandardInstrumentations(bool DebugLogging) : PrintPass(DebugLogging) {}
#include "llvm/Support/Debug.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/raw_ostream.h"
+#include <unordered_set>
#include <vector>
using namespace llvm;
cl::desc("Print all pass management debugging information. "
"`-debug-pass-manager` must also be specified"));
+// A hidden option that prints out the IR after passes, similar to
+// -print-after-all except that it only prints the IR after passes that
+// change the IR. Those passes that do not make changes to the IR are
+// reported as not making any changes. In addition, the initial IR is
+// also reported. Other hidden options affect the output from this
+// option. -filter-passes will limit the output to the named passes
+// that actually change the IR and other passes are reported as filtered out.
+// The specified passes will either be reported as making no changes (with
+// no IR reported) or the changed IR will be reported. Also, the
+// -filter-print-funcs and -print-module-scope options will do similar
+// filtering based on function name, reporting changed IRs as functions(or
+// modules if -print-module-scope is specified) for a particular function
+// or indicating that the IR has been filtered out. The extra options
+// can be combined, allowing only changed IRs for certain passes on certain
+// functions to be reported in different formats, with the rest being
+// reported as filtered out.
+static cl::opt<bool> PrintChanged("print-changed",
+ cl::desc("Print changed IRs"),
+ cl::init(false), cl::Hidden);
+// A hidden option that supports the -print-changed option. See
+// the description for -print-changed for an explanation of the use
+// of this option. Note that this option has no effect without -print-changed.
+static cl::list<std::string>
+ PrintPassesList("filter-passes", cl::value_desc("pass names"),
+ cl::desc("Only consider IR changes for passes whose names "
+ "match for the print-changed option"),
+ cl::CommaSeparated, cl::Hidden);
+
namespace {
/// Extracting Module out of \p IR unit. Also fills a textual description
}
void printIR(raw_ostream &OS, const Module *M, StringRef Banner,
- StringRef Extra = StringRef(), bool Brief = false) {
+ StringRef Extra = StringRef(), bool Brief = false,
+ bool ShouldPreserveUseListOrder = false) {
if (Brief) {
OS << M->getName() << '\n';
return;
if (llvm::isFunctionInPrintList("*") || llvm::forcePrintModuleIR()) {
OS << Banner << Extra << "\n";
- M->print(OS, nullptr, false);
+ M->print(OS, nullptr, ShouldPreserveUseListOrder);
} else {
for (const auto &F : M->functions()) {
printIR(OS, &F, Banner, Extra);
/// Generic IR-printing helper that unpacks a pointer to IRUnit wrapped into
/// llvm::Any and does actual print job.
void unwrapAndPrint(raw_ostream &OS, Any IR, StringRef Banner,
- bool ForceModule = false, bool Brief = false) {
+ bool ForceModule = false, bool Brief = false,
+ bool ShouldPreserveUseListOrder = false) {
if (ForceModule) {
if (auto UnwrappedModule = unwrapModule(IR))
- printIR(OS, UnwrappedModule->first, Banner, UnwrappedModule->second);
+ printIR(OS, UnwrappedModule->first, Banner, UnwrappedModule->second,
+ Brief, ShouldPreserveUseListOrder);
return;
}
if (any_isa<const Module *>(IR)) {
const Module *M = any_cast<const Module *>(IR);
assert(M && "module should be valid for printing");
- printIR(OS, M, Banner, "", Brief);
+ printIR(OS, M, Banner, "", Brief, ShouldPreserveUseListOrder);
return;
}
llvm_unreachable("Unknown wrapped IR type");
}
+// Return true when this is a pass for which changes should be ignored
+inline bool isIgnored(StringRef PassID) {
+ return isSpecialPass(PassID,
+ {"PassManager", "PassAdaptor", "AnalysisManagerProxy"});
+}
+
+// Return true when this is a defined function for which printing
+// of changes is desired.
+inline bool isInterestingFunction(const Function &F) {
+ return llvm::isFunctionInPrintList(F.getName());
+}
+
+// Return true when this is a pass for which printing of changes is desired.
+inline bool isInterestingPass(StringRef PassID) {
+ if (isIgnored(PassID))
+ return false;
+
+ static std::unordered_set<std::string> PrintPassNames(PrintPassesList.begin(),
+ PrintPassesList.end());
+ return PrintPassNames.empty() || PrintPassNames.count(PassID.str());
+}
+
+// Return true when this is a pass on IR for which printing
+// of changes is desired.
+bool isInteresting(Any IR, StringRef PassID) {
+ if (!isInterestingPass(PassID))
+ return false;
+ if (any_isa<const Function *>(IR))
+ return isInterestingFunction(*any_cast<const Function *>(IR));
+ return true;
+}
+
} // namespace
+template <typename IRUnitT>
+void ChangePrinter<IRUnitT>::saveIRBeforePass(Any IR, StringRef PassID) {
+ // Always need to place something on the stack because invalidated passes
+ // are not given the IR so it cannot be determined whether the pass was for
+ // something that was filtered out.
+ BeforeStack.emplace_back();
+
+ if (!isInteresting(IR, PassID))
+ return;
+ // Is this the initial IR?
+ if (InitialIR) {
+ InitialIR = false;
+ handleInitialIR(IR);
+ }
+
+ // Save the IR representation on the stack.
+ auto &Data = BeforeStack.back();
+ generateIRRepresentation(IR, PassID, Data);
+}
+
+template <typename IRUnitT>
+void ChangePrinter<IRUnitT>::handleIRAfterPass(Any IR, StringRef PassID) {
+ assert(!BeforeStack.empty() && "Unexpected empty stack encountered.");
+ std::string Name;
+
+ // unwrapModule has inconsistent handling of names for function IRs.
+ if (any_isa<const Function *>(IR)) {
+ const Function *F = any_cast<const Function *>(IR);
+ Name = formatv(" (function: {0})", F->getName()).str();
+ } else {
+ if (auto UM = unwrapModule(IR))
+ Name = UM->second;
+ }
+ if (Name == "")
+ Name = " (module)";
+
+ if (isIgnored(PassID))
+ handleIgnored(PassID, Name);
+ else if (!isInteresting(IR, PassID))
+ handleFiltered(PassID, Name);
+ else {
+ // Get the before rep from the stack
+ IRUnitT &Before = BeforeStack.back();
+ // Create the after rep
+ IRUnitT After;
+ generateIRRepresentation(IR, PassID, After);
+
+ // was there a change in IR?
+ if (same(Before, After))
+ omitAfter(PassID, Name);
+ else
+ handleAfter(PassID, Name, Before, After, IR);
+ }
+ BeforeStack.pop_back();
+}
+
+template <typename IRUnitT>
+void ChangePrinter<IRUnitT>::handleInvalidatedPass(StringRef PassID) {
+ 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.
+ handleInvalidated(PassID);
+ BeforeStack.pop_back();
+}
+
+template <typename IRUnitT> ChangePrinter<IRUnitT>::~ChangePrinter<IRUnitT>() {
+ assert(BeforeStack.empty() && "Problem with Change Printer stack.");
+}
+
+IRChangePrinter::IRChangePrinter() : Out(dbgs()) {}
+
+IRChangePrinter::~IRChangePrinter() {
+ ChangePrinter<std::string>::~ChangePrinter();
+}
+
+void IRChangePrinter::registerCallbacks(PassInstrumentationCallbacks &PIC) {
+ if (!PrintChanged)
+ return;
+
+ PIC.registerBeforePassCallback([this](StringRef P, Any IR) {
+ saveIRBeforePass(IR, P);
+ return true;
+ });
+
+ PIC.registerAfterPassCallback(
+ [this](StringRef P, Any IR, const PreservedAnalyses &) {
+ handleIRAfterPass(IR, P);
+ });
+ PIC.registerAfterPassInvalidatedCallback(
+ [this](StringRef P, const PreservedAnalyses &) {
+ handleInvalidatedPass(P);
+ });
+}
+
+void IRChangePrinter::handleInitialIR(Any IR) {
+ StringRef Banner("*** IR Dump At Start: ***");
+ unwrapAndPrint(Out, IR, Banner, true,
+ /*Brief*/ false, /*ShouldPreserveUseListOrder*/ true);
+}
+
+void IRChangePrinter::generateIRRepresentation(Any IR, StringRef PassID,
+ std::string &Output) {
+ raw_string_ostream OS(Output);
+ // use the after banner for all cases so it will match
+ SmallString<20> Banner = formatv("*** IR Dump After {0} ***", PassID);
+ unwrapAndPrint(OS, IR, Banner, llvm::forcePrintModuleIR(),
+ /*Brief*/ false, /*ShouldPreserveUseListOrder*/ true);
+ OS.str();
+}
+
+void IRChangePrinter::omitAfter(StringRef PassID, std::string &Name) {
+ Out << formatv("*** IR Dump After {0}{1} omitted because no change ***\n",
+ PassID, Name);
+}
+
+void IRChangePrinter::handleAfter(StringRef PassID, std::string &Name,
+ const std::string &Before,
+ const std::string &After, Any) {
+ assert(After.find("*** IR Dump") == 0 && "Unexpected banner format.");
+ StringRef AfterRef = After;
+ StringRef Banner =
+ AfterRef.take_until([](char C) -> bool { return C == '\n'; });
+ Out << Banner;
+
+ // LazyCallGraph::SCC already has "(scc:..." in banner so only add
+ // in the name if it isn't already there.
+ if (Name.substr(0, 6).compare(" (scc:") != 0 && !llvm::forcePrintModuleIR())
+ Out << Name;
+
+ Out << After.substr(Banner.size());
+}
+
+void IRChangePrinter::handleInvalidated(StringRef PassID) {
+ Out << formatv("*** IR Pass {0} invalidated ***\n", PassID);
+}
+
+void IRChangePrinter::handleFiltered(StringRef PassID, std::string &Name) {
+ SmallString<20> Banner =
+ formatv("*** IR Dump After {0}{1} filtered out ***\n", PassID, Name);
+ Out << Banner;
+}
+
+void IRChangePrinter::handleIgnored(StringRef PassID, std::string &Name) {
+ Out << formatv("*** IR Pass {0}{1} ignored ***\n", PassID, Name);
+}
+
+bool IRChangePrinter::same(const std::string &Before,
+ const std::string &After) {
+ return Before.compare(After) == 0;
+};
+
PrintIRInstrumentation::~PrintIRInstrumentation() {
assert(ModuleDescStack.empty() && "ModuleDescStack is not empty at exit");
}
TimePasses.registerCallbacks(PIC);
OptNone.registerCallbacks(PIC);
PreservedCFGChecker.registerCallbacks(PIC);
+ PrintChangedIR.registerCallbacks(PIC);
}
--- /dev/null
+; Simple checks of -print-changed functionality
+;
+; Note that (mostly) only the banners are checked.
+;
+; Simple functionality check.
+; RUN: opt -S -print-changed -passes=instsimplify 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK_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 -passes=instsimplify -filter-print-funcs=f 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK_FUNC_FILTER
+;
+; Check that the reporting of IRs respects -print-module-scope
+; RUN: opt -S -print-changed -passes=instsimplify -print-module-scope 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK_PRINT_MOD_SCOPE
+;
+; Check that the reporting of IRs respects -print-module-scope
+; RUN: opt -S -print-changed -passes=instsimplify -filter-print-funcs=f -print-module-scope 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK_FUNC_FILTER_MOD_SCOPE
+;
+; Check that reporting of multiple functions happens
+; RUN: opt -S -print-changed -passes=instsimplify -filter-print-funcs="f,g" 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK_FILTER_MULT_FUNC
+;
+; Check that the reporting of IRs respects -filter-passes
+; RUN: opt -S -print-changed -passes="instsimplify,no-op-function" -filter-passes="NoOpFunctionPass" 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK_FILTER_PASSES
+;
+; Check that the reporting of IRs respects -filter-passes with multiple passes
+; RUN: opt -S -print-changed -passes="instsimplify,no-op-function" -filter-passes="NoOpFunctionPass,InstSimplifyPass" 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK_FILTER_MULT_PASSES
+;
+; Check that the reporting of IRs respects both -filter-passes and -filter-print-funcs
+; RUN: opt -S -print-changed -passes="instsimplify,no-op-function" -filter-passes="NoOpFunctionPass,InstSimplifyPass" -filter-print-funcs=f 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK_FILTER_FUNC_PASSES
+;
+; Check that the reporting of IRs respects -filter-passes, -filter-print-funcs and -print-module-scope
+; RUN: opt -S -print-changed -passes="instsimplify,no-op-function" -filter-passes="NoOpFunctionPass,InstSimplifyPass" -filter-print-funcs=f -print-module-scope 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK_FILTER_FUNC_PASSES_MOD_SCOPE
+;
+; Check that repeated passes that change the IR are printed and that the
+; others (including g) are filtered out. Note that the second time
+; instsimplify is run on f, it does not change the IR
+; RUN: opt -S -print-changed -passes="instsimplify,instsimplify" -filter-print-funcs=f 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK_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_SIMPLE: *** IR Dump At Start: ***
+; CHECK_SIMPLE: ; ModuleID = '<stdin>'
+; CHECK_SIMPLE: *** IR Dump After VerifierPass (module) omitted because no change ***
+; CHECK_SIMPLE: *** IR Dump After InstSimplifyPass *** (function: g)
+; CHECK_SIMPLE: *** IR Pass PassManager<llvm::Function> (function: g) ignored ***
+; CHECK_SIMPLE: *** IR Dump After InstSimplifyPass *** (function: f)
+; CHECK_SIMPLE: *** IR Pass PassManager<llvm::Function> (function: f) ignored ***
+; CHECK_SIMPLE: *** IR Pass ModuleToFunctionPassAdaptor<llvm::PassManager<llvm::Function>{{ ?}}> (module) ignored ***
+; CHECK_SIMPLE: *** IR Dump After VerifierPass (module) omitted because no change ***
+; CHECK_SIMPLE: *** IR Dump After PrintModulePass (module) omitted because no change ***
+
+; CHECK_FUNC_FILTER: *** IR Dump At Start: ***
+; CHECK_FUNC_FILTER: *** IR Dump After InstSimplifyPass (function: g) filtered out ***
+; CHECK_FUNC_FILTER: *** IR Dump After InstSimplifyPass *** (function: f)
+
+; CHECK_PRINT_MOD_SCOPE: *** IR Dump At Start: ***
+; CHECK_PRINT_MOD_SCOPE: *** IR Dump After InstSimplifyPass *** (function: g)
+; CHECK_PRINT_MOD_SCOPE: ModuleID = '<stdin>'
+; CHECK_PRINT_MOD_SCOPE: *** IR Dump After InstSimplifyPass *** (function: f)
+; CHECK_PRINT_MOD_SCOPE: ModuleID = '<stdin>'
+
+; CHECK_FUNC_FILTER_MOD_SCOPE: *** IR Dump At Start: ***
+; CHECK_FUNC_FILTER_MOD_SCOPE: *** IR Dump After InstSimplifyPass (function: g) filtered out ***
+; CHECK_FUNC_FILTER_MOD_SCOPE: *** IR Dump After InstSimplifyPass *** (function: f)
+; CHECK_FUNC_FILTER_MOD_SCOPE: ModuleID = '<stdin>'
+
+; CHECK_FILTER_MULT_FUNC: *** IR Dump At Start: ***
+; CHECK_FILTER_MULT_FUNC: *** IR Dump After InstSimplifyPass *** (function: g)
+; CHECK_FILTER_MULT_FUNC: *** IR Dump After InstSimplifyPass *** (function: f)
+
+; CHECK_FILTER_PASSES: *** IR Dump After InstSimplifyPass (function: g) filtered out ***
+; CHECK_FILTER_PASSES: *** IR Dump At Start: *** (function: g)
+; CHECK_FILTER_PASSES: *** IR Dump After NoOpFunctionPass (function: g) omitted because no change ***
+; CHECK_FILTER_PASSES: *** IR Dump After InstSimplifyPass (function: f) filtered out ***
+; CHECK_FILTER_PASSES: *** IR Dump After NoOpFunctionPass (function: f) omitted because no change ***
+
+; CHECK_FILTER_MULT_PASSES: *** IR Dump At Start: *** (function: g)
+; CHECK_FILTER_MULT_PASSES: *** IR Dump After InstSimplifyPass *** (function: g)
+; CHECK_FILTER_MULT_PASSES: *** IR Dump After NoOpFunctionPass (function: g) omitted because no change ***
+; CHECK_FILTER_MULT_PASSES: *** IR Dump After InstSimplifyPass *** (function: f)
+; CHECK_FILTER_MULT_PASSES: *** IR Dump After NoOpFunctionPass (function: f) omitted because no change ***
+
+; CHECK_FILTER_FUNC_PASSES: *** IR Dump After InstSimplifyPass (function: g) filtered out ***
+; CHECK_FILTER_FUNC_PASSES: *** IR Dump After NoOpFunctionPass (function: g) filtered out ***
+; CHECK_FILTER_FUNC_PASSES: *** IR Dump At Start: *** (function: f)
+; CHECK_FILTER_FUNC_PASSES: *** IR Dump After InstSimplifyPass *** (function: f)
+; CHECK_FILTER_FUNC_PASSES: *** IR Dump After NoOpFunctionPass (function: f) omitted because no change ***
+
+; CHECK_FILTER_FUNC_PASSES_MOD_SCOPE: *** IR Dump After InstSimplifyPass (function: g) filtered out ***
+; CHECK_FILTER_FUNC_PASSES_MOD_SCOPE: *** IR Dump After NoOpFunctionPass (function: g) filtered out ***
+; CHECK_FILTER_FUNC_PASSES_MOD_SCOPE: *** IR Dump At Start: *** (function: f)
+; CHECK_FILTER_FUNC_PASSES_MOD_SCOPE: *** IR Dump After InstSimplifyPass *** (function: f)
+; CHECK_FILTER_FUNC_PASSES_MOD_SCOPE: ModuleID = '<stdin>'
+; CHECK_FILTER_FUNC_PASSES_MOD_SCOPE: *** IR Dump After NoOpFunctionPass (function: f) omitted because no change ***
+
+; CHECK_MULT_PASSES_FILTER_FUNC: *** IR Dump At Start: ***
+; CHECK_MULT_PASSES_FILTER_FUNC: *** IR Dump After InstSimplifyPass (function: g) filtered out ***
+; CHECK_MULT_PASSES_FILTER_FUNC: *** IR Dump After InstSimplifyPass (function: g) filtered out ***
+; CHECK_MULT_PASSES_FILTER_FUNC: *** IR Dump After InstSimplifyPass *** (function: f)
+; CHECK_MULT_PASSES_FILTER_FUNC: *** IR Dump After InstSimplifyPass (function: f) omitted because no change ***