ExecutionDriver.cpp revision 7848e68c1635ccba5a08d55314d4e5aed5ab54b9
1//===- ExecutionDriver.cpp - Allow execution of LLVM program --------------===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file was developed by the LLVM research group and is distributed under 6// the University of Illinois Open Source License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file contains code used to execute the program utilizing one of the 11// various ways of running LLVM bytecode. 12// 13//===----------------------------------------------------------------------===// 14 15#include "BugDriver.h" 16#include "llvm/Support/ToolRunner.h" 17#include "Support/CommandLine.h" 18#include "Support/Debug.h" 19#include "Support/FileUtilities.h" 20#include "Support/SystemUtils.h" 21#include <fstream> 22using namespace llvm; 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 AutoPick, RunLLI, RunJIT, RunLLC, RunCBE 30 }; 31 32 cl::opt<OutputType> 33 InterpreterSel(cl::desc("Specify how LLVM code should be executed:"), 34 cl::values(clEnumValN(AutoPick, "auto", "Use best guess"), 35 clEnumValN(RunLLI, "run-int", 36 "Execute with the interpreter"), 37 clEnumValN(RunJIT, "run-jit", "Execute with JIT"), 38 clEnumValN(RunLLC, "run-llc", "Compile with LLC"), 39 clEnumValN(RunCBE, "run-cbe", "Compile with CBE"), 40 clEnumValEnd), 41 cl::init(AutoPick)); 42 43 cl::opt<bool> 44 CheckProgramExitCode("check-exit-code", 45 cl::desc("Assume nonzero exit code is failure (default on)"), 46 cl::init(true)); 47 48 cl::opt<std::string> 49 InputFile("input", cl::init("/dev/null"), 50 cl::desc("Filename to pipe in as stdin (default: /dev/null)")); 51 52 cl::list<std::string> 53 AdditionalSOs("additional-so", 54 cl::desc("Additional shared objects to load " 55 "into executing programs")); 56} 57 58namespace llvm { 59 // Anything specified after the --args option are taken as arguments to the 60 // program being debugged. 61 cl::list<std::string> 62 InputArgv("args", cl::Positional, cl::desc("<program arguments>..."), 63 cl::ZeroOrMore, cl::PositionalEatsArgs); 64 65 cl::list<std::string> 66 ToolArgv("tool-args", cl::Positional, cl::desc("<tool arguments>..."), 67 cl::ZeroOrMore, cl::PositionalEatsArgs); 68} 69 70//===----------------------------------------------------------------------===// 71// BugDriver method implementation 72// 73 74/// initializeExecutionEnvironment - This method is used to set up the 75/// environment for executing LLVM programs. 76/// 77bool BugDriver::initializeExecutionEnvironment() { 78 std::cout << "Initializing execution environment: "; 79 80 // Create an instance of the AbstractInterpreter interface as specified on 81 // the command line 82 cbe = 0; 83 std::string Message; 84 85 switch (InterpreterSel) { 86 case AutoPick: 87 InterpreterSel = RunCBE; 88 Interpreter = cbe = AbstractInterpreter::createCBE(getToolName(), Message, 89 &ToolArgv); 90 if (!Interpreter) { 91 InterpreterSel = RunJIT; 92 Interpreter = AbstractInterpreter::createJIT(getToolName(), Message, 93 &ToolArgv); 94 } 95 if (!Interpreter) { 96 InterpreterSel = RunLLC; 97 Interpreter = AbstractInterpreter::createLLC(getToolName(), Message, 98 &ToolArgv); 99 } 100 if (!Interpreter) { 101 InterpreterSel = RunLLI; 102 Interpreter = AbstractInterpreter::createLLI(getToolName(), Message, 103 &ToolArgv); 104 } 105 if (!Interpreter) { 106 InterpreterSel = AutoPick; 107 Message = "Sorry, I can't automatically select an interpreter!\n"; 108 } 109 break; 110 case RunLLI: 111 Interpreter = AbstractInterpreter::createLLI(getToolName(), Message, 112 &ToolArgv); 113 break; 114 case RunLLC: 115 Interpreter = AbstractInterpreter::createLLC(getToolName(), Message, 116 &ToolArgv); 117 break; 118 case RunJIT: 119 Interpreter = AbstractInterpreter::createJIT(getToolName(), Message, 120 &ToolArgv); 121 break; 122 case RunCBE: 123 Interpreter = cbe = AbstractInterpreter::createCBE(getToolName(), Message, 124 &ToolArgv); 125 break; 126 default: 127 Message = "Sorry, this back-end is not supported by bugpoint right now!\n"; 128 break; 129 } 130 std::cerr << Message; 131 132 // Initialize auxiliary tools for debugging 133 if (!cbe) { 134 cbe = AbstractInterpreter::createCBE(getToolName(), Message, &ToolArgv); 135 if (!cbe) { std::cout << Message << "\nExiting.\n"; exit(1); } 136 } 137 gcc = GCC::create(getToolName(), Message); 138 if (!gcc) { std::cout << Message << "\nExiting.\n"; exit(1); } 139 140 // If there was an error creating the selected interpreter, quit with error. 141 return Interpreter == 0; 142} 143 144/// compileProgram - Try to compile the specified module, throwing an exception 145/// if an error occurs, or returning normally if not. This is used for code 146/// generation crash testing. 147/// 148void BugDriver::compileProgram(Module *M) { 149 // Emit the program to a bytecode file... 150 std::string BytecodeFile = getUniqueFilename("bugpoint-test-program.bc"); 151 if (writeProgramToFile(BytecodeFile, M)) { 152 std::cerr << ToolName << ": Error emitting bytecode to file '" 153 << BytecodeFile << "'!\n"; 154 exit(1); 155 } 156 157 // Remove the temporary bytecode file when we are done. 158 FileRemover BytecodeFileRemover(BytecodeFile); 159 160 // Actually compile the program! 161 Interpreter->compileProgram(BytecodeFile); 162} 163 164 165/// executeProgram - This method runs "Program", capturing the output of the 166/// program to a file, returning the filename of the file. A recommended 167/// filename may be optionally specified. 168/// 169std::string BugDriver::executeProgram(std::string OutputFile, 170 std::string BytecodeFile, 171 const std::string &SharedObj, 172 AbstractInterpreter *AI, 173 bool *ProgramExitedNonzero) { 174 if (AI == 0) AI = Interpreter; 175 assert(AI && "Interpreter should have been created already!"); 176 bool CreatedBytecode = false; 177 if (BytecodeFile.empty()) { 178 // Emit the program to a bytecode file... 179 BytecodeFile = getUniqueFilename("bugpoint-test-program.bc"); 180 181 if (writeProgramToFile(BytecodeFile, Program)) { 182 std::cerr << ToolName << ": Error emitting bytecode to file '" 183 << BytecodeFile << "'!\n"; 184 exit(1); 185 } 186 CreatedBytecode = true; 187 } 188 189 // Remove the temporary bytecode file when we are done. 190 FileRemover BytecodeFileRemover(BytecodeFile, CreatedBytecode); 191 192 if (OutputFile.empty()) OutputFile = "bugpoint-execution-output"; 193 194 // Check to see if this is a valid output filename... 195 OutputFile = getUniqueFilename(OutputFile); 196 197 // Figure out which shared objects to run, if any. 198 std::vector<std::string> SharedObjs(AdditionalSOs); 199 if (!SharedObj.empty()) 200 SharedObjs.push_back(SharedObj); 201 202 // Actually execute the program! 203 int RetVal = AI->ExecuteProgram(BytecodeFile, InputArgv, InputFile, 204 OutputFile, SharedObjs); 205 206 if (ProgramExitedNonzero != 0) 207 *ProgramExitedNonzero = (RetVal != 0); 208 209 // Return the filename we captured the output to. 210 return OutputFile; 211} 212 213/// executeProgramWithCBE - Used to create reference output with the C 214/// backend, if reference output is not provided. 215/// 216std::string BugDriver::executeProgramWithCBE(std::string OutputFile) { 217 bool ProgramExitedNonzero; 218 std::string outFN = executeProgram(OutputFile, "", "", 219 (AbstractInterpreter*)cbe, 220 &ProgramExitedNonzero); 221 if (ProgramExitedNonzero) { 222 std::cerr 223 << "Warning: While generating reference output, program exited with\n" 224 << "non-zero exit code. This will NOT be treated as a failure.\n"; 225 CheckProgramExitCode = false; 226 } 227 return outFN; 228} 229 230std::string BugDriver::compileSharedObject(const std::string &BytecodeFile) { 231 assert(Interpreter && "Interpreter should have been created already!"); 232 std::string OutputCFile; 233 234 // Using CBE 235 cbe->OutputC(BytecodeFile, OutputCFile); 236 237#if 0 /* This is an alternative, as yet unimplemented */ 238 // Using LLC 239 std::string Message; 240 LLC *llc = createLLCtool(Message); 241 if (llc->OutputAsm(BytecodeFile, OutputFile)) { 242 std::cerr << "Could not generate asm code with `llc', exiting.\n"; 243 exit(1); 244 } 245#endif 246 247 std::string SharedObjectFile; 248 if (gcc->MakeSharedObject(OutputCFile, GCC::CFile, SharedObjectFile)) 249 exit(1); 250 251 // Remove the intermediate C file 252 removeFile(OutputCFile); 253 254 return "./" + SharedObjectFile; 255} 256 257 258/// diffProgram - This method executes the specified module and diffs the output 259/// against the file specified by ReferenceOutputFile. If the output is 260/// different, true is returned. 261/// 262bool BugDriver::diffProgram(const std::string &BytecodeFile, 263 const std::string &SharedObject, 264 bool RemoveBytecode) { 265 bool ProgramExitedNonzero; 266 267 // Execute the program, generating an output file... 268 std::string Output = executeProgram("", BytecodeFile, SharedObject, 0, 269 &ProgramExitedNonzero); 270 271 // If we're checking the program exit code, assume anything nonzero is bad. 272 if (CheckProgramExitCode && ProgramExitedNonzero) { 273 removeFile(Output); 274 if (RemoveBytecode) removeFile(BytecodeFile); 275 return true; 276 } 277 278 std::string Error; 279 bool FilesDifferent = false; 280 if (DiffFiles(ReferenceOutputFile, Output, &Error)) { 281 if (!Error.empty()) { 282 std::cerr << "While diffing output: " << Error << "\n"; 283 exit(1); 284 } 285 FilesDifferent = true; 286 } 287 288 // Remove the generated output. 289 removeFile(Output); 290 291 // Remove the bytecode file if we are supposed to. 292 if (RemoveBytecode) removeFile(BytecodeFile); 293 return FilesDifferent; 294} 295 296bool BugDriver::isExecutingJIT() { 297 return InterpreterSel == RunJIT; 298} 299 300