b391b0492f2709569b90e89c2a58a01f43038ac1
[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 <fstream>
21 #include <iostream>
22
23 namespace {
24   // OutputType - Allow the user to specify the way code should be run, to test
25   // for miscompilation.
26   //
27   enum OutputType {
28     RunLLI, RunJIT, RunLLC, RunCBE
29   };
30   cl::opt<OutputType>
31   InterpreterSel(cl::desc("Specify how LLVM code should be executed:"),
32                  cl::values(clEnumValN(RunLLI, "run-lli", "Execute with LLI"),
33                             clEnumValN(RunJIT, "run-jit", "Execute with JIT"),
34                             clEnumValN(RunLLC, "run-llc", "Compile with LLC"),
35                             clEnumValN(RunCBE, "run-cbe", "Compile with CBE"),
36                             0));
37
38   cl::opt<std::string>
39   InputFile("input", cl::init("/dev/null"),
40             cl::desc("Filename to pipe in as stdin (default: /dev/null)"));
41
42   enum FileType { AsmFile, CFile };
43 }
44
45 /// AbstractInterpreter Class - Subclasses of this class are used to execute
46 /// LLVM bytecode in a variety of ways.  This abstract interface hides this
47 /// complexity behind a simple interface.
48 ///
49 struct AbstractInterpreter {
50
51   virtual ~AbstractInterpreter() {}
52
53   /// ExecuteProgram - Run the specified bytecode file, emitting output to the
54   /// specified filename.  This returns the exit code of the program.
55   ///
56   virtual int ExecuteProgram(const std::string &Bytecode,
57                              const std::string &OutputFile,
58                              const std::string &SharedLib = "") = 0;
59 };
60
61
62 //===----------------------------------------------------------------------===//
63 // LLI Implementation of AbstractIntepreter interface
64 //
65 class LLI : public AbstractInterpreter {
66   std::string LLIPath;          // The path to the LLI executable
67 public:
68   LLI(const std::string &Path) : LLIPath(Path) { }
69
70   // LLI create method - Try to find the LLI executable
71   static LLI *create(BugDriver *BD, std::string &Message) {
72     std::string LLIPath = FindExecutable("lli", BD->getToolName());
73     if (!LLIPath.empty()) {
74       Message = "Found lli: " + LLIPath + "\n";
75       return new LLI(LLIPath);
76     }
77
78     Message = "Cannot find `lli' in bugpoint executable directory or PATH!\n";
79     return 0;
80   }
81   virtual int ExecuteProgram(const std::string &Bytecode,
82                              const std::string &OutputFile,
83                              const std::string &SharedLib = "");
84 };
85
86 int LLI::ExecuteProgram(const std::string &Bytecode,
87                         const std::string &OutputFile,
88                         const std::string &SharedLib) {
89   if (!SharedLib.empty()) {
90     std::cerr << "LLI currently does not support loading shared libraries.\n"
91               << "Exiting.\n";
92     exit(1);
93   }
94
95   const char *Args[] = {
96     LLIPath.c_str(),
97     "-abort-on-exception",
98     "-quiet",
99     "-force-interpreter=true",
100     Bytecode.c_str(),
101     0
102   };
103
104   return RunProgramWithTimeout(LLIPath, Args,
105                                InputFile, OutputFile, OutputFile);
106 }
107
108 //===----------------------------------------------------------------------===//
109 // GCC Implementation of AbstractIntepreter interface
110 //
111 // This is not a *real* AbstractInterpreter as it does not accept bytecode
112 // files, but only input acceptable to GCC, i.e. C, C++, and assembly files
113 //
114 class GCC {
115   std::string GCCPath;          // The path to the gcc executable
116 public:
117   GCC(const std::string &gccPath) : GCCPath(gccPath) { }
118   virtual ~GCC() {}
119
120   // GCC create method - Try to find the `gcc' executable
121   static GCC *create(BugDriver *BD, std::string &Message) {
122     std::string GCCPath = FindExecutable("gcc", BD->getToolName());
123     if (GCCPath.empty()) {
124       Message = "Cannot find `gcc' in bugpoint executable directory or PATH!\n";
125       return 0;
126     }
127
128     Message = "Found gcc: " + GCCPath + "\n";
129     return new GCC(GCCPath);
130   }
131
132   virtual int ExecuteProgram(const std::string &ProgramFile,
133                              FileType fileType,
134                              const std::string &OutputFile,
135                              const std::string &SharedLib = "");
136
137   int MakeSharedObject(const std::string &InputFile,
138                        FileType fileType,
139                        std::string &OutputFile);
140   
141   void ProcessFailure(const char **Args);
142 };
143
144 int GCC::ExecuteProgram(const std::string &ProgramFile,
145                         FileType fileType,
146                         const std::string &OutputFile,
147                         const std::string &SharedLib) {
148   std::string OutputBinary = "bugpoint.gcc.exe";
149   const char **GCCArgs;
150
151   const char *ArgsWithoutSO[] = {
152     GCCPath.c_str(),
153     "-x", (fileType == AsmFile) ? "assembler" : "c",
154     ProgramFile.c_str(),         // Specify the input filename...
155     "-o", OutputBinary.c_str(),  // Output to the right filename...
156     "-lm",                       // Hard-code the math library...
157     "-O2",                       // Optimize the program a bit...
158     0
159   };
160   const char *ArgsWithSO[] = {
161     GCCPath.c_str(),
162     "-x", (fileType == AsmFile) ? "assembler" : "c",
163     ProgramFile.c_str(),         // Specify the input filename...
164     SharedLib.c_str(),           // Specify the shared library to link in...
165     "-o", OutputBinary.c_str(),  // Output to the right filename...
166     "-lm",                       // Hard-code the math library...
167     "-O2",                       // Optimize the program a bit...
168     0
169   };
170
171   GCCArgs = (SharedLib.empty()) ? ArgsWithoutSO : ArgsWithSO;
172   std::cout << "<gcc>";
173   if (RunProgramWithTimeout(GCCPath, GCCArgs, "/dev/null", "/dev/null",
174                             "/dev/null")) {
175     ProcessFailure(GCCArgs);
176     exit(1);
177   }
178
179   const char *ProgramArgs[] = {
180     OutputBinary.c_str(),
181     0
182   };
183
184   std::cout << "<program>";
185
186   // Now that we have a binary, run it!
187   int ProgramResult = RunProgramWithTimeout(OutputBinary, ProgramArgs,
188                                             InputFile, OutputFile, OutputFile);
189   std::cout << "\n";
190   removeFile(OutputBinary);
191   return ProgramResult;
192 }
193
194 int GCC::MakeSharedObject(const std::string &InputFile,
195                           FileType fileType,
196                           std::string &OutputFile) {
197   OutputFile = "./bugpoint.so";
198   // Compile the C/asm file into a shared object
199   const char* GCCArgs[] = {
200     GCCPath.c_str(),
201     "-x", (fileType == AsmFile) ? "assembler" : "c",
202     InputFile.c_str(),           // Specify the input filename...
203 #if defined(sparc) || defined(__sparc__) || defined(__sparcv9)
204     "-G",                        // Compile a shared library, `-G' for Sparc
205 #else                             
206     "-shared",                   // `-shared' for Linux/X86, maybe others
207 #endif
208     "-o", OutputFile.c_str(),    // Output to the right filename...
209     "-O2",                       // Optimize the program a bit...
210     0
211   };
212   
213   std::cout << "<gcc>";
214   if(RunProgramWithTimeout(GCCPath, GCCArgs, "/dev/null", "/dev/null",
215                            "/dev/null")) {
216     ProcessFailure(GCCArgs);
217     exit(1);
218   }
219   return 0;
220 }
221
222 void GCC::ProcessFailure(const char** GCCArgs) {
223   std::cerr << "\n*** bugpoint error: invocation of the C compiler failed!\n";
224   for (const char **Arg = GCCArgs; *Arg; ++Arg)
225     std::cerr << " " << *Arg;
226   std::cerr << "\n";
227
228   // Rerun the compiler, capturing any error messages to print them.
229   std::string ErrorFilename = getUniqueFilename("bugpoint.gcc.errors");
230   RunProgramWithTimeout(GCCPath, GCCArgs, "/dev/null", ErrorFilename.c_str(),
231                         ErrorFilename.c_str());
232
233   // Print out the error messages generated by GCC if possible...
234   std::ifstream ErrorFile(ErrorFilename.c_str());
235   if (ErrorFile) {
236     std::copy(std::istreambuf_iterator<char>(ErrorFile),
237               std::istreambuf_iterator<char>(),
238               std::ostreambuf_iterator<char>(std::cerr));
239     ErrorFile.close();
240     std::cerr << "\n";      
241   }
242
243   removeFile(ErrorFilename);
244 }
245
246 //===----------------------------------------------------------------------===//
247 // LLC Implementation of AbstractIntepreter interface
248 //
249 class LLC : public AbstractInterpreter {
250   std::string LLCPath;          // The path to the LLC executable
251   GCC *gcc;
252 public:
253   LLC(const std::string &llcPath, GCC *Gcc)
254     : LLCPath(llcPath), gcc(Gcc) { }
255   ~LLC() { delete gcc; }
256
257   // LLC create method - Try to find the LLC executable
258   static LLC *create(BugDriver *BD, std::string &Message) {
259     std::string LLCPath = FindExecutable("llc", BD->getToolName());
260     if (LLCPath.empty()) {
261       Message = "Cannot find `llc' in bugpoint executable directory or PATH!\n";
262       return 0;
263     }
264
265     Message = "Found llc: " + LLCPath + "\n";
266     GCC *gcc = GCC::create(BD, Message);
267     if (!gcc) {
268       std::cerr << Message << "\n";
269       exit(1);
270     }
271     return new LLC(LLCPath, gcc);
272   }
273
274   virtual int ExecuteProgram(const std::string &Bytecode,
275                              const std::string &OutputFile,
276                              const std::string &SharedLib = "");
277
278   int OutputAsm(const std::string &Bytecode,
279                 std::string &OutputAsmFile);
280
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   if (SharedLib.empty()) {
351     const char* Args[] = {
352       LLIPath.c_str(),
353       "-quiet",
354       "-force-interpreter=false",
355       Bytecode.c_str(),
356       0
357     };
358     return RunProgramWithTimeout(LLIPath, Args,
359                                  InputFile, OutputFile, OutputFile);
360   } else {
361     const char* Args[] = {
362       LLIPath.c_str(), "-quiet", "-force-interpreter=false", 
363       "-load", SharedLib.c_str(),
364       Bytecode.c_str(),
365       0
366     };
367     return RunProgramWithTimeout(LLIPath, Args,
368                                  InputFile, OutputFile, OutputFile);
369   }
370 }
371
372 //===----------------------------------------------------------------------===//
373 // CBE Implementation of AbstractIntepreter interface
374 //
375 class CBE : public AbstractInterpreter {
376   std::string DISPath;          // The path to the LLVM 'dis' executable
377   GCC *gcc;
378 public:
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
411 int 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
435 int 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 ///
458 bool 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 ///
494 std::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
529 std::string BugDriver::executeProgramWithCBE(std::string OutputFile,
530                                              std::string BytecodeFile,
531                                              std::string SharedObject) {
532   return executeProgram(OutputFile, BytecodeFile, SharedObject, cbe);
533 }
534
535 int 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 ///
565 bool 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 }