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/CodeCompletion.h" 17 #include "clang/Interpreter/Interpreter.h" 18 19 #include "llvm/ExecutionEngine/Orc/LLJIT.h" 20 #include "llvm/LineEditor/LineEditor.h" 21 #include "llvm/Support/CommandLine.h" 22 #include "llvm/Support/ManagedStatic.h" // llvm_shutdown 23 #include "llvm/Support/Signals.h" 24 #include "llvm/Support/TargetSelect.h" 25 #include <optional> 26 27 static llvm::cl::opt<bool> CudaEnabled("cuda", llvm::cl::Hidden); 28 static llvm::cl::opt<std::string> CudaPath("cuda-path", llvm::cl::Hidden); 29 static llvm::cl::opt<std::string> OffloadArch("offload-arch", llvm::cl::Hidden); 30 31 static llvm::cl::list<std::string> 32 ClangArgs("Xcc", 33 llvm::cl::desc("Argument to pass to the CompilerInvocation"), 34 llvm::cl::CommaSeparated); 35 static llvm::cl::opt<bool> OptHostSupportsJit("host-supports-jit", 36 llvm::cl::Hidden); 37 static llvm::cl::list<std::string> OptInputs(llvm::cl::Positional, 38 llvm::cl::desc("[code to run]")); 39 40 static void LLVMErrorHandler(void *UserData, const char *Message, 41 bool GenCrashDiag) { 42 auto &Diags = *static_cast<clang::DiagnosticsEngine *>(UserData); 43 44 Diags.Report(clang::diag::err_fe_error_backend) << Message; 45 46 // Run the interrupt handlers to make sure any special cleanups get done, in 47 // particular that we remove files registered with RemoveFileOnSignal. 48 llvm::sys::RunInterruptHandlers(); 49 50 // We cannot recover from llvm errors. When reporting a fatal error, exit 51 // with status 70 to generate crash diagnostics. For BSD systems this is 52 // defined as an internal software error. Otherwise, exit with status 1. 53 54 exit(GenCrashDiag ? 70 : 1); 55 } 56 57 // If we are running with -verify a reported has to be returned as unsuccess. 58 // This is relevant especially for the test suite. 59 static int checkDiagErrors(const clang::CompilerInstance *CI, bool HasError) { 60 unsigned Errs = CI->getDiagnostics().getClient()->getNumErrors(); 61 if (CI->getDiagnosticOpts().VerifyDiagnostics) { 62 // If there was an error that came from the verifier we must return 1 as 63 // an exit code for the process. This will make the test fail as expected. 64 clang::DiagnosticConsumer *Client = CI->getDiagnostics().getClient(); 65 Client->EndSourceFile(); 66 Errs = Client->getNumErrors(); 67 68 // The interpreter expects BeginSourceFile/EndSourceFiles to be balanced. 69 Client->BeginSourceFile(CI->getLangOpts(), &CI->getPreprocessor()); 70 } 71 return (Errs || HasError) ? EXIT_FAILURE : EXIT_SUCCESS; 72 } 73 74 struct ReplListCompleter { 75 clang::IncrementalCompilerBuilder &CB; 76 clang::Interpreter &MainInterp; 77 ReplListCompleter(clang::IncrementalCompilerBuilder &CB, 78 clang::Interpreter &Interp) 79 : CB(CB), MainInterp(Interp){}; 80 81 std::vector<llvm::LineEditor::Completion> operator()(llvm::StringRef Buffer, 82 size_t Pos) const; 83 std::vector<llvm::LineEditor::Completion> 84 operator()(llvm::StringRef Buffer, size_t Pos, llvm::Error &ErrRes) const; 85 }; 86 87 std::vector<llvm::LineEditor::Completion> 88 ReplListCompleter::operator()(llvm::StringRef Buffer, size_t Pos) const { 89 auto Err = llvm::Error::success(); 90 auto res = (*this)(Buffer, Pos, Err); 91 if (Err) 92 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); 93 return res; 94 } 95 96 std::vector<llvm::LineEditor::Completion> 97 ReplListCompleter::operator()(llvm::StringRef Buffer, size_t Pos, 98 llvm::Error &ErrRes) const { 99 std::vector<llvm::LineEditor::Completion> Comps; 100 std::vector<std::string> Results; 101 102 auto CI = CB.CreateCpp(); 103 if (auto Err = CI.takeError()) { 104 ErrRes = std::move(Err); 105 return {}; 106 } 107 108 size_t Lines = 109 std::count(Buffer.begin(), std::next(Buffer.begin(), Pos), '\n') + 1; 110 auto Interp = clang::Interpreter::create(std::move(*CI)); 111 112 if (auto Err = Interp.takeError()) { 113 // log the error and returns an empty vector; 114 ErrRes = std::move(Err); 115 116 return {}; 117 } 118 119 codeComplete( 120 const_cast<clang::CompilerInstance *>((*Interp)->getCompilerInstance()), 121 Buffer, Lines, Pos + 1, MainInterp.getCompilerInstance(), Results); 122 123 size_t space_pos = Buffer.rfind(" "); 124 llvm::StringRef Prefix; 125 if (space_pos == llvm::StringRef::npos) { 126 Prefix = Buffer; 127 } else { 128 Prefix = Buffer.substr(space_pos + 1); 129 } 130 131 for (auto c : Results) { 132 if (c.find(Prefix) == 0) 133 Comps.push_back(llvm::LineEditor::Completion(c.substr(Prefix.size()), c)); 134 } 135 return Comps; 136 } 137 138 llvm::ExitOnError ExitOnErr; 139 int main(int argc, const char **argv) { 140 ExitOnErr.setBanner("clang-repl: "); 141 llvm::cl::ParseCommandLineOptions(argc, argv); 142 143 llvm::llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. 144 145 std::vector<const char *> ClangArgv(ClangArgs.size()); 146 std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(), 147 [](const std::string &s) -> const char * { return s.data(); }); 148 // Initialize all targets (required for device offloading) 149 llvm::InitializeAllTargetInfos(); 150 llvm::InitializeAllTargets(); 151 llvm::InitializeAllTargetMCs(); 152 llvm::InitializeAllAsmPrinters(); 153 154 if (OptHostSupportsJit) { 155 auto J = llvm::orc::LLJITBuilder().create(); 156 if (J) 157 llvm::outs() << "true\n"; 158 else { 159 llvm::consumeError(J.takeError()); 160 llvm::outs() << "false\n"; 161 } 162 return 0; 163 } 164 165 clang::IncrementalCompilerBuilder CB; 166 CB.SetCompilerArgs(ClangArgv); 167 168 std::unique_ptr<clang::CompilerInstance> DeviceCI; 169 if (CudaEnabled) { 170 if (!CudaPath.empty()) 171 CB.SetCudaSDK(CudaPath); 172 173 if (OffloadArch.empty()) { 174 OffloadArch = "sm_35"; 175 } 176 CB.SetOffloadArch(OffloadArch); 177 178 DeviceCI = ExitOnErr(CB.CreateCudaDevice()); 179 } 180 181 // FIXME: Investigate if we could use runToolOnCodeWithArgs from tooling. It 182 // can replace the boilerplate code for creation of the compiler instance. 183 std::unique_ptr<clang::CompilerInstance> CI; 184 if (CudaEnabled) { 185 CI = ExitOnErr(CB.CreateCudaHost()); 186 } else { 187 CI = ExitOnErr(CB.CreateCpp()); 188 } 189 190 // Set an error handler, so that any LLVM backend diagnostics go through our 191 // error handler. 192 llvm::install_fatal_error_handler(LLVMErrorHandler, 193 static_cast<void *>(&CI->getDiagnostics())); 194 195 // Load any requested plugins. 196 CI->LoadRequestedPlugins(); 197 if (CudaEnabled) 198 DeviceCI->LoadRequestedPlugins(); 199 200 std::unique_ptr<clang::Interpreter> Interp; 201 202 if (CudaEnabled) { 203 Interp = ExitOnErr( 204 clang::Interpreter::createWithCUDA(std::move(CI), std::move(DeviceCI))); 205 206 if (CudaPath.empty()) { 207 ExitOnErr(Interp->LoadDynamicLibrary("libcudart.so")); 208 } else { 209 auto CudaRuntimeLibPath = CudaPath + "/lib/libcudart.so"; 210 ExitOnErr(Interp->LoadDynamicLibrary(CudaRuntimeLibPath.c_str())); 211 } 212 } else 213 Interp = ExitOnErr(clang::Interpreter::create(std::move(CI))); 214 215 for (const std::string &input : OptInputs) { 216 if (auto Err = Interp->ParseAndExecute(input)) 217 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); 218 } 219 220 bool HasError = false; 221 222 if (OptInputs.empty()) { 223 llvm::LineEditor LE("clang-repl"); 224 std::string Input; 225 LE.setListCompleter(ReplListCompleter(CB, *Interp)); 226 while (std::optional<std::string> Line = LE.readLine()) { 227 llvm::StringRef L = *Line; 228 L = L.trim(); 229 if (L.endswith("\\")) { 230 // FIXME: Support #ifdef X \ ... 231 Input += L.drop_back(1); 232 LE.setPrompt("clang-repl... "); 233 continue; 234 } 235 236 Input += L; 237 if (Input == R"(%quit)") { 238 break; 239 } 240 if (Input == R"(%undo)") { 241 if (auto Err = Interp->Undo()) { 242 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); 243 HasError = true; 244 } 245 } else if (Input.rfind("%lib ", 0) == 0) { 246 if (auto Err = Interp->LoadDynamicLibrary(Input.data() + 5)) { 247 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); 248 HasError = true; 249 } 250 } else if (auto Err = Interp->ParseAndExecute(Input)) { 251 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); 252 HasError = true; 253 } 254 255 Input = ""; 256 LE.setPrompt("clang-repl> "); 257 } 258 } 259 260 // Our error handler depends on the Diagnostics object, which we're 261 // potentially about to delete. Uninstall the handler now so that any 262 // later errors use the default handling behavior instead. 263 llvm::remove_fatal_error_handler(); 264 265 return checkDiagErrors(Interp->getCompilerInstance(), HasError); 266 } 267