BugDriver.h:
[oota-llvm.git] / tools / bugpoint / ExecutionDriver.cpp
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 /*
9 BUGPOINT NOTES:
10
11 1. Bugpoint should not leave any files behind if the program works properly
12 2. 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
24 namespace {
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 ///
50 struct 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 //
66 class LLI : public AbstractInterpreter {
67   std::string LLIPath;          // The path to the LLI executable
68 public:
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
87 int 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 //
116 class GCC {
117   std::string GCCPath;          // The path to the gcc executable
118 public:
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
146 int 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
195 int 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
223 void 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 //
250 class LLC : public AbstractInterpreter {
251   std::string LLCPath;          // The path to the LLC executable
252   GCC *gcc;
253 public:
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
283 int 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
306 int 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 //
326 class JIT : public AbstractInterpreter {
327   std::string LLIPath;          // The path to the LLI executable
328 public:
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
347 int 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 //
374 class CBE : public AbstractInterpreter {
375   std::string DISPath;          // The path to the LLVM 'dis' executable
376   GCC *gcc;
377 public:
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
410 int 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
434 int 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 ///
457 bool 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 ///
493 std::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
528 std::string BugDriver::executeProgramWithCBE(std::string OutputFile,
529                                              std::string BytecodeFile,
530                                              std::string SharedObject) {
531   return executeProgram(OutputFile, BytecodeFile, SharedObject, cbe);
532 }
533
534 int 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 ///
564 bool 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
598 bool BugDriver::isExecutingJIT() {
599   return InterpreterSel == RunJIT;
600 }