Skip to content

Commit 840169e

Browse files
author
Nuri Amari
committed
Add option to dump IR to files intstead of stderr
This patch adds a flag to LLVM such that the output generated by the `-print-(before|after|all)` family of flags is written to files in a directory rather than to stderr. This new flag is `-ir-dump-directory` and is used to specify where to write the files. No other flags are added, it just modifies the behavior of the print flags.
1 parent 2f3b7d3 commit 840169e

File tree

4 files changed

+282
-42
lines changed

4 files changed

+282
-42
lines changed

llvm/include/llvm/Passes/StandardInstrumentations.h

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,18 @@ class PrintIRInstrumentation {
4646
void registerCallbacks(PassInstrumentationCallbacks &PIC);
4747

4848
private:
49+
struct PassRunDescriptor {
50+
const Module *M;
51+
const std::string DumpIRFilename;
52+
const std::string IRName;
53+
const StringRef PassID;
54+
55+
PassRunDescriptor(const Module *M, std::string DumpIRFilename,
56+
std::string IRName, const StringRef PassID)
57+
: M{M}, DumpIRFilename{DumpIRFilename}, IRName{IRName}, PassID(PassID) {
58+
}
59+
};
60+
4961
void printBeforePass(StringRef PassID, Any IR);
5062
void printAfterPass(StringRef PassID, Any IR);
5163
void printAfterPassInvalidated(StringRef PassID);
@@ -55,15 +67,15 @@ class PrintIRInstrumentation {
5567
bool shouldPrintPassNumbers();
5668
bool shouldPrintAtPassNumber();
5769

58-
using PrintModuleDesc = std::tuple<const Module *, std::string, StringRef>;
59-
60-
void pushModuleDesc(StringRef PassID, Any IR);
61-
PrintModuleDesc popModuleDesc(StringRef PassID);
70+
void pushPassRunDescriptor(StringRef PassID, Any IR,
71+
std::string &DumpIRFilename);
72+
PassRunDescriptor popPassRunDescriptor(StringRef PassID);
73+
std::string fetchDumpFilename(StringRef PassId, Any IR);
6274

6375
PassInstrumentationCallbacks *PIC;
64-
/// Stack of Module description, enough to print the module after a given
76+
/// Stack of Pass Run descriptions, enough to print the IR unit after a given
6577
/// pass.
66-
SmallVector<PrintModuleDesc, 2> ModuleDescStack;
78+
SmallVector<PassRunDescriptor, 2> PassRunDescriptorStack;
6779

6880
/// Used for print-at-pass-number
6981
unsigned CurrentPassNumber = 0;

llvm/lib/Passes/StandardInstrumentations.cpp

Lines changed: 168 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "llvm/Analysis/CallGraphSCCPass.h"
1919
#include "llvm/Analysis/LazyCallGraph.h"
2020
#include "llvm/Analysis/LoopInfo.h"
21+
#include "llvm/CodeGen/StableHashing.h"
2122
#include "llvm/IR/Constants.h"
2223
#include "llvm/IR/Function.h"
2324
#include "llvm/IR/Module.h"
@@ -33,6 +34,7 @@
3334
#include "llvm/Support/FormatVariadic.h"
3435
#include "llvm/Support/GraphWriter.h"
3536
#include "llvm/Support/MemoryBuffer.h"
37+
#include "llvm/Support/Path.h"
3638
#include "llvm/Support/Program.h"
3739
#include "llvm/Support/Regex.h"
3840
#include "llvm/Support/Signals.h"
@@ -120,6 +122,13 @@ static cl::opt<unsigned>
120122
cl::desc("Print IR at pass with this number as "
121123
"reported by print-passes-names"));
122124

125+
static cl::opt<std::string> IRDumpDirectory(
126+
"ir-dump-directory",
127+
cl::desc("If specified, IR printed using the "
128+
"-print-[before|after]{-all} options will be dumped into "
129+
"files in this directory rather than written to stderr"),
130+
cl::Hidden, cl::value_desc("filename"));
131+
123132
namespace {
124133

125134
// An option for specifying an executable that will be called with the IR
@@ -681,33 +690,120 @@ bool IRComparer<T>::generateFunctionData(IRDataT<T> &Data, const Function &F) {
681690
}
682691

683692
PrintIRInstrumentation::~PrintIRInstrumentation() {
684-
assert(ModuleDescStack.empty() && "ModuleDescStack is not empty at exit");
693+
assert(PassRunDescriptorStack.empty() &&
694+
"PassRunDescriptorStack is not empty at exit");
695+
}
696+
697+
static SmallString<32> getIRFileDisplayName(Any IR) {
698+
SmallString<32> Result;
699+
raw_svector_ostream ResultStream(Result);
700+
const Module *M = unwrapModule(IR);
701+
stable_hash NameHash = stable_hash_combine_string(M->getName());
702+
unsigned int MaxHashWidth = sizeof(stable_hash) * 8 / 4;
703+
write_hex(ResultStream, NameHash, HexPrintStyle::Lower, MaxHashWidth);
704+
if (any_cast<const Module *>(&IR)) {
705+
ResultStream << "-module";
706+
} else if (const Function **F = any_cast<const Function *>(&IR)) {
707+
ResultStream << "-function-";
708+
stable_hash FunctionNameHash = stable_hash_combine_string((*F)->getName());
709+
write_hex(ResultStream, FunctionNameHash, HexPrintStyle::Lower,
710+
MaxHashWidth);
711+
} else if (const LazyCallGraph::SCC **C =
712+
any_cast<const LazyCallGraph::SCC *>(&IR)) {
713+
ResultStream << "-scc-";
714+
stable_hash SCCNameHash = stable_hash_combine_string((*C)->getName());
715+
write_hex(ResultStream, SCCNameHash, HexPrintStyle::Lower, MaxHashWidth);
716+
} else if (const Loop **L = any_cast<const Loop *>(&IR)) {
717+
ResultStream << "-loop-";
718+
stable_hash LoopNameHash = stable_hash_combine_string((*L)->getName());
719+
write_hex(ResultStream, LoopNameHash, HexPrintStyle::Lower, MaxHashWidth);
720+
} else {
721+
llvm_unreachable("Unknown wrapped IR type");
722+
}
723+
return Result;
724+
}
725+
726+
std::string PrintIRInstrumentation::fetchDumpFilename(StringRef PassName,
727+
Any IR) {
728+
const StringRef RootDirectory = IRDumpDirectory;
729+
assert(!RootDirectory.empty() &&
730+
"The flag -ir-dump-directory must be passed to dump IR to files");
731+
SmallString<128> ResultPath;
732+
ResultPath += RootDirectory;
733+
SmallString<64> Filename;
734+
raw_svector_ostream FilenameStream(Filename);
735+
FilenameStream << CurrentPassNumber;
736+
FilenameStream << "-";
737+
FilenameStream << getIRFileDisplayName(IR);
738+
FilenameStream << "-";
739+
FilenameStream << PassName;
740+
sys::path::append(ResultPath, Filename);
741+
return std::string(ResultPath);
742+
}
743+
744+
enum class IRDumpFileSuffixType {
745+
Before,
746+
After,
747+
Invalidated,
748+
};
749+
750+
static StringRef getFileSuffix(IRDumpFileSuffixType Type) {
751+
static constexpr std::array FileSuffixes = {"-before.ll", "-after.ll",
752+
"-invalidated.ll"};
753+
return FileSuffixes[static_cast<size_t>(Type)];
685754
}
686755

687-
void PrintIRInstrumentation::pushModuleDesc(StringRef PassID, Any IR) {
756+
void PrintIRInstrumentation::pushPassRunDescriptor(
757+
StringRef PassID, Any IR, std::string &DumpIRFilename) {
688758
const Module *M = unwrapModule(IR);
689-
ModuleDescStack.emplace_back(M, getIRName(IR), PassID);
759+
PassRunDescriptorStack.emplace_back(
760+
PassRunDescriptor(M, DumpIRFilename, getIRName(IR), PassID));
690761
}
691762

692-
PrintIRInstrumentation::PrintModuleDesc
693-
PrintIRInstrumentation::popModuleDesc(StringRef PassID) {
694-
assert(!ModuleDescStack.empty() && "empty ModuleDescStack");
695-
PrintModuleDesc ModuleDesc = ModuleDescStack.pop_back_val();
696-
assert(std::get<2>(ModuleDesc).equals(PassID) && "malformed ModuleDescStack");
697-
return ModuleDesc;
763+
PrintIRInstrumentation::PassRunDescriptor
764+
PrintIRInstrumentation::popPassRunDescriptor(StringRef PassID) {
765+
assert(!PassRunDescriptorStack.empty() && "empty PassRunDescriptorStack");
766+
PassRunDescriptor Descriptor = PassRunDescriptorStack.pop_back_val();
767+
assert(Descriptor.PassID.equals(PassID) &&
768+
"malformed PassRunDescriptorStack");
769+
return Descriptor;
770+
}
771+
772+
// Callers are responsible for closing the returned file descriptor
773+
static int prepareDumpIRFileDescriptor(const StringRef DumpIRFilename) {
774+
std::error_code EC;
775+
auto ParentPath = llvm::sys::path::parent_path(DumpIRFilename);
776+
if (!ParentPath.empty()) {
777+
std::error_code EC = llvm::sys::fs::create_directories(ParentPath);
778+
if (EC)
779+
report_fatal_error(Twine("Failed to create directory ") + ParentPath +
780+
" to support -ir-dump-directory: " + EC.message());
781+
}
782+
int Result = 0;
783+
EC = sys::fs::openFile(DumpIRFilename, Result, sys::fs::CD_OpenAlways,
784+
sys::fs::FA_Write, sys::fs::OF_None);
785+
if (EC)
786+
report_fatal_error(Twine("Failed to open ") + DumpIRFilename +
787+
" to support -ir-dump-directory: " + EC.message());
788+
return Result;
698789
}
699790

700791
void PrintIRInstrumentation::printBeforePass(StringRef PassID, Any IR) {
701792
if (isIgnored(PassID))
702793
return;
703794

795+
std::string DumpIRFilename;
796+
if (!IRDumpDirectory.empty() &&
797+
(shouldPrintBeforePass(PassID) || shouldPrintAfterPass(PassID)))
798+
DumpIRFilename = fetchDumpFilename(PassID, IR);
799+
704800
// Saving Module for AfterPassInvalidated operations.
705801
// Note: here we rely on a fact that we do not change modules while
706802
// traversing the pipeline, so the latest captured module is good
707803
// for all print operations that has not happen yet.
708804
if (shouldPrintPassNumbers() || shouldPrintAtPassNumber() ||
709805
shouldPrintAfterPass(PassID))
710-
pushModuleDesc(PassID, IR);
806+
pushPassRunDescriptor(PassID, IR, DumpIRFilename);
711807

712808
if (!shouldPrintIR(IR))
713809
return;
@@ -720,9 +816,20 @@ void PrintIRInstrumentation::printBeforePass(StringRef PassID, Any IR) {
720816
if (!shouldPrintBeforePass(PassID))
721817
return;
722818

723-
dbgs() << "*** IR Dump Before " << PassID << " on " << getIRName(IR)
724-
<< " ***\n";
725-
unwrapAndPrint(dbgs(), IR);
819+
auto WriteIRToStream = [&](raw_ostream &Stream) {
820+
Stream << "; *** IR Dump Before " << PassID << " on " << getIRName(IR)
821+
<< " ***\n";
822+
unwrapAndPrint(Stream, IR);
823+
};
824+
825+
if (!DumpIRFilename.empty()) {
826+
DumpIRFilename += getFileSuffix(IRDumpFileSuffixType::Before);
827+
llvm::raw_fd_ostream DumpIRFileStream{
828+
prepareDumpIRFileDescriptor(DumpIRFilename), /* shouldClose */ true};
829+
WriteIRToStream(DumpIRFileStream);
830+
} else {
831+
WriteIRToStream(dbgs());
832+
}
726833
}
727834

728835
void PrintIRInstrumentation::printAfterPass(StringRef PassID, Any IR) {
@@ -733,21 +840,33 @@ void PrintIRInstrumentation::printAfterPass(StringRef PassID, Any IR) {
733840
!shouldPrintAtPassNumber())
734841
return;
735842

736-
const Module *M;
737-
std::string IRName;
738-
StringRef StoredPassID;
739-
std::tie(M, IRName, StoredPassID) = popModuleDesc(PassID);
843+
auto [M, DumpIRFilename, IRName, StoredPassID] = popPassRunDescriptor(PassID);
740844
assert(StoredPassID == PassID && "mismatched PassID");
741845

742846
if (!shouldPrintIR(IR) || !shouldPrintAfterPass(PassID))
743847
return;
744848

745-
dbgs() << "*** IR Dump "
746-
<< (shouldPrintAtPassNumber()
747-
? StringRef(formatv("At {0}-{1}", CurrentPassNumber, PassID))
748-
: StringRef(formatv("After {0}", PassID)))
749-
<< " on " << IRName << " ***\n";
750-
unwrapAndPrint(dbgs(), IR);
849+
auto WriteIRToStream = [&](raw_ostream &Stream, const StringRef IRName) {
850+
Stream << "; *** IR Dump "
851+
<< (shouldPrintAtPassNumber()
852+
? StringRef(formatv("At {0}-{1}", CurrentPassNumber, PassID))
853+
: StringRef(formatv("After {0}", PassID)))
854+
<< " on " << IRName << " ***\n";
855+
unwrapAndPrint(Stream, IR);
856+
};
857+
858+
if (!IRDumpDirectory.empty()) {
859+
assert(!DumpIRFilename.empty() && "DumpIRFilename must not be empty and "
860+
"should be set in printBeforePass");
861+
const std::string DumpIRFilenameWithSuffix =
862+
DumpIRFilename + getFileSuffix(IRDumpFileSuffixType::After).str();
863+
llvm::raw_fd_ostream DumpIRFileStream{
864+
prepareDumpIRFileDescriptor(DumpIRFilenameWithSuffix),
865+
/* shouldClose */ true};
866+
WriteIRToStream(DumpIRFileStream, IRName);
867+
} else {
868+
WriteIRToStream(dbgs(), IRName);
869+
}
751870
}
752871

753872
void PrintIRInstrumentation::printAfterPassInvalidated(StringRef PassID) {
@@ -758,25 +877,38 @@ void PrintIRInstrumentation::printAfterPassInvalidated(StringRef PassID) {
758877
!shouldPrintAtPassNumber())
759878
return;
760879

761-
const Module *M;
762-
std::string IRName;
763-
StringRef StoredPassID;
764-
std::tie(M, IRName, StoredPassID) = popModuleDesc(PassID);
880+
auto [M, DumpIRFilename, IRName, StoredPassID] = popPassRunDescriptor(PassID);
765881
assert(StoredPassID == PassID && "mismatched PassID");
766882
// Additional filtering (e.g. -filter-print-func) can lead to module
767883
// printing being skipped.
768884
if (!M || !shouldPrintAfterPass(PassID))
769885
return;
770886

771-
SmallString<20> Banner;
772-
if (shouldPrintAtPassNumber())
773-
Banner = formatv("*** IR Dump At {0}-{1} on {2} (invalidated) ***",
774-
CurrentPassNumber, PassID, IRName);
775-
else
776-
Banner = formatv("*** IR Dump After {0} on {1} (invalidated) ***",
777-
PassID, IRName);
778-
dbgs() << Banner << "\n";
779-
printIR(dbgs(), M);
887+
auto WriteIRToStream = [&](raw_ostream &Stream, const Module *M,
888+
const StringRef IRName) {
889+
SmallString<20> Banner;
890+
if (shouldPrintAtPassNumber())
891+
Banner = formatv("; *** IR Dump At {0}-{1} on {2} (invalidated) ***",
892+
CurrentPassNumber, PassID, IRName);
893+
else
894+
Banner = formatv("; *** IR Dump After {0} on {1} (invalidated) ***",
895+
PassID, IRName);
896+
Stream << Banner << "\n";
897+
printIR(Stream, M);
898+
};
899+
900+
if (!IRDumpDirectory.empty()) {
901+
assert(!DumpIRFilename.empty() && "DumpIRFilename must not be empty and "
902+
"should be set in printBeforePass");
903+
const std::string DumpIRFilenameWithSuffix =
904+
DumpIRFilename + getFileSuffix(IRDumpFileSuffixType::Invalidated).str();
905+
llvm::raw_fd_ostream DumpIRFileStream{
906+
prepareDumpIRFileDescriptor(DumpIRFilenameWithSuffix),
907+
/* shouldClose */ true};
908+
WriteIRToStream(DumpIRFileStream, M, IRName);
909+
} else {
910+
WriteIRToStream(dbgs(), M, IRName);
911+
}
780912
}
781913

782914
bool PrintIRInstrumentation::shouldPrintBeforePass(StringRef PassID) {
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
; RUN: rm -rf %t/logs
2+
; RUN: opt %s -disable-output -passes=loop-deletion -ir-dump-directory %t/logs -print-after=loop-deletion
3+
4+
; RUN: ls %t/logs | FileCheck %s
5+
; CHECK: 2-{{[a-z0-9]+}}-loop-{{[a-z0-9]+}}-LoopDeletionPass-invalidated.ll
6+
7+
; RUN: ls %t/logs | count 1
8+
; RUN: cat %t/logs/* | FileCheck %s --check-prefix=CHECK-CONTENTS
9+
10+
; CHECK-CONTENTS: ; *** IR Dump After LoopDeletionPass on bb1 (invalidated) ***
11+
; CHECK-CONTENTS: define void @foo() {
12+
; CHECK-CONTENTS: br label %bb2
13+
; CHECK-CONTENTS: bb2: ; preds = %0
14+
; CHECK-CONTENTS: ret void
15+
; CHECK-CONTENTS: }
16+
17+
18+
define void @foo() {
19+
br label %bb1
20+
bb1:
21+
br i1 false, label %bb1, label %bb2
22+
bb2:
23+
ret void
24+
}

0 commit comments

Comments
 (0)