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