Path: blob/main/contrib/llvm-project/llvm/tools/bugpoint/OptimizerDriver.cpp
35231 views
//===- OptimizerDriver.cpp - Allow BugPoint to run passes safely ----------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file defines an interface that allows bugpoint to run various passes9// without the threat of a buggy pass corrupting bugpoint (of course, bugpoint10// may have its own bugs, but that's another story...). It achieves this by11// forking a copy of itself and having the child process do the optimizations.12// If this client dies, we can always fork a new one. :)13//14//===----------------------------------------------------------------------===//1516#include "BugDriver.h"17#include "ToolRunner.h"18#include "llvm/Bitcode/BitcodeWriter.h"19#include "llvm/IR/DataLayout.h"20#include "llvm/IR/Module.h"21#include "llvm/Support/CommandLine.h"22#include "llvm/Support/Debug.h"23#include "llvm/Support/FileUtilities.h"24#include "llvm/Support/Path.h"25#include "llvm/Support/Program.h"26#include "llvm/Support/ToolOutputFile.h"2728#define DONT_GET_PLUGIN_LOADER_OPTION29#include "llvm/Support/PluginLoader.h"303132using namespace llvm;3334#define DEBUG_TYPE "bugpoint"3536namespace llvm {37extern cl::opt<std::string> OutputPrefix;38}3940static cl::opt<bool> PreserveBitcodeUseListOrder(41"preserve-bc-uselistorder",42cl::desc("Preserve use-list order when writing LLVM bitcode."),43cl::init(true), cl::Hidden);4445static cl::opt<std::string>46OptCmd("opt-command", cl::init(""),47cl::desc("Path to opt. (default: search path "48"for 'opt'.)"));4950/// This writes the current "Program" to the named bitcode file. If an error51/// occurs, true is returned.52static bool writeProgramToFileAux(ToolOutputFile &Out, const Module &M) {53WriteBitcodeToFile(M, Out.os(), PreserveBitcodeUseListOrder);54Out.os().close();55if (!Out.os().has_error()) {56Out.keep();57return false;58}59return true;60}6162bool BugDriver::writeProgramToFile(const std::string &Filename, int FD,63const Module &M) const {64ToolOutputFile Out(Filename, FD);65return writeProgramToFileAux(Out, M);66}6768bool BugDriver::writeProgramToFile(int FD, const Module &M) const {69raw_fd_ostream OS(FD, /*shouldClose*/ false);70WriteBitcodeToFile(M, OS, PreserveBitcodeUseListOrder);71OS.flush();72if (!OS.has_error())73return false;74OS.clear_error();75return true;76}7778bool BugDriver::writeProgramToFile(const std::string &Filename,79const Module &M) const {80std::error_code EC;81ToolOutputFile Out(Filename, EC, sys::fs::OF_None);82if (!EC)83return writeProgramToFileAux(Out, M);84return true;85}8687/// This function is used to output the current Program to a file named88/// "bugpoint-ID.bc".89void BugDriver::EmitProgressBitcode(const Module &M, const std::string &ID,90bool NoFlyer) const {91// Output the input to the current pass to a bitcode file, emit a message92// telling the user how to reproduce it: opt -foo blah.bc93//94std::string Filename = OutputPrefix + "-" + ID + ".bc";95if (writeProgramToFile(Filename, M)) {96errs() << "Error opening file '" << Filename << "' for writing!\n";97return;98}99100outs() << "Emitted bitcode to '" << Filename << "'\n";101if (NoFlyer || PassesToRun.empty())102return;103outs() << "\n*** You can reproduce the problem with: ";104if (UseValgrind)105outs() << "valgrind ";106outs() << "opt " << Filename;107for (unsigned i = 0, e = PluginLoader::getNumPlugins(); i != e; ++i) {108outs() << " -load " << PluginLoader::getPlugin(i);109}110outs() << " " << getPassesString(PassesToRun) << "\n";111}112113cl::opt<bool> SilencePasses(114"silence-passes",115cl::desc("Suppress output of running passes (both stdout and stderr)"));116117static cl::list<std::string> OptArgs("opt-args", cl::Positional,118cl::desc("<opt arguments>..."),119cl::PositionalEatsArgs);120121/// runPasses - Run the specified passes on Program, outputting a bitcode file122/// and writing the filename into OutputFile if successful. If the123/// optimizations fail for some reason (optimizer crashes), return true,124/// otherwise return false. If DeleteOutput is set to true, the bitcode is125/// deleted on success, and the filename string is undefined. This prints to126/// outs() a single line message indicating whether compilation was successful127/// or failed.128///129bool BugDriver::runPasses(Module &Program,130const std::vector<std::string> &Passes,131std::string &OutputFilename, bool DeleteOutput,132bool Quiet, ArrayRef<std::string> ExtraArgs) const {133// setup the output file name134outs().flush();135SmallString<128> UniqueFilename;136std::error_code EC = sys::fs::createUniqueFile(137OutputPrefix + "-output-%%%%%%%.bc", UniqueFilename);138if (EC) {139errs() << getToolName()140<< ": Error making unique filename: " << EC.message() << "\n";141return true;142}143OutputFilename = std::string(UniqueFilename);144145// set up the input file name146Expected<sys::fs::TempFile> Temp =147sys::fs::TempFile::create(OutputPrefix + "-input-%%%%%%%.bc");148if (!Temp) {149errs() << getToolName()150<< ": Error making unique filename: " << toString(Temp.takeError())151<< "\n";152return true;153}154DiscardTemp Discard{*Temp};155raw_fd_ostream OS(Temp->FD, /*shouldClose*/ false);156157WriteBitcodeToFile(Program, OS, PreserveBitcodeUseListOrder);158OS.flush();159if (OS.has_error()) {160errs() << "Error writing bitcode file: " << Temp->TmpName << "\n";161OS.clear_error();162return true;163}164165std::string tool = OptCmd;166if (OptCmd.empty()) {167if (ErrorOr<std::string> Path =168FindProgramByName("opt", getToolName(), &OutputPrefix))169tool = *Path;170else171errs() << Path.getError().message() << "\n";172}173if (tool.empty()) {174errs() << "Cannot find `opt' in PATH!\n";175return true;176}177if (!sys::fs::exists(tool)) {178errs() << "Specified `opt' binary does not exist: " << tool << "\n";179return true;180}181182std::string Prog;183if (UseValgrind) {184if (ErrorOr<std::string> Path = sys::findProgramByName("valgrind"))185Prog = *Path;186else187errs() << Path.getError().message() << "\n";188} else189Prog = tool;190if (Prog.empty()) {191errs() << "Cannot find `valgrind' in PATH!\n";192return true;193}194195// setup the child process' arguments196SmallVector<StringRef, 8> Args;197if (UseValgrind) {198Args.push_back("valgrind");199Args.push_back("--error-exitcode=1");200Args.push_back("-q");201Args.push_back(tool);202} else203Args.push_back(tool);204205for (unsigned i = 0, e = OptArgs.size(); i != e; ++i)206Args.push_back(OptArgs[i]);207// Pin to legacy PM since bugpoint has lots of infra and hacks revolving208// around the legacy PM.209Args.push_back("-bugpoint-enable-legacy-pm");210Args.push_back("-disable-symbolication");211Args.push_back("-o");212Args.push_back(OutputFilename);213std::vector<std::string> pass_args;214for (unsigned i = 0, e = PluginLoader::getNumPlugins(); i != e; ++i) {215pass_args.push_back(std::string("-load"));216pass_args.push_back(PluginLoader::getPlugin(i));217}218for (std::vector<std::string>::const_iterator I = Passes.begin(),219E = Passes.end();220I != E; ++I)221pass_args.push_back(std::string("-") + (*I));222for (std::vector<std::string>::const_iterator I = pass_args.begin(),223E = pass_args.end();224I != E; ++I)225Args.push_back(*I);226Args.push_back(Temp->TmpName);227Args.append(ExtraArgs.begin(), ExtraArgs.end());228229LLVM_DEBUG(errs() << "\nAbout to run:\t";230for (unsigned i = 0, e = Args.size() - 1; i != e; ++i) errs()231<< " " << Args[i];232errs() << "\n";);233234std::optional<StringRef> Redirects[3] = {std::nullopt, std::nullopt,235std::nullopt};236// Redirect stdout and stderr to nowhere if SilencePasses is given.237if (SilencePasses) {238Redirects[1] = "";239Redirects[2] = "";240}241242std::string ErrMsg;243int result = sys::ExecuteAndWait(Prog, Args, std::nullopt, Redirects, Timeout,244MemoryLimit, &ErrMsg);245246// If we are supposed to delete the bitcode file or if the passes crashed,247// remove it now. This may fail if the file was never created, but that's ok.248if (DeleteOutput || result != 0)249sys::fs::remove(OutputFilename);250251if (!Quiet) {252if (result == 0)253outs() << "Success!\n";254else if (result > 0)255outs() << "Exited with error code '" << result << "'\n";256else if (result < 0) {257if (result == -1)258outs() << "Execute failed: " << ErrMsg << "\n";259else260outs() << "Crashed: " << ErrMsg << "\n";261}262if (result & 0x01000000)263outs() << "Dumped core\n";264}265266// Was the child successful?267return result != 0;268}269270std::unique_ptr<Module>271BugDriver::runPassesOn(Module *M, const std::vector<std::string> &Passes,272ArrayRef<std::string> ExtraArgs) {273std::string BitcodeResult;274if (runPasses(*M, Passes, BitcodeResult, false /*delete*/, true /*quiet*/,275ExtraArgs)) {276return nullptr;277}278279std::unique_ptr<Module> Ret = parseInputFile(BitcodeResult, Context);280if (!Ret) {281errs() << getToolName() << ": Error reading bitcode file '" << BitcodeResult282<< "'!\n";283exit(1);284}285sys::fs::remove(BitcodeResult);286return Ret;287}288289290