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