ExecutionDriver.cpp revision 91eabc13d3a456cc4b387d3d7fdb041d976732c7
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 "SystemUtils.h" 19#include "Support/CommandLine.h" 20#include "Support/Statistic.h" 21#include <fstream> 22#include <iostream> 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 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/// AbstractInterpreter Class - Subclasses of this class are used to execute 47/// LLVM bytecode in a variety of ways. This abstract interface hides this 48/// complexity behind a simple interface. 49/// 50struct AbstractInterpreter { 51 52 virtual ~AbstractInterpreter() {} 53 54 /// ExecuteProgram - Run the specified bytecode file, emitting output to the 55 /// specified filename. This returns the exit code of the program. 56 /// 57 virtual int ExecuteProgram(const std::string &Bytecode, 58 const std::string &OutputFile, 59 const std::string &SharedLib = "") = 0; 60}; 61 62 63//===----------------------------------------------------------------------===// 64// LLI Implementation of AbstractIntepreter interface 65// 66class LLI : public AbstractInterpreter { 67 std::string LLIPath; // The path to the LLI executable 68public: 69 LLI(const std::string &Path) : LLIPath(Path) { } 70 71 // LLI create method - Try to find the LLI executable 72 static LLI *create(BugDriver *BD, std::string &Message) { 73 std::string LLIPath = FindExecutable("lli", BD->getToolName()); 74 if (!LLIPath.empty()) { 75 Message = "Found lli: " + LLIPath + "\n"; 76 return new LLI(LLIPath); 77 } 78 79 Message = "Cannot find `lli' in bugpoint executable directory or PATH!\n"; 80 return 0; 81 } 82 virtual int ExecuteProgram(const std::string &Bytecode, 83 const std::string &OutputFile, 84 const std::string &SharedLib = ""); 85}; 86 87int LLI::ExecuteProgram(const std::string &Bytecode, 88 const std::string &OutputFile, 89 const std::string &SharedLib) { 90 if (!SharedLib.empty()) { 91 std::cerr << "LLI currently does not support loading shared libraries.\n" 92 << "Exiting.\n"; 93 exit(1); 94 } 95 96 const char *Args[] = { 97 LLIPath.c_str(), 98 "-abort-on-exception", 99 "-quiet", 100 "-force-interpreter=true", 101 Bytecode.c_str(), 102 0 103 }; 104 105 std::cout << "<lli>"; 106 return RunProgramWithTimeout(LLIPath, Args, 107 InputFile, OutputFile, OutputFile); 108} 109 110//===----------------------------------------------------------------------===// 111// GCC abstraction 112// 113// This is not a *real* AbstractInterpreter as it does not accept bytecode 114// files, but only input acceptable to GCC, i.e. C, C++, and assembly files 115// 116class GCC { 117 std::string GCCPath; // The path to the gcc executable 118public: 119 GCC(const std::string &gccPath) : GCCPath(gccPath) { } 120 virtual ~GCC() {} 121 122 // GCC create method - Try to find the `gcc' executable 123 static GCC *create(BugDriver *BD, std::string &Message) { 124 std::string GCCPath = FindExecutable("gcc", BD->getToolName()); 125 if (GCCPath.empty()) { 126 Message = "Cannot find `gcc' in bugpoint executable directory or PATH!\n"; 127 return 0; 128 } 129 130 Message = "Found gcc: " + GCCPath + "\n"; 131 return new GCC(GCCPath); 132 } 133 134 virtual int ExecuteProgram(const std::string &ProgramFile, 135 FileType fileType, 136 const std::string &OutputFile, 137 const std::string &SharedLib = ""); 138 139 int MakeSharedObject(const std::string &InputFile, 140 FileType fileType, 141 std::string &OutputFile); 142 143 void ProcessFailure(const char **Args); 144}; 145 146int GCC::ExecuteProgram(const std::string &ProgramFile, 147 FileType fileType, 148 const std::string &OutputFile, 149 const std::string &SharedLib) { 150 std::string OutputBinary = getUniqueFilename("bugpoint.gcc.exe"); 151 const char **GCCArgs; 152 153 const char *ArgsWithoutSO[] = { 154 GCCPath.c_str(), 155 "-x", (fileType == AsmFile) ? "assembler" : "c", 156 ProgramFile.c_str(), // Specify the input filename... 157 "-o", OutputBinary.c_str(), // Output to the right filename... 158 "-lm", // Hard-code the math library... 159 "-O2", // Optimize the program a bit... 160 0 161 }; 162 const char *ArgsWithSO[] = { 163 GCCPath.c_str(), 164 SharedLib.c_str(), // Specify the shared library to link in... 165 "-x", (fileType == AsmFile) ? "assembler" : "c", 166 ProgramFile.c_str(), // Specify the input filename... 167 "-o", OutputBinary.c_str(), // Output to the right filename... 168 "-lm", // Hard-code the math library... 169 "-O2", // Optimize the program a bit... 170 0 171 }; 172 173 GCCArgs = (SharedLib.empty()) ? ArgsWithoutSO : ArgsWithSO; 174 std::cout << "<gcc>"; 175 if (RunProgramWithTimeout(GCCPath, GCCArgs, "/dev/null", "/dev/null", 176 "/dev/null")) { 177 ProcessFailure(GCCArgs); 178 exit(1); 179 } 180 181 const char *ProgramArgs[] = { 182 OutputBinary.c_str(), 183 0 184 }; 185 186 // Now that we have a binary, run it! 187 std::cout << "<program>"; 188 int ProgramResult = RunProgramWithTimeout(OutputBinary, ProgramArgs, 189 InputFile, OutputFile, OutputFile); 190 std::cout << "\n"; 191 removeFile(OutputBinary); 192 return ProgramResult; 193} 194 195int GCC::MakeSharedObject(const std::string &InputFile, 196 FileType fileType, 197 std::string &OutputFile) { 198 OutputFile = getUniqueFilename("./bugpoint.so"); 199 // Compile the C/asm file into a shared object 200 const char* GCCArgs[] = { 201 GCCPath.c_str(), 202 "-x", (fileType == AsmFile) ? "assembler" : "c", 203 InputFile.c_str(), // Specify the input filename... 204#if defined(sparc) || defined(__sparc__) || defined(__sparcv9) 205 "-G", // Compile a shared library, `-G' for Sparc 206#else 207 "-shared", // `-shared' for Linux/X86, maybe others 208#endif 209 "-o", OutputFile.c_str(), // Output to the right filename... 210 "-O2", // Optimize the program a bit... 211 0 212 }; 213 214 std::cout << "<gcc>"; 215 if(RunProgramWithTimeout(GCCPath, GCCArgs, "/dev/null", "/dev/null", 216 "/dev/null")) { 217 ProcessFailure(GCCArgs); 218 exit(1); 219 } 220 return 0; 221} 222 223void GCC::ProcessFailure(const char** GCCArgs) { 224 std::cerr << "\n*** bugpoint error: invocation of the C compiler failed!\n"; 225 for (const char **Arg = GCCArgs; *Arg; ++Arg) 226 std::cerr << " " << *Arg; 227 std::cerr << "\n"; 228 229 // Rerun the compiler, capturing any error messages to print them. 230 std::string ErrorFilename = getUniqueFilename("bugpoint.gcc.errors"); 231 RunProgramWithTimeout(GCCPath, GCCArgs, "/dev/null", ErrorFilename.c_str(), 232 ErrorFilename.c_str()); 233 234 // Print out the error messages generated by GCC if possible... 235 std::ifstream ErrorFile(ErrorFilename.c_str()); 236 if (ErrorFile) { 237 std::copy(std::istreambuf_iterator<char>(ErrorFile), 238 std::istreambuf_iterator<char>(), 239 std::ostreambuf_iterator<char>(std::cerr)); 240 ErrorFile.close(); 241 std::cerr << "\n"; 242 } 243 244 removeFile(ErrorFilename); 245} 246 247//===----------------------------------------------------------------------===// 248// LLC Implementation of AbstractIntepreter interface 249// 250class LLC : public AbstractInterpreter { 251 std::string LLCPath; // The path to the LLC executable 252 GCC *gcc; 253public: 254 LLC(const std::string &llcPath, GCC *Gcc) 255 : LLCPath(llcPath), gcc(Gcc) { } 256 ~LLC() { delete gcc; } 257 258 // LLC create method - Try to find the LLC executable 259 static LLC *create(BugDriver *BD, std::string &Message) { 260 std::string LLCPath = FindExecutable("llc", BD->getToolName()); 261 if (LLCPath.empty()) { 262 Message = "Cannot find `llc' in bugpoint executable directory or PATH!\n"; 263 return 0; 264 } 265 266 Message = "Found llc: " + LLCPath + "\n"; 267 GCC *gcc = GCC::create(BD, Message); 268 if (!gcc) { 269 std::cerr << Message << "\n"; 270 exit(1); 271 } 272 return new LLC(LLCPath, gcc); 273 } 274 275 virtual int ExecuteProgram(const std::string &Bytecode, 276 const std::string &OutputFile, 277 const std::string &SharedLib = ""); 278 279 int OutputAsm(const std::string &Bytecode, 280 std::string &OutputAsmFile); 281}; 282 283int LLC::OutputAsm(const std::string &Bytecode, 284 std::string &OutputAsmFile) { 285 OutputAsmFile = "bugpoint.llc.s"; 286 const char *LLCArgs[] = { 287 LLCPath.c_str(), 288 "-o", OutputAsmFile.c_str(), // Output to the Asm file 289 "-f", // Overwrite as necessary... 290 Bytecode.c_str(), // This is the input bytecode 291 0 292 }; 293 294 std::cout << "<llc>"; 295 if (RunProgramWithTimeout(LLCPath, LLCArgs, "/dev/null", "/dev/null", 296 "/dev/null")) { 297 // If LLC failed on the bytecode, print error... 298 std::cerr << "bugpoint error: `llc' failed!\n"; 299 removeFile(OutputAsmFile); 300 return 1; 301 } 302 303 return 0; 304} 305 306int LLC::ExecuteProgram(const std::string &Bytecode, 307 const std::string &OutputFile, 308 const std::string &SharedLib) { 309 310 std::string OutputAsmFile; 311 if (OutputAsm(Bytecode, OutputAsmFile)) { 312 std::cerr << "Could not generate asm code with `llc', exiting.\n"; 313 exit(1); 314 } 315 316 // Assuming LLC worked, compile the result with GCC and run it. 317 int Result = gcc->ExecuteProgram(OutputAsmFile,AsmFile,OutputFile,SharedLib); 318 removeFile(OutputAsmFile); 319 return Result; 320} 321 322 323//===----------------------------------------------------------------------===// 324// JIT Implementation of AbstractIntepreter interface 325// 326class JIT : public AbstractInterpreter { 327 std::string LLIPath; // The path to the LLI executable 328public: 329 JIT(const std::string &Path) : LLIPath(Path) { } 330 331 // JIT create method - Try to find the LLI executable 332 static JIT *create(BugDriver *BD, std::string &Message) { 333 std::string LLIPath = FindExecutable("lli", BD->getToolName()); 334 if (!LLIPath.empty()) { 335 Message = "Found lli: " + LLIPath + "\n"; 336 return new JIT(LLIPath); 337 } 338 339 Message = "Cannot find `lli' in bugpoint executable directory or PATH!\n"; 340 return 0; 341 } 342 virtual int ExecuteProgram(const std::string &Bytecode, 343 const std::string &OutputFile, 344 const std::string &SharedLib = ""); 345}; 346 347int JIT::ExecuteProgram(const std::string &Bytecode, 348 const std::string &OutputFile, 349 const std::string &SharedLib) { 350 const char* ArgsWithoutSO[] = { 351 LLIPath.c_str(), "-quiet", "-force-interpreter=false", 352 Bytecode.c_str(), 353 0 354 }; 355 356 const char* ArgsWithSO[] = { 357 LLIPath.c_str(), "-quiet", "-force-interpreter=false", 358 "-load", SharedLib.c_str(), 359 Bytecode.c_str(), 360 0 361 }; 362 363 const char** JITArgs = SharedLib.empty() ? ArgsWithoutSO : ArgsWithSO; 364 365 std::cout << "<jit>"; 366 DEBUG(std::cerr << "\nSending output to " << OutputFile << "\n"); 367 return RunProgramWithTimeout(LLIPath, JITArgs, 368 InputFile, OutputFile, OutputFile); 369} 370 371//===----------------------------------------------------------------------===// 372// CBE Implementation of AbstractIntepreter interface 373// 374class CBE : public AbstractInterpreter { 375 std::string DISPath; // The path to the LLVM 'dis' executable 376 GCC *gcc; 377public: 378 CBE(const std::string &disPath, GCC *Gcc) : DISPath(disPath), gcc(Gcc) { } 379 ~CBE() { delete gcc; } 380 381 // CBE create method - Try to find the 'dis' executable 382 static CBE *create(BugDriver *BD, std::string &Message) { 383 std::string DISPath = FindExecutable("dis", BD->getToolName()); 384 if (DISPath.empty()) { 385 Message = "Cannot find `dis' in bugpoint executable directory or PATH!\n"; 386 return 0; 387 } 388 389 Message = "Found dis: " + DISPath + "\n"; 390 391 GCC *gcc = GCC::create(BD, Message); 392 if (!gcc) { 393 std::cerr << Message << "\n"; 394 exit(1); 395 } 396 return new CBE(DISPath, gcc); 397 } 398 399 virtual int ExecuteProgram(const std::string &Bytecode, 400 const std::string &OutputFile, 401 const std::string &SharedLib = ""); 402 403 // Sometimes we just want to go half-way and only generate the C file, 404 // not necessarily compile it with GCC and run the program 405 virtual int OutputC(const std::string &Bytecode, 406 std::string &OutputCFile); 407 408}; 409 410int CBE::OutputC(const std::string &Bytecode, 411 std::string &OutputCFile) { 412 OutputCFile = "bugpoint.cbe.c"; 413 const char *DisArgs[] = { 414 DISPath.c_str(), 415 "-o", OutputCFile.c_str(), // Output to the C file 416 "-c", // Output to C 417 "-f", // Overwrite as necessary... 418 Bytecode.c_str(), // This is the input bytecode 419 0 420 }; 421 422 std::cout << "<cbe>"; 423 if (RunProgramWithTimeout(DISPath, DisArgs, "/dev/null", "/dev/null", 424 "/dev/null")) { 425 // If dis failed on the bytecode, print error... 426 std::cerr << "bugpoint error: `dis -c' failed!\n"; 427 return 1; 428 } 429 430 return 0; 431} 432 433 434int CBE::ExecuteProgram(const std::string &Bytecode, 435 const std::string &OutputFile, 436 const std::string &SharedLib) { 437 std::string OutputCFile; 438 if (OutputC(Bytecode, OutputCFile)) { 439 std::cerr << "Could not generate C code with `dis', exiting.\n"; 440 exit(1); 441 } 442 443 int Result = gcc->ExecuteProgram(OutputCFile, CFile, OutputFile, SharedLib); 444 removeFile(OutputCFile); 445 446 return Result; 447} 448 449 450//===----------------------------------------------------------------------===// 451// BugDriver method implementation 452// 453 454/// initializeExecutionEnvironment - This method is used to set up the 455/// environment for executing LLVM programs. 456/// 457bool BugDriver::initializeExecutionEnvironment() { 458 std::cout << "Initializing execution environment: "; 459 460 // FIXME: This should default to searching for the best interpreter to use on 461 // this platform, which would be JIT, then LLC, then CBE, then LLI. 462 463 // Create an instance of the AbstractInterpreter interface as specified on the 464 // command line 465 std::string Message; 466 switch (InterpreterSel) { 467 case RunLLI: Interpreter = LLI::create(this, Message); break; 468 case RunLLC: Interpreter = LLC::create(this, Message); break; 469 case RunJIT: Interpreter = JIT::create(this, Message); break; 470 case RunCBE: Interpreter = CBE::create(this, Message); break; 471 default: 472 Message = " Sorry, this back-end is not supported by bugpoint right now!\n"; 473 break; 474 } 475 476 std::cout << Message; 477 478 // Initialize auxiliary tools for debugging 479 cbe = CBE::create(this, Message); 480 if (!cbe) { std::cout << Message << "\nExiting.\n"; exit(1); } 481 gcc = GCC::create(this, Message); 482 if (!gcc) { std::cout << Message << "\nExiting.\n"; exit(1); } 483 484 // If there was an error creating the selected interpreter, quit with error. 485 return Interpreter == 0; 486} 487 488 489/// executeProgram - This method runs "Program", capturing the output of the 490/// program to a file, returning the filename of the file. A recommended 491/// filename may be optionally specified. 492/// 493std::string BugDriver::executeProgram(std::string OutputFile, 494 std::string BytecodeFile, 495 std::string SharedObject, 496 AbstractInterpreter *AI) { 497 assert((Interpreter || AI) &&"Interpreter should have been created already!"); 498 bool CreatedBytecode = false; 499 if (BytecodeFile.empty()) { 500 // Emit the program to a bytecode file... 501 BytecodeFile = getUniqueFilename("bugpoint-test-program.bc"); 502 503 if (writeProgramToFile(BytecodeFile, Program)) { 504 std::cerr << ToolName << ": Error emitting bytecode to file '" 505 << BytecodeFile << "'!\n"; 506 exit(1); 507 } 508 CreatedBytecode = true; 509 } 510 511 if (OutputFile.empty()) OutputFile = "bugpoint-execution-output"; 512 513 // Check to see if this is a valid output filename... 514 OutputFile = getUniqueFilename(OutputFile); 515 516 // Actually execute the program! 517 int RetVal = (AI != 0) ? 518 AI->ExecuteProgram(BytecodeFile, OutputFile, SharedObject) : 519 Interpreter->ExecuteProgram(BytecodeFile, OutputFile, SharedObject); 520 521 // Remove the temporary bytecode file. 522 if (CreatedBytecode) removeFile(BytecodeFile); 523 524 // Return the filename we captured the output to. 525 return OutputFile; 526} 527 528std::string BugDriver::executeProgramWithCBE(std::string OutputFile, 529 std::string BytecodeFile, 530 std::string SharedObject) { 531 return executeProgram(OutputFile, BytecodeFile, SharedObject, cbe); 532} 533 534int BugDriver::compileSharedObject(const std::string &BytecodeFile, 535 std::string &SharedObject) { 536 assert(Interpreter && "Interpreter should have been created already!"); 537 std::string Message, OutputCFile; 538 539 // Using CBE 540 cbe->OutputC(BytecodeFile, OutputCFile); 541 542#if 0 /* This is an alternative, as yet unimplemented */ 543 // Using LLC 544 LLC *llc = LLC::create(this, Message); 545 if (llc->OutputAsm(BytecodeFile, OutputFile)) { 546 std::cerr << "Could not generate asm code with `llc', exiting.\n"; 547 exit(1); 548 } 549#endif 550 551 gcc->MakeSharedObject(OutputCFile, CFile, SharedObject); 552 553 // Remove the intermediate C file 554 removeFile(OutputCFile); 555 556 return 0; 557} 558 559 560/// diffProgram - This method executes the specified module and diffs the output 561/// against the file specified by ReferenceOutputFile. If the output is 562/// different, true is returned. 563/// 564bool BugDriver::diffProgram(const std::string &BytecodeFile, 565 const std::string &SharedObject, 566 bool RemoveBytecode) { 567 // Execute the program, generating an output file... 568 std::string Output = executeProgram("", BytecodeFile, SharedObject); 569 570 std::ifstream ReferenceFile(ReferenceOutputFile.c_str()); 571 if (!ReferenceFile) { 572 std::cerr << "Couldn't open reference output file '" 573 << ReferenceOutputFile << "'\n"; 574 exit(1); 575 } 576 577 std::ifstream OutputFile(Output.c_str()); 578 if (!OutputFile) { 579 std::cerr << "Couldn't open output file: " << Output << "'!\n"; 580 exit(1); 581 } 582 583 bool FilesDifferent = false; 584 585 // Compare the two files... 586 int C1, C2; 587 do { 588 C1 = ReferenceFile.get(); 589 C2 = OutputFile.get(); 590 if (C1 != C2) { FilesDifferent = true; break; } 591 } while (C1 != EOF); 592 593 //removeFile(Output); 594 if (RemoveBytecode) removeFile(BytecodeFile); 595 return FilesDifferent; 596} 597 598bool BugDriver::isExecutingJIT() { 599 return InterpreterSel == RunJIT; 600} 601