xref: /freebsd/contrib/llvm-project/llvm/tools/bugpoint/OptimizerDriver.cpp (revision 7a6dacaca14b62ca4b74406814becb87a3fefac0)
10b57cec5SDimitry Andric //===- OptimizerDriver.cpp - Allow BugPoint to run passes safely ----------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file defines an interface that allows bugpoint to run various passes
100b57cec5SDimitry Andric // without the threat of a buggy pass corrupting bugpoint (of course, bugpoint
110b57cec5SDimitry Andric // may have its own bugs, but that's another story...).  It achieves this by
120b57cec5SDimitry Andric // forking a copy of itself and having the child process do the optimizations.
130b57cec5SDimitry Andric // If this client dies, we can always fork a new one.  :)
140b57cec5SDimitry Andric //
150b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
160b57cec5SDimitry Andric 
170b57cec5SDimitry Andric #include "BugDriver.h"
180b57cec5SDimitry Andric #include "ToolRunner.h"
190b57cec5SDimitry Andric #include "llvm/Bitcode/BitcodeWriter.h"
200b57cec5SDimitry Andric #include "llvm/IR/DataLayout.h"
210b57cec5SDimitry Andric #include "llvm/IR/Module.h"
220b57cec5SDimitry Andric #include "llvm/Support/CommandLine.h"
230b57cec5SDimitry Andric #include "llvm/Support/Debug.h"
240b57cec5SDimitry Andric #include "llvm/Support/FileUtilities.h"
250b57cec5SDimitry Andric #include "llvm/Support/Path.h"
260b57cec5SDimitry Andric #include "llvm/Support/Program.h"
270b57cec5SDimitry Andric #include "llvm/Support/ToolOutputFile.h"
280b57cec5SDimitry Andric 
290b57cec5SDimitry Andric #define DONT_GET_PLUGIN_LOADER_OPTION
300b57cec5SDimitry Andric #include "llvm/Support/PluginLoader.h"
310b57cec5SDimitry Andric 
320b57cec5SDimitry Andric 
330b57cec5SDimitry Andric using namespace llvm;
340b57cec5SDimitry Andric 
350b57cec5SDimitry Andric #define DEBUG_TYPE "bugpoint"
360b57cec5SDimitry Andric 
370b57cec5SDimitry Andric namespace llvm {
380b57cec5SDimitry Andric extern cl::opt<std::string> OutputPrefix;
390b57cec5SDimitry Andric }
400b57cec5SDimitry Andric 
410b57cec5SDimitry Andric static cl::opt<bool> PreserveBitcodeUseListOrder(
420b57cec5SDimitry Andric     "preserve-bc-uselistorder",
430b57cec5SDimitry Andric     cl::desc("Preserve use-list order when writing LLVM bitcode."),
440b57cec5SDimitry Andric     cl::init(true), cl::Hidden);
450b57cec5SDimitry Andric 
460b57cec5SDimitry Andric static cl::opt<std::string>
470b57cec5SDimitry Andric     OptCmd("opt-command", cl::init(""),
480b57cec5SDimitry Andric            cl::desc("Path to opt. (default: search path "
490b57cec5SDimitry Andric                     "for 'opt'.)"));
500b57cec5SDimitry Andric 
510b57cec5SDimitry Andric /// This writes the current "Program" to the named bitcode file.  If an error
520b57cec5SDimitry Andric /// occurs, true is returned.
530b57cec5SDimitry Andric static bool writeProgramToFileAux(ToolOutputFile &Out, const Module &M) {
540b57cec5SDimitry Andric   WriteBitcodeToFile(M, Out.os(), PreserveBitcodeUseListOrder);
550b57cec5SDimitry Andric   Out.os().close();
560b57cec5SDimitry Andric   if (!Out.os().has_error()) {
570b57cec5SDimitry Andric     Out.keep();
580b57cec5SDimitry Andric     return false;
590b57cec5SDimitry Andric   }
600b57cec5SDimitry Andric   return true;
610b57cec5SDimitry Andric }
620b57cec5SDimitry Andric 
630b57cec5SDimitry Andric bool BugDriver::writeProgramToFile(const std::string &Filename, int FD,
640b57cec5SDimitry Andric                                    const Module &M) const {
650b57cec5SDimitry Andric   ToolOutputFile Out(Filename, FD);
660b57cec5SDimitry Andric   return writeProgramToFileAux(Out, M);
670b57cec5SDimitry Andric }
680b57cec5SDimitry Andric 
690b57cec5SDimitry Andric bool BugDriver::writeProgramToFile(int FD, const Module &M) const {
700b57cec5SDimitry Andric   raw_fd_ostream OS(FD, /*shouldClose*/ false);
710b57cec5SDimitry Andric   WriteBitcodeToFile(M, OS, PreserveBitcodeUseListOrder);
720b57cec5SDimitry Andric   OS.flush();
730b57cec5SDimitry Andric   if (!OS.has_error())
740b57cec5SDimitry Andric     return false;
750b57cec5SDimitry Andric   OS.clear_error();
760b57cec5SDimitry Andric   return true;
770b57cec5SDimitry Andric }
780b57cec5SDimitry Andric 
790b57cec5SDimitry Andric bool BugDriver::writeProgramToFile(const std::string &Filename,
800b57cec5SDimitry Andric                                    const Module &M) const {
810b57cec5SDimitry Andric   std::error_code EC;
828bcb0991SDimitry Andric   ToolOutputFile Out(Filename, EC, sys::fs::OF_None);
830b57cec5SDimitry Andric   if (!EC)
840b57cec5SDimitry Andric     return writeProgramToFileAux(Out, M);
850b57cec5SDimitry Andric   return true;
860b57cec5SDimitry Andric }
870b57cec5SDimitry Andric 
880b57cec5SDimitry Andric /// This function is used to output the current Program to a file named
890b57cec5SDimitry Andric /// "bugpoint-ID.bc".
900b57cec5SDimitry Andric void BugDriver::EmitProgressBitcode(const Module &M, const std::string &ID,
910b57cec5SDimitry Andric                                     bool NoFlyer) const {
920b57cec5SDimitry Andric   // Output the input to the current pass to a bitcode file, emit a message
930b57cec5SDimitry Andric   // telling the user how to reproduce it: opt -foo blah.bc
940b57cec5SDimitry Andric   //
950b57cec5SDimitry Andric   std::string Filename = OutputPrefix + "-" + ID + ".bc";
960b57cec5SDimitry Andric   if (writeProgramToFile(Filename, M)) {
970b57cec5SDimitry Andric     errs() << "Error opening file '" << Filename << "' for writing!\n";
980b57cec5SDimitry Andric     return;
990b57cec5SDimitry Andric   }
1000b57cec5SDimitry Andric 
1010b57cec5SDimitry Andric   outs() << "Emitted bitcode to '" << Filename << "'\n";
1020b57cec5SDimitry Andric   if (NoFlyer || PassesToRun.empty())
1030b57cec5SDimitry Andric     return;
1040b57cec5SDimitry Andric   outs() << "\n*** You can reproduce the problem with: ";
1050b57cec5SDimitry Andric   if (UseValgrind)
1060b57cec5SDimitry Andric     outs() << "valgrind ";
1070b57cec5SDimitry Andric   outs() << "opt " << Filename;
1080b57cec5SDimitry Andric   for (unsigned i = 0, e = PluginLoader::getNumPlugins(); i != e; ++i) {
1090b57cec5SDimitry Andric     outs() << " -load " << PluginLoader::getPlugin(i);
1100b57cec5SDimitry Andric   }
1110b57cec5SDimitry Andric   outs() << " " << getPassesString(PassesToRun) << "\n";
1120b57cec5SDimitry Andric }
1130b57cec5SDimitry Andric 
1140b57cec5SDimitry Andric cl::opt<bool> SilencePasses(
1150b57cec5SDimitry Andric     "silence-passes",
1160b57cec5SDimitry Andric     cl::desc("Suppress output of running passes (both stdout and stderr)"));
1170b57cec5SDimitry Andric 
1180b57cec5SDimitry Andric static cl::list<std::string> OptArgs("opt-args", cl::Positional,
1190b57cec5SDimitry Andric                                      cl::desc("<opt arguments>..."),
12081ad6265SDimitry Andric                                      cl::PositionalEatsArgs);
1210b57cec5SDimitry Andric 
1220b57cec5SDimitry Andric /// runPasses - Run the specified passes on Program, outputting a bitcode file
1230b57cec5SDimitry Andric /// and writing the filename into OutputFile if successful.  If the
1240b57cec5SDimitry Andric /// optimizations fail for some reason (optimizer crashes), return true,
1250b57cec5SDimitry Andric /// otherwise return false.  If DeleteOutput is set to true, the bitcode is
1260b57cec5SDimitry Andric /// deleted on success, and the filename string is undefined.  This prints to
1270b57cec5SDimitry Andric /// outs() a single line message indicating whether compilation was successful
1280b57cec5SDimitry Andric /// or failed.
1290b57cec5SDimitry Andric ///
1300b57cec5SDimitry Andric bool BugDriver::runPasses(Module &Program,
1310b57cec5SDimitry Andric                           const std::vector<std::string> &Passes,
1320b57cec5SDimitry Andric                           std::string &OutputFilename, bool DeleteOutput,
1338bcb0991SDimitry Andric                           bool Quiet, ArrayRef<std::string> ExtraArgs) const {
1340b57cec5SDimitry Andric   // setup the output file name
1350b57cec5SDimitry Andric   outs().flush();
1360b57cec5SDimitry Andric   SmallString<128> UniqueFilename;
1370b57cec5SDimitry Andric   std::error_code EC = sys::fs::createUniqueFile(
1380b57cec5SDimitry Andric       OutputPrefix + "-output-%%%%%%%.bc", UniqueFilename);
1390b57cec5SDimitry Andric   if (EC) {
1400b57cec5SDimitry Andric     errs() << getToolName()
1410b57cec5SDimitry Andric            << ": Error making unique filename: " << EC.message() << "\n";
14204eeddc0SDimitry Andric     return true;
1430b57cec5SDimitry Andric   }
144*7a6dacacSDimitry Andric   OutputFilename = std::string(UniqueFilename);
1450b57cec5SDimitry Andric 
1460b57cec5SDimitry Andric   // set up the input file name
1470b57cec5SDimitry Andric   Expected<sys::fs::TempFile> Temp =
1480b57cec5SDimitry Andric       sys::fs::TempFile::create(OutputPrefix + "-input-%%%%%%%.bc");
1490b57cec5SDimitry Andric   if (!Temp) {
1500b57cec5SDimitry Andric     errs() << getToolName()
1510b57cec5SDimitry Andric            << ": Error making unique filename: " << toString(Temp.takeError())
1520b57cec5SDimitry Andric            << "\n";
15304eeddc0SDimitry Andric     return true;
1540b57cec5SDimitry Andric   }
1550b57cec5SDimitry Andric   DiscardTemp Discard{*Temp};
1560b57cec5SDimitry Andric   raw_fd_ostream OS(Temp->FD, /*shouldClose*/ false);
1570b57cec5SDimitry Andric 
1580b57cec5SDimitry Andric   WriteBitcodeToFile(Program, OS, PreserveBitcodeUseListOrder);
1590b57cec5SDimitry Andric   OS.flush();
1600b57cec5SDimitry Andric   if (OS.has_error()) {
1610b57cec5SDimitry Andric     errs() << "Error writing bitcode file: " << Temp->TmpName << "\n";
1620b57cec5SDimitry Andric     OS.clear_error();
16304eeddc0SDimitry Andric     return true;
1640b57cec5SDimitry Andric   }
1650b57cec5SDimitry Andric 
1660b57cec5SDimitry Andric   std::string tool = OptCmd;
1670b57cec5SDimitry Andric   if (OptCmd.empty()) {
1680b57cec5SDimitry Andric     if (ErrorOr<std::string> Path =
1690b57cec5SDimitry Andric             FindProgramByName("opt", getToolName(), &OutputPrefix))
1700b57cec5SDimitry Andric       tool = *Path;
1710b57cec5SDimitry Andric     else
1720b57cec5SDimitry Andric       errs() << Path.getError().message() << "\n";
1730b57cec5SDimitry Andric   }
1740b57cec5SDimitry Andric   if (tool.empty()) {
1750b57cec5SDimitry Andric     errs() << "Cannot find `opt' in PATH!\n";
17604eeddc0SDimitry Andric     return true;
1770b57cec5SDimitry Andric   }
1780b57cec5SDimitry Andric   if (!sys::fs::exists(tool)) {
1790b57cec5SDimitry Andric     errs() << "Specified `opt' binary does not exist: " << tool << "\n";
18004eeddc0SDimitry Andric     return true;
1810b57cec5SDimitry Andric   }
1820b57cec5SDimitry Andric 
1830b57cec5SDimitry Andric   std::string Prog;
1840b57cec5SDimitry Andric   if (UseValgrind) {
1850b57cec5SDimitry Andric     if (ErrorOr<std::string> Path = sys::findProgramByName("valgrind"))
1860b57cec5SDimitry Andric       Prog = *Path;
1870b57cec5SDimitry Andric     else
1880b57cec5SDimitry Andric       errs() << Path.getError().message() << "\n";
1890b57cec5SDimitry Andric   } else
1900b57cec5SDimitry Andric     Prog = tool;
1910b57cec5SDimitry Andric   if (Prog.empty()) {
1920b57cec5SDimitry Andric     errs() << "Cannot find `valgrind' in PATH!\n";
19304eeddc0SDimitry Andric     return true;
1940b57cec5SDimitry Andric   }
1950b57cec5SDimitry Andric 
1960b57cec5SDimitry Andric   // setup the child process' arguments
1970b57cec5SDimitry Andric   SmallVector<StringRef, 8> Args;
1980b57cec5SDimitry Andric   if (UseValgrind) {
1990b57cec5SDimitry Andric     Args.push_back("valgrind");
2000b57cec5SDimitry Andric     Args.push_back("--error-exitcode=1");
2010b57cec5SDimitry Andric     Args.push_back("-q");
2020b57cec5SDimitry Andric     Args.push_back(tool);
2030b57cec5SDimitry Andric   } else
2040b57cec5SDimitry Andric     Args.push_back(tool);
2050b57cec5SDimitry Andric 
2060b57cec5SDimitry Andric   for (unsigned i = 0, e = OptArgs.size(); i != e; ++i)
2070b57cec5SDimitry Andric     Args.push_back(OptArgs[i]);
208e8d8bef9SDimitry Andric   // Pin to legacy PM since bugpoint has lots of infra and hacks revolving
209e8d8bef9SDimitry Andric   // around the legacy PM.
21006c3fb27SDimitry Andric   Args.push_back("-bugpoint-enable-legacy-pm");
2110b57cec5SDimitry Andric   Args.push_back("-disable-symbolication");
2120b57cec5SDimitry Andric   Args.push_back("-o");
2130b57cec5SDimitry Andric   Args.push_back(OutputFilename);
2140b57cec5SDimitry Andric   std::vector<std::string> pass_args;
2150b57cec5SDimitry Andric   for (unsigned i = 0, e = PluginLoader::getNumPlugins(); i != e; ++i) {
2160b57cec5SDimitry Andric     pass_args.push_back(std::string("-load"));
2170b57cec5SDimitry Andric     pass_args.push_back(PluginLoader::getPlugin(i));
2180b57cec5SDimitry Andric   }
2190b57cec5SDimitry Andric   for (std::vector<std::string>::const_iterator I = Passes.begin(),
2200b57cec5SDimitry Andric                                                 E = Passes.end();
2210b57cec5SDimitry Andric        I != E; ++I)
2220b57cec5SDimitry Andric     pass_args.push_back(std::string("-") + (*I));
2230b57cec5SDimitry Andric   for (std::vector<std::string>::const_iterator I = pass_args.begin(),
2240b57cec5SDimitry Andric                                                 E = pass_args.end();
2250b57cec5SDimitry Andric        I != E; ++I)
226349cc55cSDimitry Andric     Args.push_back(*I);
227349cc55cSDimitry Andric   Args.push_back(Temp->TmpName);
2288bcb0991SDimitry Andric   Args.append(ExtraArgs.begin(), ExtraArgs.end());
2290b57cec5SDimitry Andric 
2300b57cec5SDimitry Andric   LLVM_DEBUG(errs() << "\nAbout to run:\t";
2310b57cec5SDimitry Andric              for (unsigned i = 0, e = Args.size() - 1; i != e; ++i) errs()
2320b57cec5SDimitry Andric              << " " << Args[i];
2330b57cec5SDimitry Andric              errs() << "\n";);
2340b57cec5SDimitry Andric 
235bdd1243dSDimitry Andric   std::optional<StringRef> Redirects[3] = {std::nullopt, std::nullopt,
236bdd1243dSDimitry Andric                                            std::nullopt};
2370b57cec5SDimitry Andric   // Redirect stdout and stderr to nowhere if SilencePasses is given.
2380b57cec5SDimitry Andric   if (SilencePasses) {
2390b57cec5SDimitry Andric     Redirects[1] = "";
2400b57cec5SDimitry Andric     Redirects[2] = "";
2410b57cec5SDimitry Andric   }
2420b57cec5SDimitry Andric 
2430b57cec5SDimitry Andric   std::string ErrMsg;
244bdd1243dSDimitry Andric   int result = sys::ExecuteAndWait(Prog, Args, std::nullopt, Redirects, Timeout,
2450b57cec5SDimitry Andric                                    MemoryLimit, &ErrMsg);
2460b57cec5SDimitry Andric 
2470b57cec5SDimitry Andric   // If we are supposed to delete the bitcode file or if the passes crashed,
2480b57cec5SDimitry Andric   // remove it now.  This may fail if the file was never created, but that's ok.
2490b57cec5SDimitry Andric   if (DeleteOutput || result != 0)
2500b57cec5SDimitry Andric     sys::fs::remove(OutputFilename);
2510b57cec5SDimitry Andric 
2520b57cec5SDimitry Andric   if (!Quiet) {
2530b57cec5SDimitry Andric     if (result == 0)
2540b57cec5SDimitry Andric       outs() << "Success!\n";
2550b57cec5SDimitry Andric     else if (result > 0)
2560b57cec5SDimitry Andric       outs() << "Exited with error code '" << result << "'\n";
2570b57cec5SDimitry Andric     else if (result < 0) {
2580b57cec5SDimitry Andric       if (result == -1)
2590b57cec5SDimitry Andric         outs() << "Execute failed: " << ErrMsg << "\n";
2600b57cec5SDimitry Andric       else
2610b57cec5SDimitry Andric         outs() << "Crashed: " << ErrMsg << "\n";
2620b57cec5SDimitry Andric     }
2630b57cec5SDimitry Andric     if (result & 0x01000000)
2640b57cec5SDimitry Andric       outs() << "Dumped core\n";
2650b57cec5SDimitry Andric   }
2660b57cec5SDimitry Andric 
2670b57cec5SDimitry Andric   // Was the child successful?
2680b57cec5SDimitry Andric   return result != 0;
2690b57cec5SDimitry Andric }
2700b57cec5SDimitry Andric 
2710b57cec5SDimitry Andric std::unique_ptr<Module>
2720b57cec5SDimitry Andric BugDriver::runPassesOn(Module *M, const std::vector<std::string> &Passes,
2738bcb0991SDimitry Andric                        ArrayRef<std::string> ExtraArgs) {
2740b57cec5SDimitry Andric   std::string BitcodeResult;
2750b57cec5SDimitry Andric   if (runPasses(*M, Passes, BitcodeResult, false /*delete*/, true /*quiet*/,
2768bcb0991SDimitry Andric                 ExtraArgs)) {
2770b57cec5SDimitry Andric     return nullptr;
2780b57cec5SDimitry Andric   }
2790b57cec5SDimitry Andric 
2800b57cec5SDimitry Andric   std::unique_ptr<Module> Ret = parseInputFile(BitcodeResult, Context);
2810b57cec5SDimitry Andric   if (!Ret) {
2820b57cec5SDimitry Andric     errs() << getToolName() << ": Error reading bitcode file '" << BitcodeResult
2830b57cec5SDimitry Andric            << "'!\n";
2840b57cec5SDimitry Andric     exit(1);
2850b57cec5SDimitry Andric   }
2860b57cec5SDimitry Andric   sys::fs::remove(BitcodeResult);
2870b57cec5SDimitry Andric   return Ret;
2880b57cec5SDimitry Andric }
289