ExecutionDriver.cpp revision ca25b956f1f4440c3392eaf0351305b0c81e6124
1//===- ExecutionDriver.cpp - Allow execution of LLVM program --------------===//
2//
3// This file contains code used to execute the program utilizing one of the
4// various ways of running LLVM bytecode.
5//
6//===----------------------------------------------------------------------===//
7
8/*
9BUGPOINT NOTES:
10
111. Bugpoint should not leave any files behind if the program works properly
122. There should be an option to specify the program name, which specifies a
13   unique string to put into output files.  This allows operation in the
14   SingleSource directory, e.g. default to the first input filename.
15*/
16
17#include "BugDriver.h"
18#include "SystemUtils.h"
19#include "Support/CommandLine.h"
20#include "Support/Debug.h"
21#include "Support/FileUtilities.h"
22#include <fstream>
23#include <iostream>
24
25namespace {
26  // OutputType - Allow the user to specify the way code should be run, to test
27  // for miscompilation.
28  //
29  enum OutputType {
30    RunLLI, RunJIT, RunLLC, RunCBE
31  };
32  cl::opt<OutputType>
33  InterpreterSel(cl::desc("Specify how LLVM code should be executed:"),
34                 cl::values(clEnumValN(RunLLI, "run-lli", "Execute with LLI"),
35                            clEnumValN(RunJIT, "run-jit", "Execute with JIT"),
36                            clEnumValN(RunLLC, "run-llc", "Compile with LLC"),
37                            clEnumValN(RunCBE, "run-cbe", "Compile with CBE"),
38                            0));
39
40  cl::opt<std::string>
41  InputFile("input", cl::init("/dev/null"),
42            cl::desc("Filename to pipe in as stdin (default: /dev/null)"));
43
44  enum FileType { AsmFile, CFile };
45}
46
47// Anything specified after the --args option are taken as arguments to the
48// program being debugged.
49cl::list<std::string>
50InputArgv("args", cl::Positional, cl::desc("<program arguments>..."),
51          cl::ZeroOrMore);
52
53/// AbstractInterpreter Class - Subclasses of this class are used to execute
54/// LLVM bytecode in a variety of ways.  This abstract interface hides this
55/// complexity behind a simple interface.
56///
57struct AbstractInterpreter {
58
59  virtual ~AbstractInterpreter() {}
60
61  /// ExecuteProgram - Run the specified bytecode file, emitting output to the
62  /// specified filename.  This returns the exit code of the program.
63  ///
64  virtual int ExecuteProgram(const std::string &Bytecode,
65                             const std::string &OutputFile,
66                             const std::string &SharedLib = "") = 0;
67};
68
69
70//===----------------------------------------------------------------------===//
71// LLI Implementation of AbstractIntepreter interface
72//
73class LLI : public AbstractInterpreter {
74  std::string LLIPath;          // The path to the LLI executable
75public:
76  LLI(const std::string &Path) : LLIPath(Path) { }
77
78  // LLI create method - Try to find the LLI executable
79  static LLI *create(BugDriver *BD, std::string &Message) {
80    std::string LLIPath = FindExecutable("lli", BD->getToolName());
81    if (!LLIPath.empty()) {
82      Message = "Found lli: " + LLIPath + "\n";
83      return new LLI(LLIPath);
84    }
85
86    Message = "Cannot find `lli' in bugpoint executable directory or PATH!\n";
87    return 0;
88  }
89  virtual int ExecuteProgram(const std::string &Bytecode,
90                             const std::string &OutputFile,
91                             const std::string &SharedLib = "");
92};
93
94int LLI::ExecuteProgram(const std::string &Bytecode,
95                        const std::string &OutputFile,
96                        const std::string &SharedLib) {
97  if (!SharedLib.empty()) {
98    std::cerr << "LLI currently does not support loading shared libraries.\n"
99              << "Exiting.\n";
100    exit(1);
101  }
102
103  std::vector<const char*> LLIArgs;
104  LLIArgs.push_back(LLIPath.c_str());
105  LLIArgs.push_back("-abort-on-exception");
106  LLIArgs.push_back("-quiet");
107  LLIArgs.push_back("-force-interpreter=true");
108  LLIArgs.push_back(Bytecode.c_str());
109  // Add optional parameters to the running program from Argv
110  for (unsigned i=0, e = InputArgv.size(); i != e; ++i)
111    LLIArgs.push_back(InputArgv[i].c_str());
112  LLIArgs.push_back(0);
113
114  std::cout << "<lli>" << std::flush;
115  DEBUG(std::cerr << "\nAbout to run:\n\t";
116        for (unsigned i=0, e = LLIArgs.size(); i != e; ++i)
117          std::cerr << " " << LLIArgs[i];
118        std::cerr << "\n";
119        );
120  return RunProgramWithTimeout(LLIPath, &LLIArgs[0],
121                               InputFile, OutputFile, OutputFile);
122}
123
124//===----------------------------------------------------------------------===//
125// GCC abstraction
126//
127// This is not a *real* AbstractInterpreter as it does not accept bytecode
128// files, but only input acceptable to GCC, i.e. C, C++, and assembly files
129//
130class GCC {
131  std::string GCCPath;          // The path to the gcc executable
132public:
133  GCC(const std::string &gccPath) : GCCPath(gccPath) { }
134  virtual ~GCC() {}
135
136  // GCC create method - Try to find the `gcc' executable
137  static GCC *create(BugDriver *BD, std::string &Message) {
138    std::string GCCPath = FindExecutable("gcc", BD->getToolName());
139    if (GCCPath.empty()) {
140      Message = "Cannot find `gcc' in bugpoint executable directory or PATH!\n";
141      return 0;
142    }
143
144    Message = "Found gcc: " + GCCPath + "\n";
145    return new GCC(GCCPath);
146  }
147
148  virtual int ExecuteProgram(const std::string &ProgramFile,
149                             FileType fileType,
150                             const std::string &OutputFile,
151                             const std::string &SharedLib = "");
152
153  int MakeSharedObject(const std::string &InputFile,
154                       FileType fileType,
155                       std::string &OutputFile);
156
157  void ProcessFailure(const char **Args);
158};
159
160int GCC::ExecuteProgram(const std::string &ProgramFile,
161                        FileType fileType,
162                        const std::string &OutputFile,
163                        const std::string &SharedLib) {
164  std::string OutputBinary = getUniqueFilename("bugpoint.gcc.exe");
165  std::vector<const char*> GCCArgs;
166
167  GCCArgs.push_back(GCCPath.c_str());
168  if (!SharedLib.empty()) // Specify the shared library to link in...
169    GCCArgs.push_back(SharedLib.c_str());
170  GCCArgs.push_back("-x");
171  GCCArgs.push_back((fileType == AsmFile) ? "assembler" : "c");
172  GCCArgs.push_back(ProgramFile.c_str());  // Specify the input filename...
173  GCCArgs.push_back("-o");
174  GCCArgs.push_back(OutputBinary.c_str()); // Output to the right file...
175  GCCArgs.push_back("-lm");                // Hard-code the math library...
176  GCCArgs.push_back("-O2");                // Optimize the program a bit...
177  GCCArgs.push_back(0);                    // NULL terminator
178
179  std::cout << "<gcc>" << std::flush;
180  if (RunProgramWithTimeout(GCCPath, &GCCArgs[0], "/dev/null", "/dev/null",
181                            "/dev/null")) {
182    ProcessFailure(&GCCArgs[0]);
183    exit(1);
184  }
185
186  std::vector<const char*> ProgramArgs;
187  ProgramArgs.push_back(OutputBinary.c_str());
188  // Add optional parameters to the running program from Argv
189  for (unsigned i=0, e = InputArgv.size(); i != e; ++i)
190    ProgramArgs.push_back(InputArgv[i].c_str());
191  ProgramArgs.push_back(0);                // NULL terminator
192
193  // Now that we have a binary, run it!
194  std::cout << "<program>" << std::flush;
195  DEBUG(std::cerr << "\nAbout to run:\n\t";
196        for (unsigned i=0, e = ProgramArgs.size(); i != e; ++i)
197          std::cerr << " " << ProgramArgs[i];
198        std::cerr << "\n";
199        );
200  int ProgramResult = RunProgramWithTimeout(OutputBinary, &ProgramArgs[0],
201                                            InputFile, OutputFile, OutputFile);
202  removeFile(OutputBinary);
203  return ProgramResult;
204}
205
206int GCC::MakeSharedObject(const std::string &InputFile,
207                          FileType fileType,
208                          std::string &OutputFile) {
209  OutputFile = getUniqueFilename("./bugpoint.so");
210  // Compile the C/asm file into a shared object
211  const char* GCCArgs[] = {
212    GCCPath.c_str(),
213    "-x", (fileType == AsmFile) ? "assembler" : "c",
214    InputFile.c_str(),           // Specify the input filename...
215#if defined(sparc) || defined(__sparc__) || defined(__sparcv9)
216    "-G",                        // Compile a shared library, `-G' for Sparc
217#else
218    "-shared",                   // `-shared' for Linux/X86, maybe others
219#endif
220    "-o", OutputFile.c_str(),    // Output to the right filename...
221    "-O2",                       // Optimize the program a bit...
222    0
223  };
224
225  std::cout << "<gcc>" << std::flush;
226  if(RunProgramWithTimeout(GCCPath, GCCArgs, "/dev/null", "/dev/null",
227                           "/dev/null")) {
228    ProcessFailure(GCCArgs);
229    exit(1);
230  }
231  return 0;
232}
233
234void GCC::ProcessFailure(const char** GCCArgs) {
235  std::cerr << "\n*** bugpoint error: invocation of the C compiler failed!\n";
236  for (const char **Arg = GCCArgs; *Arg; ++Arg)
237    std::cerr << " " << *Arg;
238  std::cerr << "\n";
239
240  // Rerun the compiler, capturing any error messages to print them.
241  std::string ErrorFilename = getUniqueFilename("bugpoint.gcc.errors");
242  RunProgramWithTimeout(GCCPath, GCCArgs, "/dev/null", ErrorFilename.c_str(),
243                        ErrorFilename.c_str());
244
245  // Print out the error messages generated by GCC if possible...
246  std::ifstream ErrorFile(ErrorFilename.c_str());
247  if (ErrorFile) {
248    std::copy(std::istreambuf_iterator<char>(ErrorFile),
249              std::istreambuf_iterator<char>(),
250              std::ostreambuf_iterator<char>(std::cerr));
251    ErrorFile.close();
252    std::cerr << "\n";
253  }
254
255  removeFile(ErrorFilename);
256}
257
258//===----------------------------------------------------------------------===//
259// LLC Implementation of AbstractIntepreter interface
260//
261class LLC : public AbstractInterpreter {
262  std::string LLCPath;          // The path to the LLC executable
263  GCC *gcc;
264public:
265  LLC(const std::string &llcPath, GCC *Gcc)
266    : LLCPath(llcPath), gcc(Gcc) { }
267  ~LLC() { delete gcc; }
268
269  // LLC create method - Try to find the LLC executable
270  static LLC *create(BugDriver *BD, std::string &Message) {
271    std::string LLCPath = FindExecutable("llc", BD->getToolName());
272    if (LLCPath.empty()) {
273      Message = "Cannot find `llc' in bugpoint executable directory or PATH!\n";
274      return 0;
275    }
276
277    Message = "Found llc: " + LLCPath + "\n";
278    GCC *gcc = GCC::create(BD, Message);
279    if (!gcc) {
280      std::cerr << Message << "\n";
281      exit(1);
282    }
283    return new LLC(LLCPath, gcc);
284  }
285
286  virtual int ExecuteProgram(const std::string &Bytecode,
287                             const std::string &OutputFile,
288                             const std::string &SharedLib = "");
289
290  int OutputAsm(const std::string &Bytecode,
291                std::string &OutputAsmFile);
292};
293
294int LLC::OutputAsm(const std::string &Bytecode,
295                   std::string &OutputAsmFile) {
296  OutputAsmFile = "bugpoint.llc.s";
297  const char *LLCArgs[] = {
298    LLCPath.c_str(),
299    "-o", OutputAsmFile.c_str(), // Output to the Asm file
300    "-f",                        // Overwrite as necessary...
301    Bytecode.c_str(),            // This is the input bytecode
302    0
303  };
304
305  std::cout << "<llc>" << std::flush;
306  if (RunProgramWithTimeout(LLCPath, LLCArgs, "/dev/null", "/dev/null",
307                            "/dev/null")) {
308    // If LLC failed on the bytecode, print error...
309    std::cerr << "bugpoint error: `llc' failed!\n";
310    removeFile(OutputAsmFile);
311    return 1;
312  }
313
314  return 0;
315}
316
317int LLC::ExecuteProgram(const std::string &Bytecode,
318                        const std::string &OutputFile,
319                        const std::string &SharedLib) {
320
321  std::string OutputAsmFile;
322  if (OutputAsm(Bytecode, OutputAsmFile)) {
323    std::cerr << "Could not generate asm code with `llc', exiting.\n";
324    exit(1);
325  }
326
327  // Assuming LLC worked, compile the result with GCC and run it.
328  int Result = gcc->ExecuteProgram(OutputAsmFile,AsmFile,OutputFile,SharedLib);
329  removeFile(OutputAsmFile);
330  return Result;
331}
332
333
334//===----------------------------------------------------------------------===//
335// JIT Implementation of AbstractIntepreter interface
336//
337class JIT : public AbstractInterpreter {
338  std::string LLIPath;          // The path to the LLI executable
339public:
340  JIT(const std::string &Path) : LLIPath(Path) { }
341
342  // JIT create method - Try to find the LLI executable
343  static JIT *create(BugDriver *BD, std::string &Message) {
344    std::string LLIPath = FindExecutable("lli", BD->getToolName());
345    if (!LLIPath.empty()) {
346      Message = "Found lli: " + LLIPath + "\n";
347      return new JIT(LLIPath);
348    }
349
350    Message = "Cannot find `lli' in bugpoint executable directory or PATH!\n";
351    return 0;
352  }
353  virtual int ExecuteProgram(const std::string &Bytecode,
354                             const std::string &OutputFile,
355                             const std::string &SharedLib = "");
356};
357
358int JIT::ExecuteProgram(const std::string &Bytecode,
359                        const std::string &OutputFile,
360                        const std::string &SharedLib) {
361  // Construct a vector of parameters, incorporating those from the command-line
362  std::vector<const char*> JITArgs;
363  JITArgs.push_back(LLIPath.c_str());
364  JITArgs.push_back("-quiet");
365  JITArgs.push_back("-force-interpreter=false");
366  if (!SharedLib.empty()) {
367    JITArgs.push_back("-load");
368    JITArgs.push_back(SharedLib.c_str());
369  }
370  JITArgs.push_back(Bytecode.c_str());
371  // Add optional parameters to the running program from Argv
372  for (unsigned i=0, e = InputArgv.size(); i != e; ++i)
373    JITArgs.push_back(InputArgv[i].c_str());
374  JITArgs.push_back(0);
375
376  std::cout << "<jit>" << std::flush;
377  DEBUG(std::cerr << "\nAbout to run:\n\t";
378        for (unsigned i=0, e = JITArgs.size(); i != e; ++i)
379          std::cerr << " " << JITArgs[i];
380        std::cerr << "\n";
381        );
382  DEBUG(std::cerr << "\nSending output to " << OutputFile << "\n");
383  return RunProgramWithTimeout(LLIPath, &JITArgs[0],
384                               InputFile, OutputFile, OutputFile);
385}
386
387//===----------------------------------------------------------------------===//
388// CBE Implementation of AbstractIntepreter interface
389//
390class CBE : public AbstractInterpreter {
391  std::string DISPath;          // The path to the LLVM 'dis' executable
392  GCC *gcc;
393public:
394  CBE(const std::string &disPath, GCC *Gcc) : DISPath(disPath), gcc(Gcc) { }
395  ~CBE() { delete gcc; }
396
397  // CBE create method - Try to find the 'dis' executable
398  static CBE *create(BugDriver *BD, std::string &Message) {
399    std::string DISPath = FindExecutable("dis", BD->getToolName());
400    if (DISPath.empty()) {
401      Message = "Cannot find `dis' in bugpoint executable directory or PATH!\n";
402      return 0;
403    }
404
405    Message = "Found dis: " + DISPath + "\n";
406
407    GCC *gcc = GCC::create(BD, Message);
408    if (!gcc) {
409      std::cerr << Message << "\n";
410      exit(1);
411    }
412    return new CBE(DISPath, gcc);
413  }
414
415  virtual int ExecuteProgram(const std::string &Bytecode,
416                             const std::string &OutputFile,
417                             const std::string &SharedLib = "");
418
419  // Sometimes we just want to go half-way and only generate the C file,
420  // not necessarily compile it with GCC and run the program
421  virtual int OutputC(const std::string &Bytecode,
422                      std::string &OutputCFile);
423
424};
425
426int CBE::OutputC(const std::string &Bytecode,
427                 std::string &OutputCFile) {
428  OutputCFile = "bugpoint.cbe.c";
429  const char *DisArgs[] = {
430    DISPath.c_str(),
431    "-o", OutputCFile.c_str(),   // Output to the C file
432    "-c",                        // Output to C
433    "-f",                        // Overwrite as necessary...
434    Bytecode.c_str(),            // This is the input bytecode
435    0
436  };
437
438  std::cout << "<cbe>" << std::flush;
439  if (RunProgramWithTimeout(DISPath, DisArgs, "/dev/null", "/dev/null",
440                            "/dev/null")) {
441    // If dis failed on the bytecode, print error...
442    std::cerr << "bugpoint error: `dis -c' failed!\n";
443    return 1;
444  }
445
446  return 0;
447}
448
449
450int CBE::ExecuteProgram(const std::string &Bytecode,
451                        const std::string &OutputFile,
452                        const std::string &SharedLib) {
453  std::string OutputCFile;
454  if (OutputC(Bytecode, OutputCFile)) {
455    std::cerr << "Could not generate C code with `dis', exiting.\n";
456    exit(1);
457  }
458
459  int Result = gcc->ExecuteProgram(OutputCFile, CFile, OutputFile, SharedLib);
460  removeFile(OutputCFile);
461
462  return Result;
463}
464
465
466//===----------------------------------------------------------------------===//
467// BugDriver method implementation
468//
469
470/// initializeExecutionEnvironment - This method is used to set up the
471/// environment for executing LLVM programs.
472///
473bool BugDriver::initializeExecutionEnvironment() {
474  std::cout << "Initializing execution environment: ";
475
476  // FIXME: This should default to searching for the best interpreter to use on
477  // this platform, which would be JIT, then LLC, then CBE, then LLI.
478
479  // Create an instance of the AbstractInterpreter interface as specified on the
480  // command line
481  std::string Message;
482  switch (InterpreterSel) {
483  case RunLLI: Interpreter = LLI::create(this, Message); break;
484  case RunLLC: Interpreter = LLC::create(this, Message); break;
485  case RunJIT: Interpreter = JIT::create(this, Message); break;
486  case RunCBE: Interpreter = CBE::create(this, Message); break;
487  default:
488    Message = " Sorry, this back-end is not supported by bugpoint right now!\n";
489    break;
490  }
491
492  std::cout << Message;
493
494  // Initialize auxiliary tools for debugging
495  cbe = CBE::create(this, Message);
496  if (!cbe) { std::cout << Message << "\nExiting.\n"; exit(1); }
497  gcc = GCC::create(this, Message);
498  if (!gcc) { std::cout << Message << "\nExiting.\n"; exit(1); }
499
500  // If there was an error creating the selected interpreter, quit with error.
501  return Interpreter == 0;
502}
503
504
505/// executeProgram - This method runs "Program", capturing the output of the
506/// program to a file, returning the filename of the file.  A recommended
507/// filename may be optionally specified.
508///
509std::string BugDriver::executeProgram(std::string OutputFile,
510                                      std::string BytecodeFile,
511                                      std::string SharedObject,
512                                      AbstractInterpreter *AI) {
513  assert((Interpreter || AI) &&"Interpreter should have been created already!");
514  bool CreatedBytecode = false;
515  if (BytecodeFile.empty()) {
516    // Emit the program to a bytecode file...
517    BytecodeFile = getUniqueFilename("bugpoint-test-program.bc");
518
519    if (writeProgramToFile(BytecodeFile, Program)) {
520      std::cerr << ToolName << ": Error emitting bytecode to file '"
521                << BytecodeFile << "'!\n";
522      exit(1);
523    }
524    CreatedBytecode = true;
525  }
526
527  if (OutputFile.empty()) OutputFile = "bugpoint-execution-output";
528
529  // Check to see if this is a valid output filename...
530  OutputFile = getUniqueFilename(OutputFile);
531
532  // Actually execute the program!
533  int RetVal = (AI != 0) ?
534    AI->ExecuteProgram(BytecodeFile, OutputFile, SharedObject) :
535    Interpreter->ExecuteProgram(BytecodeFile, OutputFile, SharedObject);
536
537  // Remove the temporary bytecode file.
538  if (CreatedBytecode) removeFile(BytecodeFile);
539
540  // Return the filename we captured the output to.
541  return OutputFile;
542}
543
544std::string BugDriver::executeProgramWithCBE(std::string OutputFile,
545                                             std::string BytecodeFile,
546                                             std::string SharedObject) {
547  return executeProgram(OutputFile, BytecodeFile, SharedObject, cbe);
548}
549
550int BugDriver::compileSharedObject(const std::string &BytecodeFile,
551                                   std::string &SharedObject) {
552  assert(Interpreter && "Interpreter should have been created already!");
553  std::string Message, OutputCFile;
554
555  // Using CBE
556  cbe->OutputC(BytecodeFile, OutputCFile);
557
558#if 0 /* This is an alternative, as yet unimplemented */
559  // Using LLC
560  LLC *llc = LLC::create(this, Message);
561  if (llc->OutputAsm(BytecodeFile, OutputFile)) {
562    std::cerr << "Could not generate asm code with `llc', exiting.\n";
563    exit(1);
564  }
565#endif
566
567  gcc->MakeSharedObject(OutputCFile, CFile, SharedObject);
568
569  // Remove the intermediate C file
570  removeFile(OutputCFile);
571
572  return 0;
573}
574
575
576/// diffProgram - This method executes the specified module and diffs the output
577/// against the file specified by ReferenceOutputFile.  If the output is
578/// different, true is returned.
579///
580bool BugDriver::diffProgram(const std::string &BytecodeFile,
581                            const std::string &SharedObject,
582                            bool RemoveBytecode) {
583  // Execute the program, generating an output file...
584  std::string Output = executeProgram("", BytecodeFile, SharedObject);
585
586  std::string Error;
587  bool FilesDifferent = false;
588  if (DiffFiles(ReferenceOutputFile, Output, &Error)) {
589    if (!Error.empty()) {
590      std::cerr << "While diffing output: " << Error << "\n";
591      exit(1);
592    }
593    FilesDifferent = true;
594  }
595
596  if (RemoveBytecode) removeFile(BytecodeFile);
597  return FilesDifferent;
598}
599
600bool BugDriver::isExecutingJIT() {
601  return InterpreterSel == RunJIT;
602}
603