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.
writeProgramToFileAux(ToolOutputFile & Out,const Module & M)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
writeProgramToFile(const std::string & Filename,int FD,const Module & M) const630b57cec5SDimitry 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
writeProgramToFile(int FD,const Module & M) const690b57cec5SDimitry 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
writeProgramToFile(const std::string & Filename,const Module & M) const790b57cec5SDimitry 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".
EmitProgressBitcode(const Module & M,const std::string & ID,bool NoFlyer) const900b57cec5SDimitry 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 ///
runPasses(Module & Program,const std::vector<std::string> & Passes,std::string & OutputFilename,bool DeleteOutput,bool Quiet,ArrayRef<std::string> ExtraArgs) const1300b57cec5SDimitry 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>
runPassesOn(Module * M,const std::vector<std::string> & Passes,ArrayRef<std::string> ExtraArgs)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