ExecutionDriver.cpp revision ca25b956f1f4440c3392eaf0351305b0c81e6124
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/Debug.h" 21#include "Support/FileUtilities.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 GCCArgs.push_back((fileType == AsmFile) ? "assembler" : "c"); 172 GCCArgs.push_back(ProgramFile.c_str()); // Specify the input filename... 173 GCCArgs.push_back("-o"); 174 GCCArgs.push_back(OutputBinary.c_str()); // Output to the right file... 175 GCCArgs.push_back("-lm"); // Hard-code the math library... 176 GCCArgs.push_back("-O2"); // Optimize the program a bit... 177 GCCArgs.push_back(0); // NULL terminator 178 179 std::cout << "<gcc>" << std::flush; 180 if (RunProgramWithTimeout(GCCPath, &GCCArgs[0], "/dev/null", "/dev/null", 181 "/dev/null")) { 182 ProcessFailure(&GCCArgs[0]); 183 exit(1); 184 } 185 186 std::vector<const char*> ProgramArgs; 187 ProgramArgs.push_back(OutputBinary.c_str()); 188 // Add optional parameters to the running program from Argv 189 for (unsigned i=0, e = InputArgv.size(); i != e; ++i) 190 ProgramArgs.push_back(InputArgv[i].c_str()); 191 ProgramArgs.push_back(0); // NULL terminator 192 193 // Now that we have a binary, run it! 194 std::cout << "<program>" << std::flush; 195 DEBUG(std::cerr << "\nAbout to run:\n\t"; 196 for (unsigned i=0, e = ProgramArgs.size(); i != e; ++i) 197 std::cerr << " " << ProgramArgs[i]; 198 std::cerr << "\n"; 199 ); 200 int ProgramResult = RunProgramWithTimeout(OutputBinary, &ProgramArgs[0], 201 InputFile, OutputFile, OutputFile); 202 removeFile(OutputBinary); 203 return ProgramResult; 204} 205 206int GCC::MakeSharedObject(const std::string &InputFile, 207 FileType fileType, 208 std::string &OutputFile) { 209 OutputFile = getUniqueFilename("./bugpoint.so"); 210 // Compile the C/asm file into a shared object 211 const char* GCCArgs[] = { 212 GCCPath.c_str(), 213 "-x", (fileType == AsmFile) ? "assembler" : "c", 214 InputFile.c_str(), // Specify the input filename... 215#if defined(sparc) || defined(__sparc__) || defined(__sparcv9) 216 "-G", // Compile a shared library, `-G' for Sparc 217#else 218 "-shared", // `-shared' for Linux/X86, maybe others 219#endif 220 "-o", OutputFile.c_str(), // Output to the right filename... 221 "-O2", // Optimize the program a bit... 222 0 223 }; 224 225 std::cout << "<gcc>" << std::flush; 226 if(RunProgramWithTimeout(GCCPath, GCCArgs, "/dev/null", "/dev/null", 227 "/dev/null")) { 228 ProcessFailure(GCCArgs); 229 exit(1); 230 } 231 return 0; 232} 233 234void GCC::ProcessFailure(const char** GCCArgs) { 235 std::cerr << "\n*** bugpoint error: invocation of the C compiler failed!\n"; 236 for (const char **Arg = GCCArgs; *Arg; ++Arg) 237 std::cerr << " " << *Arg; 238 std::cerr << "\n"; 239 240 // Rerun the compiler, capturing any error messages to print them. 241 std::string ErrorFilename = getUniqueFilename("bugpoint.gcc.errors"); 242 RunProgramWithTimeout(GCCPath, GCCArgs, "/dev/null", ErrorFilename.c_str(), 243 ErrorFilename.c_str()); 244 245 // Print out the error messages generated by GCC if possible... 246 std::ifstream ErrorFile(ErrorFilename.c_str()); 247 if (ErrorFile) { 248 std::copy(std::istreambuf_iterator<char>(ErrorFile), 249 std::istreambuf_iterator<char>(), 250 std::ostreambuf_iterator<char>(std::cerr)); 251 ErrorFile.close(); 252 std::cerr << "\n"; 253 } 254 255 removeFile(ErrorFilename); 256} 257 258//===----------------------------------------------------------------------===// 259// LLC Implementation of AbstractIntepreter interface 260// 261class LLC : public AbstractInterpreter { 262 std::string LLCPath; // The path to the LLC executable 263 GCC *gcc; 264public: 265 LLC(const std::string &llcPath, GCC *Gcc) 266 : LLCPath(llcPath), gcc(Gcc) { } 267 ~LLC() { delete gcc; } 268 269 // LLC create method - Try to find the LLC executable 270 static LLC *create(BugDriver *BD, std::string &Message) { 271 std::string LLCPath = FindExecutable("llc", BD->getToolName()); 272 if (LLCPath.empty()) { 273 Message = "Cannot find `llc' in bugpoint executable directory or PATH!\n"; 274 return 0; 275 } 276 277 Message = "Found llc: " + LLCPath + "\n"; 278 GCC *gcc = GCC::create(BD, Message); 279 if (!gcc) { 280 std::cerr << Message << "\n"; 281 exit(1); 282 } 283 return new LLC(LLCPath, gcc); 284 } 285 286 virtual int ExecuteProgram(const std::string &Bytecode, 287 const std::string &OutputFile, 288 const std::string &SharedLib = ""); 289 290 int OutputAsm(const std::string &Bytecode, 291 std::string &OutputAsmFile); 292}; 293 294int LLC::OutputAsm(const std::string &Bytecode, 295 std::string &OutputAsmFile) { 296 OutputAsmFile = "bugpoint.llc.s"; 297 const char *LLCArgs[] = { 298 LLCPath.c_str(), 299 "-o", OutputAsmFile.c_str(), // Output to the Asm file 300 "-f", // Overwrite as necessary... 301 Bytecode.c_str(), // This is the input bytecode 302 0 303 }; 304 305 std::cout << "<llc>" << std::flush; 306 if (RunProgramWithTimeout(LLCPath, LLCArgs, "/dev/null", "/dev/null", 307 "/dev/null")) { 308 // If LLC failed on the bytecode, print error... 309 std::cerr << "bugpoint error: `llc' failed!\n"; 310 removeFile(OutputAsmFile); 311 return 1; 312 } 313 314 return 0; 315} 316 317int LLC::ExecuteProgram(const std::string &Bytecode, 318 const std::string &OutputFile, 319 const std::string &SharedLib) { 320 321 std::string OutputAsmFile; 322 if (OutputAsm(Bytecode, OutputAsmFile)) { 323 std::cerr << "Could not generate asm code with `llc', exiting.\n"; 324 exit(1); 325 } 326 327 // Assuming LLC worked, compile the result with GCC and run it. 328 int Result = gcc->ExecuteProgram(OutputAsmFile,AsmFile,OutputFile,SharedLib); 329 removeFile(OutputAsmFile); 330 return Result; 331} 332 333 334//===----------------------------------------------------------------------===// 335// JIT Implementation of AbstractIntepreter interface 336// 337class JIT : public AbstractInterpreter { 338 std::string LLIPath; // The path to the LLI executable 339public: 340 JIT(const std::string &Path) : LLIPath(Path) { } 341 342 // JIT create method - Try to find the LLI executable 343 static JIT *create(BugDriver *BD, std::string &Message) { 344 std::string LLIPath = FindExecutable("lli", BD->getToolName()); 345 if (!LLIPath.empty()) { 346 Message = "Found lli: " + LLIPath + "\n"; 347 return new JIT(LLIPath); 348 } 349 350 Message = "Cannot find `lli' in bugpoint executable directory or PATH!\n"; 351 return 0; 352 } 353 virtual int ExecuteProgram(const std::string &Bytecode, 354 const std::string &OutputFile, 355 const std::string &SharedLib = ""); 356}; 357 358int JIT::ExecuteProgram(const std::string &Bytecode, 359 const std::string &OutputFile, 360 const std::string &SharedLib) { 361 // Construct a vector of parameters, incorporating those from the command-line 362 std::vector<const char*> JITArgs; 363 JITArgs.push_back(LLIPath.c_str()); 364 JITArgs.push_back("-quiet"); 365 JITArgs.push_back("-force-interpreter=false"); 366 if (!SharedLib.empty()) { 367 JITArgs.push_back("-load"); 368 JITArgs.push_back(SharedLib.c_str()); 369 } 370 JITArgs.push_back(Bytecode.c_str()); 371 // Add optional parameters to the running program from Argv 372 for (unsigned i=0, e = InputArgv.size(); i != e; ++i) 373 JITArgs.push_back(InputArgv[i].c_str()); 374 JITArgs.push_back(0); 375 376 std::cout << "<jit>" << std::flush; 377 DEBUG(std::cerr << "\nAbout to run:\n\t"; 378 for (unsigned i=0, e = JITArgs.size(); i != e; ++i) 379 std::cerr << " " << JITArgs[i]; 380 std::cerr << "\n"; 381 ); 382 DEBUG(std::cerr << "\nSending output to " << OutputFile << "\n"); 383 return RunProgramWithTimeout(LLIPath, &JITArgs[0], 384 InputFile, OutputFile, OutputFile); 385} 386 387//===----------------------------------------------------------------------===// 388// CBE Implementation of AbstractIntepreter interface 389// 390class CBE : public AbstractInterpreter { 391 std::string DISPath; // The path to the LLVM 'dis' executable 392 GCC *gcc; 393public: 394 CBE(const std::string &disPath, GCC *Gcc) : DISPath(disPath), gcc(Gcc) { } 395 ~CBE() { delete gcc; } 396 397 // CBE create method - Try to find the 'dis' executable 398 static CBE *create(BugDriver *BD, std::string &Message) { 399 std::string DISPath = FindExecutable("dis", BD->getToolName()); 400 if (DISPath.empty()) { 401 Message = "Cannot find `dis' in bugpoint executable directory or PATH!\n"; 402 return 0; 403 } 404 405 Message = "Found dis: " + DISPath + "\n"; 406 407 GCC *gcc = GCC::create(BD, Message); 408 if (!gcc) { 409 std::cerr << Message << "\n"; 410 exit(1); 411 } 412 return new CBE(DISPath, gcc); 413 } 414 415 virtual int ExecuteProgram(const std::string &Bytecode, 416 const std::string &OutputFile, 417 const std::string &SharedLib = ""); 418 419 // Sometimes we just want to go half-way and only generate the C file, 420 // not necessarily compile it with GCC and run the program 421 virtual int OutputC(const std::string &Bytecode, 422 std::string &OutputCFile); 423 424}; 425 426int CBE::OutputC(const std::string &Bytecode, 427 std::string &OutputCFile) { 428 OutputCFile = "bugpoint.cbe.c"; 429 const char *DisArgs[] = { 430 DISPath.c_str(), 431 "-o", OutputCFile.c_str(), // Output to the C file 432 "-c", // Output to C 433 "-f", // Overwrite as necessary... 434 Bytecode.c_str(), // This is the input bytecode 435 0 436 }; 437 438 std::cout << "<cbe>" << std::flush; 439 if (RunProgramWithTimeout(DISPath, DisArgs, "/dev/null", "/dev/null", 440 "/dev/null")) { 441 // If dis failed on the bytecode, print error... 442 std::cerr << "bugpoint error: `dis -c' failed!\n"; 443 return 1; 444 } 445 446 return 0; 447} 448 449 450int CBE::ExecuteProgram(const std::string &Bytecode, 451 const std::string &OutputFile, 452 const std::string &SharedLib) { 453 std::string OutputCFile; 454 if (OutputC(Bytecode, OutputCFile)) { 455 std::cerr << "Could not generate C code with `dis', exiting.\n"; 456 exit(1); 457 } 458 459 int Result = gcc->ExecuteProgram(OutputCFile, CFile, OutputFile, SharedLib); 460 removeFile(OutputCFile); 461 462 return Result; 463} 464 465 466//===----------------------------------------------------------------------===// 467// BugDriver method implementation 468// 469 470/// initializeExecutionEnvironment - This method is used to set up the 471/// environment for executing LLVM programs. 472/// 473bool BugDriver::initializeExecutionEnvironment() { 474 std::cout << "Initializing execution environment: "; 475 476 // FIXME: This should default to searching for the best interpreter to use on 477 // this platform, which would be JIT, then LLC, then CBE, then LLI. 478 479 // Create an instance of the AbstractInterpreter interface as specified on the 480 // command line 481 std::string Message; 482 switch (InterpreterSel) { 483 case RunLLI: Interpreter = LLI::create(this, Message); break; 484 case RunLLC: Interpreter = LLC::create(this, Message); break; 485 case RunJIT: Interpreter = JIT::create(this, Message); break; 486 case RunCBE: Interpreter = CBE::create(this, Message); break; 487 default: 488 Message = " Sorry, this back-end is not supported by bugpoint right now!\n"; 489 break; 490 } 491 492 std::cout << Message; 493 494 // Initialize auxiliary tools for debugging 495 cbe = CBE::create(this, Message); 496 if (!cbe) { std::cout << Message << "\nExiting.\n"; exit(1); } 497 gcc = GCC::create(this, Message); 498 if (!gcc) { std::cout << Message << "\nExiting.\n"; exit(1); } 499 500 // If there was an error creating the selected interpreter, quit with error. 501 return Interpreter == 0; 502} 503 504 505/// executeProgram - This method runs "Program", capturing the output of the 506/// program to a file, returning the filename of the file. A recommended 507/// filename may be optionally specified. 508/// 509std::string BugDriver::executeProgram(std::string OutputFile, 510 std::string BytecodeFile, 511 std::string SharedObject, 512 AbstractInterpreter *AI) { 513 assert((Interpreter || AI) &&"Interpreter should have been created already!"); 514 bool CreatedBytecode = false; 515 if (BytecodeFile.empty()) { 516 // Emit the program to a bytecode file... 517 BytecodeFile = getUniqueFilename("bugpoint-test-program.bc"); 518 519 if (writeProgramToFile(BytecodeFile, Program)) { 520 std::cerr << ToolName << ": Error emitting bytecode to file '" 521 << BytecodeFile << "'!\n"; 522 exit(1); 523 } 524 CreatedBytecode = true; 525 } 526 527 if (OutputFile.empty()) OutputFile = "bugpoint-execution-output"; 528 529 // Check to see if this is a valid output filename... 530 OutputFile = getUniqueFilename(OutputFile); 531 532 // Actually execute the program! 533 int RetVal = (AI != 0) ? 534 AI->ExecuteProgram(BytecodeFile, OutputFile, SharedObject) : 535 Interpreter->ExecuteProgram(BytecodeFile, OutputFile, SharedObject); 536 537 // Remove the temporary bytecode file. 538 if (CreatedBytecode) removeFile(BytecodeFile); 539 540 // Return the filename we captured the output to. 541 return OutputFile; 542} 543 544std::string BugDriver::executeProgramWithCBE(std::string OutputFile, 545 std::string BytecodeFile, 546 std::string SharedObject) { 547 return executeProgram(OutputFile, BytecodeFile, SharedObject, cbe); 548} 549 550int BugDriver::compileSharedObject(const std::string &BytecodeFile, 551 std::string &SharedObject) { 552 assert(Interpreter && "Interpreter should have been created already!"); 553 std::string Message, OutputCFile; 554 555 // Using CBE 556 cbe->OutputC(BytecodeFile, OutputCFile); 557 558#if 0 /* This is an alternative, as yet unimplemented */ 559 // Using LLC 560 LLC *llc = LLC::create(this, Message); 561 if (llc->OutputAsm(BytecodeFile, OutputFile)) { 562 std::cerr << "Could not generate asm code with `llc', exiting.\n"; 563 exit(1); 564 } 565#endif 566 567 gcc->MakeSharedObject(OutputCFile, CFile, SharedObject); 568 569 // Remove the intermediate C file 570 removeFile(OutputCFile); 571 572 return 0; 573} 574 575 576/// diffProgram - This method executes the specified module and diffs the output 577/// against the file specified by ReferenceOutputFile. If the output is 578/// different, true is returned. 579/// 580bool BugDriver::diffProgram(const std::string &BytecodeFile, 581 const std::string &SharedObject, 582 bool RemoveBytecode) { 583 // Execute the program, generating an output file... 584 std::string Output = executeProgram("", BytecodeFile, SharedObject); 585 586 std::string Error; 587 bool FilesDifferent = false; 588 if (DiffFiles(ReferenceOutputFile, Output, &Error)) { 589 if (!Error.empty()) { 590 std::cerr << "While diffing output: " << Error << "\n"; 591 exit(1); 592 } 593 FilesDifferent = true; 594 } 595 596 if (RemoveBytecode) removeFile(BytecodeFile); 597 return FilesDifferent; 598} 599 600bool BugDriver::isExecutingJIT() { 601 return InterpreterSel == RunJIT; 602} 603