xref: /openbsd-src/gnu/llvm/llvm/tools/bugpoint/OptimizerDriver.cpp (revision d415bd752c734aee168c4ee86ff32e8cc249eb16)
109467b48Spatrick //===- OptimizerDriver.cpp - Allow BugPoint to run passes safely ----------===//
209467b48Spatrick //
309467b48Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
409467b48Spatrick // See https://llvm.org/LICENSE.txt for license information.
509467b48Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
609467b48Spatrick //
709467b48Spatrick //===----------------------------------------------------------------------===//
809467b48Spatrick //
909467b48Spatrick // This file defines an interface that allows bugpoint to run various passes
1009467b48Spatrick // without the threat of a buggy pass corrupting bugpoint (of course, bugpoint
1109467b48Spatrick // may have its own bugs, but that's another story...).  It achieves this by
1209467b48Spatrick // forking a copy of itself and having the child process do the optimizations.
1309467b48Spatrick // If this client dies, we can always fork a new one.  :)
1409467b48Spatrick //
1509467b48Spatrick //===----------------------------------------------------------------------===//
1609467b48Spatrick 
1709467b48Spatrick #include "BugDriver.h"
1809467b48Spatrick #include "ToolRunner.h"
1909467b48Spatrick #include "llvm/Bitcode/BitcodeWriter.h"
2009467b48Spatrick #include "llvm/IR/DataLayout.h"
2109467b48Spatrick #include "llvm/IR/Module.h"
2209467b48Spatrick #include "llvm/Support/CommandLine.h"
2309467b48Spatrick #include "llvm/Support/Debug.h"
2409467b48Spatrick #include "llvm/Support/FileUtilities.h"
2509467b48Spatrick #include "llvm/Support/Path.h"
2609467b48Spatrick #include "llvm/Support/Program.h"
2709467b48Spatrick #include "llvm/Support/ToolOutputFile.h"
2809467b48Spatrick 
2909467b48Spatrick #define DONT_GET_PLUGIN_LOADER_OPTION
3009467b48Spatrick #include "llvm/Support/PluginLoader.h"
3109467b48Spatrick 
3209467b48Spatrick 
3309467b48Spatrick using namespace llvm;
3409467b48Spatrick 
3509467b48Spatrick #define DEBUG_TYPE "bugpoint"
3609467b48Spatrick 
3709467b48Spatrick namespace llvm {
3809467b48Spatrick extern cl::opt<std::string> OutputPrefix;
3909467b48Spatrick }
4009467b48Spatrick 
4109467b48Spatrick static cl::opt<bool> PreserveBitcodeUseListOrder(
4209467b48Spatrick     "preserve-bc-uselistorder",
4309467b48Spatrick     cl::desc("Preserve use-list order when writing LLVM bitcode."),
4409467b48Spatrick     cl::init(true), cl::Hidden);
4509467b48Spatrick 
4609467b48Spatrick static cl::opt<std::string>
4709467b48Spatrick     OptCmd("opt-command", cl::init(""),
4809467b48Spatrick            cl::desc("Path to opt. (default: search path "
4909467b48Spatrick                     "for 'opt'.)"));
5009467b48Spatrick 
5109467b48Spatrick /// This writes the current "Program" to the named bitcode file.  If an error
5209467b48Spatrick /// occurs, true is returned.
writeProgramToFileAux(ToolOutputFile & Out,const Module & M)5309467b48Spatrick static bool writeProgramToFileAux(ToolOutputFile &Out, const Module &M) {
5409467b48Spatrick   WriteBitcodeToFile(M, Out.os(), PreserveBitcodeUseListOrder);
5509467b48Spatrick   Out.os().close();
5609467b48Spatrick   if (!Out.os().has_error()) {
5709467b48Spatrick     Out.keep();
5809467b48Spatrick     return false;
5909467b48Spatrick   }
6009467b48Spatrick   return true;
6109467b48Spatrick }
6209467b48Spatrick 
writeProgramToFile(const std::string & Filename,int FD,const Module & M) const6309467b48Spatrick bool BugDriver::writeProgramToFile(const std::string &Filename, int FD,
6409467b48Spatrick                                    const Module &M) const {
6509467b48Spatrick   ToolOutputFile Out(Filename, FD);
6609467b48Spatrick   return writeProgramToFileAux(Out, M);
6709467b48Spatrick }
6809467b48Spatrick 
writeProgramToFile(int FD,const Module & M) const6909467b48Spatrick bool BugDriver::writeProgramToFile(int FD, const Module &M) const {
7009467b48Spatrick   raw_fd_ostream OS(FD, /*shouldClose*/ false);
7109467b48Spatrick   WriteBitcodeToFile(M, OS, PreserveBitcodeUseListOrder);
7209467b48Spatrick   OS.flush();
7309467b48Spatrick   if (!OS.has_error())
7409467b48Spatrick     return false;
7509467b48Spatrick   OS.clear_error();
7609467b48Spatrick   return true;
7709467b48Spatrick }
7809467b48Spatrick 
writeProgramToFile(const std::string & Filename,const Module & M) const7909467b48Spatrick bool BugDriver::writeProgramToFile(const std::string &Filename,
8009467b48Spatrick                                    const Module &M) const {
8109467b48Spatrick   std::error_code EC;
8209467b48Spatrick   ToolOutputFile Out(Filename, EC, sys::fs::OF_None);
8309467b48Spatrick   if (!EC)
8409467b48Spatrick     return writeProgramToFileAux(Out, M);
8509467b48Spatrick   return true;
8609467b48Spatrick }
8709467b48Spatrick 
8809467b48Spatrick /// This function is used to output the current Program to a file named
8909467b48Spatrick /// "bugpoint-ID.bc".
EmitProgressBitcode(const Module & M,const std::string & ID,bool NoFlyer) const9009467b48Spatrick void BugDriver::EmitProgressBitcode(const Module &M, const std::string &ID,
9109467b48Spatrick                                     bool NoFlyer) const {
9209467b48Spatrick   // Output the input to the current pass to a bitcode file, emit a message
9309467b48Spatrick   // telling the user how to reproduce it: opt -foo blah.bc
9409467b48Spatrick   //
9509467b48Spatrick   std::string Filename = OutputPrefix + "-" + ID + ".bc";
9609467b48Spatrick   if (writeProgramToFile(Filename, M)) {
9709467b48Spatrick     errs() << "Error opening file '" << Filename << "' for writing!\n";
9809467b48Spatrick     return;
9909467b48Spatrick   }
10009467b48Spatrick 
10109467b48Spatrick   outs() << "Emitted bitcode to '" << Filename << "'\n";
10209467b48Spatrick   if (NoFlyer || PassesToRun.empty())
10309467b48Spatrick     return;
10409467b48Spatrick   outs() << "\n*** You can reproduce the problem with: ";
10509467b48Spatrick   if (UseValgrind)
10609467b48Spatrick     outs() << "valgrind ";
10709467b48Spatrick   outs() << "opt " << Filename;
10809467b48Spatrick   for (unsigned i = 0, e = PluginLoader::getNumPlugins(); i != e; ++i) {
10909467b48Spatrick     outs() << " -load " << PluginLoader::getPlugin(i);
11009467b48Spatrick   }
11109467b48Spatrick   outs() << " " << getPassesString(PassesToRun) << "\n";
11209467b48Spatrick }
11309467b48Spatrick 
11409467b48Spatrick cl::opt<bool> SilencePasses(
11509467b48Spatrick     "silence-passes",
11609467b48Spatrick     cl::desc("Suppress output of running passes (both stdout and stderr)"));
11709467b48Spatrick 
11809467b48Spatrick static cl::list<std::string> OptArgs("opt-args", cl::Positional,
11909467b48Spatrick                                      cl::desc("<opt arguments>..."),
120*d415bd75Srobert                                      cl::PositionalEatsArgs);
12109467b48Spatrick 
12209467b48Spatrick /// runPasses - Run the specified passes on Program, outputting a bitcode file
12309467b48Spatrick /// and writing the filename into OutputFile if successful.  If the
12409467b48Spatrick /// optimizations fail for some reason (optimizer crashes), return true,
12509467b48Spatrick /// otherwise return false.  If DeleteOutput is set to true, the bitcode is
12609467b48Spatrick /// deleted on success, and the filename string is undefined.  This prints to
12709467b48Spatrick /// outs() a single line message indicating whether compilation was successful
12809467b48Spatrick /// or failed.
12909467b48Spatrick ///
runPasses(Module & Program,const std::vector<std::string> & Passes,std::string & OutputFilename,bool DeleteOutput,bool Quiet,ArrayRef<std::string> ExtraArgs) const13009467b48Spatrick bool BugDriver::runPasses(Module &Program,
13109467b48Spatrick                           const std::vector<std::string> &Passes,
13209467b48Spatrick                           std::string &OutputFilename, bool DeleteOutput,
13309467b48Spatrick                           bool Quiet, ArrayRef<std::string> ExtraArgs) const {
13409467b48Spatrick   // setup the output file name
13509467b48Spatrick   outs().flush();
13609467b48Spatrick   SmallString<128> UniqueFilename;
13709467b48Spatrick   std::error_code EC = sys::fs::createUniqueFile(
13809467b48Spatrick       OutputPrefix + "-output-%%%%%%%.bc", UniqueFilename);
13909467b48Spatrick   if (EC) {
14009467b48Spatrick     errs() << getToolName()
14109467b48Spatrick            << ": Error making unique filename: " << EC.message() << "\n";
142*d415bd75Srobert     return true;
14309467b48Spatrick   }
144097a140dSpatrick   OutputFilename = std::string(UniqueFilename.str());
14509467b48Spatrick 
14609467b48Spatrick   // set up the input file name
14709467b48Spatrick   Expected<sys::fs::TempFile> Temp =
14809467b48Spatrick       sys::fs::TempFile::create(OutputPrefix + "-input-%%%%%%%.bc");
14909467b48Spatrick   if (!Temp) {
15009467b48Spatrick     errs() << getToolName()
15109467b48Spatrick            << ": Error making unique filename: " << toString(Temp.takeError())
15209467b48Spatrick            << "\n";
153*d415bd75Srobert     return true;
15409467b48Spatrick   }
15509467b48Spatrick   DiscardTemp Discard{*Temp};
15609467b48Spatrick   raw_fd_ostream OS(Temp->FD, /*shouldClose*/ false);
15709467b48Spatrick 
15809467b48Spatrick   WriteBitcodeToFile(Program, OS, PreserveBitcodeUseListOrder);
15909467b48Spatrick   OS.flush();
16009467b48Spatrick   if (OS.has_error()) {
16109467b48Spatrick     errs() << "Error writing bitcode file: " << Temp->TmpName << "\n";
16209467b48Spatrick     OS.clear_error();
163*d415bd75Srobert     return true;
16409467b48Spatrick   }
16509467b48Spatrick 
16609467b48Spatrick   std::string tool = OptCmd;
16709467b48Spatrick   if (OptCmd.empty()) {
16809467b48Spatrick     if (ErrorOr<std::string> Path =
16909467b48Spatrick             FindProgramByName("opt", getToolName(), &OutputPrefix))
17009467b48Spatrick       tool = *Path;
17109467b48Spatrick     else
17209467b48Spatrick       errs() << Path.getError().message() << "\n";
17309467b48Spatrick   }
17409467b48Spatrick   if (tool.empty()) {
17509467b48Spatrick     errs() << "Cannot find `opt' in PATH!\n";
176*d415bd75Srobert     return true;
17709467b48Spatrick   }
17809467b48Spatrick   if (!sys::fs::exists(tool)) {
17909467b48Spatrick     errs() << "Specified `opt' binary does not exist: " << tool << "\n";
180*d415bd75Srobert     return true;
18109467b48Spatrick   }
18209467b48Spatrick 
18309467b48Spatrick   std::string Prog;
18409467b48Spatrick   if (UseValgrind) {
18509467b48Spatrick     if (ErrorOr<std::string> Path = sys::findProgramByName("valgrind"))
18609467b48Spatrick       Prog = *Path;
18709467b48Spatrick     else
18809467b48Spatrick       errs() << Path.getError().message() << "\n";
18909467b48Spatrick   } else
19009467b48Spatrick     Prog = tool;
19109467b48Spatrick   if (Prog.empty()) {
19209467b48Spatrick     errs() << "Cannot find `valgrind' in PATH!\n";
193*d415bd75Srobert     return true;
19409467b48Spatrick   }
19509467b48Spatrick 
19609467b48Spatrick   // setup the child process' arguments
19709467b48Spatrick   SmallVector<StringRef, 8> Args;
19809467b48Spatrick   if (UseValgrind) {
19909467b48Spatrick     Args.push_back("valgrind");
20009467b48Spatrick     Args.push_back("--error-exitcode=1");
20109467b48Spatrick     Args.push_back("-q");
20209467b48Spatrick     Args.push_back(tool);
20309467b48Spatrick   } else
20409467b48Spatrick     Args.push_back(tool);
20509467b48Spatrick 
20609467b48Spatrick   for (unsigned i = 0, e = OptArgs.size(); i != e; ++i)
20709467b48Spatrick     Args.push_back(OptArgs[i]);
20873471bf0Spatrick   // Pin to legacy PM since bugpoint has lots of infra and hacks revolving
20973471bf0Spatrick   // around the legacy PM.
21073471bf0Spatrick   Args.push_back("-enable-new-pm=0");
21109467b48Spatrick   Args.push_back("-disable-symbolication");
21209467b48Spatrick   Args.push_back("-o");
21309467b48Spatrick   Args.push_back(OutputFilename);
21409467b48Spatrick   std::vector<std::string> pass_args;
21509467b48Spatrick   for (unsigned i = 0, e = PluginLoader::getNumPlugins(); i != e; ++i) {
21609467b48Spatrick     pass_args.push_back(std::string("-load"));
21709467b48Spatrick     pass_args.push_back(PluginLoader::getPlugin(i));
21809467b48Spatrick   }
21909467b48Spatrick   for (std::vector<std::string>::const_iterator I = Passes.begin(),
22009467b48Spatrick                                                 E = Passes.end();
22109467b48Spatrick        I != E; ++I)
22209467b48Spatrick     pass_args.push_back(std::string("-") + (*I));
22309467b48Spatrick   for (std::vector<std::string>::const_iterator I = pass_args.begin(),
22409467b48Spatrick                                                 E = pass_args.end();
22509467b48Spatrick        I != E; ++I)
226*d415bd75Srobert     Args.push_back(*I);
227*d415bd75Srobert   Args.push_back(Temp->TmpName);
22809467b48Spatrick   Args.append(ExtraArgs.begin(), ExtraArgs.end());
22909467b48Spatrick 
23009467b48Spatrick   LLVM_DEBUG(errs() << "\nAbout to run:\t";
23109467b48Spatrick              for (unsigned i = 0, e = Args.size() - 1; i != e; ++i) errs()
23209467b48Spatrick              << " " << Args[i];
23309467b48Spatrick              errs() << "\n";);
23409467b48Spatrick 
235*d415bd75Srobert   std::optional<StringRef> Redirects[3] = {std::nullopt, std::nullopt,
236*d415bd75Srobert                                            std::nullopt};
23709467b48Spatrick   // Redirect stdout and stderr to nowhere if SilencePasses is given.
23809467b48Spatrick   if (SilencePasses) {
23909467b48Spatrick     Redirects[1] = "";
24009467b48Spatrick     Redirects[2] = "";
24109467b48Spatrick   }
24209467b48Spatrick 
24309467b48Spatrick   std::string ErrMsg;
244*d415bd75Srobert   int result = sys::ExecuteAndWait(Prog, Args, std::nullopt, Redirects, Timeout,
24509467b48Spatrick                                    MemoryLimit, &ErrMsg);
24609467b48Spatrick 
24709467b48Spatrick   // If we are supposed to delete the bitcode file or if the passes crashed,
24809467b48Spatrick   // remove it now.  This may fail if the file was never created, but that's ok.
24909467b48Spatrick   if (DeleteOutput || result != 0)
25009467b48Spatrick     sys::fs::remove(OutputFilename);
25109467b48Spatrick 
25209467b48Spatrick   if (!Quiet) {
25309467b48Spatrick     if (result == 0)
25409467b48Spatrick       outs() << "Success!\n";
25509467b48Spatrick     else if (result > 0)
25609467b48Spatrick       outs() << "Exited with error code '" << result << "'\n";
25709467b48Spatrick     else if (result < 0) {
25809467b48Spatrick       if (result == -1)
25909467b48Spatrick         outs() << "Execute failed: " << ErrMsg << "\n";
26009467b48Spatrick       else
26109467b48Spatrick         outs() << "Crashed: " << ErrMsg << "\n";
26209467b48Spatrick     }
26309467b48Spatrick     if (result & 0x01000000)
26409467b48Spatrick       outs() << "Dumped core\n";
26509467b48Spatrick   }
26609467b48Spatrick 
26709467b48Spatrick   // Was the child successful?
26809467b48Spatrick   return result != 0;
26909467b48Spatrick }
27009467b48Spatrick 
27109467b48Spatrick std::unique_ptr<Module>
runPassesOn(Module * M,const std::vector<std::string> & Passes,ArrayRef<std::string> ExtraArgs)27209467b48Spatrick BugDriver::runPassesOn(Module *M, const std::vector<std::string> &Passes,
27309467b48Spatrick                        ArrayRef<std::string> ExtraArgs) {
27409467b48Spatrick   std::string BitcodeResult;
27509467b48Spatrick   if (runPasses(*M, Passes, BitcodeResult, false /*delete*/, true /*quiet*/,
27609467b48Spatrick                 ExtraArgs)) {
27709467b48Spatrick     return nullptr;
27809467b48Spatrick   }
27909467b48Spatrick 
28009467b48Spatrick   std::unique_ptr<Module> Ret = parseInputFile(BitcodeResult, Context);
28109467b48Spatrick   if (!Ret) {
28209467b48Spatrick     errs() << getToolName() << ": Error reading bitcode file '" << BitcodeResult
28309467b48Spatrick            << "'!\n";
28409467b48Spatrick     exit(1);
28509467b48Spatrick   }
28609467b48Spatrick   sys::fs::remove(BitcodeResult);
28709467b48Spatrick   return Ret;
28809467b48Spatrick }
289