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