brintos

brintos / llvm-project-archived public Read only

0
0
Text · 15.7 KiB · 066f526 Raw
426 lines · cpp
1//===--- tools/clang-repl/ClangRepl.cpp - clang-repl - the Clang REPL -----===//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 implements a REPL tool on top of clang.10//11//===----------------------------------------------------------------------===//12 13#include "clang/Basic/Diagnostic.h"14#include "clang/Basic/DiagnosticFrontend.h"15#include "clang/Basic/Version.h"16#include "clang/Config/config.h"17#include "clang/Frontend/CompilerInstance.h"18#include "clang/Interpreter/CodeCompletion.h"19#include "clang/Interpreter/Interpreter.h"20#include "clang/Lex/Preprocessor.h"21#include "clang/Sema/Sema.h"22 23#include "llvm/ADT/SmallString.h"24#include "llvm/ADT/StringRef.h"25#include "llvm/ExecutionEngine/Orc/LLJIT.h"26#include "llvm/LineEditor/LineEditor.h"27#include "llvm/Support/CommandLine.h"28#include "llvm/Support/FileSystem.h"29#include "llvm/Support/ManagedStatic.h" // llvm_shutdown30#include "llvm/Support/Path.h"31#include "llvm/Support/Signals.h"32#include "llvm/Support/TargetSelect.h"33#include "llvm/Support/VirtualFileSystem.h"34#include "llvm/Support/raw_ostream.h"35#include "llvm/TargetParser/Host.h"36#include "llvm/TargetParser/Triple.h"37#include <optional>38 39#include <string>40#include <vector>41 42#include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupport.h"43 44// Disable LSan for this test.45// FIXME: Re-enable once we can assume GCC 13.2 or higher.46// https://llvm.org/github.com/llvm/llvm-project/issues/67586.47#if LLVM_ADDRESS_SANITIZER_BUILD || LLVM_HWADDRESS_SANITIZER_BUILD48#include <sanitizer/lsan_interface.h>49LLVM_ATTRIBUTE_USED int __lsan_is_turned_off() { return 1; }50#endif51 52#define DEBUG_TYPE "clang-repl"53 54static llvm::cl::opt<bool> CudaEnabled("cuda", llvm::cl::Hidden);55static llvm::cl::opt<std::string> CudaPath("cuda-path", llvm::cl::Hidden);56static llvm::cl::opt<std::string> OffloadArch("offload-arch", llvm::cl::Hidden);57static llvm::cl::OptionCategory OOPCategory("Out-of-process Execution Options");58static llvm::cl::opt<std::string> SlabAllocateSizeString(59    "slab-allocate",60    llvm::cl::desc("Allocate from a slab of the given size "61                   "(allowable suffixes: Kb, Mb, Gb. default = "62                   "Kb)"),63    llvm::cl::init(""), llvm::cl::cat(OOPCategory));64static llvm::cl::opt<std::string>65    OOPExecutor("oop-executor",66                llvm::cl::desc("Launch an out-of-process executor to run code"),67                llvm::cl::init(""), llvm::cl::ValueOptional,68                llvm::cl::cat(OOPCategory));69static llvm::cl::opt<std::string> OOPExecutorConnect(70    "oop-executor-connect",71    llvm::cl::desc(72        "Connect to an out-of-process executor through a TCP socket"),73    llvm::cl::value_desc("<hostname>:<port>"));74static llvm::cl::opt<std::string>75    OrcRuntimePath("orc-runtime", llvm::cl::desc("Path to the ORC runtime"),76                   llvm::cl::init(""), llvm::cl::ValueOptional,77                   llvm::cl::cat(OOPCategory));78static llvm::cl::opt<bool> UseSharedMemory(79    "use-shared-memory",80    llvm::cl::desc("Use shared memory to transfer generated code and data"),81    llvm::cl::init(false), llvm::cl::cat(OOPCategory));82static llvm::cl::list<std::string>83    ClangArgs("Xcc",84              llvm::cl::desc("Argument to pass to the CompilerInvocation"),85              llvm::cl::CommaSeparated);86static llvm::cl::opt<bool> OptHostSupportsJit("host-supports-jit",87                                              llvm::cl::Hidden);88static llvm::cl::opt<bool> OptHostJitTriple("host-jit-triple",89                                            llvm::cl::Hidden);90static llvm::cl::list<std::string> OptInputs(llvm::cl::Positional,91                                             llvm::cl::desc("[code to run]"));92 93static llvm::Error sanitizeOopArguments(const char *ArgV0) {94  // Only one of -oop-executor and -oop-executor-connect can be used.95  if (!!OOPExecutor.getNumOccurrences() &&96      !!OOPExecutorConnect.getNumOccurrences())97    return llvm::make_error<llvm::StringError>(98        "Only one of -" + OOPExecutor.ArgStr + " and -" +99            OOPExecutorConnect.ArgStr + " can be specified",100        llvm::inconvertibleErrorCode());101 102  llvm::Triple SystemTriple(llvm::sys::getProcessTriple());103  // TODO: Remove once out-of-process execution support is implemented for104  // non-Unix platforms.105  if ((!SystemTriple.isOSBinFormatELF() &&106       !SystemTriple.isOSBinFormatMachO()) &&107      (OOPExecutor.getNumOccurrences() ||108       OOPExecutorConnect.getNumOccurrences()))109    return llvm::make_error<llvm::StringError>(110        "Out-of-process execution is only supported on Unix platforms",111        llvm::inconvertibleErrorCode());112 113  // If -slab-allocate is passed, check that we're not trying to use it in114  // -oop-executor or -oop-executor-connect mode.115  //116  // FIXME: Remove once we enable remote slab allocation.117  if (SlabAllocateSizeString != "") {118    if (OOPExecutor.getNumOccurrences() ||119        OOPExecutorConnect.getNumOccurrences())120      return llvm::make_error<llvm::StringError>(121          "-slab-allocate cannot be used with -oop-executor or "122          "-oop-executor-connect",123          llvm::inconvertibleErrorCode());124  }125 126  // Out-of-process executors require the ORC runtime. ORC Runtime Path127  // resolution is done in Interpreter.cpp.128 129  // If -oop-executor was used but no value was specified then use a sensible130  // default.131  if (!!OOPExecutor.getNumOccurrences() && OOPExecutor.empty()) {132    llvm::SmallString<256> OOPExecutorPath(llvm::sys::fs::getMainExecutable(133        ArgV0, reinterpret_cast<void *>(&sanitizeOopArguments)));134    llvm::sys::path::remove_filename(OOPExecutorPath);135    llvm::sys::path::append(OOPExecutorPath, "llvm-jitlink-executor");136    OOPExecutor = OOPExecutorPath.str().str();137  }138 139  return llvm::Error::success();140}141 142static llvm::Expected<unsigned> getSlabAllocSize(llvm::StringRef SizeString) {143  SizeString = SizeString.trim();144 145  uint64_t Units = 1024;146 147  if (SizeString.ends_with_insensitive("kb"))148    SizeString = SizeString.drop_back(2).rtrim();149  else if (SizeString.ends_with_insensitive("mb")) {150    Units = 1024 * 1024;151    SizeString = SizeString.drop_back(2).rtrim();152  } else if (SizeString.ends_with_insensitive("gb")) {153    Units = 1024 * 1024 * 1024;154    SizeString = SizeString.drop_back(2).rtrim();155  } else if (SizeString.empty())156    return 0;157 158  uint64_t SlabSize = 0;159  if (SizeString.getAsInteger(10, SlabSize))160    return llvm::make_error<llvm::StringError>(161        "Invalid numeric format for slab size", llvm::inconvertibleErrorCode());162 163  return SlabSize * Units;164}165 166static void LLVMErrorHandler(void *UserData, const char *Message,167                             bool GenCrashDiag) {168  auto &Diags = *static_cast<clang::DiagnosticsEngine *>(UserData);169 170  Diags.Report(clang::diag::err_fe_error_backend) << Message;171 172  // Run the interrupt handlers to make sure any special cleanups get done, in173  // particular that we remove files registered with RemoveFileOnSignal.174  llvm::sys::RunInterruptHandlers();175 176  // We cannot recover from llvm errors.  When reporting a fatal error, exit177  // with status 70 to generate crash diagnostics.  For BSD systems this is178  // defined as an internal software error. Otherwise, exit with status 1.179 180  exit(GenCrashDiag ? 70 : 1);181}182 183// If we are running with -verify a reported has to be returned as unsuccess.184// This is relevant especially for the test suite.185static int checkDiagErrors(const clang::CompilerInstance *CI, bool HasError) {186  unsigned Errs = CI->getDiagnostics().getClient()->getNumErrors();187  if (CI->getDiagnosticOpts().VerifyDiagnostics) {188    // If there was an error that came from the verifier we must return 1 as189    // an exit code for the process. This will make the test fail as expected.190    clang::DiagnosticConsumer *Client = CI->getDiagnostics().getClient();191    Client->EndSourceFile();192    Errs = Client->getNumErrors();193 194    // The interpreter expects BeginSourceFile/EndSourceFiles to be balanced.195    Client->BeginSourceFile(CI->getLangOpts(), &CI->getPreprocessor());196  }197  return (Errs || HasError) ? EXIT_FAILURE : EXIT_SUCCESS;198}199 200struct ReplListCompleter {201  clang::IncrementalCompilerBuilder &CB;202  clang::Interpreter &MainInterp;203  ReplListCompleter(clang::IncrementalCompilerBuilder &CB,204                    clang::Interpreter &Interp)205      : CB(CB), MainInterp(Interp) {};206 207  std::vector<llvm::LineEditor::Completion> operator()(llvm::StringRef Buffer,208                                                       size_t Pos) const;209  std::vector<llvm::LineEditor::Completion>210  operator()(llvm::StringRef Buffer, size_t Pos, llvm::Error &ErrRes) const;211};212 213std::vector<llvm::LineEditor::Completion>214ReplListCompleter::operator()(llvm::StringRef Buffer, size_t Pos) const {215  auto Err = llvm::Error::success();216  auto res = (*this)(Buffer, Pos, Err);217  if (Err)218    llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");219  return res;220}221 222std::vector<llvm::LineEditor::Completion>223ReplListCompleter::operator()(llvm::StringRef Buffer, size_t Pos,224                              llvm::Error &ErrRes) const {225  std::vector<llvm::LineEditor::Completion> Comps;226  std::vector<std::string> Results;227 228  auto CI = CB.CreateCpp();229  if (auto Err = CI.takeError()) {230    ErrRes = std::move(Err);231    return {};232  }233 234  size_t Lines =235      std::count(Buffer.begin(), std::next(Buffer.begin(), Pos), '\n') + 1;236  auto Interp = clang::Interpreter::create(std::move(*CI));237 238  if (auto Err = Interp.takeError()) {239    // log the error and returns an empty vector;240    ErrRes = std::move(Err);241 242    return {};243  }244  auto *MainCI = (*Interp)->getCompilerInstance();245  auto CC = clang::ReplCodeCompleter();246  CC.codeComplete(MainCI, Buffer, Lines, Pos + 1,247                  MainInterp.getCompilerInstance(), Results);248  for (auto c : Results) {249    if (c.find(CC.Prefix) == 0)250      Comps.push_back(251          llvm::LineEditor::Completion(c.substr(CC.Prefix.size()), c));252  }253  return Comps;254}255 256llvm::ExitOnError ExitOnErr;257int main(int argc, const char **argv) {258  llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);259 260  ExitOnErr.setBanner("clang-repl: ");261  llvm::cl::ParseCommandLineOptions(argc, argv);262 263  llvm::llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.264 265  std::vector<const char *> ClangArgv(ClangArgs.size());266  std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(),267                 [](const std::string &s) -> const char * { return s.data(); });268  // Initialize all targets (required for device offloading)269  llvm::InitializeAllTargetInfos();270  llvm::InitializeAllTargets();271  llvm::InitializeAllTargetMCs();272  llvm::InitializeAllAsmPrinters();273  llvm::InitializeAllAsmParsers();274 275  if (OptHostSupportsJit) {276    auto J = llvm::orc::LLJITBuilder().create();277    if (J)278      llvm::outs() << "true\n";279    else {280      llvm::consumeError(J.takeError());281      llvm::outs() << "false\n";282    }283    return 0;284  } else if (OptHostJitTriple) {285    auto J = ExitOnErr(llvm::orc::LLJITBuilder().create());286    auto T = J->getTargetTriple();287    llvm::outs() << T.normalize() << '\n';288    return 0;289  }290 291  clang::IncrementalCompilerBuilder CB;292  CB.SetCompilerArgs(ClangArgv);293 294  std::unique_ptr<clang::CompilerInstance> DeviceCI;295  if (CudaEnabled) {296    if (!CudaPath.empty())297      CB.SetCudaSDK(CudaPath);298 299    if (OffloadArch.empty()) {300      OffloadArch = "sm_35";301    }302    CB.SetOffloadArch(OffloadArch);303 304    DeviceCI = ExitOnErr(CB.CreateCudaDevice());305  }306 307  ExitOnErr(sanitizeOopArguments(argv[0]));308 309  clang::Interpreter::JITConfig Config;310  Config.IsOutOfProcess = !OOPExecutor.empty() || !OOPExecutorConnect.empty();311  Config.OOPExecutor = OOPExecutor;312  Config.OrcRuntimePath = OrcRuntimePath;313  auto SizeOrErr = getSlabAllocSize(SlabAllocateSizeString);314  if (!SizeOrErr) {315    llvm::logAllUnhandledErrors(SizeOrErr.takeError(), llvm::errs(), "error: ");316    return EXIT_FAILURE;317  }318  Config.SlabAllocateSize = *SizeOrErr;319  Config.UseSharedMemory = UseSharedMemory;320 321  // FIXME: Investigate if we could use runToolOnCodeWithArgs from tooling. It322  // can replace the boilerplate code for creation of the compiler instance.323  std::unique_ptr<clang::CompilerInstance> CI;324  if (CudaEnabled) {325    CI = ExitOnErr(CB.CreateCudaHost());326  } else {327    CI = ExitOnErr(CB.CreateCpp());328  }329 330  // Set an error handler, so that any LLVM backend diagnostics go through our331  // error handler.332  llvm::install_fatal_error_handler(LLVMErrorHandler,333                                    static_cast<void *>(&CI->getDiagnostics()));334 335  // Load any requested plugins.336  CI->LoadRequestedPlugins();337  if (CudaEnabled)338    DeviceCI->LoadRequestedPlugins();339 340  std::unique_ptr<clang::Interpreter> Interp;341 342  if (CudaEnabled) {343    Interp = ExitOnErr(344        clang::Interpreter::createWithCUDA(std::move(CI), std::move(DeviceCI)));345 346    if (CudaPath.empty()) {347      ExitOnErr(Interp->LoadDynamicLibrary("libcudart.so"));348    } else {349      auto CudaRuntimeLibPath = CudaPath + "/lib/libcudart.so";350      ExitOnErr(Interp->LoadDynamicLibrary(CudaRuntimeLibPath.c_str()));351    }352  } else {353    Interp = ExitOnErr(clang::Interpreter::create(std::move(CI), Config));354  }355 356  bool HasError = false;357 358  for (const std::string &input : OptInputs) {359    if (auto Err = Interp->ParseAndExecute(input)) {360      llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");361      HasError = true;362    }363  }364 365  if (OptInputs.empty()) {366    llvm::LineEditor LE("clang-repl");367    std::string Input;368    LE.setListCompleter(ReplListCompleter(CB, *Interp));369    while (std::optional<std::string> Line = LE.readLine()) {370      llvm::StringRef L = *Line;371      L = L.trim();372      if (L.ends_with("\\")) {373        Input += L.drop_back(1);374        // If it is a preprocessor directive, new lines matter.375        if (L.starts_with('#'))376          Input += "\n";377        LE.setPrompt("clang-repl...   ");378        continue;379      }380 381      Input += L;382      // If we add more % commands, there should be better architecture than383      // this.384      if (Input == R"(%quit)") {385        break;386      }387      if (Input == R"(%undo)") {388        if (auto Err = Interp->Undo())389          llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");390      } else if (Input == R"(%help)") {391        llvm::outs() << "%help\t\tlist clang-repl %commands\n"392                     << "%undo\t\tundo the previous input\n"393                     << "%lib\t<path>\tlink a dynamic library\n"394                     << "%quit\t\texit clang-repl\n";395      } else if (Input == R"(%lib)") {396        auto Err = llvm::make_error<llvm::StringError>(397            "%lib expects 1 argument: the path to a dynamic library\n",398            std::error_code());399        llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");400      } else if (Input.rfind("%lib ", 0) == 0) {401        if (auto Err = Interp->LoadDynamicLibrary(Input.data() + 5))402          llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");403      } else if (Input[0] == '%') {404        auto Err = llvm::make_error<llvm::StringError>(405            llvm::formatv(406                "Invalid % command \"{0}\", use \"%help\" to list commands\n",407                Input),408            std::error_code());409        llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");410      } else if (auto Err = Interp->ParseAndExecute(Input)) {411        llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");412      }413 414      Input = "";415      LE.setPrompt("clang-repl> ");416    }417  }418 419  // Our error handler depends on the Diagnostics object, which we're420  // potentially about to delete. Uninstall the handler now so that any421  // later errors use the default handling behavior instead.422  llvm::remove_fatal_error_handler();423 424  return checkDiagErrors(Interp->getCompilerInstance(), HasError);425}426