1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-19 11:02:59 +02:00

Add new hidden option -print-changed which only reports changes to IR

A new hidden option -print-changed is added along with code to support
printing the IR as it passes through the opt pipeline in the new pass
manager. Only those passes that change the IR are reported, with others
only having the banner reported, indicating that they did not change the
IR, were filtered out or ignored. Filtering of output via the
-filter-print-funcs is supported and a new supporting hidden option
-filter-passes is added. The latter takes a comma separated list of pass
names and filters the output to only show those passes in the list that
change the IR. The output can also be modified via the -print-module-scope
function.

The code introduces a template base class that generalizes the comparison
of IRs that takes an IR representation as template parameter. The
constructor takes a series of lambdas that provide an event based API
for generalized reporting of IRs as they are changed in the opt pipeline
through the new pass manager.

The first of several instantiations is provided that prints the IR
in a form similar to that produced by -print-after-all with the above
mentioned filtering capabilities. This version, and the others to
follow will be introduced at the upcoming developer's conference.
See https://hotcrp.llvm.org/usllvm2020/paper/29 for more information.

Reviewed By: yrouban (Yevgeny Rouban)

Differential Revision: https://reviews.llvm.org/D86360
This commit is contained in:
Jamie Schmeiser 2020-09-03 15:52:27 +00:00 committed by Anh Tuyen Tran
parent 5da192d70b
commit 60c5153584
4 changed files with 424 additions and 2 deletions

View File

@ -25,6 +25,7 @@
namespace llvm { namespace llvm {
class Function;
class Module; class Module;
/// Instrumentation to print IR before/after passes. /// Instrumentation to print IR before/after passes.
@ -73,6 +74,98 @@ private:
bool DebugLogging; bool DebugLogging;
}; };
// Base class for classes that report changes to the IR.
// It presents an interface for such classes and provides callbacks
// 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.
// Callbacks 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 T> class ChangePrinter {
protected:
ChangePrinter(
std::function<void(Any IR)> HandleInitialIRFunc,
std::function<void(Any IR, StringRef PassID, T &Output)>
GenerateIRRepresentationFunc,
std::function<void(StringRef PassID, std::string &Name)> OmitAfterFunc,
std::function<void(StringRef PassID, std::string &Name, const T &Before,
const T &After, Any IR)>
HandleAfterFunc,
std::function<void(StringRef PassID)> HandleInvalidatedFunc,
std::function<void(StringRef PassID, std::string &Name)>
HandleFilteredFunc,
std::function<void(StringRef PassID, std::string &Name)>
HandleIgnoredFunc,
std::function<bool(const T &Before, const T &After)> SameFunc)
: HandleInitialIR(HandleInitialIRFunc),
GenerateIRRepresentation(GenerateIRRepresentationFunc),
OmitAfter(OmitAfterFunc), HandleAfter(HandleAfterFunc),
HandleInvalidated(HandleInvalidatedFunc),
HandleFiltered(HandleFilteredFunc), HandleIgnored(HandleIgnoredFunc),
Same(SameFunc), InitialIR(true) {}
public:
// Not virtual as classes are expected to be referenced as derived classes.
~ChangePrinter() {
assert(BeforeStack.empty() && "Problem with Change Printer stack.");
}
// 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);
private:
// callback on the first IR processed
std::function<void(Any IR)> HandleInitialIR;
// callback before and after a pass to get the representation of the IR
std::function<void(Any IR, StringRef PassID, T &Output)>
GenerateIRRepresentation;
// callback when the pass is not iteresting
std::function<void(StringRef PassID, std::string &Name)> OmitAfter;
// callback when interesting IR has changed
std::function<void(StringRef PassID, std::string &Name, const T &Before,
const T &After, Any)>
HandleAfter;
// callback when an interesting pass is invalidated
std::function<void(StringRef PassID)> HandleInvalidated;
// callback when the IR or pass is not interesting
std::function<void(StringRef PassID, std::string &Name)> HandleFiltered;
// callback when an ignored pass is encountered
std::function<void(StringRef PassID, std::string &Name)> HandleIgnored;
// callback to compare the before and after representations of the IR
std::function<bool(const T &Before, const T &After)> Same;
// stack of IRs before passes
std::vector<T> 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();
void registerCallbacks(PassInstrumentationCallbacks &PIC);
protected:
raw_ostream &Out;
};
/// This class provides an interface to register all the standard pass /// This class provides an interface to register all the standard pass
/// instrumentations and manages their state (if any). /// instrumentations and manages their state (if any).
class StandardInstrumentations { class StandardInstrumentations {
@ -80,6 +173,7 @@ class StandardInstrumentations {
PrintPassInstrumentation PrintPass; PrintPassInstrumentation PrintPass;
TimePassesHandler TimePasses; TimePassesHandler TimePasses;
OptNoneInstrumentation OptNone; OptNoneInstrumentation OptNone;
IRChangePrinter PrintChangedIR;
public: public:
StandardInstrumentations(bool DebugLogging) : PrintPass(DebugLogging) {} StandardInstrumentations(bool DebugLogging) : PrintPass(DebugLogging) {}

View File

@ -87,14 +87,14 @@ static cl::opt<bool> PrintAfterAll("print-after-all",
static cl::opt<bool> static cl::opt<bool>
PrintModuleScope("print-module-scope", PrintModuleScope("print-module-scope",
cl::desc("When printing IR for print-[before|after]{-all} " cl::desc("When printing IR for print-[before|after]{-all} "
"always print a module IR"), "and change reporters always print a module IR"),
cl::init(false), cl::Hidden); cl::init(false), cl::Hidden);
static cl::list<std::string> static cl::list<std::string>
PrintFuncsList("filter-print-funcs", cl::value_desc("function names"), PrintFuncsList("filter-print-funcs", cl::value_desc("function names"),
cl::desc("Only print IR for functions whose name " cl::desc("Only print IR for functions whose name "
"match this for all print-[before|after][-all] " "match this for all print-[before|after][-all] "
"options"), "and change reporter options"),
cl::CommaSeparated, cl::Hidden); cl::CommaSeparated, cl::Hidden);
/// This is a helper to determine whether to print IR before or /// This is a helper to determine whether to print IR before or

View File

@ -26,6 +26,7 @@
#include "llvm/Support/Debug.h" #include "llvm/Support/Debug.h"
#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/raw_ostream.h" #include "llvm/Support/raw_ostream.h"
#include <unordered_set>
#include <vector> #include <vector>
using namespace llvm; using namespace llvm;
@ -43,6 +44,34 @@ static cl::opt<bool>
cl::desc("Print all pass management debugging information. " cl::desc("Print all pass management debugging information. "
"`-debug-pass-manager` must also be specified")); "`-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 { namespace {
/// Extracting Module out of \p IR unit. Also fills a textual description /// Extracting Module out of \p IR unit. Also fills a textual description
@ -189,8 +218,197 @@ void unwrapAndPrint(raw_ostream &OS, Any IR, StringRef Banner,
llvm_unreachable("Unknown wrapped IR type"); llvm_unreachable("Unknown wrapped IR type");
} }
// Return true when this is a pass for which printing of changes is desired.
inline bool isIgnored(StringRef PassID) {
return PassID.startswith("PassManager<") || PassID.contains("PassAdaptor<");
}
// 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 } // namespace
template <typename T>
void ChangePrinter<T>::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 T>
void ChangePrinter<T>::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
T &Before = BeforeStack.back();
// Create the after rep
T 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 T>
void ChangePrinter<T>::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 callback. Also, the output is just alternate
// forms of the banner anyway.
HandleInvalidated(PassID);
BeforeStack.pop_back();
}
void handleInitialIR(Any IR, raw_ostream &Out) {
StringRef Banner("*** IR Dump At Start: ***");
unwrapAndPrint(Out, IR, Banner, true);
}
void generateOutput(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());
OS.str();
}
void omitAfter(StringRef PassID, std::string &Name, raw_ostream &Out) {
Out << formatv("*** IR Dump After {0}{1} omitted because no change ***\n",
PassID, Name);
}
void handleAfter(const StringRef After, std::string &Name, raw_ostream &Out) {
assert(After.find("*** IR Dump") == 0 && "Unexpected banner format.");
StringRef Banner = After.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 handleInvalidated(StringRef PassID, raw_ostream &Out) {
Out << formatv("*** IR Pass {0} invalidated ***\n", PassID);
}
void handleFiltered(StringRef PassID, std::string &Name, raw_ostream &Out) {
SmallString<20> Banner =
formatv("*** IR Dump After {0}{1} filtered out ***\n", PassID, Name);
Out << Banner;
}
void handleIgnored(StringRef PassID, std::string &Name, raw_ostream &Out) {
Out << formatv("*** IR Pass {0}{1} ignored ***\n", PassID, Name);
}
bool sameIR(const std::string &S1, const std::string &S2) {
return S1.compare(S2) == 0;
}
IRChangePrinter::IRChangePrinter()
: ChangePrinter<std::string>(
[this](Any IR) -> void { ::handleInitialIR(IR, Out); },
::generateOutput,
[this](StringRef PassID, std::string &Name) -> void {
::omitAfter(PassID, Name, Out);
},
[this](StringRef PassID, std::string &Name, const std::string &Before,
const std::string &After,
Any IR) -> void { ::handleAfter(After, Name, Out); },
[this](StringRef PassID) -> void {
::handleInvalidated(PassID, Out);
},
[this](StringRef PassID, std::string &Name) -> void {
::handleFiltered(PassID, Name, Out);
},
[this](StringRef PassID, std::string &Name) -> void {
::handleIgnored(PassID, Name, Out);
},
::sameIR),
Out(dbgs()) {}
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);
});
}
PrintIRInstrumentation::~PrintIRInstrumentation() { PrintIRInstrumentation::~PrintIRInstrumentation() {
assert(ModuleDescStack.empty() && "ModuleDescStack is not empty at exit"); assert(ModuleDescStack.empty() && "ModuleDescStack is not empty at exit");
} }
@ -344,4 +562,5 @@ void StandardInstrumentations::registerCallbacks(
PrintPass.registerCallbacks(PIC); PrintPass.registerCallbacks(PIC);
TimePasses.registerCallbacks(PIC); TimePasses.registerCallbacks(PIC);
OptNone.registerCallbacks(PIC); OptNone.registerCallbacks(PIC);
PrintChangedIR.registerCallbacks(PIC);
} }

View File

@ -0,0 +1,109 @@
; 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=CHECK0
;
; 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=CHECK1
;
; 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=CHECK2
;
; 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=CHECK3
;
; 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=CHECK4
;
; 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=CHECK5
;
; 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=CHECK6
;
; 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=CHECK7
;
; 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=CHECK8
;
; 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=CHECK9
define i32 @g() {
entry:
%a = add i32 2, 3
ret i32 %a
}
define i32 @f() {
entry:
%a = add i32 2, 3
ret i32 %a
}
; CHECK0: *** IR Dump At Start: ***
; CHECK0: ; ModuleID = '<stdin>'
; CHECK0: *** IR Dump After VerifierPass (module) omitted because no change ***
; CHECK0: *** IR Dump After InstSimplifyPass *** (function: g)
; CHECK0: *** IR Pass PassManager<llvm::Function> (function: g) ignored ***
; CHECK0: *** IR Dump After InstSimplifyPass *** (function: f)
; CHECK0: *** IR Pass PassManager<llvm::Function> (function: f) ignored ***
; CHECK0: *** IR Pass ModuleToFunctionPassAdaptor<llvm::PassManager<llvm::Function> > (module) ignored ***
; CHECK0: *** IR Dump After VerifierPass (module) omitted because no change ***
; CHECK0: *** IR Dump After PrintModulePass (module) omitted because no change ***
; CHECK1: *** IR Dump At Start: ***
; CHECK1: *** IR Dump After InstSimplifyPass (function: g) filtered out ***
; CHECK1: *** IR Dump After InstSimplifyPass *** (function: f)
; CHECK2: *** IR Dump At Start: ***
; CHECK2: *** IR Dump After InstSimplifyPass *** (function: g)
; CHECK2: ModuleID = '<stdin>'
; CHECK2: *** IR Dump After InstSimplifyPass *** (function: f)
; CHECK2: ModuleID = '<stdin>'
; CHECK3: *** IR Dump At Start: ***
; CHECK3: *** IR Dump After InstSimplifyPass (function: g) filtered out ***
; CHECK3: *** IR Dump After InstSimplifyPass *** (function: f)
; CHECK3: ModuleID = '<stdin>'
; CHECK4: *** IR Dump At Start: ***
; CHECK4: *** IR Dump After InstSimplifyPass *** (function: g)
; CHECK4: *** IR Dump After InstSimplifyPass *** (function: f)
; CHECK5: *** IR Dump After InstSimplifyPass (function: g) filtered out ***
; CHECK5: *** IR Dump At Start: *** (function: g)
; CHECK5: *** IR Dump After NoOpFunctionPass (function: g) omitted because no change ***
; CHECK5: *** IR Dump After InstSimplifyPass (function: f) filtered out ***
; CHECK5: *** IR Dump After NoOpFunctionPass (function: f) omitted because no change ***
; CHECK6: *** IR Dump At Start: *** (function: g)
; CHECK6: *** IR Dump After InstSimplifyPass *** (function: g)
; CHECK6: *** IR Dump After NoOpFunctionPass (function: g) omitted because no change ***
; CHECK6: *** IR Dump After InstSimplifyPass *** (function: f)
; CHECK6: *** IR Dump After NoOpFunctionPass (function: f) omitted because no change ***
; CHECK7: *** IR Dump After InstSimplifyPass (function: g) filtered out ***
; CHECK7: *** IR Dump After NoOpFunctionPass (function: g) filtered out ***
; CHECK7: *** IR Dump At Start: *** (function: f)
; CHECK7: *** IR Dump After InstSimplifyPass *** (function: f)
; CHECK7: *** IR Dump After NoOpFunctionPass (function: f) omitted because no change ***
; CHECK8: *** IR Dump After InstSimplifyPass (function: g) filtered out ***
; CHECK8: *** IR Dump After NoOpFunctionPass (function: g) filtered out ***
; CHECK8: *** IR Dump At Start: *** (function: f)
; CHECK8: *** IR Dump After InstSimplifyPass *** (function: f)
; CHECK8: ModuleID = '<stdin>'
; CHECK8: *** IR Dump After NoOpFunctionPass (function: f) omitted because no change ***
; CHECK9: *** IR Dump At Start: ***
; CHECK9: *** IR Dump After InstSimplifyPass (function: g) filtered out ***
; CHECK9: *** IR Dump After InstSimplifyPass (function: g) filtered out ***
; CHECK9: *** IR Dump After InstSimplifyPass *** (function: f)
; CHECK9: *** IR Dump After InstSimplifyPass (function: f) omitted because no change ***