ExecutionDriver.cpp revision 4a10645c70199c8d8567fbc46312158c419720ab
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 f.e.  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
22namespace {
23  // OutputType - Allow the user to specify the way code should be run, to test
24  // for miscompilation.
25  //
26  enum OutputType {
27    RunLLI, RunJIT, RunLLC, RunCBE
28  };
29  cl::opt<OutputType>
30  InterpreterSel(cl::desc("Specify how LLVM code should be executed:"),
31		 cl::values(clEnumValN(RunLLI, "run-lli", "Execute with LLI"),
32			    clEnumValN(RunJIT, "run-jit", "Execute with JIT"),
33			    clEnumValN(RunLLC, "run-llc", "Compile with LLC"),
34			    clEnumValN(RunCBE, "run-cbe", "Compile with CBE"),
35			    0));
36}
37
38/// AbstractInterpreter Class - Subclasses of this class are used to execute
39/// LLVM bytecode in a variety of ways.  This abstract interface hides this
40/// complexity behind a simple interface.
41///
42struct AbstractInterpreter {
43
44  virtual ~AbstractInterpreter() {}
45
46  /// ExecuteProgram - Run the specified bytecode file, emitting output to the
47  /// specified filename.  This returns the exit code of the program.
48  ///
49  virtual int ExecuteProgram(const std::string &Bytecode,
50			     const std::string &OutputFile) = 0;
51
52};
53
54
55//===----------------------------------------------------------------------===//
56// LLI Implementation of AbstractIntepreter interface
57//
58class LLI : public AbstractInterpreter {
59  std::string LLIPath;          // The path to the LLI executable
60public:
61  LLI(const std::string &Path) : LLIPath(Path) { }
62
63  // LLI create method - Try to find the LLI executable
64  static LLI *create(BugDriver *BD, std::string &Message) {
65    std::string LLIPath = FindExecutable("lli", BD->getToolName());
66    if (!LLIPath.empty()) {
67      Message = "Found lli: " + LLIPath + "\n";
68      return new LLI(LLIPath);
69    }
70
71    Message = "Cannot find 'lli' in bugpoint executable directory or PATH!\n";
72    return 0;
73  }
74  virtual int ExecuteProgram(const std::string &Bytecode,
75			     const std::string &OutputFile);
76};
77
78int LLI::ExecuteProgram(const std::string &Bytecode,
79			const std::string &OutputFile) {
80  const char *Args[] = {
81    "-abort-on-exception",
82    "-quiet",
83    Bytecode.c_str(),
84    0
85  };
86
87  return RunProgramWithTimeout(LLIPath, Args,
88			       "/dev/null", OutputFile, OutputFile);
89}
90
91
92//===----------------------------------------------------------------------===//
93// BugDriver method implementation
94//
95
96/// initializeExecutionEnvironment - This method is used to set up the
97/// environment for executing LLVM programs.
98///
99bool BugDriver::initializeExecutionEnvironment() {
100  std::cout << "Initializing execution environment: ";
101
102  // FIXME: This should default to searching for the best interpreter to use on
103  // this platform, which would be JIT, then LLC, then CBE, then LLI.
104
105  // Create an instance of the AbstractInterpreter interface as specified on the
106  // command line
107  std::string Message;
108  if (InterpreterSel == RunLLI) {
109    Interpreter = LLI::create(this, Message);
110  } else {
111    Message = " Sorry, only LLI is supported right now!";
112  }
113
114  std::cout << Message;
115
116  // If there was an error creating the selected interpreter, quit with error.
117  return Interpreter == 0;
118}
119
120
121/// executeProgram - This method runs "Program", capturing the output of the
122/// program to a file, returning the filename of the file.  A recommended
123/// filename may be optionally specified.
124///
125std::string BugDriver::executeProgram(std::string OutputFile,
126				      std::string BytecodeFile) {
127  assert(Interpreter && "Interpreter should have been created already!");
128  bool CreatedBytecode = false;
129  if (BytecodeFile.empty()) {
130    // Emit the program to a bytecode file...
131    BytecodeFile = getUniqueFilename("bugpoint-test-program.bc");
132
133    if (writeProgramToFile(BytecodeFile, Program)) {
134      std::cerr << ToolName << ": Error emitting bytecode to file '"
135		<< BytecodeFile << "'!\n";
136      exit(1);
137    }
138    CreatedBytecode = true;
139  }
140
141  if (OutputFile.empty()) OutputFile = "bugpoint-execution-output";
142
143  // Check to see if this is a valid output filename...
144  OutputFile = getUniqueFilename(OutputFile);
145
146  // Actually execute the program!
147  int RetVal = Interpreter->ExecuteProgram(BytecodeFile, OutputFile);
148
149  // Remove the temporary bytecode file.
150  if (CreatedBytecode)
151    removeFile(BytecodeFile);
152
153  // Return the filename we captured the output to.
154  return OutputFile;
155}
156
157/// diffProgram - This method executes the specified module and diffs the output
158/// against the file specified by ReferenceOutputFile.  If the output is
159/// different, true is returned.
160///
161bool BugDriver::diffProgram(const std::string &ReferenceOutputFile,
162			    const std::string &BytecodeFile) {
163  // Execute the program, generating an output file...
164  std::string Output = executeProgram("", BytecodeFile);
165
166  std::ifstream ReferenceFile(ReferenceOutputFile.c_str());
167  if (!ReferenceFile) {
168    std::cerr << "Couldn't open reference output file '"
169	      << ReferenceOutputFile << "'\n";
170    exit(1);
171  }
172
173  std::ifstream OutputFile(Output.c_str());
174  if (!OutputFile) {
175    std::cerr << "Couldn't open output file: " << Output << "'!\n";
176    exit(1);
177  }
178
179  bool FilesDifferent = false;
180
181  // Compare the two files...
182  int C1, C2;
183  do {
184    C1 = ReferenceFile.get();
185    C2 = OutputFile.get();
186    if (C1 != C2) { FilesDifferent = true; break; }
187  } while (C1 != EOF);
188
189  removeFile(Output);
190  return FilesDifferent;
191}
192