OptimizerDriver.cpp revision 212793
1175261Sobrien//===- OptimizerDriver.cpp - Allow BugPoint to run passes safely ----------===// 281404Speter// 3175261Sobrien// The LLVM Compiler Infrastructure 4175261Sobrien// 5175261Sobrien// This file is distributed under the University of Illinois Open Source 6175261Sobrien// License. See LICENSE.TXT for details. 7175261Sobrien// 8175261Sobrien//===----------------------------------------------------------------------===// 9175261Sobrien// 1081404Speter// This file defines an interface that allows bugpoint to run various passes 11175261Sobrien// without the threat of a buggy pass corrupting bugpoint (of course, bugpoint 12175261Sobrien// may have its own bugs, but that's another story...). It achieves this by 13175261Sobrien// forking a copy of itself and having the child process do the optimizations. 14175261Sobrien// If this client dies, we can always fork a new one. :) 15175261Sobrien// 16175261Sobrien//===----------------------------------------------------------------------===// 17175261Sobrien 18175261Sobrien// Note: as a short term hack, the old Unix-specific code and platform- 1981404Speter// independent code co-exist via conditional compilation until it is verified 20175261Sobrien// that the new code works correctly on Unix. 21175261Sobrien 22175261Sobrien#include "BugDriver.h" 23175261Sobrien#include "llvm/Module.h" 24175261Sobrien#include "llvm/PassManager.h" 25175261Sobrien#include "llvm/Analysis/Verifier.h" 2681404Speter#include "llvm/Bitcode/ReaderWriter.h" 27175261Sobrien#include "llvm/Target/TargetData.h" 28175261Sobrien#include "llvm/Support/FileUtilities.h" 2981404Speter#include "llvm/Support/CommandLine.h" 30175261Sobrien#include "llvm/Support/SystemUtils.h" 31175261Sobrien#include "llvm/Support/Debug.h" 32175261Sobrien#include "llvm/Support/raw_ostream.h" 3381404Speter#include "llvm/System/Path.h" 34175261Sobrien#include "llvm/System/Program.h" 35175261Sobrien 36175261Sobrien#define DONT_GET_PLUGIN_LOADER_OPTION 37175261Sobrien#include "llvm/Support/PluginLoader.h" 38175261Sobrien 39175261Sobrien#include <fstream> 40175261Sobrienusing namespace llvm; 41175261Sobrien 42175261Sobriennamespace llvm { 43175261Sobrien extern cl::opt<std::string> OutputPrefix; 44175261Sobrien} 45175261Sobrien 46175261Sobriennamespace { 47175261Sobrien // ChildOutput - This option captures the name of the child output file that 48175261Sobrien // is set up by the parent bugpoint process 49175261Sobrien cl::opt<std::string> ChildOutput("child-output", cl::ReallyHidden); 50175261Sobrien} 51175261Sobrien 52175261Sobrien/// writeProgramToFile - This writes the current "Program" to the named bitcode 53175261Sobrien/// file. If an error occurs, true is returned. 54175261Sobrien/// 55175261Sobrienbool BugDriver::writeProgramToFile(const std::string &Filename, 56175261Sobrien const Module *M) const { 57175261Sobrien std::string ErrInfo; 58175261Sobrien tool_output_file Out(Filename.c_str(), ErrInfo, 59175261Sobrien raw_fd_ostream::F_Binary); 60175261Sobrien if (ErrInfo.empty()) { 61175261Sobrien WriteBitcodeToFile(M, Out.os()); 62175261Sobrien Out.os().close(); 63175261Sobrien if (!Out.os().has_error()) { 64175261Sobrien Out.keep(); 65175261Sobrien return false; 66175261Sobrien } 67175261Sobrien } 68175261Sobrien Out.os().clear_error(); 69175261Sobrien return true; 70175261Sobrien} 71175261Sobrien 72175261Sobrien 73175261Sobrien/// EmitProgressBitcode - This function is used to output the current Program 74175261Sobrien/// to a file named "bugpoint-ID.bc". 75175261Sobrien/// 76175261Sobrienvoid BugDriver::EmitProgressBitcode(const Module *M, 77175261Sobrien const std::string &ID, 78175261Sobrien bool NoFlyer) const { 79175261Sobrien // Output the input to the current pass to a bitcode file, emit a message 80175261Sobrien // telling the user how to reproduce it: opt -foo blah.bc 81175261Sobrien // 82175261Sobrien std::string Filename = OutputPrefix + "-" + ID + ".bc"; 83175261Sobrien if (writeProgramToFile(Filename, M)) { 84175261Sobrien errs() << "Error opening file '" << Filename << "' for writing!\n"; 85175261Sobrien return; 86175261Sobrien } 87175261Sobrien 88175261Sobrien outs() << "Emitted bitcode to '" << Filename << "'\n"; 89175261Sobrien if (NoFlyer || PassesToRun.empty()) return; 90175261Sobrien outs() << "\n*** You can reproduce the problem with: "; 91175261Sobrien if (UseValgrind) outs() << "valgrind "; 92175261Sobrien outs() << "opt " << Filename << " "; 93175261Sobrien outs() << getPassesString(PassesToRun) << "\n"; 94175261Sobrien} 95175261Sobrien 96175261Sobriencl::opt<bool> SilencePasses("silence-passes", cl::desc("Suppress output of running passes (both stdout and stderr)")); 97175261Sobrien 98175261Sobrienstatic cl::list<std::string> OptArgs("opt-args", cl::Positional, 99175261Sobrien cl::desc("<opt arguments>..."), 100175261Sobrien cl::ZeroOrMore, cl::PositionalEatsArgs); 101175261Sobrien 102175261Sobrien/// runPasses - Run the specified passes on Program, outputting a bitcode file 103175261Sobrien/// and writing the filename into OutputFile if successful. If the 104175261Sobrien/// optimizations fail for some reason (optimizer crashes), return true, 105175261Sobrien/// otherwise return false. If DeleteOutput is set to true, the bitcode is 106175261Sobrien/// deleted on success, and the filename string is undefined. This prints to 107175261Sobrien/// outs() a single line message indicating whether compilation was successful 108175261Sobrien/// or failed. 109175261Sobrien/// 110175261Sobrienbool BugDriver::runPasses(Module *Program, 111175261Sobrien const std::vector<std::string> &Passes, 112175261Sobrien std::string &OutputFilename, bool DeleteOutput, 113175261Sobrien bool Quiet, unsigned NumExtraArgs, 114175261Sobrien const char * const *ExtraArgs) const { 115175261Sobrien // setup the output file name 116175261Sobrien outs().flush(); 117175261Sobrien sys::Path uniqueFilename(OutputPrefix + "-output.bc"); 118175261Sobrien std::string ErrMsg; 119175261Sobrien if (uniqueFilename.makeUnique(true, &ErrMsg)) { 120175261Sobrien errs() << getToolName() << ": Error making unique filename: " 121175261Sobrien << ErrMsg << "\n"; 122175261Sobrien return(1); 123175261Sobrien } 124175261Sobrien OutputFilename = uniqueFilename.str(); 125175261Sobrien 126175261Sobrien // set up the input file name 127175261Sobrien sys::Path inputFilename(OutputPrefix + "-input.bc"); 128175261Sobrien if (inputFilename.makeUnique(true, &ErrMsg)) { 129175261Sobrien errs() << getToolName() << ": Error making unique filename: " 130175261Sobrien << ErrMsg << "\n"; 131175261Sobrien return(1); 132175261Sobrien } 133175261Sobrien 134175261Sobrien std::string ErrInfo; 135175261Sobrien tool_output_file InFile(inputFilename.c_str(), ErrInfo, 136175261Sobrien raw_fd_ostream::F_Binary); 137175261Sobrien 138175261Sobrien 139175261Sobrien if (!ErrInfo.empty()) { 140175261Sobrien errs() << "Error opening bitcode file: " << inputFilename.str() << "\n"; 141175261Sobrien return 1; 142175261Sobrien } 143175261Sobrien WriteBitcodeToFile(Program, InFile.os()); 144175261Sobrien InFile.os().close(); 145175261Sobrien if (InFile.os().has_error()) { 146175261Sobrien errs() << "Error writing bitcode file: " << inputFilename.str() << "\n"; 147175261Sobrien InFile.os().clear_error(); 148175261Sobrien return 1; 149175261Sobrien } 150175261Sobrien InFile.keep(); 151175261Sobrien 152175261Sobrien // setup the child process' arguments 153175261Sobrien SmallVector<const char*, 8> Args; 154175261Sobrien sys::Path tool = FindExecutable("opt", getToolName(), (void*)"opt"); 155175261Sobrien std::string Opt = tool.str(); 156175261Sobrien if (UseValgrind) { 157175261Sobrien Args.push_back("valgrind"); 158175261Sobrien Args.push_back("--error-exitcode=1"); 159175261Sobrien Args.push_back("-q"); 160175261Sobrien Args.push_back(tool.c_str()); 161175261Sobrien } else 162175261Sobrien Args.push_back(Opt.c_str()); 163175261Sobrien 164175261Sobrien Args.push_back("-o"); 165175261Sobrien Args.push_back(OutputFilename.c_str()); 166175261Sobrien for (unsigned i = 0, e = OptArgs.size(); i != e; ++i) 167175261Sobrien Args.push_back(OptArgs[i].c_str()); 168175261Sobrien std::vector<std::string> pass_args; 169175261Sobrien for (unsigned i = 0, e = PluginLoader::getNumPlugins(); i != e; ++i) { 170175261Sobrien pass_args.push_back( std::string("-load")); 171175261Sobrien pass_args.push_back( PluginLoader::getPlugin(i)); 172175261Sobrien } 173175261Sobrien for (std::vector<std::string>::const_iterator I = Passes.begin(), 174175261Sobrien E = Passes.end(); I != E; ++I ) 175175261Sobrien pass_args.push_back( std::string("-") + (*I) ); 176175261Sobrien for (std::vector<std::string>::const_iterator I = pass_args.begin(), 177175261Sobrien E = pass_args.end(); I != E; ++I ) 178175261Sobrien Args.push_back(I->c_str()); 179175261Sobrien Args.push_back(inputFilename.c_str()); 180175261Sobrien for (unsigned i = 0; i < NumExtraArgs; ++i) 181175261Sobrien Args.push_back(*ExtraArgs); 182175261Sobrien Args.push_back(0); 183175261Sobrien 184175261Sobrien DEBUG(errs() << "\nAbout to run:\t"; 185175261Sobrien for (unsigned i = 0, e = Args.size()-1; i != e; ++i) 186175261Sobrien errs() << " " << Args[i]; 187175261Sobrien errs() << "\n"; 188175261Sobrien ); 189175261Sobrien 190175261Sobrien sys::Path prog; 191175261Sobrien if (UseValgrind) 192175261Sobrien prog = sys::Program::FindProgramByName("valgrind"); 193175261Sobrien else 194175261Sobrien prog = tool; 195175261Sobrien 196175261Sobrien // Redirect stdout and stderr to nowhere if SilencePasses is given 197175261Sobrien sys::Path Nowhere; 198175261Sobrien const sys::Path *Redirects[3] = {0, &Nowhere, &Nowhere}; 199175261Sobrien 200175261Sobrien int result = sys::Program::ExecuteAndWait(prog, Args.data(), 0, 201175261Sobrien (SilencePasses ? Redirects : 0), 202175261Sobrien Timeout, MemoryLimit, &ErrMsg); 203175261Sobrien 204175261Sobrien // If we are supposed to delete the bitcode file or if the passes crashed, 205175261Sobrien // remove it now. This may fail if the file was never created, but that's ok. 206175261Sobrien if (DeleteOutput || result != 0) 207175261Sobrien sys::Path(OutputFilename).eraseFromDisk(); 208175261Sobrien 209175261Sobrien // Remove the temporary input file as well 210175261Sobrien inputFilename.eraseFromDisk(); 211175261Sobrien 212175261Sobrien if (!Quiet) { 213175261Sobrien if (result == 0) 214175261Sobrien outs() << "Success!\n"; 215175261Sobrien else if (result > 0) 216175261Sobrien outs() << "Exited with error code '" << result << "'\n"; 217175261Sobrien else if (result < 0) { 218175261Sobrien if (result == -1) 219175261Sobrien outs() << "Execute failed: " << ErrMsg << "\n"; 220175261Sobrien else 221175261Sobrien outs() << "Crashed with signal #" << abs(result) << "\n"; 222175261Sobrien } 223175261Sobrien if (result & 0x01000000) 224175261Sobrien outs() << "Dumped core\n"; 225175261Sobrien } 226175261Sobrien 227175261Sobrien // Was the child successful? 228175261Sobrien return result != 0; 229175261Sobrien} 230175261Sobrien 231175261Sobrien 232175261Sobrien/// runPassesOn - Carefully run the specified set of pass on the specified 233175261Sobrien/// module, returning the transformed module on success, or a null pointer on 234175261Sobrien/// failure. 235175261SobrienModule *BugDriver::runPassesOn(Module *M, 236175261Sobrien const std::vector<std::string> &Passes, 237175261Sobrien bool AutoDebugCrashes, unsigned NumExtraArgs, 238175261Sobrien const char * const *ExtraArgs) { 239175261Sobrien std::string BitcodeResult; 240175261Sobrien if (runPasses(M, Passes, BitcodeResult, false/*delete*/, true/*quiet*/, 241175261Sobrien NumExtraArgs, ExtraArgs)) { 242175261Sobrien if (AutoDebugCrashes) { 243175261Sobrien errs() << " Error running this sequence of passes" 244175261Sobrien << " on the input program!\n"; 245175261Sobrien delete swapProgramIn(M); 246175261Sobrien EmitProgressBitcode(M, "pass-error", false); 247175261Sobrien exit(debugOptimizerCrash()); 248175261Sobrien } 249175261Sobrien return 0; 250175261Sobrien } 251175261Sobrien 252175261Sobrien Module *Ret = ParseInputFile(BitcodeResult, Context); 253175261Sobrien if (Ret == 0) { 254175261Sobrien errs() << getToolName() << ": Error reading bitcode file '" 255175261Sobrien << BitcodeResult << "'!\n"; 256175261Sobrien exit(1); 257175261Sobrien } 258175261Sobrien sys::Path(BitcodeResult).eraseFromDisk(); // No longer need the file on disk 259175261Sobrien return Ret; 260175261Sobrien} 261175261Sobrien