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