[llvm] [llvm] Improve IR dump to files (PR #165712)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Oct 30 06:37:10 PDT 2025
https://github.com/macurtis-amd updated https://github.com/llvm/llvm-project/pull/165712
>From c3f5555be20dc3655b400aa0b82de1e991499f00 Mon Sep 17 00:00:00 2001
From: Matthew Curtis <macurtis at amd.com>
Date: Thu, 30 Oct 2025 07:41:06 -0500
Subject: [PATCH] [llvm] Improve IR dump to files
---
llvm/include/llvm/IR/PrintPasses.h | 37 ++++
.../llvm/Passes/StandardInstrumentations.h | 9 +-
llvm/lib/Analysis/CallGraphSCCPass.cpp | 16 +-
llvm/lib/Analysis/LoopPass.cpp | 3 +-
llvm/lib/Analysis/RegionPass.cpp | 8 +-
.../CodeGen/MachineFunctionPrinterPass.cpp | 5 +-
llvm/lib/IR/IRPrintingPasses.cpp | 25 ++-
llvm/lib/IR/LegacyPassManager.cpp | 16 +-
llvm/lib/IR/PrintPasses.cpp | 174 ++++++++++++++++++
llvm/lib/Passes/StandardInstrumentations.cpp | 33 +---
llvm/test/Other/check-ir-dump-filenames.py | 141 ++++++++++++++
llvm/test/Other/dump-filenames.ll | 85 +++++++++
12 files changed, 485 insertions(+), 67 deletions(-)
create mode 100644 llvm/test/Other/check-ir-dump-filenames.py
create mode 100644 llvm/test/Other/dump-filenames.ll
diff --git a/llvm/include/llvm/IR/PrintPasses.h b/llvm/include/llvm/IR/PrintPasses.h
index 0aa1b379c35cf..0ec6f4c7b84d0 100644
--- a/llvm/include/llvm/IR/PrintPasses.h
+++ b/llvm/include/llvm/IR/PrintPasses.h
@@ -15,6 +15,9 @@
namespace llvm {
+class raw_ostream;
+class raw_fd_ostream;
+
enum class ChangePrinter {
None,
Verbose,
@@ -81,6 +84,40 @@ std::string doSystemDiff(StringRef Before, StringRef After,
StringRef OldLineFormat, StringRef NewLineFormat,
StringRef UnchangedLineFormat);
+// If not empty, print IR to files in this directory rather than written to
+// stderr.
+StringRef irDumpDirectory();
+inline bool shouldUseIRDumpDirectory() { return !irDumpDirectory().empty(); }
+
+// Generate the filename to use when dumping IR to files.
+enum class IRDumpFileSuffixType { Before, After, Invalidated };
+std::string irDumpFilename(StringRef Kind, StringRef PassName,
+ std::optional<unsigned> PassNumber,
+ IRDumpFileSuffixType SuffixType);
+
+// Helper class to manage dumping IR to files vs stderr.
+class IRDumpStream {
+ raw_fd_ostream *fstream;
+ raw_ostream &fallback;
+
+public:
+ // Build a banner appropriate for passing to the IRDumpStream constructor
+ // below.
+ //
+ // FIXME: Use structured data rather than a string.
+ static std::string buildBanner(llvm::StringRef PassName,
+ llvm::StringRef PassID,
+ IRDumpFileSuffixType SuffixType);
+
+ // If dumping to files, this will open a file with the appropriate
+ // pathname. 'Kind' and 'Banner' are used to generate the filename used.
+ IRDumpStream(StringRef Kind, StringRef Banner, raw_ostream &fallback);
+ ~IRDumpStream();
+
+ // If dumping to files, returns the file output stream, otherwise 'fallback'.
+ raw_ostream &os();
+};
+
} // namespace llvm
#endif // LLVM_IR_PRINTPASSES_H
diff --git a/llvm/include/llvm/Passes/StandardInstrumentations.h b/llvm/include/llvm/Passes/StandardInstrumentations.h
index 4ee5ab2554868..d524f32997867 100644
--- a/llvm/include/llvm/Passes/StandardInstrumentations.h
+++ b/llvm/include/llvm/Passes/StandardInstrumentations.h
@@ -25,6 +25,7 @@
#include "llvm/IR/DroppedVariableStatsIR.h"
#include "llvm/IR/OptBisect.h"
#include "llvm/IR/PassTimingInfo.h"
+#include "llvm/IR/PrintPasses.h"
#include "llvm/IR/ValueHandle.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h"
@@ -81,14 +82,6 @@ class PrintIRInstrumentation {
void pushPassRunDescriptor(StringRef PassID, Any IR, unsigned PassNumber);
PassRunDescriptor popPassRunDescriptor(StringRef PassID);
- enum class IRDumpFileSuffixType {
- Before,
- After,
- Invalidated,
- };
-
- static StringRef
- getFileSuffix(PrintIRInstrumentation::IRDumpFileSuffixType Type);
std::string fetchDumpFilename(StringRef PassId, StringRef IRFileDisplayName,
unsigned PassNumber,
IRDumpFileSuffixType SuffixType);
diff --git a/llvm/lib/Analysis/CallGraphSCCPass.cpp b/llvm/lib/Analysis/CallGraphSCCPass.cpp
index de64fdb9548e6..4f9d3b6dfbb7e 100644
--- a/llvm/lib/Analysis/CallGraphSCCPass.cpp
+++ b/llvm/lib/Analysis/CallGraphSCCPass.cpp
@@ -675,18 +675,20 @@ namespace {
bool runOnSCC(CallGraphSCC &SCC) override {
bool BannerPrinted = false;
+
+ IRDumpStream dmp("call-graph-scc", Banner, OS);
auto PrintBannerOnce = [&]() {
if (BannerPrinted)
return;
- OS << Banner;
+ dmp.os() << Banner;
BannerPrinted = true;
};
bool NeedModule = llvm::forcePrintModuleIR();
if (isFunctionInPrintList("*") && NeedModule) {
PrintBannerOnce();
- OS << "\n";
- SCC.getCallGraph().getModule().print(OS, nullptr);
+ dmp.os() << "\n";
+ SCC.getCallGraph().getModule().print(dmp.os(), nullptr);
return false;
}
bool FoundFunction = false;
@@ -696,18 +698,18 @@ namespace {
FoundFunction = true;
if (!NeedModule) {
PrintBannerOnce();
- F->print(OS);
+ F->print(dmp.os());
}
}
} else if (isFunctionInPrintList("*")) {
PrintBannerOnce();
- OS << "\nPrinting <null> Function\n";
+ dmp.os() << "\nPrinting <null> Function\n";
}
}
if (NeedModule && FoundFunction) {
PrintBannerOnce();
- OS << "\n";
- SCC.getCallGraph().getModule().print(OS, nullptr);
+ dmp.os() << "\n";
+ SCC.getCallGraph().getModule().print(dmp.os(), nullptr);
}
return false;
}
diff --git a/llvm/lib/Analysis/LoopPass.cpp b/llvm/lib/Analysis/LoopPass.cpp
index d8680aac74b22..fcdc56a75ba77 100644
--- a/llvm/lib/Analysis/LoopPass.cpp
+++ b/llvm/lib/Analysis/LoopPass.cpp
@@ -51,7 +51,8 @@ class PrintLoopPassWrapper : public LoopPass {
auto BBI = llvm::find_if(L->blocks(), [](BasicBlock *BB) { return BB; });
if (BBI != L->blocks().end() &&
isFunctionInPrintList((*BBI)->getParent()->getName())) {
- printLoop(*L, OS, Banner);
+ IRDumpStream dmp("loop", Banner, OS);
+ printLoop(*L, dmp.os(), Banner);
}
return false;
}
diff --git a/llvm/lib/Analysis/RegionPass.cpp b/llvm/lib/Analysis/RegionPass.cpp
index ae1d84659de86..66eab22d64570 100644
--- a/llvm/lib/Analysis/RegionPass.cpp
+++ b/llvm/lib/Analysis/RegionPass.cpp
@@ -191,12 +191,14 @@ class PrintRegionPass : public RegionPass {
bool runOnRegion(Region *R, RGPassManager &RGM) override {
if (!isFunctionInPrintList(R->getEntry()->getParent()->getName()))
return false;
- Out << Banner;
+
+ IRDumpStream dmp("region", Banner, Out);
+ dmp.os() << Banner;
for (const auto *BB : R->blocks()) {
if (BB)
- BB->print(Out);
+ BB->print(dmp.os());
else
- Out << "Printing <null> Block";
+ dmp.os() << "Printing <null> Block";
}
return false;
diff --git a/llvm/lib/CodeGen/MachineFunctionPrinterPass.cpp b/llvm/lib/CodeGen/MachineFunctionPrinterPass.cpp
index 5111322023d04..33bddb7a150eb 100644
--- a/llvm/lib/CodeGen/MachineFunctionPrinterPass.cpp
+++ b/llvm/lib/CodeGen/MachineFunctionPrinterPass.cpp
@@ -46,9 +46,10 @@ struct MachineFunctionPrinterPass : public MachineFunctionPass {
bool runOnMachineFunction(MachineFunction &MF) override {
if (!isFunctionInPrintList(MF.getName()))
return false;
- OS << "# " << Banner << ":\n";
+ IRDumpStream dmp("machine-function", Banner, OS);
+ dmp.os() << "# " << Banner << ":\n";
auto *SIWrapper = getAnalysisIfAvailable<SlotIndexesWrapperPass>();
- MF.print(OS, SIWrapper ? &SIWrapper->getSI() : nullptr);
+ MF.print(dmp.os(), SIWrapper ? &SIWrapper->getSI() : nullptr);
return false;
}
};
diff --git a/llvm/lib/IR/IRPrintingPasses.cpp b/llvm/lib/IR/IRPrintingPasses.cpp
index 5c062800198fc..0a48b67b8f0ef 100644
--- a/llvm/lib/IR/IRPrintingPasses.cpp
+++ b/llvm/lib/IR/IRPrintingPasses.cpp
@@ -12,6 +12,8 @@
//===----------------------------------------------------------------------===//
#include "llvm/IR/IRPrintingPasses.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Module.h"
@@ -20,6 +22,8 @@
#include "llvm/Pass.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
@@ -28,7 +32,7 @@ namespace {
class PrintModulePassWrapper : public ModulePass {
raw_ostream &OS;
- std::string Banner;
+ const std::string Banner;
bool ShouldPreserveUseListOrder;
public:
@@ -44,19 +48,21 @@ class PrintModulePassWrapper : public ModulePass {
// TODO: consider removing this as debug-intrinsics are gone.
M.removeDebugIntrinsicDeclarations();
+ IRDumpStream dmp("module", Banner, OS);
+
if (llvm::isFunctionInPrintList("*")) {
if (!Banner.empty())
- OS << Banner << "\n";
- M.print(OS, nullptr, ShouldPreserveUseListOrder);
+ dmp.os() << Banner << "\n";
+ M.print(dmp.os(), nullptr, ShouldPreserveUseListOrder);
} else {
bool BannerPrinted = false;
for (const auto &F : M.functions()) {
if (llvm::isFunctionInPrintList(F.getName())) {
if (!BannerPrinted && !Banner.empty()) {
- OS << Banner << "\n";
+ dmp.os() << Banner << "\n";
BannerPrinted = true;
}
- F.print(OS);
+ F.print(dmp.os());
}
}
}
@@ -73,7 +79,7 @@ class PrintModulePassWrapper : public ModulePass {
class PrintFunctionPassWrapper : public FunctionPass {
raw_ostream &OS;
- std::string Banner;
+ const std::string Banner;
public:
static char ID;
@@ -84,11 +90,12 @@ class PrintFunctionPassWrapper : public FunctionPass {
// This pass just prints a banner followed by the function as it's processed.
bool runOnFunction(Function &F) override {
if (isFunctionInPrintList(F.getName())) {
+ IRDumpStream dmp("function", Banner, OS);
if (forcePrintModuleIR())
- OS << Banner << " (function: " << F.getName() << ")\n"
- << *F.getParent();
+ dmp.os() << Banner << " (function: " << F.getName() << ")\n"
+ << *F.getParent();
else
- OS << Banner << '\n' << static_cast<Value &>(F);
+ dmp.os() << Banner << '\n' << static_cast<Value &>(F);
}
return false;
diff --git a/llvm/lib/IR/LegacyPassManager.cpp b/llvm/lib/IR/LegacyPassManager.cpp
index 47a828842b481..282882b6caf89 100644
--- a/llvm/lib/IR/LegacyPassManager.cpp
+++ b/llvm/lib/IR/LegacyPassManager.cpp
@@ -736,10 +736,10 @@ void PMTopLevelManager::schedulePass(Pass *P) {
}
if (PI && !PI->isAnalysis() && shouldPrintBeforePass(PI->getPassArgument())) {
- Pass *PP =
- P->createPrinterPass(dbgs(), ("*** IR Dump Before " + P->getPassName() +
- " (" + PI->getPassArgument() + ") ***")
- .str());
+ Pass *PP = P->createPrinterPass(
+ dbgs(),
+ IRDumpStream::buildBanner(P->getPassName(), PI->getPassArgument(),
+ IRDumpFileSuffixType::Before));
PP->assignPassManager(activeStack, getTopLevelPassManagerType());
}
@@ -747,10 +747,10 @@ void PMTopLevelManager::schedulePass(Pass *P) {
P->assignPassManager(activeStack, getTopLevelPassManagerType());
if (PI && !PI->isAnalysis() && shouldPrintAfterPass(PI->getPassArgument())) {
- Pass *PP =
- P->createPrinterPass(dbgs(), ("*** IR Dump After " + P->getPassName() +
- " (" + PI->getPassArgument() + ") ***")
- .str());
+ Pass *PP = P->createPrinterPass(
+ dbgs(),
+ IRDumpStream::buildBanner(P->getPassName(), PI->getPassArgument(),
+ IRDumpFileSuffixType::After));
PP->assignPassManager(activeStack, getTopLevelPassManagerType());
}
}
diff --git a/llvm/lib/IR/PrintPasses.cpp b/llvm/lib/IR/PrintPasses.cpp
index 610411a3cf978..d9b8b13e33fec 100644
--- a/llvm/lib/IR/PrintPasses.cpp
+++ b/llvm/lib/IR/PrintPasses.cpp
@@ -7,11 +7,16 @@
//===----------------------------------------------------------------------===//
#include "llvm/IR/PrintPasses.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
#include "llvm/Support/Program.h"
+#include "llvm/Support/Threading.h"
+#include <atomic>
#include <unordered_set>
using namespace llvm;
@@ -33,6 +38,27 @@ static cl::opt<bool> PrintAfterAll("print-after-all",
llvm::cl::desc("Print IR after each pass"),
cl::init(false), cl::Hidden);
+static cl::opt<std::string> IRDumpDirectory(
+ "ir-dump-directory",
+ cl::desc("If specified, IR printed using the "
+ "-print-[before|after]{-all} options will be dumped into "
+ "files in this directory rather than written to stderr"),
+ cl::Hidden, cl::value_desc("filename"));
+
+StringRef llvm::irDumpDirectory() { return IRDumpDirectory; }
+
+static cl::opt<std::string> IRDumpFilenameFormat(
+ "ir-dump-filename-format",
+ cl::desc("Specifies how filenames are generated when dumping IR to files."
+ " Supported values are 'default' and 'sortable'."),
+ cl::Hidden, cl::init("default"));
+
+static cl::opt<bool> IRDumpFilenamePrependThreadId(
+ "ir-dump-filename-prepend-thread-id",
+ cl::desc("Prepend the filename with the current thread id. Usefule for"
+ " multi-threaded LTO compiles"),
+ 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,
@@ -251,3 +277,151 @@ std::string llvm::doSystemDiff(StringRef Before, StringRef After,
return Diff;
}
+
+namespace {
+const char *getFileSuffix(IRDumpFileSuffixType Type) {
+ if (Type == IRDumpFileSuffixType::Before)
+ return "before";
+ if (Type == IRDumpFileSuffixType::After)
+ return "after";
+ if (Type == IRDumpFileSuffixType::Invalidated)
+ return "invalidated";
+ return "unknown";
+};
+} // namespace
+
+std::string llvm::irDumpFilename(StringRef Kind, StringRef PassNameIn,
+ std::optional<unsigned> PassNumber,
+ IRDumpFileSuffixType SuffixType) {
+
+ // sanitize PassName
+ std::string PassName = PassNameIn.str();
+ for (char &c : PassName) {
+ if (!isAlnum(c) && c != '-')
+ c = '_';
+ }
+
+ // One-time initialization of format string and generation of placeholders
+ static std::string Fmt;
+ static std::once_flag InitFmtFlag;
+ static auto InitFmt = []() {
+ static const char *FmtTidPrefix = "{6,0+8}-";
+ static const char *FmtDefault = "{1}-{2}-{3}-{5}.ll";
+ static const char *FmtSortable = "{0,0+8}-{1,0+8}-{2}-{3}-{4}-{5}.ll";
+ if (IRDumpFilenamePrependThreadId)
+ Fmt += FmtTidPrefix;
+ if (IRDumpFilenameFormat == "sortable")
+ Fmt += FmtSortable;
+ else if (IRDumpFilenameFormat == "default")
+ Fmt += FmtDefault;
+ else {
+ Fmt += FmtDefault;
+ errs() << "warning: invalid value for -ir-dump-filename-format '"
+ << IRDumpFilenameFormat << "'; using 'default'\n";
+ }
+ };
+ std::call_once(InitFmtFlag, InitFmt);
+
+ // Generate filename
+ static std::atomic<int> Ordinal;
+ int Ord = Ordinal++;
+ std::string Filename = formatv(false, Fmt.c_str(),
+ /* 0 */ Ord,
+ /* 1 */ PassNumber.value_or(Ord),
+ /* 2 */ Kind,
+ /* 3 */ PassName,
+ /* 4 */ static_cast<size_t>(SuffixType),
+ /* 5 */ getFileSuffix(SuffixType),
+ /* 6 */ llvm::get_threadid());
+
+ // Generate path
+ const StringRef DumpDir = irDumpDirectory();
+ assert(!DumpDir.empty() &&
+ "The flag -ir-dump-directory must be passed to dump IR to files");
+ SmallString<128> ResultPath;
+ sys::path::append(ResultPath, DumpDir, Filename);
+ return std::string(ResultPath);
+}
+
+std::string llvm::IRDumpStream::buildBanner(llvm::StringRef PassName,
+ llvm::StringRef PassID,
+ IRDumpFileSuffixType SuffixType) {
+
+ const char *Suffix = "Unknown";
+ if (SuffixType == IRDumpFileSuffixType::Before)
+ Suffix = "Before";
+ else if (SuffixType == IRDumpFileSuffixType::After)
+ Suffix = "After";
+ else if (SuffixType == IRDumpFileSuffixType::Invalidated)
+ Suffix = "Invalidated";
+
+ return formatv("*** IR Dump {0} {1} ({2}) ***", Suffix, PassName, PassID)
+ .str();
+}
+
+static std::string extractPassName(StringRef Banner,
+ IRDumpFileSuffixType &PhaseOut) {
+ StringRef Phase;
+ if (Banner.consume_front("*** IR Dump Before ")) {
+ PhaseOut = IRDumpFileSuffixType::Before;
+ } else if (Banner.consume_front("*** IR Dump After ")) {
+ PhaseOut = IRDumpFileSuffixType::After;
+ } else {
+ return std::string();
+ }
+
+ size_t open = Banner.find(" (");
+ if (open == StringRef::npos) {
+ return std::string();
+ }
+
+ open += 2;
+ size_t close = Banner.find(')', open);
+ if (close == StringRef::npos) {
+ return std::string();
+ }
+
+ return Banner.substr(open, close - open).str();
+}
+
+llvm::IRDumpStream::IRDumpStream(StringRef Kind, StringRef Banner,
+ raw_ostream &fallback)
+ : fstream(nullptr), fallback(fallback) {
+
+ IRDumpFileSuffixType Phase;
+ std::string PassName = extractPassName(Banner, Phase);
+
+ StringRef Dir = irDumpDirectory();
+ if (Dir.empty() || PassName.empty())
+ return;
+
+ std::string DumpIRFilename =
+ irDumpFilename(Kind, PassName, std::nullopt, Phase);
+
+ std::error_code EC = llvm::sys::fs::create_directories(Dir);
+ if (EC) {
+ report_fatal_error(Twine("Failed to create directory ") + Dir +
+ " to support -ir-dump-directory: " + EC.message());
+ }
+ if (sys::fs::exists(DumpIRFilename)) {
+ errs() << "warning: overwriting existing file '" << DumpIRFilename << "'\n";
+ }
+ int FD = 0;
+ EC = sys::fs::openFile(DumpIRFilename, FD, sys::fs::CD_OpenAlways,
+ sys::fs::FA_Write, sys::fs::OF_Text);
+ if (EC) {
+ report_fatal_error(Twine("Failed to open ") + DumpIRFilename +
+ " to support -ir-dump-directory: " + EC.message());
+ }
+ // return FD;
+ fstream = new raw_fd_ostream(FD, /* shouldClose */ true);
+}
+
+llvm::IRDumpStream::~IRDumpStream() {
+ if (fstream) {
+ fstream->close();
+ delete fstream;
+ }
+}
+
+raw_ostream &llvm::IRDumpStream::os() { return fstream ? *fstream : fallback; }
diff --git a/llvm/lib/Passes/StandardInstrumentations.cpp b/llvm/lib/Passes/StandardInstrumentations.cpp
index 7290a86503120..940f8bb6461d2 100644
--- a/llvm/lib/Passes/StandardInstrumentations.cpp
+++ b/llvm/lib/Passes/StandardInstrumentations.cpp
@@ -128,13 +128,6 @@ static cl::list<unsigned> PrintAfterPassNumber(
cl::desc("Print IR after the passes with specified numbers as "
"reported by print-pass-numbers"));
-static cl::opt<std::string> IRDumpDirectory(
- "ir-dump-directory",
- cl::desc("If specified, IR printed using the "
- "-print-[before|after]{-all} options will be dumped into "
- "files in this directory rather than written to stderr"),
- cl::Hidden, cl::value_desc("filename"));
-
static cl::opt<bool>
DroppedVarStats("dropped-variable-stats", cl::Hidden,
cl::desc("Dump dropped debug variables stats"),
@@ -788,28 +781,10 @@ static std::string getIRFileDisplayName(Any IR) {
return Result;
}
-StringRef PrintIRInstrumentation::getFileSuffix(IRDumpFileSuffixType Type) {
- static constexpr std::array FileSuffixes = {"-before.ll", "-after.ll",
- "-invalidated.ll"};
- return FileSuffixes[static_cast<size_t>(Type)];
-}
-
std::string PrintIRInstrumentation::fetchDumpFilename(
StringRef PassName, StringRef IRFileDisplayName, unsigned PassNumber,
IRDumpFileSuffixType SuffixType) {
- assert(!IRDumpDirectory.empty() &&
- "The flag -ir-dump-directory must be passed to dump IR to files");
-
- SmallString<64> Filename;
- raw_svector_ostream FilenameStream(Filename);
- FilenameStream << PassNumber;
- FilenameStream << '-' << IRFileDisplayName << '-';
- FilenameStream << PassName;
- FilenameStream << getFileSuffix(SuffixType);
-
- SmallString<128> ResultPath;
- sys::path::append(ResultPath, IRDumpDirectory, Filename);
- return std::string(ResultPath);
+ return irDumpFilename(IRFileDisplayName, PassName, PassNumber, SuffixType);
}
void PrintIRInstrumentation::pushPassRunDescriptor(StringRef PassID, Any IR,
@@ -880,7 +855,7 @@ void PrintIRInstrumentation::printBeforePass(StringRef PassID, Any IR) {
unwrapAndPrint(Stream, IR);
};
- if (!IRDumpDirectory.empty()) {
+ if (shouldUseIRDumpDirectory()) {
std::string DumpIRFilename =
fetchDumpFilename(PassID, getIRFileDisplayName(IR), CurrentPassNumber,
IRDumpFileSuffixType::Before);
@@ -915,7 +890,7 @@ void PrintIRInstrumentation::printAfterPass(StringRef PassID, Any IR) {
unwrapAndPrint(Stream, IR);
};
- if (!IRDumpDirectory.empty()) {
+ if (shouldUseIRDumpDirectory()) {
std::string DumpIRFilename =
fetchDumpFilename(PassID, getIRFileDisplayName(IR), CurrentPassNumber,
IRDumpFileSuffixType::After);
@@ -953,7 +928,7 @@ void PrintIRInstrumentation::printAfterPassInvalidated(StringRef PassID) {
printIR(Stream, M);
};
- if (!IRDumpDirectory.empty()) {
+ if (shouldUseIRDumpDirectory()) {
std::string DumpIRFilename =
fetchDumpFilename(PassID, IRFileDisplayName, PassNumber,
IRDumpFileSuffixType::Invalidated);
diff --git a/llvm/test/Other/check-ir-dump-filenames.py b/llvm/test/Other/check-ir-dump-filenames.py
new file mode 100644
index 0000000000000..132f3fe223750
--- /dev/null
+++ b/llvm/test/Other/check-ir-dump-filenames.py
@@ -0,0 +1,141 @@
+#!/usr/bin/python3
+
+import re
+import sys
+import os
+
+dir = sys.argv[1]
+pat = pat_arg = sys.argv[2:]
+pat = "".join(pat)
+pat = re.compile(pat)
+
+filenames = os.listdir(dir)
+if len(filenames) < 2:
+ print(f"expecting at least 2 files in {dir} but got {len(filenames)}")
+ sys.exit(1)
+
+
+def fndict(fn):
+ m = pat.match(fn)
+ if not m:
+ print(f"filename '{fn}' does not match pattern '{pat_arg}'")
+ sys.exit(1)
+ return m.groupdict()
+
+
+first = fndict(filenames[0])
+should_check_ordinal = "ordinal" in first
+should_check_suffix = "suffix" in first
+should_check_suffix_ordinal = should_check_suffix and "suffix_ordinal" in first
+should_check_before_after = should_check_suffix and should_check_ordinal
+
+should_check_pass_id = "pass_id" in first
+should_check_kind = "kind" in first
+should_check_pass_number = "pass_number" in first
+
+if should_check_ordinal:
+ filenames = sorted(filenames, key=lambda x: fndict(x)["ordinal"])
+first = fndict(filenames[0])
+second = fndict(filenames[1])
+
+
+def failed(msg):
+ print(f"error: {msg}")
+ sys.exit(1)
+
+
+def check(actual, expected, name):
+ if actual != expected:
+ failed(f"error: expected {name} '{expected}' but got '{actual}' ")
+
+
+# ------------------------------------------------------------------------------
+# ordinal
+# ------------------------------------------------------------------------------
+def check_ordinal(d, prev):
+ if not should_check_ordinal:
+ return
+ actual = int(d["ordinal"])
+ expected = int(prev["ordinal"]) + 1
+ check(actual, expected, "ordinal")
+
+
+# ------------------------------------------------------------------------------
+# suffix
+# ------------------------------------------------------------------------------
+suffix_ordinal_list = [ "before", "after" ]
+suffix_map = {}
+if should_check_before_after:
+ suffix_map |= { first["suffix"] : second["suffix"] }
+ suffix_map |= { second["suffix"] : first["suffix"] }
+
+
+def check_suffix(d, prev):
+ if not should_check_suffix:
+ return
+ suffix = d["suffix"]
+ if should_check_before_after:
+ if suffix not in suffix_map:
+ failed(f"suffix '{suffix}' not in {suffix_map}")
+ if prev is not None:
+ check(suffix, suffix_map[prev["suffix"]], "suffix")
+ else:
+ if suffix not in suffix_ordinal_list:
+ failed(f"suffix '{suffix}' not in {suffix_ordinal_list}")
+ if should_check_suffix_ordinal:
+ suffix_ordinal = int(d["suffix_ordinal"])
+ check(suffix_ordinal, suffix_ordinal_list.index(suffix),
+ "suffix ordinal")
+
+
+# ------------------------------------------------------------------------------
+# pass number
+# ------------------------------------------------------------------------------
+def check_pass_number(d, prev):
+ if not should_check_pass_number or not should_check_before_after:
+ return
+ actual = d["pass_number"]
+ if d["suffix"] == "after":
+ expected = prev["pass_number"]
+ check(actual, expected, "pass number")
+
+
+# ------------------------------------------------------------------------------
+# pass id
+# ------------------------------------------------------------------------------
+def check_pass_id(d, prev):
+ if not should_check_pass_id or not should_check_before_after:
+ return
+ actual = d["pass_id"]
+ if d["suffix"] == "after":
+ expected = prev["pass_id"]
+ check(actual, expected, "pass id")
+
+
+# ------------------------------------------------------------------------------
+# kind
+# ------------------------------------------------------------------------------
+def check_kind(d, prev):
+ if not should_check_kind or not should_check_before_after:
+ return
+ actual = d["kind"]
+ if d["suffix"] == "after":
+ expected = prev["kind"]
+ check(actual, expected, "kind")
+
+
+# ------------------------------------------------------------------------------
+#
+# ------------------------------------------------------------------------------
+check_ordinal(first, { "ordinal" : -1 })
+
+prev = first
+filenames = filenames[1:]
+for fn in filenames:
+ d = fndict(fn)
+ check_ordinal(d, prev)
+ check_suffix(d, prev)
+ check_pass_id(d, prev)
+ check_pass_number(d, prev)
+ check_kind(d, prev)
+ prev = d
diff --git a/llvm/test/Other/dump-filenames.ll b/llvm/test/Other/dump-filenames.ll
new file mode 100644
index 0000000000000..aa2170a4cf564
--- /dev/null
+++ b/llvm/test/Other/dump-filenames.ll
@@ -0,0 +1,85 @@
+; REQUIRES: default_triple
+
+;-------------------------------------------------------------------------------
+; default
+;-------------------------------------------------------------------------------
+; RUN: rm -rf %t/logs
+; RUN: llc %s -o - -O3 -print-after-all -print-before-all \
+; RUN: -ir-dump-directory %t/logs
+; RUN: %python %p/check-ir-dump-filenames.py %t/logs \
+; RUN: "(?P<pass_number>[0-9]+)" \
+; RUN: "-(?P<kind>module|function|machine-function)" \
+; RUN: "-(?P<pass_id>.+)" \
+; RUN: "-(?P<suffix>before|after)"
+
+; RUN: rm -rf %t/logs
+; RUN: opt %s -disable-output -O3 -print-after-all -print-before-all \
+; RUN: -ir-dump-directory %t/logs
+; RUN: %python %p/check-ir-dump-filenames.py %t/logs \
+; RUN: "(?P<pass_number>[0-9]+)" \
+; RUN: "-(?P<kind>[0-9a-f]+-(module|((function|scc)-[0-9a-f]+)))" \
+; RUN: "-(?P<pass_id>.+)" \
+; RUN: "-(?P<suffix>before|after)"
+
+; RUN: rm -rf %t/logs
+; RUN: llc %s -o - -O3 -print-before-all \
+; RUN: -ir-dump-directory %t/logs
+; RUN: %python %p/check-ir-dump-filenames.py %t/logs \
+; RUN: "(?P<pass_number>[0-9]+)" \
+; RUN: "-(?P<kind>module|function|machine-function)" \
+; RUN: "-(?P<pass_id>.+)" \
+; RUN: "-(?P<before>before)"
+
+; RUN: rm -rf %t/logs
+; RUN: opt %s -disable-output -O3 -print-after-all \
+; RUN: -ir-dump-directory %t/logs
+; RUN: %python %p/check-ir-dump-filenames.py %t/logs \
+; RUN: "(?P<pass_number>[0-9]+)" \
+; RUN: "-(?P<kind>[0-9a-f]+-(module|((function|scc)-[0-9a-f]+)))" \
+; RUN: "-(?P<pass_id>.+)" \
+; RUN: "-(?P<after>after)"
+
+;-------------------------------------------------------------------------------
+; sortable
+;-------------------------------------------------------------------------------
+; RUN: rm -rf %t/logs
+; RUN: llc %s -o - -O3 -print-after-all -print-before-all \
+; RUN: -ir-dump-directory %t/logs \
+; RUN: -ir-dump-filename-format sortable
+; RUN: %python %p/check-ir-dump-filenames.py %t/logs \
+; RUN: "(?P<ordinal>[0-9]{8})" \
+; RUN: "-(?P<pass_ordinal>[0-9]{8})" \
+; RUN: "-(?P<kind>module|function|machine-function)" \
+; RUN: "-(?P<pass_id>.+)" \
+; RUN: "-(?P<suffix_ordinal>[01])" \
+; RUN: "-(?P<suffix>before|after)"
+
+; RUN: rm -rf %t/logs
+; RUN: opt %s -disable-output -O3 -print-after-all -print-before-all \
+; RUN: -ir-dump-directory %t/logs \
+; RUN: -ir-dump-filename-format sortable
+; RUN: %python %p/check-ir-dump-filenames.py %t/logs \
+; RUN: "(?P<ordinal>[0-9]{8})" \
+; RUN: "-(?P<pass_number>[0-9]+)" \
+; RUN: "-(?P<kind>[0-9a-f]+-(module|((function|scc)-[0-9a-f]+)))" \
+; RUN: "-(?P<pass_id>.+)" \
+; RUN: "-(?P<suffix_ordinal>[01])" \
+; RUN: "-(?P<suffix>before|after)"
+
+;-------------------------------------------------------------------------------
+; thread id
+;-------------------------------------------------------------------------------
+; RUN: rm -rf %t/logs
+; RUN: llc %s -o - -O3 -print-after-all \
+; RUN: -ir-dump-directory %t/logs \
+; RUN: -ir-dump-filename-prepend-thread-id
+; RUN: %python %p/check-ir-dump-filenames.py %t/logs \
+; RUN: "(?P<tid>[0-9]+)" \
+; RUN: "-(?P<pass_number>[0-9]+)" \
+; RUN: "-(?P<kind>module|function|machine-function)" \
+; RUN: "-(?P<pass_id>.+)" \
+; RUN: "-(?P<after>after)"
+
+define void @foo() {
+ ret void
+}
More information about the llvm-commits
mailing list