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" 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 // Initialize all targets (required for device offloading) 84 llvm::InitializeAllTargetInfos(); 85 llvm::InitializeAllTargets(); 86 llvm::InitializeAllTargetMCs(); 87 llvm::InitializeAllAsmPrinters(); 88 89 if (OptHostSupportsJit) { 90 auto J = llvm::orc::LLJITBuilder().create(); 91 if (J) 92 llvm::outs() << "true\n"; 93 else { 94 llvm::consumeError(J.takeError()); 95 llvm::outs() << "false\n"; 96 } 97 return 0; 98 } 99 100 clang::IncrementalCompilerBuilder CB; 101 CB.SetCompilerArgs(ClangArgv); 102 103 std::unique_ptr<clang::CompilerInstance> DeviceCI; 104 if (CudaEnabled) { 105 if (!CudaPath.empty()) 106 CB.SetCudaSDK(CudaPath); 107 108 if (OffloadArch.empty()) { 109 OffloadArch = "sm_35"; 110 } 111 CB.SetOffloadArch(OffloadArch); 112 113 DeviceCI = ExitOnErr(CB.CreateCudaDevice()); 114 } 115 116 // FIXME: Investigate if we could use runToolOnCodeWithArgs from tooling. It 117 // can replace the boilerplate code for creation of the compiler instance. 118 std::unique_ptr<clang::CompilerInstance> CI; 119 if (CudaEnabled) { 120 CI = ExitOnErr(CB.CreateCudaHost()); 121 } else { 122 CI = ExitOnErr(CB.CreateCpp()); 123 } 124 125 // Set an error handler, so that any LLVM backend diagnostics go through our 126 // error handler. 127 llvm::install_fatal_error_handler(LLVMErrorHandler, 128 static_cast<void *>(&CI->getDiagnostics())); 129 130 // Load any requested plugins. 131 CI->LoadRequestedPlugins(); 132 if (CudaEnabled) 133 DeviceCI->LoadRequestedPlugins(); 134 135 std::unique_ptr<clang::Interpreter> Interp; 136 if (CudaEnabled) { 137 Interp = ExitOnErr( 138 clang::Interpreter::createWithCUDA(std::move(CI), std::move(DeviceCI))); 139 140 if (CudaPath.empty()) { 141 ExitOnErr(Interp->LoadDynamicLibrary("libcudart.so")); 142 } else { 143 auto CudaRuntimeLibPath = CudaPath + "/lib/libcudart.so"; 144 ExitOnErr(Interp->LoadDynamicLibrary(CudaRuntimeLibPath.c_str())); 145 } 146 } else 147 Interp = ExitOnErr(clang::Interpreter::create(std::move(CI))); 148 149 for (const std::string &input : OptInputs) { 150 if (auto Err = Interp->ParseAndExecute(input)) 151 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); 152 } 153 154 bool HasError = false; 155 156 if (OptInputs.empty()) { 157 llvm::LineEditor LE("clang-repl"); 158 // FIXME: Add LE.setListCompleter 159 std::string Input; 160 while (std::optional<std::string> Line = LE.readLine()) { 161 llvm::StringRef L = *Line; 162 L = L.trim(); 163 if (L.endswith("\\")) { 164 // FIXME: Support #ifdef X \ ... 165 Input += L.drop_back(1); 166 LE.setPrompt("clang-repl... "); 167 continue; 168 } 169 170 Input += L; 171 172 if (Input == R"(%quit)") { 173 break; 174 } else if (Input == R"(%undo)") { 175 if (auto Err = Interp->Undo()) { 176 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); 177 HasError = true; 178 } 179 } else if (Input.rfind("%lib ", 0) == 0) { 180 if (auto Err = Interp->LoadDynamicLibrary(Input.data() + 5)) { 181 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); 182 HasError = true; 183 } 184 } else if (auto Err = Interp->ParseAndExecute(Input)) { 185 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); 186 HasError = true; 187 } 188 189 Input = ""; 190 LE.setPrompt("clang-repl> "); 191 } 192 } 193 194 // Our error handler depends on the Diagnostics object, which we're 195 // potentially about to delete. Uninstall the handler now so that any 196 // later errors use the default handling behavior instead. 197 llvm::remove_fatal_error_handler(); 198 199 return checkDiagErrors(Interp->getCompilerInstance(), HasError); 200 } 201