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