12f0de582SArthur Eubanks //===- PrintPasses.cpp ----------------------------------------------------===// 22f0de582SArthur Eubanks // 32f0de582SArthur Eubanks // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 42f0de582SArthur Eubanks // See https://llvm.org/LICENSE.txt for license information. 52f0de582SArthur Eubanks // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 62f0de582SArthur Eubanks // 72f0de582SArthur Eubanks //===----------------------------------------------------------------------===// 82f0de582SArthur Eubanks 92f0de582SArthur Eubanks #include "llvm/IR/PrintPasses.h" 102f0de582SArthur Eubanks #include "llvm/Support/CommandLine.h" 1145553042SJamie Schmeiser #include "llvm/Support/Errc.h" 122b70bebcSFangrui Song #include "llvm/Support/FileSystem.h" 132b70bebcSFangrui Song #include "llvm/Support/MemoryBuffer.h" 142b70bebcSFangrui Song #include "llvm/Support/Program.h" 152f0de582SArthur Eubanks #include <unordered_set> 162f0de582SArthur Eubanks 172f0de582SArthur Eubanks using namespace llvm; 182f0de582SArthur Eubanks 192f0de582SArthur Eubanks // Print IR out before/after specified passes. 202f0de582SArthur Eubanks static cl::list<std::string> 212f0de582SArthur Eubanks PrintBefore("print-before", 222f0de582SArthur Eubanks llvm::cl::desc("Print IR before specified passes"), 232f0de582SArthur Eubanks cl::CommaSeparated, cl::Hidden); 242f0de582SArthur Eubanks 252f0de582SArthur Eubanks static cl::list<std::string> 262f0de582SArthur Eubanks PrintAfter("print-after", llvm::cl::desc("Print IR after specified passes"), 272f0de582SArthur Eubanks cl::CommaSeparated, cl::Hidden); 282f0de582SArthur Eubanks 292f0de582SArthur Eubanks static cl::opt<bool> PrintBeforeAll("print-before-all", 302f0de582SArthur Eubanks llvm::cl::desc("Print IR before each pass"), 312f0de582SArthur Eubanks cl::init(false), cl::Hidden); 322f0de582SArthur Eubanks static cl::opt<bool> PrintAfterAll("print-after-all", 332f0de582SArthur Eubanks llvm::cl::desc("Print IR after each pass"), 342f0de582SArthur Eubanks cl::init(false), cl::Hidden); 352f0de582SArthur Eubanks 36f106525dSFangrui Song // Print out the IR after passes, similar to -print-after-all except that it 37f106525dSFangrui Song // only prints the IR after passes that change the IR. Those passes that do not 38f106525dSFangrui Song // make changes to the IR are reported as not making any changes. In addition, 39f106525dSFangrui Song // the initial IR is also reported. Other hidden options affect the output from 40f106525dSFangrui Song // this option. -filter-passes will limit the output to the named passes that 41f106525dSFangrui Song // actually change the IR and other passes are reported as filtered out. The 42f106525dSFangrui Song // specified passes will either be reported as making no changes (with no IR 43f106525dSFangrui Song // reported) or the changed IR will be reported. Also, the -filter-print-funcs 44f106525dSFangrui Song // and -print-module-scope options will do similar filtering based on function 45f106525dSFangrui Song // name, reporting changed IRs as functions(or modules if -print-module-scope is 46f106525dSFangrui Song // specified) for a particular function or indicating that the IR has been 47f106525dSFangrui Song // filtered out. The extra options can be combined, allowing only changed IRs 48f106525dSFangrui Song // for certain passes on certain functions to be reported in different formats, 49f106525dSFangrui Song // with the rest being reported as filtered out. The -print-before-changed 50f106525dSFangrui Song // option will print the IR as it was before each pass that changed it. The 51f106525dSFangrui Song // optional value of quiet will only report when the IR changes, suppressing all 52f106525dSFangrui Song // other messages, including the initial IR. The values "diff" and "diff-quiet" 53f106525dSFangrui Song // will present the changes in a form similar to a patch, in either verbose or 54f106525dSFangrui Song // quiet mode, respectively. The lines that are removed and added are prefixed 55f106525dSFangrui Song // with '-' and '+', respectively. The -filter-print-funcs and -filter-passes 56f106525dSFangrui Song // can be used to filter the output. This reporter relies on the linux diff 57f106525dSFangrui Song // utility to do comparisons and insert the prefixes. For systems that do not 58f106525dSFangrui Song // have the necessary facilities, the error message will be shown in place of 59f106525dSFangrui Song // the expected output. 60f106525dSFangrui Song cl::opt<ChangePrinter> llvm::PrintChanged( 61f106525dSFangrui Song "print-changed", cl::desc("Print changed IRs"), cl::Hidden, 62f106525dSFangrui Song cl::ValueOptional, cl::init(ChangePrinter::None), 63f106525dSFangrui Song cl::values( 64f106525dSFangrui Song clEnumValN(ChangePrinter::Quiet, "quiet", "Run in quiet mode"), 65f106525dSFangrui Song clEnumValN(ChangePrinter::DiffVerbose, "diff", 66f106525dSFangrui Song "Display patch-like changes"), 67f106525dSFangrui Song clEnumValN(ChangePrinter::DiffQuiet, "diff-quiet", 68f106525dSFangrui Song "Display patch-like changes in quiet mode"), 69f106525dSFangrui Song clEnumValN(ChangePrinter::ColourDiffVerbose, "cdiff", 70f106525dSFangrui Song "Display patch-like changes with color"), 71f106525dSFangrui Song clEnumValN(ChangePrinter::ColourDiffQuiet, "cdiff-quiet", 72f106525dSFangrui Song "Display patch-like changes in quiet mode with color"), 73f106525dSFangrui Song clEnumValN(ChangePrinter::DotCfgVerbose, "dot-cfg", 74f106525dSFangrui Song "Create a website with graphical changes"), 75f106525dSFangrui Song clEnumValN(ChangePrinter::DotCfgQuiet, "dot-cfg-quiet", 76f106525dSFangrui Song "Create a website with graphical changes in quiet mode"), 77f106525dSFangrui Song // Sentinel value for unspecified option. 78f106525dSFangrui Song clEnumValN(ChangePrinter::Verbose, "", ""))); 79f106525dSFangrui Song 802b70bebcSFangrui Song // An option for specifying the diff used by print-changed=[diff | diff-quiet] 812b70bebcSFangrui Song static cl::opt<std::string> 822b70bebcSFangrui Song DiffBinary("print-changed-diff-path", cl::Hidden, cl::init("diff"), 832b70bebcSFangrui Song cl::desc("system diff used by change reporters")); 842b70bebcSFangrui Song 852f0de582SArthur Eubanks static cl::opt<bool> 862f0de582SArthur Eubanks PrintModuleScope("print-module-scope", 872f0de582SArthur Eubanks cl::desc("When printing IR for print-[before|after]{-all} " 882f0de582SArthur Eubanks "always print a module IR"), 892f0de582SArthur Eubanks cl::init(false), cl::Hidden); 902f0de582SArthur Eubanks 91*5b6a26ccSAkshay Deodhar static cl::opt<bool> LoopPrintFuncScope( 92*5b6a26ccSAkshay Deodhar "print-loop-func-scope", 93*5b6a26ccSAkshay Deodhar cl::desc("When printing IR for print-[before|after]{-all} " 94*5b6a26ccSAkshay Deodhar "for a loop pass, always print function IR"), 95*5b6a26ccSAkshay Deodhar cl::init(false), cl::Hidden); 96*5b6a26ccSAkshay Deodhar 978d95fd7eSFangrui Song // See the description for -print-changed for an explanation of the use 988d95fd7eSFangrui Song // of this option. 998d95fd7eSFangrui Song static cl::list<std::string> FilterPasses( 1008d95fd7eSFangrui Song "filter-passes", cl::value_desc("pass names"), 1018d95fd7eSFangrui Song cl::desc("Only consider IR changes for passes whose names " 1028d95fd7eSFangrui Song "match the specified value. No-op without -print-changed"), 1038d95fd7eSFangrui Song cl::CommaSeparated, cl::Hidden); 1048d95fd7eSFangrui Song 1052f0de582SArthur Eubanks static cl::list<std::string> 1062f0de582SArthur Eubanks PrintFuncsList("filter-print-funcs", cl::value_desc("function names"), 1072f0de582SArthur Eubanks cl::desc("Only print IR for functions whose name " 1082f0de582SArthur Eubanks "match this for all print-[before|after][-all] " 1092f0de582SArthur Eubanks "options"), 1102f0de582SArthur Eubanks cl::CommaSeparated, cl::Hidden); 1112f0de582SArthur Eubanks 1122f0de582SArthur Eubanks /// This is a helper to determine whether to print IR before or 1132f0de582SArthur Eubanks /// after a pass. 1142f0de582SArthur Eubanks 1152f0de582SArthur Eubanks bool llvm::shouldPrintBeforeSomePass() { 1162f0de582SArthur Eubanks return PrintBeforeAll || !PrintBefore.empty(); 1172f0de582SArthur Eubanks } 1182f0de582SArthur Eubanks 1192f0de582SArthur Eubanks bool llvm::shouldPrintAfterSomePass() { 1202f0de582SArthur Eubanks return PrintAfterAll || !PrintAfter.empty(); 1212f0de582SArthur Eubanks } 1222f0de582SArthur Eubanks 1232f0de582SArthur Eubanks static bool shouldPrintBeforeOrAfterPass(StringRef PassID, 1242f0de582SArthur Eubanks ArrayRef<std::string> PassesToPrint) { 12570de3240SKazu Hirata return llvm::is_contained(PassesToPrint, PassID); 1262f0de582SArthur Eubanks } 1272f0de582SArthur Eubanks 1282f0de582SArthur Eubanks bool llvm::shouldPrintBeforeAll() { return PrintBeforeAll; } 1292f0de582SArthur Eubanks 1302f0de582SArthur Eubanks bool llvm::shouldPrintAfterAll() { return PrintAfterAll; } 1312f0de582SArthur Eubanks 1322f0de582SArthur Eubanks bool llvm::shouldPrintBeforePass(StringRef PassID) { 1332f0de582SArthur Eubanks return PrintBeforeAll || shouldPrintBeforeOrAfterPass(PassID, PrintBefore); 1342f0de582SArthur Eubanks } 1352f0de582SArthur Eubanks 1362f0de582SArthur Eubanks bool llvm::shouldPrintAfterPass(StringRef PassID) { 1372f0de582SArthur Eubanks return PrintAfterAll || shouldPrintBeforeOrAfterPass(PassID, PrintAfter); 1382f0de582SArthur Eubanks } 1392f0de582SArthur Eubanks 1402f0de582SArthur Eubanks std::vector<std::string> llvm::printBeforePasses() { 1412f0de582SArthur Eubanks return std::vector<std::string>(PrintBefore); 1422f0de582SArthur Eubanks } 1432f0de582SArthur Eubanks 1442f0de582SArthur Eubanks std::vector<std::string> llvm::printAfterPasses() { 1452f0de582SArthur Eubanks return std::vector<std::string>(PrintAfter); 1462f0de582SArthur Eubanks } 1472f0de582SArthur Eubanks 1482f0de582SArthur Eubanks bool llvm::forcePrintModuleIR() { return PrintModuleScope; } 1492f0de582SArthur Eubanks 150*5b6a26ccSAkshay Deodhar bool llvm::forcePrintFuncIR() { return LoopPrintFuncScope; } 151*5b6a26ccSAkshay Deodhar 1528d95fd7eSFangrui Song bool llvm::isPassInPrintList(StringRef PassName) { 1538d95fd7eSFangrui Song static std::unordered_set<std::string> Set(FilterPasses.begin(), 1548d95fd7eSFangrui Song FilterPasses.end()); 1558d95fd7eSFangrui Song return Set.empty() || Set.count(std::string(PassName)); 1568d95fd7eSFangrui Song } 1578d95fd7eSFangrui Song 158f48931f3SFangrui Song bool llvm::isFilterPassesEmpty() { return FilterPasses.empty(); } 159f48931f3SFangrui Song 1602f0de582SArthur Eubanks bool llvm::isFunctionInPrintList(StringRef FunctionName) { 1612f0de582SArthur Eubanks static std::unordered_set<std::string> PrintFuncNames(PrintFuncsList.begin(), 1622f0de582SArthur Eubanks PrintFuncsList.end()); 1632f0de582SArthur Eubanks return PrintFuncNames.empty() || 1642f0de582SArthur Eubanks PrintFuncNames.count(std::string(FunctionName)); 1652f0de582SArthur Eubanks } 1662b70bebcSFangrui Song 16745553042SJamie Schmeiser std::error_code cleanUpTempFilesImpl(ArrayRef<std::string> FileName, 16845553042SJamie Schmeiser unsigned N) { 16945553042SJamie Schmeiser std::error_code RC; 17045553042SJamie Schmeiser for (unsigned I = 0; I < N; ++I) { 17145553042SJamie Schmeiser std::error_code EC = sys::fs::remove(FileName[I]); 17245553042SJamie Schmeiser if (EC) 17345553042SJamie Schmeiser RC = EC; 17445553042SJamie Schmeiser } 17545553042SJamie Schmeiser return RC; 17645553042SJamie Schmeiser } 17745553042SJamie Schmeiser 17845553042SJamie Schmeiser std::error_code llvm::prepareTempFiles(SmallVector<int> &FD, 17945553042SJamie Schmeiser ArrayRef<StringRef> SR, 18045553042SJamie Schmeiser SmallVector<std::string> &FileName) { 18145553042SJamie Schmeiser assert(FD.size() >= SR.size() && FileName.size() == FD.size() && 18245553042SJamie Schmeiser "Unexpected array sizes"); 18345553042SJamie Schmeiser std::error_code EC; 18445553042SJamie Schmeiser unsigned I = 0; 18545553042SJamie Schmeiser for (; I < FD.size(); ++I) { 18645553042SJamie Schmeiser if (FD[I] == -1) { 18745553042SJamie Schmeiser SmallVector<char, 200> SV; 18845553042SJamie Schmeiser EC = sys::fs::createTemporaryFile("tmpfile", "txt", FD[I], SV); 18945553042SJamie Schmeiser if (EC) 19045553042SJamie Schmeiser break; 19145553042SJamie Schmeiser FileName[I] = Twine(SV).str(); 19245553042SJamie Schmeiser } 19345553042SJamie Schmeiser if (I < SR.size()) { 19445553042SJamie Schmeiser EC = sys::fs::openFileForWrite(FileName[I], FD[I]); 19545553042SJamie Schmeiser if (EC) 19645553042SJamie Schmeiser break; 19745553042SJamie Schmeiser raw_fd_ostream OutStream(FD[I], /*shouldClose=*/true); 19845553042SJamie Schmeiser if (FD[I] == -1) { 19945553042SJamie Schmeiser EC = make_error_code(errc::io_error); 20045553042SJamie Schmeiser break; 20145553042SJamie Schmeiser } 20245553042SJamie Schmeiser OutStream << SR[I]; 20345553042SJamie Schmeiser } 20445553042SJamie Schmeiser } 20545553042SJamie Schmeiser if (EC && I > 0) 20645553042SJamie Schmeiser // clean up created temporary files 20745553042SJamie Schmeiser cleanUpTempFilesImpl(FileName, I); 20845553042SJamie Schmeiser return EC; 20945553042SJamie Schmeiser } 21045553042SJamie Schmeiser 21145553042SJamie Schmeiser std::error_code llvm::cleanUpTempFiles(ArrayRef<std::string> FileName) { 21245553042SJamie Schmeiser return cleanUpTempFilesImpl(FileName, FileName.size()); 21345553042SJamie Schmeiser } 21445553042SJamie Schmeiser 2152b70bebcSFangrui Song std::string llvm::doSystemDiff(StringRef Before, StringRef After, 2162b70bebcSFangrui Song StringRef OldLineFormat, StringRef NewLineFormat, 2172b70bebcSFangrui Song StringRef UnchangedLineFormat) { 2182b70bebcSFangrui Song // Store the 2 bodies into temporary files and call diff on them 2192b70bebcSFangrui Song // to get the body of the node. 22045553042SJamie Schmeiser static SmallVector<int> FD{-1, -1, -1}; 22145553042SJamie Schmeiser SmallVector<StringRef> SR{Before, After}; 22245553042SJamie Schmeiser static SmallVector<std::string> FileName{"", "", ""}; 22301b88dd6STakuya Shimizu if (prepareTempFiles(FD, SR, FileName)) 2242b70bebcSFangrui Song return "Unable to create temporary file."; 2252b70bebcSFangrui Song 2262b70bebcSFangrui Song static ErrorOr<std::string> DiffExe = sys::findProgramByName(DiffBinary); 2272b70bebcSFangrui Song if (!DiffExe) 2282b70bebcSFangrui Song return "Unable to find diff executable."; 2292b70bebcSFangrui Song 2302b70bebcSFangrui Song SmallString<128> OLF, NLF, ULF; 2312b70bebcSFangrui Song ("--old-line-format=" + OldLineFormat).toVector(OLF); 2322b70bebcSFangrui Song ("--new-line-format=" + NewLineFormat).toVector(NLF); 2332b70bebcSFangrui Song ("--unchanged-line-format=" + UnchangedLineFormat).toVector(ULF); 2342b70bebcSFangrui Song 2352b70bebcSFangrui Song StringRef Args[] = {DiffBinary, "-w", "-d", OLF, 2362b70bebcSFangrui Song NLF, ULF, FileName[0], FileName[1]}; 237e748db0fSMatt Arsenault std::optional<StringRef> Redirects[] = {std::nullopt, StringRef(FileName[2]), 238e748db0fSMatt Arsenault std::nullopt}; 239e748db0fSMatt Arsenault int Result = sys::ExecuteAndWait(*DiffExe, Args, std::nullopt, Redirects); 2402b70bebcSFangrui Song if (Result < 0) 2412b70bebcSFangrui Song return "Error executing system diff."; 2422b70bebcSFangrui Song std::string Diff; 2432b70bebcSFangrui Song auto B = MemoryBuffer::getFile(FileName[2]); 2442b70bebcSFangrui Song if (B && *B) 2452b70bebcSFangrui Song Diff = (*B)->getBuffer().str(); 2462b70bebcSFangrui Song else 2472b70bebcSFangrui Song return "Unable to read result."; 2482b70bebcSFangrui Song 24901b88dd6STakuya Shimizu if (cleanUpTempFiles(FileName)) 2502b70bebcSFangrui Song return "Unable to remove temporary file."; 25145553042SJamie Schmeiser 2522b70bebcSFangrui Song return Diff; 2532b70bebcSFangrui Song } 254