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 llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); 143 144 ExitOnErr.setBanner("clang-repl: "); 145 llvm::cl::ParseCommandLineOptions(argc, argv); 146 147 llvm::llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. 148 149 std::vector<const char *> ClangArgv(ClangArgs.size()); 150 std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(), 151 [](const std::string &s) -> const char * { return s.data(); }); 152 // Initialize all targets (required for device offloading) 153 llvm::InitializeAllTargetInfos(); 154 llvm::InitializeAllTargets(); 155 llvm::InitializeAllTargetMCs(); 156 llvm::InitializeAllAsmPrinters(); 157 llvm::InitializeAllAsmParsers(); 158 159 if (OptHostSupportsJit) { 160 auto J = llvm::orc::LLJITBuilder().create(); 161 if (J) 162 llvm::outs() << "true\n"; 163 else { 164 llvm::consumeError(J.takeError()); 165 llvm::outs() << "false\n"; 166 } 167 return 0; 168 } 169 170 clang::IncrementalCompilerBuilder CB; 171 CB.SetCompilerArgs(ClangArgv); 172 173 std::unique_ptr<clang::CompilerInstance> DeviceCI; 174 if (CudaEnabled) { 175 if (!CudaPath.empty()) 176 CB.SetCudaSDK(CudaPath); 177 178 if (OffloadArch.empty()) { 179 OffloadArch = "sm_35"; 180 } 181 CB.SetOffloadArch(OffloadArch); 182 183 DeviceCI = ExitOnErr(CB.CreateCudaDevice()); 184 } 185 186 // FIXME: Investigate if we could use runToolOnCodeWithArgs from tooling. It 187 // can replace the boilerplate code for creation of the compiler instance. 188 std::unique_ptr<clang::CompilerInstance> CI; 189 if (CudaEnabled) { 190 CI = ExitOnErr(CB.CreateCudaHost()); 191 } else { 192 CI = ExitOnErr(CB.CreateCpp()); 193 } 194 195 // Set an error handler, so that any LLVM backend diagnostics go through our 196 // error handler. 197 llvm::install_fatal_error_handler(LLVMErrorHandler, 198 static_cast<void *>(&CI->getDiagnostics())); 199 200 // Load any requested plugins. 201 CI->LoadRequestedPlugins(); 202 if (CudaEnabled) 203 DeviceCI->LoadRequestedPlugins(); 204 205 std::unique_ptr<clang::Interpreter> Interp; 206 207 if (CudaEnabled) { 208 Interp = ExitOnErr( 209 clang::Interpreter::createWithCUDA(std::move(CI), std::move(DeviceCI))); 210 211 if (CudaPath.empty()) { 212 ExitOnErr(Interp->LoadDynamicLibrary("libcudart.so")); 213 } else { 214 auto CudaRuntimeLibPath = CudaPath + "/lib/libcudart.so"; 215 ExitOnErr(Interp->LoadDynamicLibrary(CudaRuntimeLibPath.c_str())); 216 } 217 } else 218 Interp = ExitOnErr(clang::Interpreter::create(std::move(CI))); 219 220 bool HasError = false; 221 222 for (const std::string &input : OptInputs) { 223 if (auto Err = Interp->ParseAndExecute(input)) { 224 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); 225 HasError = true; 226 } 227 } 228 229 if (OptInputs.empty()) { 230 llvm::LineEditor LE("clang-repl"); 231 std::string Input; 232 LE.setListCompleter(ReplListCompleter(CB, *Interp)); 233 while (std::optional<std::string> Line = LE.readLine()) { 234 llvm::StringRef L = *Line; 235 L = L.trim(); 236 if (L.ends_with("\\")) { 237 Input += L.drop_back(1); 238 // If it is a preprocessor directive, new lines matter. 239 if (L.starts_with('#')) 240 Input += "\n"; 241 LE.setPrompt("clang-repl... "); 242 continue; 243 } 244 245 Input += L; 246 if (Input == R"(%quit)") { 247 break; 248 } 249 if (Input == R"(%undo)") { 250 if (auto Err = Interp->Undo()) 251 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); 252 } else if (Input.rfind("%lib ", 0) == 0) { 253 if (auto Err = Interp->LoadDynamicLibrary(Input.data() + 5)) 254 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); 255 } else if (auto Err = Interp->ParseAndExecute(Input)) { 256 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); 257 } 258 259 Input = ""; 260 LE.setPrompt("clang-repl> "); 261 } 262 } 263 264 // Our error handler depends on the Diagnostics object, which we're 265 // potentially about to delete. Uninstall the handler now so that any 266 // later errors use the default handling behavior instead. 267 llvm::remove_fatal_error_handler(); 268 269 return checkDiagErrors(Interp->getCompilerInstance(), HasError); 270 } 271