ExecutionDriver.cpp revision 4148556a9c767fdf3047ae8e004a759356b893dd
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, e.g. default to the first input filename.
15*/
16
17#include "BugDriver.h"
18#include "Support/ToolRunner.h"
19#include "Support/CommandLine.h"
20#include "Support/Debug.h"
21#include "Support/FileUtilities.h"
22#include "Support/SystemUtils.h"
23#include <fstream>
24#include <iostream>
25
26namespace {
27  // OutputType - Allow the user to specify the way code should be run, to test
28  // for miscompilation.
29  //
30  enum OutputType {
31    RunLLI, RunJIT, RunLLC, RunCBE
32  };
33
34  cl::opt<OutputType>
35  InterpreterSel(cl::desc("Specify how LLVM code should be executed:"),
36                 cl::values(clEnumValN(RunLLI, "run-lli", "Execute with LLI"),
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                            0));
41
42  cl::opt<std::string>
43  InputFile("input", cl::init("/dev/null"),
44            cl::desc("Filename to pipe in as stdin (default: /dev/null)"));
45}
46
47// Anything specified after the --args option are taken as arguments to the
48// program being debugged.
49cl::list<std::string>
50InputArgv("args", cl::Positional, cl::desc("<program arguments>..."),
51          cl::ZeroOrMore);
52
53//===----------------------------------------------------------------------===//
54// BugDriver method implementation
55//
56
57/// initializeExecutionEnvironment - This method is used to set up the
58/// environment for executing LLVM programs.
59///
60bool BugDriver::initializeExecutionEnvironment() {
61  std::cout << "Initializing execution environment: ";
62
63  // FIXME: This should default to searching for the best interpreter to use on
64  // this platform, which would be JIT, then LLC, then CBE, then LLI.
65
66  // Create an instance of the AbstractInterpreter interface as specified on
67  // the command line
68  std::string Message;
69  switch (InterpreterSel) {
70  case RunLLI: Interpreter = createLLItool(getToolName(), Message); break;
71  case RunLLC: Interpreter = createLLCtool(getToolName(), Message); break;
72  case RunJIT: Interpreter = createJITtool(getToolName(), Message); break;
73  case RunCBE: Interpreter = createCBEtool(getToolName(), Message); break;
74  default:
75    Message = "Sorry, this back-end is not supported by bugpoint right now!\n";
76    break;
77  }
78  std::cerr << Message;
79
80  // Initialize auxiliary tools for debugging
81  cbe = createCBEtool(getToolName(), Message);
82  if (!cbe) { std::cout << Message << "\nExiting.\n"; exit(1); }
83  gcc = createGCCtool(getToolName(), Message);
84  if (!gcc) { std::cout << Message << "\nExiting.\n"; exit(1); }
85
86  // If there was an error creating the selected interpreter, quit with error.
87  return Interpreter == 0;
88}
89
90
91/// executeProgram - This method runs "Program", capturing the output of the
92/// program to a file, returning the filename of the file.  A recommended
93/// filename may be optionally specified.
94///
95std::string BugDriver::executeProgram(std::string OutputFile,
96                                      std::string BytecodeFile,
97                                      std::string SharedObject,
98                                      AbstractInterpreter *AI) {
99  assert((Interpreter||AI) && "Interpreter should have been created already!");
100  bool CreatedBytecode = false;
101  if (BytecodeFile.empty()) {
102    // Emit the program to a bytecode file...
103    BytecodeFile = getUniqueFilename("bugpoint-test-program.bc");
104
105    if (writeProgramToFile(BytecodeFile, Program)) {
106      std::cerr << ToolName << ": Error emitting bytecode to file '"
107                << BytecodeFile << "'!\n";
108      exit(1);
109    }
110    CreatedBytecode = true;
111  }
112
113  if (OutputFile.empty()) OutputFile = "bugpoint-execution-output";
114
115  // Check to see if this is a valid output filename...
116  OutputFile = getUniqueFilename(OutputFile);
117
118  // Actually execute the program!
119  int RetVal = (AI != 0) ?
120    AI->ExecuteProgram(BytecodeFile, InputArgv, InputFile, OutputFile,
121                       SharedObject) :
122    Interpreter->ExecuteProgram(BytecodeFile, InputArgv,
123                                InputFile, OutputFile, SharedObject);
124
125  // Remove the temporary bytecode file.
126  if (CreatedBytecode) removeFile(BytecodeFile);
127
128  // Return the filename we captured the output to.
129  return OutputFile;
130}
131
132std::string BugDriver::executeProgramWithCBE(std::string OutputFile,
133                                             std::string BytecodeFile,
134                                             std::string SharedObject) {
135  return executeProgram(OutputFile, BytecodeFile, SharedObject, cbe);
136}
137
138int BugDriver::compileSharedObject(const std::string &BytecodeFile,
139                                   std::string &SharedObject) {
140  assert(Interpreter && "Interpreter should have been created already!");
141  std::string Message, OutputCFile;
142
143  // Using CBE
144  cbe->OutputC(BytecodeFile, OutputCFile);
145
146#if 0 /* This is an alternative, as yet unimplemented */
147  // Using LLC
148  LLC *llc = createLLCtool(Message);
149  if (llc->OutputAsm(BytecodeFile, OutputFile)) {
150    std::cerr << "Could not generate asm code with `llc', exiting.\n";
151    exit(1);
152  }
153#endif
154
155  gcc->MakeSharedObject(OutputCFile, CFile, SharedObject);
156
157  // Remove the intermediate C file
158  removeFile(OutputCFile);
159
160  return 0;
161}
162
163
164/// diffProgram - This method executes the specified module and diffs the output
165/// against the file specified by ReferenceOutputFile.  If the output is
166/// different, true is returned.
167///
168bool BugDriver::diffProgram(const std::string &BytecodeFile,
169                            const std::string &SharedObject,
170                            bool RemoveBytecode) {
171  // Execute the program, generating an output file...
172  std::string Output = executeProgram("", BytecodeFile, SharedObject);
173
174  std::string Error;
175  bool FilesDifferent = false;
176  if (DiffFiles(ReferenceOutputFile, Output, &Error)) {
177    if (!Error.empty()) {
178      std::cerr << "While diffing output: " << Error << "\n";
179      exit(1);
180    }
181    FilesDifferent = true;
182  }
183
184  if (RemoveBytecode) removeFile(BytecodeFile);
185  return FilesDifferent;
186}
187
188bool BugDriver::isExecutingJIT() {
189  return InterpreterSel == RunJIT;
190}
191