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-exception 6 // 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/Frontend/CompilerInstance.h" 15 #include "clang/Frontend/FrontendDiagnostic.h" 16 #include "clang/Interpreter/Interpreter.h" 17 18 #include "llvm/ExecutionEngine/Orc/LLJIT.h" 19 #include "llvm/LineEditor/LineEditor.h" 20 #include "llvm/Support/CommandLine.h" 21 #include "llvm/Support/ManagedStatic.h" // llvm_shutdown 22 #include "llvm/Support/Signals.h" 23 #include "llvm/Support/TargetSelect.h" // llvm::Initialize* 24 #include <optional> 25 26 static llvm::cl::opt<bool> CudaEnabled("cuda", llvm::cl::Hidden); 27 static llvm::cl::opt<std::string> CudaPath("cuda-path", llvm::cl::Hidden); 28 static llvm::cl::opt<std::string> OffloadArch("offload-arch", llvm::cl::Hidden); 29 30 static llvm::cl::list<std::string> 31 ClangArgs("Xcc", 32 llvm::cl::desc("Argument to pass to the CompilerInvocation"), 33 llvm::cl::CommaSeparated); 34 static llvm::cl::opt<bool> OptHostSupportsJit("host-supports-jit", 35 llvm::cl::Hidden); 36 static llvm::cl::list<std::string> OptInputs(llvm::cl::Positional, 37 llvm::cl::desc("[code to run]")); 38 39 static void LLVMErrorHandler(void *UserData, const char *Message, 40 bool GenCrashDiag) { 41 auto &Diags = *static_cast<clang::DiagnosticsEngine *>(UserData); 42 43 Diags.Report(clang::diag::err_fe_error_backend) << Message; 44 45 // Run the interrupt handlers to make sure any special cleanups get done, in 46 // particular that we remove files registered with RemoveFileOnSignal. 47 llvm::sys::RunInterruptHandlers(); 48 49 // We cannot recover from llvm errors. When reporting a fatal error, exit 50 // with status 70 to generate crash diagnostics. For BSD systems this is 51 // defined as an internal software error. Otherwise, exit with status 1. 52 53 exit(GenCrashDiag ? 70 : 1); 54 } 55 56 // If we are running with -verify a reported has to be returned as unsuccess. 57 // This is relevant especially for the test suite. 58 static int checkDiagErrors(const clang::CompilerInstance *CI, bool HasError) { 59 unsigned Errs = CI->getDiagnostics().getClient()->getNumErrors(); 60 if (CI->getDiagnosticOpts().VerifyDiagnostics) { 61 // If there was an error that came from the verifier we must return 1 as 62 // an exit code for the process. This will make the test fail as expected. 63 clang::DiagnosticConsumer *Client = CI->getDiagnostics().getClient(); 64 Client->EndSourceFile(); 65 Errs = Client->getNumErrors(); 66 67 // The interpreter expects BeginSourceFile/EndSourceFiles to be balanced. 68 Client->BeginSourceFile(CI->getLangOpts(), &CI->getPreprocessor()); 69 } 70 return (Errs || HasError) ? EXIT_FAILURE : EXIT_SUCCESS; 71 } 72 73 llvm::ExitOnError ExitOnErr; 74 int main(int argc, const char **argv) { 75 ExitOnErr.setBanner("clang-repl: "); 76 llvm::cl::ParseCommandLineOptions(argc, argv); 77 78 llvm::llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. 79 80 std::vector<const char *> ClangArgv(ClangArgs.size()); 81 std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(), 82 [](const std::string &s) -> const char * { return s.data(); }); 83 llvm::InitializeNativeTarget(); 84 llvm::InitializeNativeTargetAsmPrinter(); 85 86 if (OptHostSupportsJit) { 87 auto J = llvm::orc::LLJITBuilder().create(); 88 if (J) 89 llvm::outs() << "true\n"; 90 else { 91 llvm::consumeError(J.takeError()); 92 llvm::outs() << "false\n"; 93 } 94 return 0; 95 } 96 97 clang::IncrementalCompilerBuilder CB; 98 CB.SetCompilerArgs(ClangArgv); 99 100 std::unique_ptr<clang::CompilerInstance> DeviceCI; 101 if (CudaEnabled) { 102 // initialize NVPTX backend 103 LLVMInitializeNVPTXTargetInfo(); 104 LLVMInitializeNVPTXTarget(); 105 LLVMInitializeNVPTXTargetMC(); 106 LLVMInitializeNVPTXAsmPrinter(); 107 108 if (!CudaPath.empty()) 109 CB.SetCudaSDK(CudaPath); 110 111 if (OffloadArch.empty()) { 112 OffloadArch = "sm_35"; 113 } 114 CB.SetOffloadArch(OffloadArch); 115 116 DeviceCI = ExitOnErr(CB.CreateCudaDevice()); 117 } 118 119 // FIXME: Investigate if we could use runToolOnCodeWithArgs from tooling. It 120 // can replace the boilerplate code for creation of the compiler instance. 121 std::unique_ptr<clang::CompilerInstance> CI; 122 if (CudaEnabled) { 123 CI = ExitOnErr(CB.CreateCudaHost()); 124 } else { 125 CI = ExitOnErr(CB.CreateCpp()); 126 } 127 128 // Set an error handler, so that any LLVM backend diagnostics go through our 129 // error handler. 130 llvm::install_fatal_error_handler(LLVMErrorHandler, 131 static_cast<void *>(&CI->getDiagnostics())); 132 133 // Load any requested plugins. 134 CI->LoadRequestedPlugins(); 135 if (CudaEnabled) 136 DeviceCI->LoadRequestedPlugins(); 137 138 std::unique_ptr<clang::Interpreter> Interp; 139 if (CudaEnabled) { 140 Interp = ExitOnErr( 141 clang::Interpreter::createWithCUDA(std::move(CI), std::move(DeviceCI))); 142 143 if (CudaPath.empty()) { 144 ExitOnErr(Interp->LoadDynamicLibrary("libcudart.so")); 145 } else { 146 auto CudaRuntimeLibPath = CudaPath + "/lib/libcudart.so"; 147 ExitOnErr(Interp->LoadDynamicLibrary(CudaRuntimeLibPath.c_str())); 148 } 149 } else 150 Interp = ExitOnErr(clang::Interpreter::create(std::move(CI))); 151 152 for (const std::string &input : OptInputs) { 153 if (auto Err = Interp->ParseAndExecute(input)) 154 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); 155 } 156 157 bool HasError = false; 158 159 if (OptInputs.empty()) { 160 llvm::LineEditor LE("clang-repl"); 161 // FIXME: Add LE.setListCompleter 162 while (std::optional<std::string> Line = LE.readLine()) { 163 if (*Line == R"(%quit)") 164 break; 165 if (*Line == R"(%undo)") { 166 if (auto Err = Interp->Undo()) { 167 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); 168 HasError = true; 169 } 170 continue; 171 } 172 if (Line->rfind("%lib ", 0) == 0) { 173 if (auto Err = Interp->LoadDynamicLibrary(Line->data() + 5)) { 174 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); 175 HasError = true; 176 } 177 continue; 178 } 179 180 if (auto Err = Interp->ParseAndExecute(*Line)) { 181 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); 182 HasError = true; 183 } 184 } 185 } 186 187 // Our error handler depends on the Diagnostics object, which we're 188 // potentially about to delete. Uninstall the handler now so that any 189 // later errors use the default handling behavior instead. 190 llvm::remove_fatal_error_handler(); 191 192 return checkDiagErrors(Interp->getCompilerInstance(), HasError); 193 } 194