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 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 Function\n"; + dmp.os() << "\nPrinting 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 Block"; + dmp.os() << "Printing 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(); - 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(F); + dmp.os() << Banner << '\n' << static_cast(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..c0977a0c94a03 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 #include using namespace llvm; @@ -33,6 +38,27 @@ static cl::opt PrintAfterAll("print-after-all", llvm::cl::desc("Print IR after each pass"), cl::init(false), cl::Hidden); +static cl::opt 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 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 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,150 @@ 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 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 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(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) { + 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 PrintAfterPassNumber( cl::desc("Print IR after the passes with specified numbers as " "reported by print-pass-numbers")); -static cl::opt 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 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(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..ec18f1441157e --- /dev/null +++ b/llvm/test/Other/check-ir-dump-filenames.py @@ -0,0 +1,140 @@ +#!/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[0-9]+)" \ +; RUN: "-(?Pmodule|function|machine-function)" \ +; RUN: "-(?P.+)" \ +; RUN: "-(?Pbefore|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[0-9]+)" \ +; RUN: "-(?P[0-9a-f]+-(module|((function|scc)-[0-9a-f]+)))" \ +; RUN: "-(?P.+)" \ +; RUN: "-(?Pbefore|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[0-9]+)" \ +; RUN: "-(?Pmodule|function|machine-function)" \ +; RUN: "-(?P.+)" \ +; RUN: "-(?Pbefore)" + +; 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[0-9]+)" \ +; RUN: "-(?P[0-9a-f]+-(module|((function|scc)-[0-9a-f]+)))" \ +; RUN: "-(?P.+)" \ +; RUN: "-(?Pafter)" + +;------------------------------------------------------------------------------- +; 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[0-9]{8})" \ +; RUN: "-(?P[0-9]{8})" \ +; RUN: "-(?Pmodule|function|machine-function)" \ +; RUN: "-(?P.+)" \ +; RUN: "-(?P[01])" \ +; RUN: "-(?Pbefore|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[0-9]{8})" \ +; RUN: "-(?P[0-9]+)" \ +; RUN: "-(?P[0-9a-f]+-(module|((function|scc)-[0-9a-f]+)))" \ +; RUN: "-(?P.+)" \ +; RUN: "-(?P[01])" \ +; RUN: "-(?Pbefore|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[0-9]+)" \ +; RUN: "-(?P[0-9]+)" \ +; RUN: "-(?Pmodule|function|machine-function)" \ +; RUN: "-(?P.+)" \ +; RUN: "-(?Pafter)" + +define void @foo() { + ret void +}