* Moved InputArgv out of anonymous scope to be extern'd in another file.
[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 // Anything specified after the --args option are taken as arguments to the
47 // program being debugged.
48 cl::list<std::string>
49 InputArgv("args", cl::Positional, cl::desc("<program arguments>..."),
50           cl::ZeroOrMore);
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 ///
56 struct 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 //
72 class LLI : public AbstractInterpreter {
73   std::string LLIPath;          // The path to the LLI executable
74 public:
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
93 int 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*> LLIArgs;
103   LLIArgs.push_back(LLIPath.c_str());
104   LLIArgs.push_back("-abort-on-exception");
105   LLIArgs.push_back("-quiet");
106   LLIArgs.push_back("-force-interpreter=true");
107   LLIArgs.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     LLIArgs.push_back(InputArgv[i].c_str());
111   LLIArgs.push_back(0);
112
113   std::cout << "<lli>";
114   DEBUG(std::cerr << "About to run:\n\t";
115         for (unsigned i=0, e = LLIArgs.size(); i != e; ++i)
116           std::cerr << " " << LLIArgs[i];
117         std::cerr << "\n";
118         );
119   return RunProgramWithTimeout(LLIPath, &LLIArgs[0],
120                                InputFile, OutputFile, OutputFile);
121 }
122
123 //===----------------------------------------------------------------------===//
124 // GCC abstraction
125 //
126 // This is not a *real* AbstractInterpreter as it does not accept bytecode
127 // files, but only input acceptable to GCC, i.e. C, C++, and assembly files
128 //
129 class GCC {
130   std::string GCCPath;          // The path to the gcc executable
131 public:
132   GCC(const std::string &gccPath) : GCCPath(gccPath) { }
133   virtual ~GCC() {}
134
135   // GCC create method - Try to find the `gcc' executable
136   static GCC *create(BugDriver *BD, std::string &Message) {
137     std::string GCCPath = FindExecutable("gcc", BD->getToolName());
138     if (GCCPath.empty()) {
139       Message = "Cannot find `gcc' in bugpoint executable directory or PATH!\n";
140       return 0;
141     }
142
143     Message = "Found gcc: " + GCCPath + "\n";
144     return new GCC(GCCPath);
145   }
146
147   virtual int ExecuteProgram(const std::string &ProgramFile,
148                              FileType fileType,
149                              const std::string &OutputFile,
150                              const std::string &SharedLib = "");
151
152   int MakeSharedObject(const std::string &InputFile,
153                        FileType fileType,
154                        std::string &OutputFile);
155   
156   void ProcessFailure(const char **Args);
157 };
158
159 int GCC::ExecuteProgram(const std::string &ProgramFile,
160                         FileType fileType,
161                         const std::string &OutputFile,
162                         const std::string &SharedLib) {
163   std::string OutputBinary = getUniqueFilename("bugpoint.gcc.exe");
164   std::vector<const char*> GCCArgs;
165
166   GCCArgs.push_back(GCCPath.c_str());
167   if (!SharedLib.empty()) // Specify the shared library to link in...
168     GCCArgs.push_back(SharedLib.c_str());
169   GCCArgs.push_back("-x");
170   GCCArgs.push_back((fileType == AsmFile) ? "assembler" : "c");
171   GCCArgs.push_back(ProgramFile.c_str());  // Specify the input filename...
172   GCCArgs.push_back("-o");
173   GCCArgs.push_back(OutputBinary.c_str()); // Output to the right file...
174   GCCArgs.push_back("-lm");                // Hard-code the math library...
175   GCCArgs.push_back("-O2");                // Optimize the program a bit...
176   GCCArgs.push_back(0);                    // NULL terminator
177
178   std::cout << "<gcc>";
179   if (RunProgramWithTimeout(GCCPath, &GCCArgs[0], "/dev/null", "/dev/null",
180                             "/dev/null")) {
181     ProcessFailure(&GCCArgs[0]);
182     exit(1);
183   }
184
185   std::vector<const char*> ProgramArgs;
186   ProgramArgs.push_back(OutputBinary.c_str());
187   // Add optional parameters to the running program from Argv
188   for (unsigned i=0, e = InputArgv.size(); i != e; ++i)
189     ProgramArgs.push_back(InputArgv[i].c_str());
190   ProgramArgs.push_back(0);                // NULL terminator
191
192   // Now that we have a binary, run it!
193   std::cout << "<program>";
194   DEBUG(std::cerr << "About to run:\n\t";
195         for (unsigned i=0, e = ProgramArgs.size(); i != e; ++i)
196           std::cerr << " " << ProgramArgs[i];
197         std::cerr << "\n";
198         );
199   int ProgramResult = RunProgramWithTimeout(OutputBinary, &ProgramArgs[0],
200                                             InputFile, OutputFile, OutputFile);
201   std::cout << "\n";
202   removeFile(OutputBinary);
203   return ProgramResult;
204 }
205
206 int 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>";
226   if(RunProgramWithTimeout(GCCPath, GCCArgs, "/dev/null", "/dev/null",
227                            "/dev/null")) {
228     ProcessFailure(GCCArgs);
229     exit(1);
230   }
231   return 0;
232 }
233
234 void 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 //
261 class LLC : public AbstractInterpreter {
262   std::string LLCPath;          // The path to the LLC executable
263   GCC *gcc;
264 public:
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
294 int 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>";
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
317 int 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 //
337 class JIT : public AbstractInterpreter {
338   std::string LLIPath;          // The path to the LLI executable
339 public:
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
358 int 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>\n";
377   DEBUG(std::cerr << "About 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 //
390 class CBE : public AbstractInterpreter {
391   std::string DISPath;          // The path to the LLVM 'dis' executable
392   GCC *gcc;
393 public:
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
426 int 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>";
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
450 int 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 ///
473 bool 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 ///
509 std::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
544 std::string BugDriver::executeProgramWithCBE(std::string OutputFile,
545                                              std::string BytecodeFile,
546                                              std::string SharedObject) {
547   return executeProgram(OutputFile, BytecodeFile, SharedObject, cbe);
548 }
549
550 int 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 ///
580 bool 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::ifstream ReferenceFile(ReferenceOutputFile.c_str());
587   if (!ReferenceFile) {
588     std::cerr << "Couldn't open reference output file '"
589               << ReferenceOutputFile << "'\n";
590     exit(1);
591   }
592
593   std::ifstream OutputFile(Output.c_str());
594   if (!OutputFile) {
595     std::cerr << "Couldn't open output file: " << Output << "'!\n";
596     exit(1);
597   }
598
599   bool FilesDifferent = false;
600
601   // Compare the two files...
602   int C1, C2;
603   do {
604     C1 = ReferenceFile.get();
605     C2 = OutputFile.get();
606     if (C1 != C2) { FilesDifferent = true; break; }
607   } while (C1 != EOF);
608
609   //removeFile(Output);
610   if (RemoveBytecode) removeFile(BytecodeFile);
611   return FilesDifferent;
612 }
613
614 bool BugDriver::isExecutingJIT() {
615   return InterpreterSel == RunJIT;
616 }