ExecutionDriver.cpp revision 7848e68c1635ccba5a08d55314d4e5aed5ab54b9
1//===- ExecutionDriver.cpp - Allow execution of LLVM program --------------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file was developed by the LLVM research group and is distributed under
6// the University of Illinois Open Source License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file contains code used to execute the program utilizing one of the
11// various ways of running LLVM bytecode.
12//
13//===----------------------------------------------------------------------===//
14
15#include "BugDriver.h"
16#include "llvm/Support/ToolRunner.h"
17#include "Support/CommandLine.h"
18#include "Support/Debug.h"
19#include "Support/FileUtilities.h"
20#include "Support/SystemUtils.h"
21#include <fstream>
22using namespace llvm;
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    AutoPick, RunLLI, RunJIT, RunLLC, RunCBE
30  };
31
32  cl::opt<OutputType>
33  InterpreterSel(cl::desc("Specify how LLVM code should be executed:"),
34                 cl::values(clEnumValN(AutoPick, "auto", "Use best guess"),
35                            clEnumValN(RunLLI, "run-int",
36                                       "Execute with the interpreter"),
37                            clEnumValN(RunJIT, "run-jit", "Execute with JIT"),
38                            clEnumValN(RunLLC, "run-llc", "Compile with LLC"),
39                            clEnumValN(RunCBE, "run-cbe", "Compile with CBE"),
40                            clEnumValEnd),
41                 cl::init(AutoPick));
42
43  cl::opt<bool>
44  CheckProgramExitCode("check-exit-code",
45                       cl::desc("Assume nonzero exit code is failure (default on)"),
46                       cl::init(true));
47
48  cl::opt<std::string>
49  InputFile("input", cl::init("/dev/null"),
50            cl::desc("Filename to pipe in as stdin (default: /dev/null)"));
51
52  cl::list<std::string>
53  AdditionalSOs("additional-so",
54                cl::desc("Additional shared objects to load "
55                         "into executing programs"));
56}
57
58namespace llvm {
59  // Anything specified after the --args option are taken as arguments to the
60  // program being debugged.
61  cl::list<std::string>
62  InputArgv("args", cl::Positional, cl::desc("<program arguments>..."),
63            cl::ZeroOrMore, cl::PositionalEatsArgs);
64
65  cl::list<std::string>
66  ToolArgv("tool-args", cl::Positional, cl::desc("<tool arguments>..."),
67           cl::ZeroOrMore, cl::PositionalEatsArgs);
68}
69
70//===----------------------------------------------------------------------===//
71// BugDriver method implementation
72//
73
74/// initializeExecutionEnvironment - This method is used to set up the
75/// environment for executing LLVM programs.
76///
77bool BugDriver::initializeExecutionEnvironment() {
78  std::cout << "Initializing execution environment: ";
79
80  // Create an instance of the AbstractInterpreter interface as specified on
81  // the command line
82  cbe = 0;
83  std::string Message;
84
85  switch (InterpreterSel) {
86  case AutoPick:
87    InterpreterSel = RunCBE;
88    Interpreter = cbe = AbstractInterpreter::createCBE(getToolName(), Message,
89                                                       &ToolArgv);
90    if (!Interpreter) {
91      InterpreterSel = RunJIT;
92      Interpreter = AbstractInterpreter::createJIT(getToolName(), Message,
93                                                   &ToolArgv);
94    }
95    if (!Interpreter) {
96      InterpreterSel = RunLLC;
97      Interpreter = AbstractInterpreter::createLLC(getToolName(), Message,
98                                                   &ToolArgv);
99    }
100    if (!Interpreter) {
101      InterpreterSel = RunLLI;
102      Interpreter = AbstractInterpreter::createLLI(getToolName(), Message,
103                                                   &ToolArgv);
104    }
105    if (!Interpreter) {
106      InterpreterSel = AutoPick;
107      Message = "Sorry, I can't automatically select an interpreter!\n";
108    }
109    break;
110  case RunLLI:
111    Interpreter = AbstractInterpreter::createLLI(getToolName(), Message,
112                                                 &ToolArgv);
113    break;
114  case RunLLC:
115    Interpreter = AbstractInterpreter::createLLC(getToolName(), Message,
116                                                 &ToolArgv);
117    break;
118  case RunJIT:
119    Interpreter = AbstractInterpreter::createJIT(getToolName(), Message,
120                                                 &ToolArgv);
121    break;
122  case RunCBE:
123    Interpreter = cbe = AbstractInterpreter::createCBE(getToolName(), Message,
124                                                       &ToolArgv);
125    break;
126  default:
127    Message = "Sorry, this back-end is not supported by bugpoint right now!\n";
128    break;
129  }
130  std::cerr << Message;
131
132  // Initialize auxiliary tools for debugging
133  if (!cbe) {
134    cbe = AbstractInterpreter::createCBE(getToolName(), Message, &ToolArgv);
135    if (!cbe) { std::cout << Message << "\nExiting.\n"; exit(1); }
136  }
137  gcc = GCC::create(getToolName(), Message);
138  if (!gcc) { std::cout << Message << "\nExiting.\n"; exit(1); }
139
140  // If there was an error creating the selected interpreter, quit with error.
141  return Interpreter == 0;
142}
143
144/// compileProgram - Try to compile the specified module, throwing an exception
145/// if an error occurs, or returning normally if not.  This is used for code
146/// generation crash testing.
147///
148void BugDriver::compileProgram(Module *M) {
149  // Emit the program to a bytecode file...
150  std::string BytecodeFile = getUniqueFilename("bugpoint-test-program.bc");
151  if (writeProgramToFile(BytecodeFile, M)) {
152    std::cerr << ToolName << ": Error emitting bytecode to file '"
153              << BytecodeFile << "'!\n";
154    exit(1);
155  }
156
157    // Remove the temporary bytecode file when we are done.
158  FileRemover BytecodeFileRemover(BytecodeFile);
159
160  // Actually compile the program!
161  Interpreter->compileProgram(BytecodeFile);
162}
163
164
165/// executeProgram - This method runs "Program", capturing the output of the
166/// program to a file, returning the filename of the file.  A recommended
167/// filename may be optionally specified.
168///
169std::string BugDriver::executeProgram(std::string OutputFile,
170                                      std::string BytecodeFile,
171                                      const std::string &SharedObj,
172                                      AbstractInterpreter *AI,
173                                      bool *ProgramExitedNonzero) {
174  if (AI == 0) AI = Interpreter;
175  assert(AI && "Interpreter should have been created already!");
176  bool CreatedBytecode = false;
177  if (BytecodeFile.empty()) {
178    // Emit the program to a bytecode file...
179    BytecodeFile = getUniqueFilename("bugpoint-test-program.bc");
180
181    if (writeProgramToFile(BytecodeFile, Program)) {
182      std::cerr << ToolName << ": Error emitting bytecode to file '"
183                << BytecodeFile << "'!\n";
184      exit(1);
185    }
186    CreatedBytecode = true;
187  }
188
189  // Remove the temporary bytecode file when we are done.
190  FileRemover BytecodeFileRemover(BytecodeFile, CreatedBytecode);
191
192  if (OutputFile.empty()) OutputFile = "bugpoint-execution-output";
193
194  // Check to see if this is a valid output filename...
195  OutputFile = getUniqueFilename(OutputFile);
196
197  // Figure out which shared objects to run, if any.
198  std::vector<std::string> SharedObjs(AdditionalSOs);
199  if (!SharedObj.empty())
200    SharedObjs.push_back(SharedObj);
201
202  // Actually execute the program!
203  int RetVal = AI->ExecuteProgram(BytecodeFile, InputArgv, InputFile,
204                                  OutputFile, SharedObjs);
205
206  if (ProgramExitedNonzero != 0)
207    *ProgramExitedNonzero = (RetVal != 0);
208
209  // Return the filename we captured the output to.
210  return OutputFile;
211}
212
213/// executeProgramWithCBE - Used to create reference output with the C
214/// backend, if reference output is not provided.
215///
216std::string BugDriver::executeProgramWithCBE(std::string OutputFile) {
217  bool ProgramExitedNonzero;
218  std::string outFN = executeProgram(OutputFile, "", "",
219                                     (AbstractInterpreter*)cbe,
220                                     &ProgramExitedNonzero);
221  if (ProgramExitedNonzero) {
222    std::cerr
223      << "Warning: While generating reference output, program exited with\n"
224      << "non-zero exit code. This will NOT be treated as a failure.\n";
225    CheckProgramExitCode = false;
226  }
227  return outFN;
228}
229
230std::string BugDriver::compileSharedObject(const std::string &BytecodeFile) {
231  assert(Interpreter && "Interpreter should have been created already!");
232  std::string OutputCFile;
233
234  // Using CBE
235  cbe->OutputC(BytecodeFile, OutputCFile);
236
237#if 0 /* This is an alternative, as yet unimplemented */
238  // Using LLC
239  std::string Message;
240  LLC *llc = createLLCtool(Message);
241  if (llc->OutputAsm(BytecodeFile, OutputFile)) {
242    std::cerr << "Could not generate asm code with `llc', exiting.\n";
243    exit(1);
244  }
245#endif
246
247  std::string SharedObjectFile;
248  if (gcc->MakeSharedObject(OutputCFile, GCC::CFile, SharedObjectFile))
249    exit(1);
250
251  // Remove the intermediate C file
252  removeFile(OutputCFile);
253
254  return "./" + SharedObjectFile;
255}
256
257
258/// diffProgram - This method executes the specified module and diffs the output
259/// against the file specified by ReferenceOutputFile.  If the output is
260/// different, true is returned.
261///
262bool BugDriver::diffProgram(const std::string &BytecodeFile,
263                            const std::string &SharedObject,
264                            bool RemoveBytecode) {
265  bool ProgramExitedNonzero;
266
267  // Execute the program, generating an output file...
268  std::string Output = executeProgram("", BytecodeFile, SharedObject, 0,
269                                      &ProgramExitedNonzero);
270
271  // If we're checking the program exit code, assume anything nonzero is bad.
272  if (CheckProgramExitCode && ProgramExitedNonzero) {
273    removeFile(Output);
274    if (RemoveBytecode) removeFile(BytecodeFile);
275    return true;
276  }
277
278  std::string Error;
279  bool FilesDifferent = false;
280  if (DiffFiles(ReferenceOutputFile, Output, &Error)) {
281    if (!Error.empty()) {
282      std::cerr << "While diffing output: " << Error << "\n";
283      exit(1);
284    }
285    FilesDifferent = true;
286  }
287
288  // Remove the generated output.
289  removeFile(Output);
290
291  // Remove the bytecode file if we are supposed to.
292  if (RemoveBytecode) removeFile(BytecodeFile);
293  return FilesDifferent;
294}
295
296bool BugDriver::isExecutingJIT() {
297  return InterpreterSel == RunJIT;
298}
299
300