| //===- 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; | |
| } |