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