1e8d8bef9SDimitry Andric //===- PrintPasses.cpp ----------------------------------------------------===// 2e8d8bef9SDimitry Andric // 3e8d8bef9SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4e8d8bef9SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5e8d8bef9SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6e8d8bef9SDimitry Andric // 7e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===// 8e8d8bef9SDimitry Andric 9e8d8bef9SDimitry Andric #include "llvm/IR/PrintPasses.h" 10e8d8bef9SDimitry Andric #include "llvm/Support/CommandLine.h" 11*bdd1243dSDimitry Andric #include "llvm/Support/Errc.h" 12*bdd1243dSDimitry Andric #include "llvm/Support/FileSystem.h" 13*bdd1243dSDimitry Andric #include "llvm/Support/MemoryBuffer.h" 14*bdd1243dSDimitry Andric #include "llvm/Support/Program.h" 15e8d8bef9SDimitry Andric #include <unordered_set> 16e8d8bef9SDimitry Andric 17e8d8bef9SDimitry Andric using namespace llvm; 18e8d8bef9SDimitry Andric 19e8d8bef9SDimitry Andric // Print IR out before/after specified passes. 20e8d8bef9SDimitry Andric static cl::list<std::string> 21e8d8bef9SDimitry Andric PrintBefore("print-before", 22e8d8bef9SDimitry Andric llvm::cl::desc("Print IR before specified passes"), 23e8d8bef9SDimitry Andric cl::CommaSeparated, cl::Hidden); 24e8d8bef9SDimitry Andric 25e8d8bef9SDimitry Andric static cl::list<std::string> 26e8d8bef9SDimitry Andric PrintAfter("print-after", llvm::cl::desc("Print IR after specified passes"), 27e8d8bef9SDimitry Andric cl::CommaSeparated, cl::Hidden); 28e8d8bef9SDimitry Andric 29e8d8bef9SDimitry Andric static cl::opt<bool> PrintBeforeAll("print-before-all", 30e8d8bef9SDimitry Andric llvm::cl::desc("Print IR before each pass"), 31e8d8bef9SDimitry Andric cl::init(false), cl::Hidden); 32e8d8bef9SDimitry Andric static cl::opt<bool> PrintAfterAll("print-after-all", 33e8d8bef9SDimitry Andric llvm::cl::desc("Print IR after each pass"), 34e8d8bef9SDimitry Andric cl::init(false), cl::Hidden); 35e8d8bef9SDimitry Andric 36972a253aSDimitry Andric // Print out the IR after passes, similar to -print-after-all except that it 37972a253aSDimitry Andric // only prints the IR after passes that change the IR. Those passes that do not 38972a253aSDimitry Andric // make changes to the IR are reported as not making any changes. In addition, 39972a253aSDimitry Andric // the initial IR is also reported. Other hidden options affect the output from 40972a253aSDimitry Andric // this option. -filter-passes will limit the output to the named passes that 41972a253aSDimitry Andric // actually change the IR and other passes are reported as filtered out. The 42972a253aSDimitry Andric // specified passes will either be reported as making no changes (with no IR 43972a253aSDimitry Andric // reported) or the changed IR will be reported. Also, the -filter-print-funcs 44972a253aSDimitry Andric // and -print-module-scope options will do similar filtering based on function 45972a253aSDimitry Andric // name, reporting changed IRs as functions(or modules if -print-module-scope is 46972a253aSDimitry Andric // specified) for a particular function or indicating that the IR has been 47972a253aSDimitry Andric // filtered out. The extra options can be combined, allowing only changed IRs 48972a253aSDimitry Andric // for certain passes on certain functions to be reported in different formats, 49972a253aSDimitry Andric // with the rest being reported as filtered out. The -print-before-changed 50972a253aSDimitry Andric // option will print the IR as it was before each pass that changed it. The 51972a253aSDimitry Andric // optional value of quiet will only report when the IR changes, suppressing all 52972a253aSDimitry Andric // other messages, including the initial IR. The values "diff" and "diff-quiet" 53972a253aSDimitry Andric // will present the changes in a form similar to a patch, in either verbose or 54972a253aSDimitry Andric // quiet mode, respectively. The lines that are removed and added are prefixed 55972a253aSDimitry Andric // with '-' and '+', respectively. The -filter-print-funcs and -filter-passes 56972a253aSDimitry Andric // can be used to filter the output. This reporter relies on the linux diff 57972a253aSDimitry Andric // utility to do comparisons and insert the prefixes. For systems that do not 58972a253aSDimitry Andric // have the necessary facilities, the error message will be shown in place of 59972a253aSDimitry Andric // the expected output. 60972a253aSDimitry Andric cl::opt<ChangePrinter> llvm::PrintChanged( 61972a253aSDimitry Andric "print-changed", cl::desc("Print changed IRs"), cl::Hidden, 62972a253aSDimitry Andric cl::ValueOptional, cl::init(ChangePrinter::None), 63972a253aSDimitry Andric cl::values( 64972a253aSDimitry Andric clEnumValN(ChangePrinter::Quiet, "quiet", "Run in quiet mode"), 65972a253aSDimitry Andric clEnumValN(ChangePrinter::DiffVerbose, "diff", 66972a253aSDimitry Andric "Display patch-like changes"), 67972a253aSDimitry Andric clEnumValN(ChangePrinter::DiffQuiet, "diff-quiet", 68972a253aSDimitry Andric "Display patch-like changes in quiet mode"), 69972a253aSDimitry Andric clEnumValN(ChangePrinter::ColourDiffVerbose, "cdiff", 70972a253aSDimitry Andric "Display patch-like changes with color"), 71972a253aSDimitry Andric clEnumValN(ChangePrinter::ColourDiffQuiet, "cdiff-quiet", 72972a253aSDimitry Andric "Display patch-like changes in quiet mode with color"), 73972a253aSDimitry Andric clEnumValN(ChangePrinter::DotCfgVerbose, "dot-cfg", 74972a253aSDimitry Andric "Create a website with graphical changes"), 75972a253aSDimitry Andric clEnumValN(ChangePrinter::DotCfgQuiet, "dot-cfg-quiet", 76972a253aSDimitry Andric "Create a website with graphical changes in quiet mode"), 77972a253aSDimitry Andric // Sentinel value for unspecified option. 78972a253aSDimitry Andric clEnumValN(ChangePrinter::Verbose, "", ""))); 79972a253aSDimitry Andric 80*bdd1243dSDimitry Andric // An option for specifying the diff used by print-changed=[diff | diff-quiet] 81*bdd1243dSDimitry Andric static cl::opt<std::string> 82*bdd1243dSDimitry Andric DiffBinary("print-changed-diff-path", cl::Hidden, cl::init("diff"), 83*bdd1243dSDimitry Andric cl::desc("system diff used by change reporters")); 84*bdd1243dSDimitry Andric 85e8d8bef9SDimitry Andric static cl::opt<bool> 86e8d8bef9SDimitry Andric PrintModuleScope("print-module-scope", 87e8d8bef9SDimitry Andric cl::desc("When printing IR for print-[before|after]{-all} " 88e8d8bef9SDimitry Andric "always print a module IR"), 89e8d8bef9SDimitry Andric cl::init(false), cl::Hidden); 90e8d8bef9SDimitry Andric 91*bdd1243dSDimitry Andric // See the description for -print-changed for an explanation of the use 92*bdd1243dSDimitry Andric // of this option. 93*bdd1243dSDimitry Andric static cl::list<std::string> FilterPasses( 94*bdd1243dSDimitry Andric "filter-passes", cl::value_desc("pass names"), 95*bdd1243dSDimitry Andric cl::desc("Only consider IR changes for passes whose names " 96*bdd1243dSDimitry Andric "match the specified value. No-op without -print-changed"), 97*bdd1243dSDimitry Andric cl::CommaSeparated, cl::Hidden); 98*bdd1243dSDimitry Andric 99e8d8bef9SDimitry Andric static cl::list<std::string> 100e8d8bef9SDimitry Andric PrintFuncsList("filter-print-funcs", cl::value_desc("function names"), 101e8d8bef9SDimitry Andric cl::desc("Only print IR for functions whose name " 102e8d8bef9SDimitry Andric "match this for all print-[before|after][-all] " 103e8d8bef9SDimitry Andric "options"), 104e8d8bef9SDimitry Andric cl::CommaSeparated, cl::Hidden); 105e8d8bef9SDimitry Andric 106e8d8bef9SDimitry Andric /// This is a helper to determine whether to print IR before or 107e8d8bef9SDimitry Andric /// after a pass. 108e8d8bef9SDimitry Andric 109e8d8bef9SDimitry Andric bool llvm::shouldPrintBeforeSomePass() { 110e8d8bef9SDimitry Andric return PrintBeforeAll || !PrintBefore.empty(); 111e8d8bef9SDimitry Andric } 112e8d8bef9SDimitry Andric 113e8d8bef9SDimitry Andric bool llvm::shouldPrintAfterSomePass() { 114e8d8bef9SDimitry Andric return PrintAfterAll || !PrintAfter.empty(); 115e8d8bef9SDimitry Andric } 116e8d8bef9SDimitry Andric 117e8d8bef9SDimitry Andric static bool shouldPrintBeforeOrAfterPass(StringRef PassID, 118e8d8bef9SDimitry Andric ArrayRef<std::string> PassesToPrint) { 119e8d8bef9SDimitry Andric return llvm::is_contained(PassesToPrint, PassID); 120e8d8bef9SDimitry Andric } 121e8d8bef9SDimitry Andric 122e8d8bef9SDimitry Andric bool llvm::shouldPrintBeforeAll() { return PrintBeforeAll; } 123e8d8bef9SDimitry Andric 124e8d8bef9SDimitry Andric bool llvm::shouldPrintAfterAll() { return PrintAfterAll; } 125e8d8bef9SDimitry Andric 126e8d8bef9SDimitry Andric bool llvm::shouldPrintBeforePass(StringRef PassID) { 127e8d8bef9SDimitry Andric return PrintBeforeAll || shouldPrintBeforeOrAfterPass(PassID, PrintBefore); 128e8d8bef9SDimitry Andric } 129e8d8bef9SDimitry Andric 130e8d8bef9SDimitry Andric bool llvm::shouldPrintAfterPass(StringRef PassID) { 131e8d8bef9SDimitry Andric return PrintAfterAll || shouldPrintBeforeOrAfterPass(PassID, PrintAfter); 132e8d8bef9SDimitry Andric } 133e8d8bef9SDimitry Andric 134e8d8bef9SDimitry Andric std::vector<std::string> llvm::printBeforePasses() { 135e8d8bef9SDimitry Andric return std::vector<std::string>(PrintBefore); 136e8d8bef9SDimitry Andric } 137e8d8bef9SDimitry Andric 138e8d8bef9SDimitry Andric std::vector<std::string> llvm::printAfterPasses() { 139e8d8bef9SDimitry Andric return std::vector<std::string>(PrintAfter); 140e8d8bef9SDimitry Andric } 141e8d8bef9SDimitry Andric 142e8d8bef9SDimitry Andric bool llvm::forcePrintModuleIR() { return PrintModuleScope; } 143e8d8bef9SDimitry Andric 144*bdd1243dSDimitry Andric bool llvm::isPassInPrintList(StringRef PassName) { 145*bdd1243dSDimitry Andric static std::unordered_set<std::string> Set(FilterPasses.begin(), 146*bdd1243dSDimitry Andric FilterPasses.end()); 147*bdd1243dSDimitry Andric return Set.empty() || Set.count(std::string(PassName)); 148*bdd1243dSDimitry Andric } 149*bdd1243dSDimitry Andric 150*bdd1243dSDimitry Andric bool llvm::isFilterPassesEmpty() { return FilterPasses.empty(); } 151*bdd1243dSDimitry Andric 152e8d8bef9SDimitry Andric bool llvm::isFunctionInPrintList(StringRef FunctionName) { 153e8d8bef9SDimitry Andric static std::unordered_set<std::string> PrintFuncNames(PrintFuncsList.begin(), 154e8d8bef9SDimitry Andric PrintFuncsList.end()); 155e8d8bef9SDimitry Andric return PrintFuncNames.empty() || 156e8d8bef9SDimitry Andric PrintFuncNames.count(std::string(FunctionName)); 157e8d8bef9SDimitry Andric } 158*bdd1243dSDimitry Andric 159*bdd1243dSDimitry Andric std::error_code cleanUpTempFilesImpl(ArrayRef<std::string> FileName, 160*bdd1243dSDimitry Andric unsigned N) { 161*bdd1243dSDimitry Andric std::error_code RC; 162*bdd1243dSDimitry Andric for (unsigned I = 0; I < N; ++I) { 163*bdd1243dSDimitry Andric std::error_code EC = sys::fs::remove(FileName[I]); 164*bdd1243dSDimitry Andric if (EC) 165*bdd1243dSDimitry Andric RC = EC; 166*bdd1243dSDimitry Andric } 167*bdd1243dSDimitry Andric return RC; 168*bdd1243dSDimitry Andric } 169*bdd1243dSDimitry Andric 170*bdd1243dSDimitry Andric std::error_code llvm::prepareTempFiles(SmallVector<int> &FD, 171*bdd1243dSDimitry Andric ArrayRef<StringRef> SR, 172*bdd1243dSDimitry Andric SmallVector<std::string> &FileName) { 173*bdd1243dSDimitry Andric assert(FD.size() >= SR.size() && FileName.size() == FD.size() && 174*bdd1243dSDimitry Andric "Unexpected array sizes"); 175*bdd1243dSDimitry Andric std::error_code EC; 176*bdd1243dSDimitry Andric unsigned I = 0; 177*bdd1243dSDimitry Andric for (; I < FD.size(); ++I) { 178*bdd1243dSDimitry Andric if (FD[I] == -1) { 179*bdd1243dSDimitry Andric SmallVector<char, 200> SV; 180*bdd1243dSDimitry Andric EC = sys::fs::createTemporaryFile("tmpfile", "txt", FD[I], SV); 181*bdd1243dSDimitry Andric if (EC) 182*bdd1243dSDimitry Andric break; 183*bdd1243dSDimitry Andric FileName[I] = Twine(SV).str(); 184*bdd1243dSDimitry Andric } 185*bdd1243dSDimitry Andric if (I < SR.size()) { 186*bdd1243dSDimitry Andric EC = sys::fs::openFileForWrite(FileName[I], FD[I]); 187*bdd1243dSDimitry Andric if (EC) 188*bdd1243dSDimitry Andric break; 189*bdd1243dSDimitry Andric raw_fd_ostream OutStream(FD[I], /*shouldClose=*/true); 190*bdd1243dSDimitry Andric if (FD[I] == -1) { 191*bdd1243dSDimitry Andric EC = make_error_code(errc::io_error); 192*bdd1243dSDimitry Andric break; 193*bdd1243dSDimitry Andric } 194*bdd1243dSDimitry Andric OutStream << SR[I]; 195*bdd1243dSDimitry Andric } 196*bdd1243dSDimitry Andric } 197*bdd1243dSDimitry Andric if (EC && I > 0) 198*bdd1243dSDimitry Andric // clean up created temporary files 199*bdd1243dSDimitry Andric cleanUpTempFilesImpl(FileName, I); 200*bdd1243dSDimitry Andric return EC; 201*bdd1243dSDimitry Andric } 202*bdd1243dSDimitry Andric 203*bdd1243dSDimitry Andric std::error_code llvm::cleanUpTempFiles(ArrayRef<std::string> FileName) { 204*bdd1243dSDimitry Andric return cleanUpTempFilesImpl(FileName, FileName.size()); 205*bdd1243dSDimitry Andric } 206*bdd1243dSDimitry Andric 207*bdd1243dSDimitry Andric std::string llvm::doSystemDiff(StringRef Before, StringRef After, 208*bdd1243dSDimitry Andric StringRef OldLineFormat, StringRef NewLineFormat, 209*bdd1243dSDimitry Andric StringRef UnchangedLineFormat) { 210*bdd1243dSDimitry Andric // Store the 2 bodies into temporary files and call diff on them 211*bdd1243dSDimitry Andric // to get the body of the node. 212*bdd1243dSDimitry Andric static SmallVector<int> FD{-1, -1, -1}; 213*bdd1243dSDimitry Andric SmallVector<StringRef> SR{Before, After}; 214*bdd1243dSDimitry Andric static SmallVector<std::string> FileName{"", "", ""}; 215*bdd1243dSDimitry Andric if (auto Err = prepareTempFiles(FD, SR, FileName)) 216*bdd1243dSDimitry Andric return "Unable to create temporary file."; 217*bdd1243dSDimitry Andric 218*bdd1243dSDimitry Andric static ErrorOr<std::string> DiffExe = sys::findProgramByName(DiffBinary); 219*bdd1243dSDimitry Andric if (!DiffExe) 220*bdd1243dSDimitry Andric return "Unable to find diff executable."; 221*bdd1243dSDimitry Andric 222*bdd1243dSDimitry Andric SmallString<128> OLF, NLF, ULF; 223*bdd1243dSDimitry Andric ("--old-line-format=" + OldLineFormat).toVector(OLF); 224*bdd1243dSDimitry Andric ("--new-line-format=" + NewLineFormat).toVector(NLF); 225*bdd1243dSDimitry Andric ("--unchanged-line-format=" + UnchangedLineFormat).toVector(ULF); 226*bdd1243dSDimitry Andric 227*bdd1243dSDimitry Andric StringRef Args[] = {DiffBinary, "-w", "-d", OLF, 228*bdd1243dSDimitry Andric NLF, ULF, FileName[0], FileName[1]}; 229*bdd1243dSDimitry Andric std::optional<StringRef> Redirects[] = {std::nullopt, StringRef(FileName[2]), 230*bdd1243dSDimitry Andric std::nullopt}; 231*bdd1243dSDimitry Andric int Result = sys::ExecuteAndWait(*DiffExe, Args, std::nullopt, Redirects); 232*bdd1243dSDimitry Andric if (Result < 0) 233*bdd1243dSDimitry Andric return "Error executing system diff."; 234*bdd1243dSDimitry Andric std::string Diff; 235*bdd1243dSDimitry Andric auto B = MemoryBuffer::getFile(FileName[2]); 236*bdd1243dSDimitry Andric if (B && *B) 237*bdd1243dSDimitry Andric Diff = (*B)->getBuffer().str(); 238*bdd1243dSDimitry Andric else 239*bdd1243dSDimitry Andric return "Unable to read result."; 240*bdd1243dSDimitry Andric 241*bdd1243dSDimitry Andric if (auto Err = cleanUpTempFiles(FileName)) 242*bdd1243dSDimitry Andric return "Unable to remove temporary file."; 243*bdd1243dSDimitry Andric 244*bdd1243dSDimitry Andric return Diff; 245*bdd1243dSDimitry Andric } 246