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