diff --git a/llvm/include/llvm/IR/PrintPasses.h b/llvm/include/llvm/IR/PrintPasses.h index 95b97e76c867cb24945e9aaa7a0abdde1274248c..f5ff0dade8777e6182cf4624c663fd9b317fe732 100644 --- a/llvm/include/llvm/IR/PrintPasses.h +++ b/llvm/include/llvm/IR/PrintPasses.h @@ -43,6 +43,11 @@ bool shouldPrintAfterPass(StringRef PassID); bool shouldPrintBeforeAll(); bool shouldPrintAfterAll(); +// Returns true if we should dump the module IR to a file when +// shouldPrintBeforeAll/ shouldPrintAfterAll/ shouldPrintBeforeSomePass/ +// shouldPrintAfterSomePass is enabled. +bool shouldDumpFile(); + // The list of passes to print before/after, if we only want to print // before/after specific passes. std::vector printBeforePasses(); diff --git a/llvm/include/llvm/Passes/StandardInstrumentations.h b/llvm/include/llvm/Passes/StandardInstrumentations.h index 331130c6b22d990a3b79bd718483d358951bda60..7a3723e1453af9eea2b796da8bf87e0eddab0e67 100644 --- a/llvm/include/llvm/Passes/StandardInstrumentations.h +++ b/llvm/include/llvm/Passes/StandardInstrumentations.h @@ -48,6 +48,7 @@ public: private: void printBeforePass(StringRef PassID, Any IR); void printAfterPass(StringRef PassID, Any IR); + void dumpFileAfterPass(StringRef PassID, Any IR); void printAfterPassInvalidated(StringRef PassID); bool shouldPrintBeforePass(StringRef PassID); @@ -178,6 +179,8 @@ public: // 8. To compare two IR representations (of type \p T). template class ChangeReporter { protected: + unsigned CurrentPassNumber = 0; + ChangeReporter(bool RunInVerboseMode) : VerboseMode(RunInVerboseMode) {} public: diff --git a/llvm/lib/IR/PrintPasses.cpp b/llvm/lib/IR/PrintPasses.cpp index d2a9827fd07c439f6fa9cd7218902ee513c6b696..5ae9e804e493e654b575dfdfdf56d39aba18e74d 100644 --- a/llvm/lib/IR/PrintPasses.cpp +++ b/llvm/lib/IR/PrintPasses.cpp @@ -33,6 +33,10 @@ static cl::opt PrintAfterAll("print-after-all", llvm::cl::desc("Print IR after each pass"), cl::init(false), cl::Hidden); +static cl::opt DumpFile("dump-file", + llvm::cl::desc("Dump IR to a file after passes"), + cl::init(false), cl::Hidden); + // Print 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, @@ -123,6 +127,8 @@ bool llvm::shouldPrintBeforeAll() { return PrintBeforeAll; } bool llvm::shouldPrintAfterAll() { return PrintAfterAll; } +bool llvm::shouldDumpFile() { return DumpFile; } + bool llvm::shouldPrintBeforePass(StringRef PassID) { return PrintBeforeAll || shouldPrintBeforeOrAfterPass(PassID, PrintBefore); } diff --git a/llvm/lib/Passes/StandardInstrumentations.cpp b/llvm/lib/Passes/StandardInstrumentations.cpp index 8653027ceed2d5b5018dfe9931f0aecb0925a947..d309945b04daed5dad356f89f1688f905519bf76 100644 --- a/llvm/lib/Passes/StandardInstrumentations.cpp +++ b/llvm/lib/Passes/StandardInstrumentations.cpp @@ -37,6 +37,7 @@ #include "llvm/Support/Regex.h" #include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" +#include #include #include #include @@ -338,6 +339,31 @@ bool isInteresting(Any IR, StringRef PassID, StringRef PassName) { return true; } +// print the IR to a file named according to the pass number and pass ID. +void dumpIRToFile(Any IR, const std::string outputFilename) { + std::string outputFilenameCopy = outputFilename; + outputFilenameCopy.erase( + std::remove(outputFilenameCopy.begin(), outputFilenameCopy.end(), ' '), + outputFilenameCopy.end()); + std::string InvalidChars = "/\\:*?\"<>|()[],"; + for (size_t i = 0; i < InvalidChars.size(); i++) { + outputFilenameCopy.erase(std::remove(outputFilenameCopy.begin(), + outputFilenameCopy.end(), + InvalidChars[i]), + outputFilenameCopy.end()); + } + + const Module *M = unwrapModule(IR); + std::error_code EC; + raw_fd_ostream OS(outputFilenameCopy, EC); + if (EC) { + errs() << "Error opening file: " << EC.message() << "\n"; + return; + } + assert(M && "should have unwrapped module"); + M->print(OS, nullptr); +} + } // namespace template ChangeReporter::~ChangeReporter() { @@ -354,6 +380,8 @@ void ChangeReporter::saveIRBeforePass(Any IR, StringRef PassID, handleInitialIR(IR); } + ++CurrentPassNumber; + // 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. @@ -436,8 +464,18 @@ template void TextChangeReporter::handleInitialIR(Any IR) { // Unwrap and print directly to avoid filtering problems in general routines. auto *M = unwrapModule(IR, /*Force=*/true); assert(M && "Expected module to be unwrapped when forced."); - Out << "*** IR Dump At Start ***\n"; - M->print(Out, nullptr); + if (!shouldDumpFile()) { + Out << "*** IR Dump At Start ***\n"; + M->print(Out, nullptr); + } else { + const Module *M = unwrapModule(IR); + std::string ModuleName = M->getName().str(); + std::string OutputFilename = std::to_string(this->CurrentPassNumber) + + "_InitialIR" + "_on_module_" + ModuleName; + Out << "*** IR Dump At Start to file at index - " << this->CurrentPassNumber + << " ***\n"; + dumpIRToFile(IR, OutputFilename); + } } template @@ -481,11 +519,23 @@ void IRChangedPrinter::generateIRRepresentation(Any IR, StringRef PassID, void IRChangedPrinter::handleAfter(StringRef PassID, std::string &Name, const std::string &Before, - const std::string &After, Any) { + const std::string &After, Any IR) { // Report the IR before the changes when requested. - if (PrintChangedBefore) - Out << "*** IR Dump Before " << PassID << " on " << Name << " ***\n" - << Before; + if (PrintChangedBefore) { + if (!shouldDumpFile()) { + Out << "*** IR Dump Before " << PassID << " on " << Name << " ***\n" + << Before; + } else { + Out << "*** IR Dump Before " << PassID << " on " << Name + << " to file at index - " << this->CurrentPassNumber << " ***\n"; + const Module *M = unwrapModule(IR); + std::string ModuleName = M->getName().str(); + const std::string OutputFilename = + std::to_string(this->CurrentPassNumber) + "_before_" + PassID.str() + + "_on_" + Name + "_" + ModuleName; + dumpIRToFile(IR, OutputFilename); + } + } // We might not get anything to print if we only want to print a specific // function but it gets deleted. @@ -494,7 +544,19 @@ void IRChangedPrinter::handleAfter(StringRef PassID, std::string &Name, return; } - Out << "*** IR Dump After " << PassID << " on " << Name << " ***\n" << After; + if (!shouldDumpFile()) { + Out << "*** IR Dump After " << PassID << " on " << Name << " ***\n" + << After; + } else { + Out << "*** IR Dump After " << PassID << " on " << Name + << " to file at index - " << this->CurrentPassNumber << " ***\n"; + const Module *M = unwrapModule(IR); + std::string ModuleName = M->getName().str(); + const std::string OutputFilename = std::to_string(this->CurrentPassNumber) + + "_after_" + PassID.str() + "_on_" + + Name + "_" + ModuleName; + dumpIRToFile(IR, OutputFilename); + } } IRChangedTester::~IRChangedTester() {} @@ -758,6 +820,35 @@ void PrintIRInstrumentation::printAfterPass(StringRef PassID, Any IR) { unwrapAndPrint(dbgs(), IR); } +void PrintIRInstrumentation::dumpFileAfterPass(StringRef PassID, Any IR) { + if (isIgnored(PassID)) + return; + + if (!shouldPrintAfterPass(PassID) && !shouldPrintPassNumbers() && + !shouldPrintAtPassNumber()) + return; + + const Module *M = unwrapModule(IR); + std::string IRName; + StringRef StoredPassID; + std::tie(M, IRName, StoredPassID) = popModuleDesc(PassID); + assert(StoredPassID == PassID && "mismatched PassID"); + + if (!shouldPrintIR(IR) || !shouldPrintAfterPass(PassID)) + return; + + std::string ModuleName = M->getName().str(); + + const std::string OutputFilename = std::to_string(this->CurrentPassNumber) + + "_after_" + PassID.str() + "_on_" + + IRName + "_" + ModuleName; + llvm::dbgs() << "*** IR Dump After " << PassID << " on " << IRName + << " to file at index - " << this->CurrentPassNumber << " ***\n"; + dumpIRToFile(IR, OutputFilename); + + return; +} + void PrintIRInstrumentation::printAfterPassInvalidated(StringRef PassID) { if (isIgnored(PassID)) return; @@ -829,7 +920,11 @@ void PrintIRInstrumentation::registerCallbacks( shouldPrintAfterSomePass()) { PIC.registerAfterPassCallback( [this](StringRef P, Any IR, const PreservedAnalyses &) { - this->printAfterPass(P, IR); + if (shouldDumpFile()) { + this->dumpFileAfterPass(P, IR); + } else { + this->printAfterPass(P, IR); + } }); PIC.registerAfterPassInvalidatedCallback( [this](StringRef P, const PreservedAnalyses &) { diff --git a/llvm/test/Other/dump-file.ll b/llvm/test/Other/dump-file.ll new file mode 100644 index 0000000000000000000000000000000000000000..c14ffd674bb2d72871fab5463fc9896d9cbee50c --- /dev/null +++ b/llvm/test/Other/dump-file.ll @@ -0,0 +1,101 @@ +; Simple checks of -dump-file functionality +; +; Note that (mostly) only the banners are checked. +; +; Simple functionality check +; RUN: opt -S -passes=early-cse -print-after-all -dump-file 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK-PRINT-AFTER-ALL +; +; Simple functionality check with multiple passes +; RUN: opt -S -passes="function-attrs,early-cse" -print-after-all -dump-file 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK-PRINT-AFTER-ALL-MULTI-PASSES +; +; Simple functionality check with -print-after-all and -filter-print-funcs +; RUN: opt -S -passes="function-attrs,early-cse" -print-after-all -filter-print-funcs=g -dump-file 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK-PRINT-AFTER-ALL-FILTER-FUNC +; +; Simple functionality check with -print-after +; RUN: opt -S -passes="function-attrs,early-cse" -print-after=early-cse -dump-file 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK-PRINT-AFTER +; +; Simple functionality check with multiple passes run on g are printed and the others are filtered out +; RUN: opt -S -passes="function-attrs,early-cse" -print-after=early-cse -filter-print-funcs=g -dump-file 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK-PRINT-AFTER-FILTER-FUNC + +; Simple functionality check with -print-changed +; RUN: opt -S -O2 -print-changed -dump-file 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK-PRINT-CHANGED +; +; 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=early-cse -filter-print-funcs=f -dump-file 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK-PRINT-CHANGED-FUNC-FILTER +; +; Check that reporting of multiple functions happens +; RUN: opt -S -print-changed -passes=early-cse -filter-print-funcs="f,g" -dump-file 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK-PRINT-CHANGED-FILTER-MULT-FUNC +; +; Check that the reporting of IRs respects -filter-passes +; RUN: opt -S -print-changed -passes="function-attrs,early-cse" -filter-passes="function-attrs" -dump-file 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK-PRINT-CHANGED-FILTER-PASSES +; +; Check that the reporting of IRs respects -filter-passes with multiple passes +; RUN: opt -S -print-changed -passes="function-attrs,early-cse" -filter-passes="function-attrs,early-cse" -dump-file 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK-PRINT-CHANGED-FILTER-MULT-PASSES +; +; Check that the reporting of IRs respects both -filter-passes and -filter-print-funcs +; RUN: opt -S -print-changed -passes="function-attrs,early-cse" -filter-passes="function-attrs,early-cse" -filter-print-funcs=f -dump-file 2>&1 -o /dev/null < %s | FileCheck %s --check-prefix=CHECK-PRINT-CHANGED-FILTER-FUNC-PASSES + +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-PRINT-AFTER-ALL: IR Dump After EarlyCSEPass on g to file at index - 1 +; CHECK-PRINT-AFTER-ALL: IR Dump After EarlyCSEPass on f to file at index - 2 + +; CHECK-PRINT-AFTER-ALL-MULTI-PASSES: IR Dump After PostOrderFunctionAttrsPass on (g) to file at index - 1 +; CHECK-PRINT-AFTER-ALL-MULTI-PASSES: IR Dump After EarlyCSEPass on g to file at index - 2 +; CHECK-PRINT-AFTER-ALL-MULTI-PASSES: IR Dump After PostOrderFunctionAttrsPass on (f) to file at index - 3 +; CHECK-PRINT-AFTER-ALL-MULTI-PASSES: IR Dump After EarlyCSEPass on f to file at index - 4 + +; CHECK-PRINT-AFTER-ALL-FILTER-FUNC: IR Dump After PostOrderFunctionAttrsPass on (g) to file at index - 1 +; CHECK-PRINT-AFTER-ALL-FILTER-FUNC: IR Dump After EarlyCSEPass on g to file at index - 2 + +; CHECK-PRINT-AFTER: IR Dump After EarlyCSEPass on g to file at index - 2 +; CHECK-PRINT-AFTER: IR Dump After EarlyCSEPass on f to file at index - 4 + +; CHECK-PRINT-AFTER-FILTER-FUNC: IR Dump After EarlyCSEPass on g to file at index - 2 + +; CHECK-PRINT-CHANGED: IR Dump At Start to file at index - 0 +; CHECK-PRINT-CHANGED: IR Dump After EarlyCSEPass on g to file at index - 10 +; CHECK-PRINT-CHANGED: IR Dump After EarlyCSEPass on f to file at index - 15 +; CHECK-PRINT-CHANGED: IR Dump After GlobalOptPass on [module] to file at index - 19 +; CHECK-PRINT-CHANGED: IR Dump After PostOrderFunctionAttrsPass on (g) to file at index - 87 +; CHECK-PRINT-CHANGED: IR Dump After PostOrderFunctionAttrsPass on (f) to file at index - 142 + + +; CHECK-PRINT-CHANGED-FUNC-FILTER: IR Dump At Start to file at index - 0 +; CHECK-PRINT-CHANGED-FUNC-FILTER-NEXT: IR Dump After EarlyCSEPass on g filtered out +; CHECK-PRINT-CHANGED-FUNC-FILTER: IR Dump After EarlyCSEPass on f to file at index - 5 + +; CHECK-PRINT-CHANGED-FILTER-MULT-FUNC: IR Dump At Start to file at index - 0 +; CHECK-PRINT-CHANGED-FILTER-MULT-FUNC-NEXT: IR Dump After EarlyCSEPass on g to file at index - 3 +; CHECK-PRINT-CHANGED-FILTER-MULT-FUNC: IR Dump After EarlyCSEPass on f to file at index - 5 + +; CHECK-PRINT-CHANGED-FILTER-PASSES: IR Dump At Start to file at index - 0 +; CHECK-PRINT-CHANGED-FILTER-PASSES: IR Dump After PostOrderFunctionAttrsPass on (g) to file at index - 3 +; CHECK-PRINT-CHANGED-FILTER-PASSES-NEXT: IR Dump After EarlyCSEPass on g filtered out +; CHECK-PRINT-CHANGED-FILTER-PASSES: IR Dump After PostOrderFunctionAttrsPass on (f) to file at index - 7 +; CHECK-PRINT-CHANGED-FILTER-PASSES-NEXT: IR Dump After EarlyCSEPass on f filtered out + +; CHECK-PRINT-CHANGED-FILTER-MULT-PASSES: IR Dump At Start to file at index - 0 +; CHECK-PRINT-CHANGED-FILTER-MULT-PASSES-NEXT: IR Dump After PostOrderFunctionAttrsPass on (g) to file at index - 3 +; CHECK-PRINT-CHANGED-FILTER-MULT-PASSES: IR Dump After EarlyCSEPass on g to file at index - 5 +; CHECK-PRINT-CHANGED-FILTER-MULT-PASSES: IR Dump After PostOrderFunctionAttrsPass on (f) to file at index - 7 +; CHECK-PRINT-CHANGED-FILTER-MULT-PASSES-NEXT: IR Dump After EarlyCSEPass on f to file at index - 9 +; CHECK-PRINT-CHANGED-FILTER-MULT-PASSES: IR Dump After VerifierPass on [module] filtered out + +; CHECK-PRINT-CHANGED-FILTER-FUNC-PASSES: IR Dump At Start to file at index - 0 +; CHECK-PRINT-CHANGED-FILTER-FUNC-PASSES: IR Dump After PostOrderFunctionAttrsPass on (g) omitted because no change +; CHECK-PRINT-CHANGED-FILTER-FUNC-PASSES: IR Dump After EarlyCSEPass on g filtered out +; CHECK-PRINT-CHANGED-FILTER-FUNC-PASSES: IR Dump After PostOrderFunctionAttrsPass on (f) to file at index - 7 +; CHECK-PRINT-CHANGED-FILTER-FUNC-PASSES: IR Dump After EarlyCSEPass on f to file at index - 9