192f9852fSVassil Vassilev //===--- tools/clang-repl/ClangRepl.cpp - clang-repl - the Clang REPL -----===// 292f9852fSVassil Vassilev // 392f9852fSVassil Vassilev // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 492f9852fSVassil Vassilev // See https://llvm.org/LICENSE.txt for license information. 592f9852fSVassil Vassilev // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 692f9852fSVassil Vassilev // 792f9852fSVassil Vassilev //===----------------------------------------------------------------------===// 892f9852fSVassil Vassilev // 992f9852fSVassil Vassilev // This file implements a REPL tool on top of clang. 1092f9852fSVassil Vassilev // 1192f9852fSVassil Vassilev //===----------------------------------------------------------------------===// 1292f9852fSVassil Vassilev 1392f9852fSVassil Vassilev #include "clang/Basic/Diagnostic.h" 1492f9852fSVassil Vassilev #include "clang/Frontend/CompilerInstance.h" 1592f9852fSVassil Vassilev #include "clang/Frontend/FrontendDiagnostic.h" 1679af92bbSFred Fu #include "clang/Interpreter/CodeCompletion.h" 1792f9852fSVassil Vassilev #include "clang/Interpreter/Interpreter.h" 1835b366acSFred Fu #include "clang/Lex/Preprocessor.h" 1935b366acSFred Fu #include "clang/Sema/Sema.h" 2092f9852fSVassil Vassilev 2192f9852fSVassil Vassilev #include "llvm/ExecutionEngine/Orc/LLJIT.h" 2292f9852fSVassil Vassilev #include "llvm/LineEditor/LineEditor.h" 2392f9852fSVassil Vassilev #include "llvm/Support/CommandLine.h" 2492f9852fSVassil Vassilev #include "llvm/Support/ManagedStatic.h" // llvm_shutdown 2592f9852fSVassil Vassilev #include "llvm/Support/Signals.h" 26ddeab07cSAnubhab Ghosh #include "llvm/Support/TargetSelect.h" 27a1580d7bSKazu Hirata #include <optional> 2892f9852fSVassil Vassilev 2953a87b4aSLang Hames // Disable LSan for this test. 3053a87b4aSLang Hames // FIXME: Re-enable once we can assume GCC 13.2 or higher. 3153a87b4aSLang Hames // https://llvm.org/github.com/llvm/llvm-project/issues/67586. 3253a87b4aSLang Hames #if LLVM_ADDRESS_SANITIZER_BUILD || LLVM_HWADDRESS_SANITIZER_BUILD 3353a87b4aSLang Hames #include <sanitizer/lsan_interface.h> 3453a87b4aSLang Hames LLVM_ATTRIBUTE_USED int __lsan_is_turned_off() { return 1; } 3553a87b4aSLang Hames #endif 3653a87b4aSLang Hames 37ddeab07cSAnubhab Ghosh static llvm::cl::opt<bool> CudaEnabled("cuda", llvm::cl::Hidden); 38ddeab07cSAnubhab Ghosh static llvm::cl::opt<std::string> CudaPath("cuda-path", llvm::cl::Hidden); 39ddeab07cSAnubhab Ghosh static llvm::cl::opt<std::string> OffloadArch("offload-arch", llvm::cl::Hidden); 40ebb35088SSahilPatidar 4192f9852fSVassil Vassilev static llvm::cl::list<std::string> 42d86a206fSFangrui Song ClangArgs("Xcc", 4392f9852fSVassil Vassilev llvm::cl::desc("Argument to pass to the CompilerInvocation"), 4492f9852fSVassil Vassilev llvm::cl::CommaSeparated); 4592f9852fSVassil Vassilev static llvm::cl::opt<bool> OptHostSupportsJit("host-supports-jit", 4692f9852fSVassil Vassilev llvm::cl::Hidden); 47f01d45c3SVassil Vassilev static llvm::cl::list<std::string> OptInputs(llvm::cl::Positional, 48f01d45c3SVassil Vassilev llvm::cl::desc("[code to run]")); 4992f9852fSVassil Vassilev 50e463b697SSimon Pilgrim static void LLVMErrorHandler(void *UserData, const char *Message, 5192f9852fSVassil Vassilev bool GenCrashDiag) { 5292f9852fSVassil Vassilev auto &Diags = *static_cast<clang::DiagnosticsEngine *>(UserData); 5392f9852fSVassil Vassilev 5492f9852fSVassil Vassilev Diags.Report(clang::diag::err_fe_error_backend) << Message; 5592f9852fSVassil Vassilev 5692f9852fSVassil Vassilev // Run the interrupt handlers to make sure any special cleanups get done, in 5792f9852fSVassil Vassilev // particular that we remove files registered with RemoveFileOnSignal. 5892f9852fSVassil Vassilev llvm::sys::RunInterruptHandlers(); 5992f9852fSVassil Vassilev 6092f9852fSVassil Vassilev // We cannot recover from llvm errors. When reporting a fatal error, exit 6192f9852fSVassil Vassilev // with status 70 to generate crash diagnostics. For BSD systems this is 6292f9852fSVassil Vassilev // defined as an internal software error. Otherwise, exit with status 1. 6392f9852fSVassil Vassilev 6492f9852fSVassil Vassilev exit(GenCrashDiag ? 70 : 1); 6592f9852fSVassil Vassilev } 6692f9852fSVassil Vassilev 67946c45a4STapasweni Pathak // If we are running with -verify a reported has to be returned as unsuccess. 68946c45a4STapasweni Pathak // This is relevant especially for the test suite. 699caee577SJun Zhang static int checkDiagErrors(const clang::CompilerInstance *CI, bool HasError) { 70946c45a4STapasweni Pathak unsigned Errs = CI->getDiagnostics().getClient()->getNumErrors(); 71946c45a4STapasweni Pathak if (CI->getDiagnosticOpts().VerifyDiagnostics) { 72946c45a4STapasweni Pathak // If there was an error that came from the verifier we must return 1 as 73946c45a4STapasweni Pathak // an exit code for the process. This will make the test fail as expected. 74946c45a4STapasweni Pathak clang::DiagnosticConsumer *Client = CI->getDiagnostics().getClient(); 75946c45a4STapasweni Pathak Client->EndSourceFile(); 76946c45a4STapasweni Pathak Errs = Client->getNumErrors(); 77946c45a4STapasweni Pathak 78946c45a4STapasweni Pathak // The interpreter expects BeginSourceFile/EndSourceFiles to be balanced. 79946c45a4STapasweni Pathak Client->BeginSourceFile(CI->getLangOpts(), &CI->getPreprocessor()); 80946c45a4STapasweni Pathak } 819caee577SJun Zhang return (Errs || HasError) ? EXIT_FAILURE : EXIT_SUCCESS; 82946c45a4STapasweni Pathak } 83946c45a4STapasweni Pathak 8479af92bbSFred Fu struct ReplListCompleter { 8579af92bbSFred Fu clang::IncrementalCompilerBuilder &CB; 8679af92bbSFred Fu clang::Interpreter &MainInterp; 8779af92bbSFred Fu ReplListCompleter(clang::IncrementalCompilerBuilder &CB, 8879af92bbSFred Fu clang::Interpreter &Interp) 8979af92bbSFred Fu : CB(CB), MainInterp(Interp){}; 9079af92bbSFred Fu 9179af92bbSFred Fu std::vector<llvm::LineEditor::Completion> operator()(llvm::StringRef Buffer, 9279af92bbSFred Fu size_t Pos) const; 9379af92bbSFred Fu std::vector<llvm::LineEditor::Completion> 9479af92bbSFred Fu operator()(llvm::StringRef Buffer, size_t Pos, llvm::Error &ErrRes) const; 9579af92bbSFred Fu }; 9679af92bbSFred Fu 9779af92bbSFred Fu std::vector<llvm::LineEditor::Completion> 9879af92bbSFred Fu ReplListCompleter::operator()(llvm::StringRef Buffer, size_t Pos) const { 9979af92bbSFred Fu auto Err = llvm::Error::success(); 10079af92bbSFred Fu auto res = (*this)(Buffer, Pos, Err); 10179af92bbSFred Fu if (Err) 10279af92bbSFred Fu llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); 10379af92bbSFred Fu return res; 10479af92bbSFred Fu } 10579af92bbSFred Fu 10679af92bbSFred Fu std::vector<llvm::LineEditor::Completion> 10779af92bbSFred Fu ReplListCompleter::operator()(llvm::StringRef Buffer, size_t Pos, 10879af92bbSFred Fu llvm::Error &ErrRes) const { 10979af92bbSFred Fu std::vector<llvm::LineEditor::Completion> Comps; 11079af92bbSFred Fu std::vector<std::string> Results; 11179af92bbSFred Fu 11279af92bbSFred Fu auto CI = CB.CreateCpp(); 11379af92bbSFred Fu if (auto Err = CI.takeError()) { 11479af92bbSFred Fu ErrRes = std::move(Err); 11579af92bbSFred Fu return {}; 11679af92bbSFred Fu } 11779af92bbSFred Fu 11879af92bbSFred Fu size_t Lines = 11979af92bbSFred Fu std::count(Buffer.begin(), std::next(Buffer.begin(), Pos), '\n') + 1; 12079af92bbSFred Fu auto Interp = clang::Interpreter::create(std::move(*CI)); 12179af92bbSFred Fu 12279af92bbSFred Fu if (auto Err = Interp.takeError()) { 12379af92bbSFred Fu // log the error and returns an empty vector; 12479af92bbSFred Fu ErrRes = std::move(Err); 12579af92bbSFred Fu 12679af92bbSFred Fu return {}; 12779af92bbSFred Fu } 12835b366acSFred Fu auto *MainCI = (*Interp)->getCompilerInstance(); 12935b366acSFred Fu auto CC = clang::ReplCodeCompleter(); 13035b366acSFred Fu CC.codeComplete(MainCI, Buffer, Lines, Pos + 1, 13135b366acSFred Fu MainInterp.getCompilerInstance(), Results); 13279af92bbSFred Fu for (auto c : Results) { 13335b366acSFred Fu if (c.find(CC.Prefix) == 0) 13435b366acSFred Fu Comps.push_back( 13535b366acSFred Fu llvm::LineEditor::Completion(c.substr(CC.Prefix.size()), c)); 13679af92bbSFred Fu } 13779af92bbSFred Fu return Comps; 13879af92bbSFred Fu } 13979af92bbSFred Fu 14092f9852fSVassil Vassilev llvm::ExitOnError ExitOnErr; 14192f9852fSVassil Vassilev int main(int argc, const char **argv) { 142*8358437bSMészáros Gergely llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); 143*8358437bSMészáros Gergely 14492f9852fSVassil Vassilev ExitOnErr.setBanner("clang-repl: "); 14592f9852fSVassil Vassilev llvm::cl::ParseCommandLineOptions(argc, argv); 14692f9852fSVassil Vassilev 147c619d4f8SSunho Kim llvm::llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. 148c619d4f8SSunho Kim 14992f9852fSVassil Vassilev std::vector<const char *> ClangArgv(ClangArgs.size()); 15092f9852fSVassil Vassilev std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(), 15192f9852fSVassil Vassilev [](const std::string &s) -> const char * { return s.data(); }); 152ddeab07cSAnubhab Ghosh // Initialize all targets (required for device offloading) 153ddeab07cSAnubhab Ghosh llvm::InitializeAllTargetInfos(); 154ddeab07cSAnubhab Ghosh llvm::InitializeAllTargets(); 155ddeab07cSAnubhab Ghosh llvm::InitializeAllTargetMCs(); 156ddeab07cSAnubhab Ghosh llvm::InitializeAllAsmPrinters(); 157cbdc86e4SAndrew V. Teylu llvm::InitializeAllAsmParsers(); 15892f9852fSVassil Vassilev 15992f9852fSVassil Vassilev if (OptHostSupportsJit) { 16061b0f12dSLang Hames auto J = llvm::orc::LLJITBuilder().create(); 16192f9852fSVassil Vassilev if (J) 16292f9852fSVassil Vassilev llvm::outs() << "true\n"; 16392f9852fSVassil Vassilev else { 16492f9852fSVassil Vassilev llvm::consumeError(J.takeError()); 16592f9852fSVassil Vassilev llvm::outs() << "false\n"; 16692f9852fSVassil Vassilev } 16792f9852fSVassil Vassilev return 0; 16892f9852fSVassil Vassilev } 16992f9852fSVassil Vassilev 170ddeab07cSAnubhab Ghosh clang::IncrementalCompilerBuilder CB; 171ddeab07cSAnubhab Ghosh CB.SetCompilerArgs(ClangArgv); 172ddeab07cSAnubhab Ghosh 173ddeab07cSAnubhab Ghosh std::unique_ptr<clang::CompilerInstance> DeviceCI; 174ddeab07cSAnubhab Ghosh if (CudaEnabled) { 175ddeab07cSAnubhab Ghosh if (!CudaPath.empty()) 176ddeab07cSAnubhab Ghosh CB.SetCudaSDK(CudaPath); 177ddeab07cSAnubhab Ghosh 178ddeab07cSAnubhab Ghosh if (OffloadArch.empty()) { 179ddeab07cSAnubhab Ghosh OffloadArch = "sm_35"; 180ddeab07cSAnubhab Ghosh } 181ddeab07cSAnubhab Ghosh CB.SetOffloadArch(OffloadArch); 182ddeab07cSAnubhab Ghosh 183ddeab07cSAnubhab Ghosh DeviceCI = ExitOnErr(CB.CreateCudaDevice()); 184ddeab07cSAnubhab Ghosh } 185ddeab07cSAnubhab Ghosh 18692f9852fSVassil Vassilev // FIXME: Investigate if we could use runToolOnCodeWithArgs from tooling. It 18792f9852fSVassil Vassilev // can replace the boilerplate code for creation of the compiler instance. 188ddeab07cSAnubhab Ghosh std::unique_ptr<clang::CompilerInstance> CI; 189ddeab07cSAnubhab Ghosh if (CudaEnabled) { 190ddeab07cSAnubhab Ghosh CI = ExitOnErr(CB.CreateCudaHost()); 191ddeab07cSAnubhab Ghosh } else { 192ddeab07cSAnubhab Ghosh CI = ExitOnErr(CB.CreateCpp()); 193ddeab07cSAnubhab Ghosh } 19492f9852fSVassil Vassilev 19592f9852fSVassil Vassilev // Set an error handler, so that any LLVM backend diagnostics go through our 19692f9852fSVassil Vassilev // error handler. 19792f9852fSVassil Vassilev llvm::install_fatal_error_handler(LLVMErrorHandler, 19892f9852fSVassil Vassilev static_cast<void *>(&CI->getDiagnostics())); 19992f9852fSVassil Vassilev 200f4f9ad0fSVassil Vassilev // Load any requested plugins. 201f4f9ad0fSVassil Vassilev CI->LoadRequestedPlugins(); 202ddeab07cSAnubhab Ghosh if (CudaEnabled) 203ddeab07cSAnubhab Ghosh DeviceCI->LoadRequestedPlugins(); 204f4f9ad0fSVassil Vassilev 205ddeab07cSAnubhab Ghosh std::unique_ptr<clang::Interpreter> Interp; 20679af92bbSFred Fu 207ddeab07cSAnubhab Ghosh if (CudaEnabled) { 208ddeab07cSAnubhab Ghosh Interp = ExitOnErr( 209ddeab07cSAnubhab Ghosh clang::Interpreter::createWithCUDA(std::move(CI), std::move(DeviceCI))); 210ddeab07cSAnubhab Ghosh 211ddeab07cSAnubhab Ghosh if (CudaPath.empty()) { 212ddeab07cSAnubhab Ghosh ExitOnErr(Interp->LoadDynamicLibrary("libcudart.so")); 213ddeab07cSAnubhab Ghosh } else { 214ddeab07cSAnubhab Ghosh auto CudaRuntimeLibPath = CudaPath + "/lib/libcudart.so"; 215ddeab07cSAnubhab Ghosh ExitOnErr(Interp->LoadDynamicLibrary(CudaRuntimeLibPath.c_str())); 216ddeab07cSAnubhab Ghosh } 217ddeab07cSAnubhab Ghosh } else 218ddeab07cSAnubhab Ghosh Interp = ExitOnErr(clang::Interpreter::create(std::move(CI))); 219ddeab07cSAnubhab Ghosh 2209caee577SJun Zhang bool HasError = false; 2219caee577SJun Zhang 222fd5f06ebSVassil Vassilev for (const std::string &input : OptInputs) { 223fd5f06ebSVassil Vassilev if (auto Err = Interp->ParseAndExecute(input)) { 224fd5f06ebSVassil Vassilev llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); 225fd5f06ebSVassil Vassilev HasError = true; 226fd5f06ebSVassil Vassilev } 227fd5f06ebSVassil Vassilev } 228fd5f06ebSVassil Vassilev 229f01d45c3SVassil Vassilev if (OptInputs.empty()) { 23092f9852fSVassil Vassilev llvm::LineEditor LE("clang-repl"); 231c96c5edbSVassil Vassilev std::string Input; 23279af92bbSFred Fu LE.setListCompleter(ReplListCompleter(CB, *Interp)); 2336ad0788cSKazu Hirata while (std::optional<std::string> Line = LE.readLine()) { 234c96c5edbSVassil Vassilev llvm::StringRef L = *Line; 235c96c5edbSVassil Vassilev L = L.trim(); 236f3dcc235SKazu Hirata if (L.ends_with("\\")) { 237c96c5edbSVassil Vassilev Input += L.drop_back(1); 23896ae7c4fSVassil Vassilev // If it is a preprocessor directive, new lines matter. 23996ae7c4fSVassil Vassilev if (L.starts_with('#')) 24096ae7c4fSVassil Vassilev Input += "\n"; 241c96c5edbSVassil Vassilev LE.setPrompt("clang-repl... "); 242c96c5edbSVassil Vassilev continue; 243c96c5edbSVassil Vassilev } 244c96c5edbSVassil Vassilev 245c96c5edbSVassil Vassilev Input += L; 246c96c5edbSVassil Vassilev if (Input == R"(%quit)") { 24792f9852fSVassil Vassilev break; 24879af92bbSFred Fu } 24979af92bbSFred Fu if (Input == R"(%undo)") { 250fd5f06ebSVassil Vassilev if (auto Err = Interp->Undo()) 251dea5a9ccSJun Zhang llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); 252c96c5edbSVassil Vassilev } else if (Input.rfind("%lib ", 0) == 0) { 253fd5f06ebSVassil Vassilev if (auto Err = Interp->LoadDynamicLibrary(Input.data() + 5)) 254d978730dSAnubhab Ghosh llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); 255c96c5edbSVassil Vassilev } else if (auto Err = Interp->ParseAndExecute(Input)) { 256c96c5edbSVassil Vassilev llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); 257d978730dSAnubhab Ghosh } 258dea5a9ccSJun Zhang 259c96c5edbSVassil Vassilev Input = ""; 260c96c5edbSVassil Vassilev LE.setPrompt("clang-repl> "); 26192f9852fSVassil Vassilev } 262f01d45c3SVassil Vassilev } 26392f9852fSVassil Vassilev 26492f9852fSVassil Vassilev // Our error handler depends on the Diagnostics object, which we're 26592f9852fSVassil Vassilev // potentially about to delete. Uninstall the handler now so that any 26692f9852fSVassil Vassilev // later errors use the default handling behavior instead. 26792f9852fSVassil Vassilev llvm::remove_fatal_error_handler(); 26892f9852fSVassil Vassilev 2699caee577SJun Zhang return checkDiagErrors(Interp->getCompilerInstance(), HasError); 27092f9852fSVassil Vassilev } 271