Path: blob/main/contrib/llvm-project/llvm/tools/bugpoint/ExecutionDriver.cpp
35231 views
//===- ExecutionDriver.cpp - Allow execution of LLVM program --------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file contains code used to execute the program utilizing one of the9// various ways of running LLVM bitcode.10//11//===----------------------------------------------------------------------===//1213#include "BugDriver.h"14#include "ToolRunner.h"15#include "llvm/Support/CommandLine.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>2223using namespace llvm;2425namespace {26// OutputType - Allow the user to specify the way code should be run, to test27// for miscompilation.28//29enum OutputType {30AutoPick,31RunLLI,32RunJIT,33RunLLC,34RunLLCIA,35CompileCustom,36Custom37};3839cl::opt<double> AbsTolerance("abs-tolerance",40cl::desc("Absolute error tolerated"),41cl::init(0.0));42cl::opt<double> RelTolerance("rel-tolerance",43cl::desc("Relative error tolerated"),44cl::init(0.0));4546cl::opt<OutputType> InterpreterSel(47cl::desc("Specify the \"test\" i.e. suspect back-end:"),48cl::values(clEnumValN(AutoPick, "auto", "Use best guess"),49clEnumValN(RunLLI, "run-int", "Execute with the interpreter"),50clEnumValN(RunJIT, "run-jit", "Execute with JIT"),51clEnumValN(RunLLC, "run-llc", "Compile with LLC"),52clEnumValN(RunLLCIA, "run-llc-ia",53"Compile with LLC with integrated assembler"),54clEnumValN(CompileCustom, "compile-custom",55"Use -compile-command to define a command to "56"compile the bitcode. Useful to avoid linking."),57clEnumValN(Custom, "run-custom",58"Use -exec-command to define a command to execute "59"the bitcode. Useful for cross-compilation.")),60cl::init(AutoPick));6162cl::opt<OutputType> SafeInterpreterSel(63cl::desc("Specify \"safe\" i.e. known-good backend:"),64cl::values(clEnumValN(AutoPick, "safe-auto", "Use best guess"),65clEnumValN(RunLLC, "safe-run-llc", "Compile with LLC"),66clEnumValN(Custom, "safe-run-custom",67"Use -exec-command to define a command to execute "68"the bitcode. Useful for cross-compilation.")),69cl::init(AutoPick));7071cl::opt<std::string> SafeInterpreterPath(72"safe-path", cl::desc("Specify the path to the \"safe\" backend program"),73cl::init(""));7475cl::opt<bool> AppendProgramExitCode(76"append-exit-code",77cl::desc("Append the exit code to the output so it gets diff'd too"),78cl::init(false));7980cl::opt<std::string>81InputFile("input", cl::init("/dev/null"),82cl::desc("Filename to pipe in as stdin (default: /dev/null)"));8384cl::list<std::string>85AdditionalSOs("additional-so", cl::desc("Additional shared objects to load "86"into executing programs"));8788cl::list<std::string> AdditionalLinkerArgs(89"Xlinker", cl::desc("Additional arguments to pass to the linker"));9091cl::opt<std::string> CustomCompileCommand(92"compile-command", cl::init("llc"),93cl::desc("Command to compile the bitcode (use with -compile-custom) "94"(default: llc)"));9596cl::opt<std::string> CustomExecCommand(97"exec-command", cl::init("simulate"),98cl::desc("Command to execute the bitcode (use with -run-custom) "99"(default: simulate)"));100}101102namespace llvm {103// Anything specified after the --args option are taken as arguments to the104// program being debugged.105cl::list<std::string> InputArgv("args", cl::Positional,106cl::desc("<program arguments>..."),107cl::PositionalEatsArgs);108109cl::opt<std::string>110OutputPrefix("output-prefix", cl::init("bugpoint"),111cl::desc("Prefix to use for outputs (default: 'bugpoint')"));112}113114namespace {115cl::list<std::string> ToolArgv("tool-args", cl::Positional,116cl::desc("<tool arguments>..."),117cl::PositionalEatsArgs);118119cl::list<std::string> SafeToolArgv("safe-tool-args", cl::Positional,120cl::desc("<safe-tool arguments>..."),121cl::PositionalEatsArgs);122123cl::opt<std::string> CCBinary("gcc", cl::init(""),124cl::desc("The gcc binary to use."));125126cl::list<std::string> CCToolArgv("gcc-tool-args", cl::Positional,127cl::desc("<gcc-tool arguments>..."),128cl::PositionalEatsArgs);129}130131//===----------------------------------------------------------------------===//132// BugDriver method implementation133//134135/// initializeExecutionEnvironment - This method is used to set up the136/// environment for executing LLVM programs.137///138Error BugDriver::initializeExecutionEnvironment() {139outs() << "Initializing execution environment: ";140141// Create an instance of the AbstractInterpreter interface as specified on142// the command line143SafeInterpreter = nullptr;144std::string Message;145146if (CCBinary.empty()) {147if (ErrorOr<std::string> ClangPath =148FindProgramByName("clang", getToolName(), &AbsTolerance))149CCBinary = *ClangPath;150else151CCBinary = "gcc";152}153154switch (InterpreterSel) {155case AutoPick:156if (!Interpreter) {157InterpreterSel = RunJIT;158Interpreter =159AbstractInterpreter::createJIT(getToolName(), Message, &ToolArgv);160}161if (!Interpreter) {162InterpreterSel = RunLLC;163Interpreter = AbstractInterpreter::createLLC(164getToolName(), Message, CCBinary, &ToolArgv, &CCToolArgv);165}166if (!Interpreter) {167InterpreterSel = RunLLI;168Interpreter =169AbstractInterpreter::createLLI(getToolName(), Message, &ToolArgv);170}171if (!Interpreter) {172InterpreterSel = AutoPick;173Message = "Sorry, I can't automatically select an interpreter!\n";174}175break;176case RunLLI:177Interpreter =178AbstractInterpreter::createLLI(getToolName(), Message, &ToolArgv);179break;180case RunLLC:181case RunLLCIA:182Interpreter = AbstractInterpreter::createLLC(183getToolName(), Message, CCBinary, &ToolArgv, &CCToolArgv,184InterpreterSel == RunLLCIA);185break;186case RunJIT:187Interpreter =188AbstractInterpreter::createJIT(getToolName(), Message, &ToolArgv);189break;190case CompileCustom:191Interpreter = AbstractInterpreter::createCustomCompiler(192getToolName(), Message, CustomCompileCommand);193break;194case Custom:195Interpreter = AbstractInterpreter::createCustomExecutor(196getToolName(), Message, CustomExecCommand);197break;198}199if (!Interpreter)200errs() << Message;201else // Display informational messages on stdout instead of stderr202outs() << Message;203204std::string Path = SafeInterpreterPath;205if (Path.empty())206Path = getToolName();207std::vector<std::string> SafeToolArgs = SafeToolArgv;208switch (SafeInterpreterSel) {209case AutoPick:210// In "llc-safe" mode, default to using LLC as the "safe" backend.211if (InterpreterSel == RunLLC) {212SafeInterpreterSel = RunLLC;213SafeToolArgs.push_back("--relocation-model=pic");214SafeInterpreter = AbstractInterpreter::createLLC(215Path.c_str(), Message, CCBinary, &SafeToolArgs, &CCToolArgv);216} else if (InterpreterSel != CompileCustom) {217SafeInterpreterSel = AutoPick;218Message = "Sorry, I can't automatically select a safe interpreter!\n";219}220break;221case RunLLC:222case RunLLCIA:223SafeToolArgs.push_back("--relocation-model=pic");224SafeInterpreter = AbstractInterpreter::createLLC(225Path.c_str(), Message, CCBinary, &SafeToolArgs, &CCToolArgv,226SafeInterpreterSel == RunLLCIA);227break;228case Custom:229SafeInterpreter = AbstractInterpreter::createCustomExecutor(230getToolName(), Message, CustomExecCommand);231break;232default:233Message = "Sorry, this back-end is not supported by bugpoint as the "234"\"safe\" backend right now!\n";235break;236}237if (!SafeInterpreter && InterpreterSel != CompileCustom) {238outs() << Message << "\nExiting.\n";239exit(1);240}241242cc = CC::create(getToolName(), Message, CCBinary, &CCToolArgv);243if (!cc) {244outs() << Message << "\nExiting.\n";245exit(1);246}247248// If there was an error creating the selected interpreter, quit with error.249if (Interpreter == nullptr)250return make_error<StringError>("Failed to init execution environment",251inconvertibleErrorCode());252return Error::success();253}254255/// Try to compile the specified module, returning false and setting Error if an256/// error occurs. This is used for code generation crash testing.257Error BugDriver::compileProgram(Module &M) const {258// Emit the program to a bitcode file...259auto Temp =260sys::fs::TempFile::create(OutputPrefix + "-test-program-%%%%%%%.bc");261if (!Temp) {262errs() << ToolName263<< ": Error making unique filename: " << toString(Temp.takeError())264<< "\n";265exit(1);266}267DiscardTemp Discard{*Temp};268if (writeProgramToFile(Temp->FD, M)) {269errs() << ToolName << ": Error emitting bitcode to file '" << Temp->TmpName270<< "'!\n";271exit(1);272}273274// Actually compile the program!275return Interpreter->compileProgram(Temp->TmpName, Timeout, MemoryLimit);276}277278/// This method runs "Program", capturing the output of the program to a file,279/// returning the filename of the file. A recommended filename may be280/// optionally specified.281Expected<std::string> BugDriver::executeProgram(const Module &Program,282std::string OutputFile,283std::string BitcodeFile,284const std::string &SharedObj,285AbstractInterpreter *AI) const {286if (!AI)287AI = Interpreter;288assert(AI && "Interpreter should have been created already!");289bool CreatedBitcode = false;290if (BitcodeFile.empty()) {291// Emit the program to a bitcode file...292SmallString<128> UniqueFilename;293int UniqueFD;294std::error_code EC = sys::fs::createUniqueFile(295OutputPrefix + "-test-program-%%%%%%%.bc", UniqueFD, UniqueFilename);296if (EC) {297errs() << ToolName << ": Error making unique filename: " << EC.message()298<< "!\n";299exit(1);300}301BitcodeFile = std::string(UniqueFilename);302303if (writeProgramToFile(BitcodeFile, UniqueFD, Program)) {304errs() << ToolName << ": Error emitting bitcode to file '" << BitcodeFile305<< "'!\n";306exit(1);307}308CreatedBitcode = true;309}310311// Remove the temporary bitcode file when we are done.312std::string BitcodePath(BitcodeFile);313FileRemover BitcodeFileRemover(BitcodePath, CreatedBitcode && !SaveTemps);314315if (OutputFile.empty())316OutputFile = OutputPrefix + "-execution-output-%%%%%%%";317318// Check to see if this is a valid output filename...319SmallString<128> UniqueFile;320std::error_code EC = sys::fs::createUniqueFile(OutputFile, UniqueFile);321if (EC) {322errs() << ToolName << ": Error making unique filename: " << EC.message()323<< "\n";324exit(1);325}326OutputFile = std::string(UniqueFile);327328// Figure out which shared objects to run, if any.329std::vector<std::string> SharedObjs(AdditionalSOs);330if (!SharedObj.empty())331SharedObjs.push_back(SharedObj);332333Expected<int> RetVal = AI->ExecuteProgram(BitcodeFile, InputArgv, InputFile,334OutputFile, AdditionalLinkerArgs,335SharedObjs, Timeout, MemoryLimit);336if (Error E = RetVal.takeError())337return std::move(E);338339if (*RetVal == -1) {340errs() << "<timeout>";341static bool FirstTimeout = true;342if (FirstTimeout) {343outs()344<< "\n"345"*** Program execution timed out! This mechanism is designed to "346"handle\n"347" programs stuck in infinite loops gracefully. The -timeout "348"option\n"349" can be used to change the timeout threshold or disable it "350"completely\n"351" (with -timeout=0). This message is only displayed once.\n";352FirstTimeout = false;353}354}355356if (AppendProgramExitCode) {357std::ofstream outFile(OutputFile.c_str(), std::ios_base::app);358outFile << "exit " << *RetVal << '\n';359outFile.close();360}361362// Return the filename we captured the output to.363return OutputFile;364}365366/// Used to create reference output with the "safe" backend, if reference output367/// is not provided.368Expected<std::string>369BugDriver::executeProgramSafely(const Module &Program,370const std::string &OutputFile) const {371return executeProgram(Program, OutputFile, "", "", SafeInterpreter);372}373374Expected<std::string>375BugDriver::compileSharedObject(const std::string &BitcodeFile) {376assert(Interpreter && "Interpreter should have been created already!");377std::string OutputFile;378379// Using the known-good backend.380Expected<CC::FileType> FT =381SafeInterpreter->OutputCode(BitcodeFile, OutputFile);382if (Error E = FT.takeError())383return std::move(E);384385std::string SharedObjectFile;386if (Error E = cc->MakeSharedObject(OutputFile, *FT, SharedObjectFile,387AdditionalLinkerArgs))388return std::move(E);389390// Remove the intermediate C file391sys::fs::remove(OutputFile);392393return SharedObjectFile;394}395396/// Calls compileProgram and then records the output into ReferenceOutputFile.397/// Returns true if reference file created, false otherwise. Note:398/// initializeExecutionEnvironment should be called BEFORE this function.399Error BugDriver::createReferenceFile(Module &M, const std::string &Filename) {400if (Error E = compileProgram(*Program))401return E;402403Expected<std::string> Result = executeProgramSafely(*Program, Filename);404if (Error E = Result.takeError()) {405if (Interpreter != SafeInterpreter) {406E = joinErrors(407std::move(E),408make_error<StringError>(409"*** There is a bug running the \"safe\" backend. Either"410" debug it (for example with the -run-jit bugpoint option,"411" if JIT is being used as the \"safe\" backend), or fix the"412" error some other way.\n",413inconvertibleErrorCode()));414}415return E;416}417ReferenceOutputFile = *Result;418outs() << "\nReference output is: " << ReferenceOutputFile << "\n\n";419return Error::success();420}421422/// This method executes the specified module and diffs the output against the423/// file specified by ReferenceOutputFile. If the output is different, 1 is424/// returned. If there is a problem with the code generator (e.g., llc425/// crashes), this will set ErrMsg.426Expected<bool> BugDriver::diffProgram(const Module &Program,427const std::string &BitcodeFile,428const std::string &SharedObject,429bool RemoveBitcode) const {430// Execute the program, generating an output file...431Expected<std::string> Output =432executeProgram(Program, "", BitcodeFile, SharedObject, nullptr);433if (Error E = Output.takeError())434return std::move(E);435436std::string Error;437bool FilesDifferent = false;438if (int Diff = DiffFilesWithTolerance(ReferenceOutputFile, *Output,439AbsTolerance, RelTolerance, &Error)) {440if (Diff == 2) {441errs() << "While diffing output: " << Error << '\n';442exit(1);443}444FilesDifferent = true;445} else {446// Remove the generated output if there are no differences.447sys::fs::remove(*Output);448}449450// Remove the bitcode file if we are supposed to.451if (RemoveBitcode)452sys::fs::remove(BitcodeFile);453return FilesDifferent;454}455456bool BugDriver::isExecutingJIT() { return InterpreterSel == RunJIT; }457458459