//===- 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 using namespace llvm; // Print IR out before/after specified passes. static cl::list PrintBefore("print-before", llvm::cl::desc("Print IR before specified passes"), cl::CommaSeparated, cl::Hidden); static cl::list PrintAfter("print-after", llvm::cl::desc("Print IR after specified passes"), cl::CommaSeparated, cl::Hidden); static cl::opt PrintBeforeAll("print-before-all", llvm::cl::desc("Print IR before each pass"), cl::init(false), cl::Hidden); static cl::opt 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 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 DiffBinary("print-changed-diff-path", cl::Hidden, cl::init("diff"), cl::desc("system diff used by change reporters")); static cl::opt 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 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 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 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 llvm::printBeforePasses() { return std::vector(PrintBefore); } std::vector llvm::printAfterPasses() { return std::vector(PrintAfter); } bool llvm::forcePrintModuleIR() { return PrintModuleScope; } bool llvm::isPassInPrintList(StringRef PassName) { static std::unordered_set 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 PrintFuncNames(PrintFuncsList.begin(), PrintFuncsList.end()); return PrintFuncNames.empty() || PrintFuncNames.count(std::string(FunctionName)); } std::error_code cleanUpTempFilesImpl(ArrayRef 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 &FD, ArrayRef SR, SmallVector &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 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 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 FD{-1, -1, -1}; SmallVector SR{Before, After}; static SmallVector FileName{"", "", ""}; if (auto Err = prepareTempFiles(FD, SR, FileName)) return "Unable to create temporary file."; static ErrorOr 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 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; }