brintos

brintos / llvm-project-archived public Read only

0
0
Text · 16.4 KiB · 96eeb35 Raw
452 lines · cpp
1//===- ExecutionDriver.cpp - Allow execution of LLVM program --------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file contains code used to execute the program utilizing one of the10// various ways of running LLVM bitcode.11//12//===----------------------------------------------------------------------===//13 14#include "BugDriver.h"15#include "ToolRunner.h"16#include "llvm/Support/Debug.h"17#include "llvm/Support/FileUtilities.h"18#include "llvm/Support/Program.h"19#include "llvm/Support/SystemUtils.h"20#include "llvm/Support/raw_ostream.h"21#include <fstream>22 23using namespace llvm;24 25namespace {26// OutputType - Allow the user to specify the way code should be run, to test27// for miscompilation.28//29enum OutputType {30  AutoPick,31  RunLLI,32  RunJIT,33  RunLLC,34  RunLLCIA,35  CompileCustom,36  Custom37};38} // namespace39 40static cl::opt<double> AbsTolerance("abs-tolerance",41                                    cl::desc("Absolute error tolerated"),42                                    cl::init(0.0));43static cl::opt<double> RelTolerance("rel-tolerance",44                                    cl::desc("Relative error tolerated"),45                                    cl::init(0.0));46 47static cl::opt<OutputType> InterpreterSel(48    cl::desc("Specify the \"test\" i.e. suspect back-end:"),49    cl::values(clEnumValN(AutoPick, "auto", "Use best guess"),50               clEnumValN(RunLLI, "run-int", "Execute with the interpreter"),51               clEnumValN(RunJIT, "run-jit", "Execute with JIT"),52               clEnumValN(RunLLC, "run-llc", "Compile with LLC"),53               clEnumValN(RunLLCIA, "run-llc-ia",54                          "Compile with LLC with integrated assembler"),55               clEnumValN(CompileCustom, "compile-custom",56                          "Use -compile-command to define a command to "57                          "compile the bitcode. Useful to avoid linking."),58               clEnumValN(Custom, "run-custom",59                          "Use -exec-command to define a command to execute "60                          "the bitcode. Useful for cross-compilation.")),61    cl::init(AutoPick));62 63static cl::opt<OutputType> SafeInterpreterSel(64    cl::desc("Specify \"safe\" i.e. known-good backend:"),65    cl::values(clEnumValN(AutoPick, "safe-auto", "Use best guess"),66               clEnumValN(RunLLC, "safe-run-llc", "Compile with LLC"),67               clEnumValN(Custom, "safe-run-custom",68                          "Use -exec-command to define a command to execute "69                          "the bitcode. Useful for cross-compilation.")),70    cl::init(AutoPick));71 72static cl::opt<std::string> SafeInterpreterPath(73    "safe-path", cl::desc("Specify the path to the \"safe\" backend program"),74    cl::init(""));75 76static cl::opt<bool> AppendProgramExitCode(77    "append-exit-code",78    cl::desc("Append the exit code to the output so it gets diff'd too"),79    cl::init(false));80 81static cl::opt<std::string>82    InputFile("input", cl::init("/dev/null"),83              cl::desc("Filename to pipe in as stdin (default: /dev/null)"));84 85static cl::list<std::string>86    AdditionalSOs("additional-so", cl::desc("Additional shared objects to load "87                                            "into executing programs"));88 89static cl::list<std::string> AdditionalLinkerArgs(90    "Xlinker", cl::desc("Additional arguments to pass to the linker"));91 92static cl::opt<std::string> CustomCompileCommand(93    "compile-command", cl::init("llc"),94    cl::desc("Command to compile the bitcode (use with -compile-custom) "95             "(default: llc)"));96 97static cl::opt<std::string> CustomExecCommand(98    "exec-command", cl::init("simulate"),99    cl::desc("Command to execute the bitcode (use with -run-custom) "100             "(default: simulate)"));101 102// Anything specified after the --args option are taken as arguments to the103// program being debugged.104cl::list<std::string> llvm::InputArgv("args", cl::Positional,105                                      cl::desc("<program arguments>..."),106                                      cl::PositionalEatsArgs);107 108cl::opt<std::string> llvm::OutputPrefix(109    "output-prefix", cl::init("bugpoint"),110    cl::desc("Prefix to use for outputs (default: 'bugpoint')"));111 112static cl::list<std::string> ToolArgv("tool-args", cl::Positional,113                                      cl::desc("<tool arguments>..."),114                                      cl::PositionalEatsArgs);115 116static cl::list<std::string> SafeToolArgv("safe-tool-args", cl::Positional,117                                          cl::desc("<safe-tool arguments>..."),118                                          cl::PositionalEatsArgs);119 120static cl::opt<std::string> CCBinary("gcc", cl::init(""),121                                     cl::desc("The gcc binary to use."));122 123static cl::list<std::string> CCToolArgv("gcc-tool-args", cl::Positional,124                                        cl::desc("<gcc-tool arguments>..."),125                                        cl::PositionalEatsArgs);126//===----------------------------------------------------------------------===//127// BugDriver method implementation128//129 130/// initializeExecutionEnvironment - This method is used to set up the131/// environment for executing LLVM programs.132///133Error BugDriver::initializeExecutionEnvironment() {134  outs() << "Initializing execution environment: ";135 136  // Create an instance of the AbstractInterpreter interface as specified on137  // the command line138  SafeInterpreter = nullptr;139  std::string Message;140 141  if (CCBinary.empty()) {142    if (ErrorOr<std::string> ClangPath =143            FindProgramByName("clang", getToolName(), &AbsTolerance))144      CCBinary = *ClangPath;145    else146      CCBinary = "gcc";147  }148 149  switch (InterpreterSel) {150  case AutoPick:151    if (!Interpreter) {152      InterpreterSel = RunJIT;153      Interpreter =154          AbstractInterpreter::createJIT(getToolName(), Message, &ToolArgv);155    }156    if (!Interpreter) {157      InterpreterSel = RunLLC;158      Interpreter = AbstractInterpreter::createLLC(159          getToolName(), Message, CCBinary, &ToolArgv, &CCToolArgv);160    }161    if (!Interpreter) {162      InterpreterSel = RunLLI;163      Interpreter =164          AbstractInterpreter::createLLI(getToolName(), Message, &ToolArgv);165    }166    if (!Interpreter) {167      InterpreterSel = AutoPick;168      Message = "Sorry, I can't automatically select an interpreter!\n";169    }170    break;171  case RunLLI:172    Interpreter =173        AbstractInterpreter::createLLI(getToolName(), Message, &ToolArgv);174    break;175  case RunLLC:176  case RunLLCIA:177    Interpreter = AbstractInterpreter::createLLC(178        getToolName(), Message, CCBinary, &ToolArgv, &CCToolArgv,179        InterpreterSel == RunLLCIA);180    break;181  case RunJIT:182    Interpreter =183        AbstractInterpreter::createJIT(getToolName(), Message, &ToolArgv);184    break;185  case CompileCustom:186    Interpreter = AbstractInterpreter::createCustomCompiler(187        getToolName(), Message, CustomCompileCommand);188    break;189  case Custom:190    Interpreter = AbstractInterpreter::createCustomExecutor(191        getToolName(), Message, CustomExecCommand);192    break;193  }194  if (!Interpreter)195    errs() << Message;196  else // Display informational messages on stdout instead of stderr197    outs() << Message;198 199  std::string Path = SafeInterpreterPath;200  if (Path.empty())201    Path = getToolName();202  std::vector<std::string> SafeToolArgs = SafeToolArgv;203  switch (SafeInterpreterSel) {204  case AutoPick:205    // In "llc-safe" mode, default to using LLC as the "safe" backend.206    if (InterpreterSel == RunLLC) {207      SafeInterpreterSel = RunLLC;208      SafeToolArgs.push_back("--relocation-model=pic");209      SafeInterpreter = AbstractInterpreter::createLLC(210          Path.c_str(), Message, CCBinary, &SafeToolArgs, &CCToolArgv);211    } else if (InterpreterSel != CompileCustom) {212      SafeInterpreterSel = AutoPick;213      Message = "Sorry, I can't automatically select a safe interpreter!\n";214    }215    break;216  case RunLLC:217  case RunLLCIA:218    SafeToolArgs.push_back("--relocation-model=pic");219    SafeInterpreter = AbstractInterpreter::createLLC(220        Path.c_str(), Message, CCBinary, &SafeToolArgs, &CCToolArgv,221        SafeInterpreterSel == RunLLCIA);222    break;223  case Custom:224    SafeInterpreter = AbstractInterpreter::createCustomExecutor(225        getToolName(), Message, CustomExecCommand);226    break;227  default:228    Message = "Sorry, this back-end is not supported by bugpoint as the "229              "\"safe\" backend right now!\n";230    break;231  }232  if (!SafeInterpreter && InterpreterSel != CompileCustom) {233    outs() << Message << "\nExiting.\n";234    exit(1);235  }236 237  cc = CC::create(getToolName(), Message, CCBinary, &CCToolArgv);238  if (!cc) {239    outs() << Message << "\nExiting.\n";240    exit(1);241  }242 243  // If there was an error creating the selected interpreter, quit with error.244  if (Interpreter == nullptr)245    return make_error<StringError>("Failed to init execution environment",246                                   inconvertibleErrorCode());247  return Error::success();248}249 250/// Try to compile the specified module, returning false and setting Error if an251/// error occurs.  This is used for code generation crash testing.252Error BugDriver::compileProgram(Module &M) const {253  // Emit the program to a bitcode file...254  auto Temp =255      sys::fs::TempFile::create(OutputPrefix + "-test-program-%%%%%%%.bc");256  if (!Temp) {257    errs() << ToolName258           << ": Error making unique filename: " << toString(Temp.takeError())259           << "\n";260    exit(1);261  }262  DiscardTemp Discard{*Temp};263  if (writeProgramToFile(Temp->FD, M)) {264    errs() << ToolName << ": Error emitting bitcode to file '" << Temp->TmpName265           << "'!\n";266    exit(1);267  }268 269  // Actually compile the program!270  return Interpreter->compileProgram(Temp->TmpName, Timeout, MemoryLimit);271}272 273/// This method runs "Program", capturing the output of the program to a file,274/// returning the filename of the file.  A recommended filename may be275/// optionally specified.276Expected<std::string> BugDriver::executeProgram(const Module &Program,277                                                std::string OutputFile,278                                                std::string BitcodeFile,279                                                const std::string &SharedObj,280                                                AbstractInterpreter *AI) const {281  if (!AI)282    AI = Interpreter;283  assert(AI && "Interpreter should have been created already!");284  bool CreatedBitcode = false;285  if (BitcodeFile.empty()) {286    // Emit the program to a bitcode file...287    SmallString<128> UniqueFilename;288    int UniqueFD;289    std::error_code EC = sys::fs::createUniqueFile(290        OutputPrefix + "-test-program-%%%%%%%.bc", UniqueFD, UniqueFilename);291    if (EC) {292      errs() << ToolName << ": Error making unique filename: " << EC.message()293             << "!\n";294      exit(1);295    }296    BitcodeFile = std::string(UniqueFilename);297 298    if (writeProgramToFile(BitcodeFile, UniqueFD, Program)) {299      errs() << ToolName << ": Error emitting bitcode to file '" << BitcodeFile300             << "'!\n";301      exit(1);302    }303    CreatedBitcode = true;304  }305 306  // Remove the temporary bitcode file when we are done.307  std::string BitcodePath(BitcodeFile);308  FileRemover BitcodeFileRemover(BitcodePath, CreatedBitcode && !SaveTemps);309 310  if (OutputFile.empty())311    OutputFile = OutputPrefix + "-execution-output-%%%%%%%";312 313  // Check to see if this is a valid output filename...314  SmallString<128> UniqueFile;315  std::error_code EC = sys::fs::createUniqueFile(OutputFile, UniqueFile);316  if (EC) {317    errs() << ToolName << ": Error making unique filename: " << EC.message()318           << "\n";319    exit(1);320  }321  OutputFile = std::string(UniqueFile);322 323  // Figure out which shared objects to run, if any.324  std::vector<std::string> SharedObjs(AdditionalSOs);325  if (!SharedObj.empty())326    SharedObjs.push_back(SharedObj);327 328  Expected<int> RetVal = AI->ExecuteProgram(BitcodeFile, InputArgv, InputFile,329                                            OutputFile, AdditionalLinkerArgs,330                                            SharedObjs, Timeout, MemoryLimit);331  if (Error E = RetVal.takeError())332    return std::move(E);333 334  if (*RetVal == -1) {335    errs() << "<timeout>";336    static bool FirstTimeout = true;337    if (FirstTimeout) {338      outs()339          << "\n"340             "*** Program execution timed out!  This mechanism is designed to "341             "handle\n"342             "    programs stuck in infinite loops gracefully.  The -timeout "343             "option\n"344             "    can be used to change the timeout threshold or disable it "345             "completely\n"346             "    (with -timeout=0).  This message is only displayed once.\n";347      FirstTimeout = false;348    }349  }350 351  if (AppendProgramExitCode) {352    std::ofstream outFile(OutputFile.c_str(), std::ios_base::app);353    outFile << "exit " << *RetVal << '\n';354    outFile.close();355  }356 357  // Return the filename we captured the output to.358  return OutputFile;359}360 361/// Used to create reference output with the "safe" backend, if reference output362/// is not provided.363Expected<std::string>364BugDriver::executeProgramSafely(const Module &Program,365                                const std::string &OutputFile) const {366  return executeProgram(Program, OutputFile, "", "", SafeInterpreter);367}368 369Expected<std::string>370BugDriver::compileSharedObject(const std::string &BitcodeFile) {371  assert(Interpreter && "Interpreter should have been created already!");372  std::string OutputFile;373 374  // Using the known-good backend.375  Expected<CC::FileType> FT =376      SafeInterpreter->OutputCode(BitcodeFile, OutputFile);377  if (Error E = FT.takeError())378    return std::move(E);379 380  std::string SharedObjectFile;381  if (Error E = cc->MakeSharedObject(OutputFile, *FT, SharedObjectFile,382                                     AdditionalLinkerArgs))383    return std::move(E);384 385  // Remove the intermediate C file386  sys::fs::remove(OutputFile);387 388  return SharedObjectFile;389}390 391/// Calls compileProgram and then records the output into ReferenceOutputFile.392/// Returns true if reference file created, false otherwise. Note:393/// initializeExecutionEnvironment should be called BEFORE this function.394Error BugDriver::createReferenceFile(Module &M, const std::string &Filename) {395  if (Error E = compileProgram(*Program))396    return E;397 398  Expected<std::string> Result = executeProgramSafely(*Program, Filename);399  if (Error E = Result.takeError()) {400    if (Interpreter != SafeInterpreter) {401      E = joinErrors(402              std::move(E),403              make_error<StringError>(404                  "*** There is a bug running the \"safe\" backend.  Either"405                  " debug it (for example with the -run-jit bugpoint option,"406                  " if JIT is being used as the \"safe\" backend), or fix the"407                  " error some other way.\n",408                  inconvertibleErrorCode()));409    }410    return E;411  }412  ReferenceOutputFile = *Result;413  outs() << "\nReference output is: " << ReferenceOutputFile << "\n\n";414  return Error::success();415}416 417/// This method executes the specified module and diffs the output against the418/// file specified by ReferenceOutputFile.  If the output is different, 1 is419/// returned.  If there is a problem with the code generator (e.g., llc420/// crashes), this will set ErrMsg.421Expected<bool> BugDriver::diffProgram(const Module &Program,422                                      const std::string &BitcodeFile,423                                      const std::string &SharedObject,424                                      bool RemoveBitcode) const {425  // Execute the program, generating an output file...426  Expected<std::string> Output =427      executeProgram(Program, "", BitcodeFile, SharedObject, nullptr);428  if (Error E = Output.takeError())429    return std::move(E);430 431  std::string Error;432  bool FilesDifferent = false;433  if (int Diff = DiffFilesWithTolerance(ReferenceOutputFile, *Output,434                                        AbsTolerance, RelTolerance, &Error)) {435    if (Diff == 2) {436      errs() << "While diffing output: " << Error << '\n';437      exit(1);438    }439    FilesDifferent = true;440  } else {441    // Remove the generated output if there are no differences.442    sys::fs::remove(*Output);443  }444 445  // Remove the bitcode file if we are supposed to.446  if (RemoveBitcode)447    sys::fs::remove(BitcodeFile);448  return FilesDifferent;449}450 451bool BugDriver::isExecutingJIT() { return InterpreterSel == RunJIT; }452