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>
__lsan_is_turned_off()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
LLVMErrorHandler(void * UserData,const char * Message,bool GenCrashDiag)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.
checkDiagErrors(const clang::CompilerInstance * CI,bool HasError)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;
ReplListCompleterReplListCompleter87 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>
operator ()(llvm::StringRef Buffer,size_t Pos) const98 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>
operator ()(llvm::StringRef Buffer,size_t Pos,llvm::Error & ErrRes) const107 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;
main(int argc,const char ** argv)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