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