18
18
#include " llvm/Analysis/CallGraphSCCPass.h"
19
19
#include " llvm/Analysis/LazyCallGraph.h"
20
20
#include " llvm/Analysis/LoopInfo.h"
21
+ #include " llvm/CodeGen/StableHashing.h"
21
22
#include " llvm/IR/Constants.h"
22
23
#include " llvm/IR/Function.h"
23
24
#include " llvm/IR/Module.h"
33
34
#include " llvm/Support/FormatVariadic.h"
34
35
#include " llvm/Support/GraphWriter.h"
35
36
#include " llvm/Support/MemoryBuffer.h"
37
+ #include " llvm/Support/Path.h"
36
38
#include " llvm/Support/Program.h"
37
39
#include " llvm/Support/Regex.h"
38
40
#include " llvm/Support/Signals.h"
@@ -120,6 +122,13 @@ static cl::opt<unsigned>
120
122
cl::desc(" Print IR at pass with this number as "
121
123
" reported by print-passes-names" ));
122
124
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
+
123
132
namespace {
124
133
125
134
// 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) {
681
690
}
682
691
683
692
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)];
685
754
}
686
755
687
- void PrintIRInstrumentation::pushModuleDesc (StringRef PassID, Any IR) {
756
+ void PrintIRInstrumentation::pushPassRunDescriptor (
757
+ StringRef PassID, Any IR, std::string &DumpIRFilename) {
688
758
const Module *M = unwrapModule (IR);
689
- ModuleDescStack.emplace_back (M, getIRName (IR), PassID);
759
+ PassRunDescriptorStack.emplace_back (
760
+ PassRunDescriptor (M, DumpIRFilename, getIRName (IR), PassID));
690
761
}
691
762
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;
698
789
}
699
790
700
791
void PrintIRInstrumentation::printBeforePass (StringRef PassID, Any IR) {
701
792
if (isIgnored (PassID))
702
793
return ;
703
794
795
+ std::string DumpIRFilename;
796
+ if (!IRDumpDirectory.empty () &&
797
+ (shouldPrintBeforePass (PassID) || shouldPrintAfterPass (PassID)))
798
+ DumpIRFilename = fetchDumpFilename (PassID, IR);
799
+
704
800
// Saving Module for AfterPassInvalidated operations.
705
801
// Note: here we rely on a fact that we do not change modules while
706
802
// traversing the pipeline, so the latest captured module is good
707
803
// for all print operations that has not happen yet.
708
804
if (shouldPrintPassNumbers () || shouldPrintAtPassNumber () ||
709
805
shouldPrintAfterPass (PassID))
710
- pushModuleDesc (PassID, IR);
806
+ pushPassRunDescriptor (PassID, IR, DumpIRFilename );
711
807
712
808
if (!shouldPrintIR (IR))
713
809
return ;
@@ -720,9 +816,20 @@ void PrintIRInstrumentation::printBeforePass(StringRef PassID, Any IR) {
720
816
if (!shouldPrintBeforePass (PassID))
721
817
return ;
722
818
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
+ }
726
833
}
727
834
728
835
void PrintIRInstrumentation::printAfterPass (StringRef PassID, Any IR) {
@@ -733,21 +840,33 @@ void PrintIRInstrumentation::printAfterPass(StringRef PassID, Any IR) {
733
840
!shouldPrintAtPassNumber ())
734
841
return ;
735
842
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);
740
844
assert (StoredPassID == PassID && " mismatched PassID" );
741
845
742
846
if (!shouldPrintIR (IR) || !shouldPrintAfterPass (PassID))
743
847
return ;
744
848
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
+ }
751
870
}
752
871
753
872
void PrintIRInstrumentation::printAfterPassInvalidated (StringRef PassID) {
@@ -758,25 +877,38 @@ void PrintIRInstrumentation::printAfterPassInvalidated(StringRef PassID) {
758
877
!shouldPrintAtPassNumber ())
759
878
return ;
760
879
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);
765
881
assert (StoredPassID == PassID && " mismatched PassID" );
766
882
// Additional filtering (e.g. -filter-print-func) can lead to module
767
883
// printing being skipped.
768
884
if (!M || !shouldPrintAfterPass (PassID))
769
885
return ;
770
886
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
+ }
780
912
}
781
913
782
914
bool PrintIRInstrumentation::shouldPrintBeforePass (StringRef PassID) {
0 commit comments