ExecutionDriver.cpp revision cc876a7421f6dbcca98446058d5f0637092c6e1a
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#include <iostream> 22 23namespace { 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 43/// AbstractInterpreter Class - Subclasses of this class are used to execute 44/// LLVM bytecode in a variety of ways. This abstract interface hides this 45/// complexity behind a simple interface. 46/// 47struct AbstractInterpreter { 48 49 virtual ~AbstractInterpreter() {} 50 51 /// ExecuteProgram - Run the specified bytecode file, emitting output to the 52 /// specified filename. This returns the exit code of the program. 53 /// 54 virtual int ExecuteProgram(const std::string &Bytecode, 55 const std::string &OutputFile) = 0; 56 57}; 58 59 60//===----------------------------------------------------------------------===// 61// LLI Implementation of AbstractIntepreter interface 62// 63class LLI : public AbstractInterpreter { 64 std::string LLIPath; // The path to the LLI executable 65public: 66 LLI(const std::string &Path) : LLIPath(Path) { } 67 68 // LLI create method - Try to find the LLI executable 69 static LLI *create(BugDriver *BD, std::string &Message) { 70 std::string LLIPath = FindExecutable("lli", BD->getToolName()); 71 if (!LLIPath.empty()) { 72 Message = "Found lli: " + LLIPath + "\n"; 73 return new LLI(LLIPath); 74 } 75 76 Message = "Cannot find 'lli' in bugpoint executable directory or PATH!\n"; 77 return 0; 78 } 79 virtual int ExecuteProgram(const std::string &Bytecode, 80 const std::string &OutputFile); 81}; 82 83int LLI::ExecuteProgram(const std::string &Bytecode, 84 const std::string &OutputFile) { 85 const char *Args[] = { 86 "lli", 87 "-abort-on-exception", 88 "-quiet", 89 "-force-interpreter=true", 90 Bytecode.c_str(), 91 0 92 }; 93 94 return RunProgramWithTimeout(LLIPath, Args, 95 InputFile, OutputFile, OutputFile); 96} 97 98//===----------------------------------------------------------------------===// 99// JIT Implementation of AbstractIntepreter interface 100// 101class JIT : public AbstractInterpreter { 102 std::string LLIPath; // The path to the LLI executable 103public: 104 JIT(const std::string &Path) : LLIPath(Path) { } 105 106 // JIT create method - Try to find the LLI executable 107 static JIT *create(BugDriver *BD, std::string &Message) { 108 std::string LLIPath = FindExecutable("lli", BD->getToolName()); 109 if (!LLIPath.empty()) { 110 Message = "Found lli: " + LLIPath + "\n"; 111 return new JIT(LLIPath); 112 } 113 114 Message = "Cannot find 'lli' in bugpoint executable directory or PATH!\n"; 115 return 0; 116 } 117 virtual int ExecuteProgram(const std::string &Bytecode, 118 const std::string &OutputFile); 119}; 120 121int JIT::ExecuteProgram(const std::string &Bytecode, 122 const std::string &OutputFile) { 123 const char *Args[] = { 124 "-lli", 125 "-quiet", 126 "-force-interpreter=false", 127 Bytecode.c_str(), 128 0 129 }; 130 131 return RunProgramWithTimeout(LLIPath, Args, 132 InputFile, OutputFile, OutputFile); 133} 134 135//===----------------------------------------------------------------------===// 136// CBE Implementation of AbstractIntepreter interface 137// 138class CBE : public AbstractInterpreter { 139 std::string DISPath; // The path to the LLVM 'dis' executable 140 std::string GCCPath; // The path to the gcc executable 141public: 142 CBE(const std::string &disPath, const std::string &gccPath) 143 : DISPath(disPath), GCCPath(gccPath) { } 144 145 // CBE create method - Try to find the 'dis' executable 146 static CBE *create(BugDriver *BD, std::string &Message) { 147 std::string DISPath = FindExecutable("dis", BD->getToolName()); 148 if (DISPath.empty()) { 149 Message = "Cannot find 'dis' in bugpoint executable directory or PATH!\n"; 150 return 0; 151 } 152 153 Message = "Found dis: " + DISPath + "\n"; 154 155 std::string GCCPath = FindExecutable("gcc", BD->getToolName()); 156 if (GCCPath.empty()) { 157 Message = "Cannot find 'gcc' in bugpoint executable directory or PATH!\n"; 158 return 0; 159 } 160 161 Message += "Found gcc: " + GCCPath + "\n"; 162 return new CBE(DISPath, GCCPath); 163 } 164 virtual int ExecuteProgram(const std::string &Bytecode, 165 const std::string &OutputFile); 166}; 167 168int CBE::ExecuteProgram(const std::string &Bytecode, 169 const std::string &OutputFile) { 170 std::string OutputCFile = getUniqueFilename("bugpoint.cbe.c"); 171 const char *DisArgs[] = { 172 DISPath.c_str(), 173 "-o", OutputCFile.c_str(), // Output to the C file 174 "-c", // Output to C 175 "-f", // Overwrite as necessary... 176 Bytecode.c_str(), // This is the input bytecode 177 0 178 }; 179 180 std::cout << "<cbe>"; 181 if (RunProgramWithTimeout(DISPath, DisArgs, "/dev/null", "/dev/null", 182 "/dev/null")) { 183 // If dis failed on the bytecode, print error... 184 std::cerr << "bugpoint error: dis -c failed!?\n"; 185 removeFile(OutputCFile); 186 return 1; 187 } 188 189 // Assuming the c backend worked, compile the result with GCC... 190 std::string OutputBinary = getUniqueFilename("bugpoint.cbe.exe"); 191 const char *GCCArgs[] = { 192 GCCPath.c_str(), 193 "-x", "c", // Force recognition as a C file 194 "-o", OutputBinary.c_str(), // Output to the right filename... 195 OutputCFile.c_str(), // Specify the input filename... 196 "-O2", // Optimize the program a bit... 197 0 198 }; 199 200 // FIXME: Eventually the CC program and arguments for it should be settable on 201 // the bugpoint command line! 202 203 std::cout << "<gcc>"; 204 205 // Run the C compiler on the output of the C backend... 206 if (RunProgramWithTimeout(GCCPath, GCCArgs, "/dev/null", "/dev/null", 207 "/dev/null")) { 208 std::cerr << "\n*** bugpoint error: invocation of the C compiler " 209 "failed on CBE result!\n"; 210 for (const char **Arg = DisArgs; *Arg; ++Arg) 211 std::cerr << " " << *Arg; 212 std::cerr << "\n"; 213 for (const char **Arg = GCCArgs; *Arg; ++Arg) 214 std::cerr << " " << *Arg; 215 std::cerr << "\n"; 216 217 // Rerun the compiler, capturing any error messages to print them. 218 std::string ErrorFilename = getUniqueFilename("bugpoint.cbe.errors"); 219 RunProgramWithTimeout(GCCPath, GCCArgs, "/dev/null", ErrorFilename.c_str(), 220 ErrorFilename.c_str()); 221 222 // Read the error message into a string. 223 std::ifstream ErrorFile(ErrorFilename.c_str()); 224 if (ErrorFile) { 225 std::copy(std::istreambuf_iterator<char>(ErrorFile), 226 std::istreambuf_iterator<char>(), 227 std::ostreambuf_iterator<char>(std::cerr)); 228 ErrorFile.close(); 229 std::cerr << "\n"; 230 } 231 232 removeFile(ErrorFilename); 233 exit(1); // Leave stuff around for the user to inspect or debug the CBE 234 } 235 236 const char *ProgramArgs[] = { 237 OutputBinary.c_str(), 238 0 239 }; 240 241 std::cout << "<program>"; 242 243 // Now that we have a binary, run it! 244 int Result = RunProgramWithTimeout(OutputBinary, ProgramArgs, 245 InputFile, OutputFile, OutputFile); 246 std::cout << " "; 247 removeFile(OutputCFile); 248 removeFile(OutputBinary); 249 return Result; 250} 251 252//===----------------------------------------------------------------------===// 253// BugDriver method implementation 254// 255 256/// initializeExecutionEnvironment - This method is used to set up the 257/// environment for executing LLVM programs. 258/// 259bool BugDriver::initializeExecutionEnvironment() { 260 std::cout << "Initializing execution environment: "; 261 262 // FIXME: This should default to searching for the best interpreter to use on 263 // this platform, which would be JIT, then LLC, then CBE, then LLI. 264 265 // Create an instance of the AbstractInterpreter interface as specified on the 266 // command line 267 std::string Message; 268 switch (InterpreterSel) { 269 case RunLLI: Interpreter = LLI::create(this, Message); break; 270 case RunJIT: Interpreter = JIT::create(this, Message); break; 271 case RunCBE: Interpreter = CBE::create(this, Message); break; 272 default: 273 Message = " Sorry, this back-end is not supported by bugpoint right now!\n"; 274 break; 275 } 276 277 std::cout << Message; 278 279 // If there was an error creating the selected interpreter, quit with error. 280 return Interpreter == 0; 281} 282 283 284/// executeProgram - This method runs "Program", capturing the output of the 285/// program to a file, returning the filename of the file. A recommended 286/// filename may be optionally specified. 287/// 288std::string BugDriver::executeProgram(std::string OutputFile, 289 std::string BytecodeFile) { 290 assert(Interpreter && "Interpreter should have been created already!"); 291 bool CreatedBytecode = false; 292 if (BytecodeFile.empty()) { 293 // Emit the program to a bytecode file... 294 BytecodeFile = getUniqueFilename("bugpoint-test-program.bc"); 295 296 if (writeProgramToFile(BytecodeFile, Program)) { 297 std::cerr << ToolName << ": Error emitting bytecode to file '" 298 << BytecodeFile << "'!\n"; 299 exit(1); 300 } 301 CreatedBytecode = true; 302 } 303 304 if (OutputFile.empty()) OutputFile = "bugpoint-execution-output"; 305 306 // Check to see if this is a valid output filename... 307 OutputFile = getUniqueFilename(OutputFile); 308 309 // Actually execute the program! 310 int RetVal = Interpreter->ExecuteProgram(BytecodeFile, OutputFile); 311 312 // Remove the temporary bytecode file. 313 if (CreatedBytecode) 314 removeFile(BytecodeFile); 315 316 // Return the filename we captured the output to. 317 return OutputFile; 318} 319 320/// diffProgram - This method executes the specified module and diffs the output 321/// against the file specified by ReferenceOutputFile. If the output is 322/// different, true is returned. 323/// 324bool BugDriver::diffProgram(const std::string &ReferenceOutputFile, 325 const std::string &BytecodeFile, 326 bool RemoveBytecode) { 327 // Execute the program, generating an output file... 328 std::string Output = executeProgram("", BytecodeFile); 329 330 std::ifstream ReferenceFile(ReferenceOutputFile.c_str()); 331 if (!ReferenceFile) { 332 std::cerr << "Couldn't open reference output file '" 333 << ReferenceOutputFile << "'\n"; 334 exit(1); 335 } 336 337 std::ifstream OutputFile(Output.c_str()); 338 if (!OutputFile) { 339 std::cerr << "Couldn't open output file: " << Output << "'!\n"; 340 exit(1); 341 } 342 343 bool FilesDifferent = false; 344 345 // Compare the two files... 346 int C1, C2; 347 do { 348 C1 = ReferenceFile.get(); 349 C2 = OutputFile.get(); 350 if (C1 != C2) { FilesDifferent = true; break; } 351 } while (C1 != EOF); 352 353 removeFile(Output); 354 if (RemoveBytecode) removeFile(BytecodeFile); 355 return FilesDifferent; 356} 357