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