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(Val: false), cl::Hidden); |
32 | static cl::opt<bool> PrintAfterAll("print-after-all" , |
33 | llvm::cl::desc("Print IR after each pass" ), |
34 | cl::init(Val: 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(Val: 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(Val: "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(Val: 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(Range&: PassesToPrint, Element: 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, PassesToPrint: PrintBefore); |
128 | } |
129 | |
130 | bool llvm::shouldPrintAfterPass(StringRef PassID) { |
131 | return PrintAfterAll || shouldPrintBeforeOrAfterPass(PassID, PassesToPrint: 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(x: 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(x: 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(path: 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(Prefix: "tmpfile" , Suffix: "txt" , ResultFD&: FD[I], ResultPath&: SV); |
181 | if (EC) |
182 | break; |
183 | FileName[I] = Twine(SV).str(); |
184 | } |
185 | if (I < SR.size()) { |
186 | EC = sys::fs::openFileForWrite(Name: FileName[I], ResultFD&: 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(E: 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, N: I); |
200 | return EC; |
201 | } |
202 | |
203 | std::error_code llvm::cleanUpTempFiles(ArrayRef<std::string> FileName) { |
204 | return cleanUpTempFilesImpl(FileName, N: 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 (prepareTempFiles(FD, SR, FileName)) |
216 | return "Unable to create temporary file." ; |
217 | |
218 | static ErrorOr<std::string> DiffExe = sys::findProgramByName(Name: DiffBinary); |
219 | if (!DiffExe) |
220 | return "Unable to find diff executable." ; |
221 | |
222 | SmallString<128> OLF, NLF, ULF; |
223 | ("--old-line-format=" + OldLineFormat).toVector(Out&: OLF); |
224 | ("--new-line-format=" + NewLineFormat).toVector(Out&: NLF); |
225 | ("--unchanged-line-format=" + UnchangedLineFormat).toVector(Out&: 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(Program: *DiffExe, Args, Env: std::nullopt, Redirects); |
232 | if (Result < 0) |
233 | return "Error executing system diff." ; |
234 | std::string Diff; |
235 | auto B = MemoryBuffer::getFile(Filename: FileName[2]); |
236 | if (B && *B) |
237 | Diff = (*B)->getBuffer().str(); |
238 | else |
239 | return "Unable to read result." ; |
240 | |
241 | if (cleanUpTempFiles(FileName)) |
242 | return "Unable to remove temporary file." ; |
243 | |
244 | return Diff; |
245 | } |
246 | |