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