279 lines · cpp
1//===- OptimizerDriver.cpp - Allow BugPoint to run passes safely ----------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file defines an interface that allows bugpoint to run various passes10// without the threat of a buggy pass corrupting bugpoint (of course, bugpoint11// may have its own bugs, but that's another story...). It achieves this by12// forking a copy of itself and having the child process do the optimizations.13// If this client dies, we can always fork a new one. :)14//15//===----------------------------------------------------------------------===//16 17#include "BugDriver.h"18#include "ToolRunner.h"19#include "llvm/Bitcode/BitcodeWriter.h"20#include "llvm/IR/DataLayout.h"21#include "llvm/IR/Module.h"22#include "llvm/Support/CommandLine.h"23#include "llvm/Support/Debug.h"24#include "llvm/Support/FileUtilities.h"25#include "llvm/Support/Path.h"26#include "llvm/Support/Program.h"27#include "llvm/Support/ToolOutputFile.h"28 29#define DONT_GET_PLUGIN_LOADER_OPTION30#include "llvm/Support/PluginLoader.h"31 32 33using namespace llvm;34 35#define DEBUG_TYPE "bugpoint"36 37static cl::opt<std::string>38 OptCmd("opt-command", cl::init(""),39 cl::desc("Path to opt. (default: search path "40 "for 'opt'.)"));41 42/// This writes the current "Program" to the named bitcode file. If an error43/// occurs, true is returned.44static bool writeProgramToFileAux(ToolOutputFile &Out, const Module &M) {45 WriteBitcodeToFile(M, Out.os(), /* ShouldPreserveUseListOrder */ true);46 Out.os().close();47 if (!Out.os().has_error()) {48 Out.keep();49 return false;50 }51 return true;52}53 54bool BugDriver::writeProgramToFile(const std::string &Filename, int FD,55 const Module &M) const {56 ToolOutputFile Out(Filename, FD);57 return writeProgramToFileAux(Out, M);58}59 60bool BugDriver::writeProgramToFile(int FD, const Module &M) const {61 raw_fd_ostream OS(FD, /*shouldClose*/ false);62 WriteBitcodeToFile(M, OS, /* ShouldPreserveUseListOrder */ true);63 OS.flush();64 if (!OS.has_error())65 return false;66 OS.clear_error();67 return true;68}69 70bool BugDriver::writeProgramToFile(const std::string &Filename,71 const Module &M) const {72 std::error_code EC;73 ToolOutputFile Out(Filename, EC, sys::fs::OF_None);74 if (!EC)75 return writeProgramToFileAux(Out, M);76 return true;77}78 79/// This function is used to output the current Program to a file named80/// "bugpoint-ID.bc".81void BugDriver::emitProgressBitcode(const Module &M, const std::string &ID,82 bool NoFlyer) const {83 // Output the input to the current pass to a bitcode file, emit a message84 // telling the user how to reproduce it: opt -foo blah.bc85 //86 std::string Filename = OutputPrefix + "-" + ID + ".bc";87 if (writeProgramToFile(Filename, M)) {88 errs() << "Error opening file '" << Filename << "' for writing!\n";89 return;90 }91 92 outs() << "Emitted bitcode to '" << Filename << "'\n";93 if (NoFlyer || PassesToRun.empty())94 return;95 outs() << "\n*** You can reproduce the problem with: ";96 if (UseValgrind)97 outs() << "valgrind ";98 outs() << "opt " << Filename;99 for (unsigned i = 0, e = PluginLoader::getNumPlugins(); i != e; ++i) {100 outs() << " -load " << PluginLoader::getPlugin(i);101 }102 outs() << " " << getPassesString(PassesToRun) << "\n";103}104 105static cl::opt<bool> SilencePasses(106 "silence-passes",107 cl::desc("Suppress output of running passes (both stdout and stderr)"));108 109static cl::list<std::string> OptArgs("opt-args", cl::Positional,110 cl::desc("<opt arguments>..."),111 cl::PositionalEatsArgs);112 113/// runPasses - Run the specified passes on Program, outputting a bitcode file114/// and writing the filename into OutputFile if successful. If the115/// optimizations fail for some reason (optimizer crashes), return true,116/// otherwise return false. If DeleteOutput is set to true, the bitcode is117/// deleted on success, and the filename string is undefined. This prints to118/// outs() a single line message indicating whether compilation was successful119/// or failed.120///121bool BugDriver::runPasses(Module &Program,122 const std::vector<std::string> &Passes,123 std::string &OutputFilename, bool DeleteOutput,124 bool Quiet, ArrayRef<std::string> ExtraArgs) const {125 // setup the output file name126 outs().flush();127 SmallString<128> UniqueFilename;128 std::error_code EC = sys::fs::createUniqueFile(129 OutputPrefix + "-output-%%%%%%%.bc", UniqueFilename);130 if (EC) {131 errs() << getToolName()132 << ": Error making unique filename: " << EC.message() << "\n";133 return true;134 }135 OutputFilename = std::string(UniqueFilename);136 137 // set up the input file name138 Expected<sys::fs::TempFile> Temp =139 sys::fs::TempFile::create(OutputPrefix + "-input-%%%%%%%.bc");140 if (!Temp) {141 errs() << getToolName()142 << ": Error making unique filename: " << toString(Temp.takeError())143 << "\n";144 return true;145 }146 DiscardTemp Discard{*Temp};147 raw_fd_ostream OS(Temp->FD, /*shouldClose*/ false);148 149 WriteBitcodeToFile(Program, OS, /* ShouldPreserveUseListOrder */ true);150 OS.flush();151 if (OS.has_error()) {152 errs() << "Error writing bitcode file: " << Temp->TmpName << "\n";153 OS.clear_error();154 return true;155 }156 157 std::string tool = OptCmd;158 if (OptCmd.empty()) {159 if (ErrorOr<std::string> Path =160 FindProgramByName("opt", getToolName(), &OutputPrefix))161 tool = *Path;162 else163 errs() << Path.getError().message() << "\n";164 }165 if (tool.empty()) {166 errs() << "Cannot find `opt' in PATH!\n";167 return true;168 }169 if (!sys::fs::exists(tool)) {170 errs() << "Specified `opt' binary does not exist: " << tool << "\n";171 return true;172 }173 174 std::string Prog;175 if (UseValgrind) {176 if (ErrorOr<std::string> Path = sys::findProgramByName("valgrind"))177 Prog = *Path;178 else179 errs() << Path.getError().message() << "\n";180 } else181 Prog = tool;182 if (Prog.empty()) {183 errs() << "Cannot find `valgrind' in PATH!\n";184 return true;185 }186 187 // setup the child process' arguments188 SmallVector<StringRef, 8> Args;189 if (UseValgrind) {190 Args.push_back("valgrind");191 Args.push_back("--error-exitcode=1");192 Args.push_back("-q");193 Args.push_back(tool);194 } else195 Args.push_back(tool);196 197 llvm::append_range(Args, OptArgs);198 // Pin to legacy PM since bugpoint has lots of infra and hacks revolving199 // around the legacy PM.200 Args.push_back("-bugpoint-enable-legacy-pm");201 Args.push_back("-disable-symbolication");202 Args.push_back("-o");203 Args.push_back(OutputFilename);204 std::vector<std::string> pass_args;205 for (unsigned i = 0, e = PluginLoader::getNumPlugins(); i != e; ++i) {206 pass_args.push_back(std::string("-load"));207 pass_args.push_back(PluginLoader::getPlugin(i));208 }209 for (std::vector<std::string>::const_iterator I = Passes.begin(),210 E = Passes.end();211 I != E; ++I)212 pass_args.push_back(std::string("-") + (*I));213 for (std::vector<std::string>::const_iterator I = pass_args.begin(),214 E = pass_args.end();215 I != E; ++I)216 Args.push_back(*I);217 Args.push_back(Temp->TmpName);218 Args.append(ExtraArgs.begin(), ExtraArgs.end());219 220 LLVM_DEBUG(errs() << "\nAbout to run:\t";221 for (unsigned i = 0, e = Args.size() - 1; i != e; ++i) errs()222 << " " << Args[i];223 errs() << "\n";);224 225 std::optional<StringRef> Redirects[3] = {std::nullopt, std::nullopt,226 std::nullopt};227 // Redirect stdout and stderr to nowhere if SilencePasses is given.228 if (SilencePasses) {229 Redirects[1] = "";230 Redirects[2] = "";231 }232 233 std::string ErrMsg;234 int result = sys::ExecuteAndWait(Prog, Args, std::nullopt, Redirects, Timeout,235 MemoryLimit, &ErrMsg);236 237 // If we are supposed to delete the bitcode file or if the passes crashed,238 // remove it now. This may fail if the file was never created, but that's ok.239 if (DeleteOutput || result != 0)240 sys::fs::remove(OutputFilename);241 242 if (!Quiet) {243 if (result == 0)244 outs() << "Success!\n";245 else if (result > 0)246 outs() << "Exited with error code '" << result << "'\n";247 else if (result < 0) {248 if (result == -1)249 outs() << "Execute failed: " << ErrMsg << "\n";250 else251 outs() << "Crashed: " << ErrMsg << "\n";252 }253 if (result & 0x01000000)254 outs() << "Dumped core\n";255 }256 257 // Was the child successful?258 return result != 0;259}260 261std::unique_ptr<Module>262BugDriver::runPassesOn(Module *M, const std::vector<std::string> &Passes,263 ArrayRef<std::string> ExtraArgs) {264 std::string BitcodeResult;265 if (runPasses(*M, Passes, BitcodeResult, false /*delete*/, true /*quiet*/,266 ExtraArgs)) {267 return nullptr;268 }269 270 std::unique_ptr<Module> Ret = parseInputFile(BitcodeResult, Context);271 if (!Ret) {272 errs() << getToolName() << ": Error reading bitcode file '" << BitcodeResult273 << "'!\n";274 exit(1);275 }276 sys::fs::remove(BitcodeResult);277 return Ret;278}279