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/Basic/DiagnosticFrontend.h"
15#include "clang/Basic/Version.h"
16#include "clang/Config/config.h"
17#include "clang/Frontend/CompilerInstance.h"
18#include "clang/Interpreter/CodeCompletion.h"
19#include "clang/Interpreter/IncrementalExecutor.h"
20#include "clang/Interpreter/Interpreter.h"
21#include "clang/Lex/Preprocessor.h"
22#include "clang/Sema/Sema.h"
23
24#include "llvm/ADT/SmallString.h"
25#include "llvm/ADT/StringRef.h"
26#include "llvm/ExecutionEngine/Orc/LLJIT.h"
27#include "llvm/LineEditor/LineEditor.h"
28#include "llvm/Support/CommandLine.h"
29#include "llvm/Support/FileSystem.h"
30#include "llvm/Support/ManagedStatic.h" // llvm_shutdown
31#include "llvm/Support/Path.h"
32#include "llvm/Support/Signals.h"
33#include "llvm/Support/TargetSelect.h"
34#include "llvm/Support/VirtualFileSystem.h"
35#include "llvm/Support/raw_ostream.h"
36#include "llvm/TargetParser/Host.h"
37#include "llvm/TargetParser/Triple.h"
38#include <optional>
39
40#include <string>
41#include <vector>
42
43#include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupport.h"
44
45// Disable LSan for this test.
46// FIXME: Re-enable once we can assume GCC 13.2 or higher.
47// https://llvm.org/github.com/llvm/llvm-project/issues/67586.
48#if LLVM_ADDRESS_SANITIZER_BUILD || LLVM_HWADDRESS_SANITIZER_BUILD
49#include <sanitizer/lsan_interface.h>
50LLVM_ATTRIBUTE_USED int __lsan_is_turned_off() { return 1; }
51#endif
52
53#define DEBUG_TYPE "clang-repl"
54
55static llvm::cl::opt<bool> CudaEnabled("cuda", llvm::cl::Hidden);
56static llvm::cl::opt<std::string> CudaPath("cuda-path", llvm::cl::Hidden);
57static llvm::cl::opt<std::string> OffloadArch("offload-arch", llvm::cl::Hidden);
58static llvm::cl::OptionCategory OOPCategory("Out-of-process Execution Options");
59static llvm::cl::opt<std::string> SlabAllocateSizeString(
60 "slab-allocate",
61 llvm::cl::desc("Allocate from a slab of the given size "
62 "(allowable suffixes: Kb, Mb, Gb. default = "
63 "Kb)"),
64 llvm::cl::init(Val: ""), llvm::cl::cat(OOPCategory));
65static llvm::cl::opt<std::string>
66 OOPExecutor("oop-executor",
67 llvm::cl::desc("Launch an out-of-process executor to run code"),
68 llvm::cl::init(Val: ""), llvm::cl::ValueOptional,
69 llvm::cl::cat(OOPCategory));
70static llvm::cl::opt<std::string> OOPExecutorConnect(
71 "oop-executor-connect",
72 llvm::cl::desc(
73 "Connect to an out-of-process executor through a TCP socket"),
74 llvm::cl::value_desc("<hostname>:<port>"));
75static llvm::cl::opt<std::string>
76 OrcRuntimePath("orc-runtime", llvm::cl::desc("Path to the ORC runtime"),
77 llvm::cl::init(Val: ""), llvm::cl::ValueOptional,
78 llvm::cl::cat(OOPCategory));
79static llvm::cl::opt<bool> UseSharedMemory(
80 "use-shared-memory",
81 llvm::cl::desc("Use shared memory to transfer generated code and data"),
82 llvm::cl::init(Val: false), llvm::cl::cat(OOPCategory));
83static llvm::cl::list<std::string>
84 ClangArgs("Xcc",
85 llvm::cl::desc("Argument to pass to the CompilerInvocation"),
86 llvm::cl::CommaSeparated);
87static llvm::cl::opt<bool> OptHostSupportsJit("host-supports-jit",
88 llvm::cl::Hidden);
89static llvm::cl::opt<bool> OptHostJitTriple("host-jit-triple",
90 llvm::cl::Hidden);
91static llvm::cl::list<std::string> OptInputs(llvm::cl::Positional,
92 llvm::cl::desc("[code to run]"));
93
94static llvm::Error sanitizeOopArguments(const char *ArgV0) {
95 // Only one of -oop-executor and -oop-executor-connect can be used.
96 if (!!OOPExecutor.getNumOccurrences() &&
97 !!OOPExecutorConnect.getNumOccurrences())
98 return llvm::make_error<llvm::StringError>(
99 Args: "Only one of -" + OOPExecutor.ArgStr + " and -" +
100 OOPExecutorConnect.ArgStr + " can be specified",
101 Args: llvm::inconvertibleErrorCode());
102
103 llvm::Triple SystemTriple(llvm::sys::getProcessTriple());
104 // TODO: Remove once out-of-process execution support is implemented for
105 // non-Unix platforms.
106 if ((!SystemTriple.isOSBinFormatELF() &&
107 !SystemTriple.isOSBinFormatMachO()) &&
108 (OOPExecutor.getNumOccurrences() ||
109 OOPExecutorConnect.getNumOccurrences()))
110 return llvm::make_error<llvm::StringError>(
111 Args: "Out-of-process execution is only supported on Unix platforms",
112 Args: llvm::inconvertibleErrorCode());
113
114 // If -slab-allocate is passed, check that we're not trying to use it in
115 // -oop-executor or -oop-executor-connect mode.
116 //
117 // FIXME: Remove once we enable remote slab allocation.
118 if (SlabAllocateSizeString != "") {
119 if (OOPExecutor.getNumOccurrences() ||
120 OOPExecutorConnect.getNumOccurrences())
121 return llvm::make_error<llvm::StringError>(
122 Args: "-slab-allocate cannot be used with -oop-executor or "
123 "-oop-executor-connect",
124 Args: llvm::inconvertibleErrorCode());
125 }
126
127 // Out-of-process executors require the ORC runtime. ORC Runtime Path
128 // resolution is done in Interpreter.cpp.
129
130 // If -oop-executor was used but no value was specified then use a sensible
131 // default.
132 if (!!OOPExecutor.getNumOccurrences() && OOPExecutor.empty()) {
133 llvm::SmallString<256> OOPExecutorPath(llvm::sys::fs::getMainExecutable(
134 argv0: ArgV0, MainExecAddr: reinterpret_cast<void *>(&sanitizeOopArguments)));
135 llvm::sys::path::remove_filename(path&: OOPExecutorPath);
136 llvm::sys::path::append(path&: OOPExecutorPath, a: "llvm-jitlink-executor");
137 OOPExecutor = OOPExecutorPath.str().str();
138 }
139
140 return llvm::Error::success();
141}
142
143static llvm::Expected<unsigned> getSlabAllocSize(llvm::StringRef SizeString) {
144 SizeString = SizeString.trim();
145
146 uint64_t Units = 1024;
147
148 if (SizeString.ends_with_insensitive(Suffix: "kb"))
149 SizeString = SizeString.drop_back(N: 2).rtrim();
150 else if (SizeString.ends_with_insensitive(Suffix: "mb")) {
151 Units = 1024 * 1024;
152 SizeString = SizeString.drop_back(N: 2).rtrim();
153 } else if (SizeString.ends_with_insensitive(Suffix: "gb")) {
154 Units = 1024 * 1024 * 1024;
155 SizeString = SizeString.drop_back(N: 2).rtrim();
156 } else if (SizeString.empty())
157 return 0;
158
159 uint64_t SlabSize = 0;
160 if (SizeString.getAsInteger(Radix: 10, Result&: SlabSize))
161 return llvm::make_error<llvm::StringError>(
162 Args: "Invalid numeric format for slab size", Args: llvm::inconvertibleErrorCode());
163
164 return SlabSize * Units;
165}
166
167static void LLVMErrorHandler(void *UserData, const char *Message,
168 bool GenCrashDiag) {
169 auto &Diags = *static_cast<clang::DiagnosticsEngine *>(UserData);
170
171 Diags.Report(DiagID: clang::diag::err_fe_error_backend) << Message;
172
173 // Run the interrupt handlers to make sure any special cleanups get done, in
174 // particular that we remove files registered with RemoveFileOnSignal.
175 llvm::sys::RunInterruptHandlers();
176
177 // We cannot recover from llvm errors. When reporting a fatal error, exit
178 // with status 70 to generate crash diagnostics. For BSD systems this is
179 // defined as an internal software error. Otherwise, exit with status 1.
180
181 exit(status: GenCrashDiag ? 70 : 1);
182}
183
184// If we are running with -verify a reported has to be returned as unsuccess.
185// This is relevant especially for the test suite.
186static int checkDiagErrors(const clang::CompilerInstance *CI, bool HasError) {
187 unsigned Errs = CI->getDiagnostics().getClient()->getNumErrors();
188 if (CI->getDiagnosticOpts().VerifyDiagnostics) {
189 // If there was an error that came from the verifier we must return 1 as
190 // an exit code for the process. This will make the test fail as expected.
191 clang::DiagnosticConsumer *Client = CI->getDiagnostics().getClient();
192 Client->EndSourceFile();
193 Errs = Client->getNumErrors();
194
195 // The interpreter expects BeginSourceFile/EndSourceFiles to be balanced.
196 Client->BeginSourceFile(LangOpts: CI->getLangOpts(), PP: &CI->getPreprocessor());
197 }
198 return (Errs || HasError) ? EXIT_FAILURE : EXIT_SUCCESS;
199}
200
201struct ReplListCompleter {
202 clang::IncrementalCompilerBuilder &CB;
203 clang::Interpreter &MainInterp;
204 ReplListCompleter(clang::IncrementalCompilerBuilder &CB,
205 clang::Interpreter &Interp)
206 : CB(CB), MainInterp(Interp) {};
207
208 std::vector<llvm::LineEditor::Completion> operator()(llvm::StringRef Buffer,
209 size_t Pos) const;
210 std::vector<llvm::LineEditor::Completion>
211 operator()(llvm::StringRef Buffer, size_t Pos, llvm::Error &ErrRes) const;
212};
213
214std::vector<llvm::LineEditor::Completion>
215ReplListCompleter::operator()(llvm::StringRef Buffer, size_t Pos) const {
216 auto Err = llvm::Error::success();
217 auto res = (*this)(Buffer, Pos, Err);
218 if (Err)
219 llvm::logAllUnhandledErrors(E: std::move(Err), OS&: llvm::errs(), ErrorBanner: "error: ");
220 return res;
221}
222
223std::vector<llvm::LineEditor::Completion>
224ReplListCompleter::operator()(llvm::StringRef Buffer, size_t Pos,
225 llvm::Error &ErrRes) const {
226 std::vector<llvm::LineEditor::Completion> Comps;
227 std::vector<std::string> Results;
228
229 auto CI = CB.CreateCpp();
230 if (auto Err = CI.takeError()) {
231 ErrRes = std::move(Err);
232 return {};
233 }
234
235 size_t Lines =
236 std::count(first: Buffer.begin(), last: std::next(x: Buffer.begin(), n: Pos), value: '\n') + 1;
237 auto Interp = clang::Interpreter::create(CI: std::move(*CI));
238
239 if (auto Err = Interp.takeError()) {
240 // log the error and returns an empty vector;
241 ErrRes = std::move(Err);
242
243 return {};
244 }
245 auto *MainCI = (*Interp)->getCompilerInstance();
246 auto CC = clang::ReplCodeCompleter();
247 CC.codeComplete(InterpCI: MainCI, Content: Buffer, Line: Lines, Col: Pos + 1,
248 ParentCI: MainInterp.getCompilerInstance(), CCResults&: Results);
249 for (auto c : Results) {
250 if (c.find(str: CC.Prefix) == 0)
251 Comps.push_back(
252 x: llvm::LineEditor::Completion(c.substr(pos: CC.Prefix.size()), c));
253 }
254 return Comps;
255}
256
257llvm::ExitOnError ExitOnErr;
258int main(int argc, const char **argv) {
259 llvm::sys::PrintStackTraceOnErrorSignal(Argv0: argv[0]);
260
261 ExitOnErr.setBanner("clang-repl: ");
262 llvm::cl::ParseCommandLineOptions(argc, argv);
263
264 llvm::llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
265
266 std::vector<const char *> ClangArgv(ClangArgs.size());
267 std::transform(first: ClangArgs.begin(), last: ClangArgs.end(), result: ClangArgv.begin(),
268 unary_op: [](const std::string &s) -> const char * { return s.data(); });
269 // Initialize all targets (required for device offloading)
270 llvm::InitializeAllTargetInfos();
271 llvm::InitializeAllTargets();
272 llvm::InitializeAllTargetMCs();
273 llvm::InitializeAllAsmPrinters();
274 llvm::InitializeAllAsmParsers();
275
276 if (OptHostSupportsJit) {
277 auto J = llvm::orc::LLJITBuilder().create();
278 if (J)
279 llvm::outs() << "true\n";
280 else {
281 llvm::consumeError(Err: J.takeError());
282 llvm::outs() << "false\n";
283 }
284 return 0;
285 } else if (OptHostJitTriple) {
286 auto J = ExitOnErr(llvm::orc::LLJITBuilder().create());
287 auto T = J->getTargetTriple();
288 llvm::outs() << T.normalize() << '\n';
289 return 0;
290 }
291
292 ExitOnErr(sanitizeOopArguments(ArgV0: argv[0]));
293
294 clang::IncrementalCompilerBuilder CB;
295 CB.SetCompilerArgs(ClangArgv);
296
297 auto IEB = std::make_unique<clang::IncrementalExecutorBuilder>();
298 IEB->IsOutOfProcess = !OOPExecutor.empty() || !OOPExecutorConnect.empty();
299 IEB->OOPExecutor = OOPExecutor;
300 if (!OrcRuntimePath.empty())
301 IEB->OrcRuntimePath = OrcRuntimePath;
302 else
303 CB.SetDriverCompilationCallback(IEB->UpdateOrcRuntimePathCB);
304
305 auto SizeOrErr = getSlabAllocSize(SizeString: SlabAllocateSizeString);
306 if (!SizeOrErr) {
307 llvm::logAllUnhandledErrors(E: SizeOrErr.takeError(), OS&: llvm::errs(), ErrorBanner: "error: ");
308 return EXIT_FAILURE;
309 }
310 IEB->SlabAllocateSize = *SizeOrErr;
311 IEB->UseSharedMemory = UseSharedMemory;
312
313 std::unique_ptr<clang::CompilerInstance> DeviceCI;
314 if (CudaEnabled) {
315 if (!CudaPath.empty())
316 CB.SetCudaSDK(CudaPath);
317
318 if (OffloadArch.empty()) {
319 OffloadArch = "sm_35";
320 }
321 CB.SetOffloadArch(OffloadArch);
322
323 DeviceCI = ExitOnErr(CB.CreateCudaDevice());
324 }
325
326 // FIXME: Investigate if we could use runToolOnCodeWithArgs from tooling. It
327 // can replace the boilerplate code for creation of the compiler instance.
328 std::unique_ptr<clang::CompilerInstance> CI;
329 if (CudaEnabled) {
330 CI = ExitOnErr(CB.CreateCudaHost());
331 } else {
332 CI = ExitOnErr(CB.CreateCpp());
333 }
334
335 // Set an error handler, so that any LLVM backend diagnostics go through our
336 // error handler.
337 llvm::install_fatal_error_handler(handler: LLVMErrorHandler,
338 user_data: static_cast<void *>(&CI->getDiagnostics()));
339
340 // Load any requested plugins.
341 CI->LoadRequestedPlugins();
342 if (CudaEnabled)
343 DeviceCI->LoadRequestedPlugins();
344
345 std::unique_ptr<clang::Interpreter> Interp;
346
347 if (CudaEnabled) {
348 Interp = ExitOnErr(
349 clang::Interpreter::createWithCUDA(CI: std::move(CI), DCI: std::move(DeviceCI)));
350
351 if (CudaPath.empty()) {
352 ExitOnErr(Interp->LoadDynamicLibrary(name: "libcudart.so"));
353 } else {
354 auto CudaRuntimeLibPath = CudaPath + "/lib/libcudart.so";
355 ExitOnErr(Interp->LoadDynamicLibrary(name: CudaRuntimeLibPath.c_str()));
356 }
357 } else {
358 Interp =
359 ExitOnErr(clang::Interpreter::create(CI: std::move(CI), IEB: std::move(IEB)));
360 }
361
362 bool HasError = false;
363
364 for (const std::string &input : OptInputs) {
365 if (auto Err = Interp->ParseAndExecute(Code: input)) {
366 llvm::logAllUnhandledErrors(E: std::move(Err), OS&: llvm::errs(), ErrorBanner: "error: ");
367 HasError = true;
368 }
369 }
370
371 if (OptInputs.empty()) {
372 llvm::LineEditor LE("clang-repl");
373 std::string Input;
374 LE.setListCompleter(ReplListCompleter(CB, *Interp));
375 while (std::optional<std::string> Line = LE.readLine()) {
376 llvm::StringRef L = *Line;
377 L = L.trim();
378 if (L.ends_with(Suffix: "\\")) {
379 Input += L.drop_back(N: 1);
380 // If it is a preprocessor directive, new lines matter.
381 if (L.starts_with(Prefix: '#'))
382 Input += "\n";
383 LE.setPrompt("clang-repl... ");
384 continue;
385 }
386
387 Input += L;
388 // If we add more % commands, there should be better architecture than
389 // this.
390 if (Input == R"(%quit)") {
391 break;
392 }
393 if (Input == R"(%undo)") {
394 if (auto Err = Interp->Undo())
395 llvm::logAllUnhandledErrors(E: std::move(Err), OS&: llvm::errs(), ErrorBanner: "error: ");
396 } else if (Input == R"(%help)") {
397 llvm::outs() << "%help\t\tlist clang-repl %commands\n"
398 << "%undo\t\tundo the previous input\n"
399 << "%lib\t<path>\tlink a dynamic library\n"
400 << "%quit\t\texit clang-repl\n";
401 } else if (Input == R"(%lib)") {
402 auto Err = llvm::make_error<llvm::StringError>(
403 Args: "%lib expects 1 argument: the path to a dynamic library\n",
404 Args: std::error_code());
405 llvm::logAllUnhandledErrors(E: std::move(Err), OS&: llvm::errs(), ErrorBanner: "error: ");
406 } else if (Input.rfind(s: "%lib ", pos: 0) == 0) {
407 if (auto Err = Interp->LoadDynamicLibrary(name: Input.data() + 5))
408 llvm::logAllUnhandledErrors(E: std::move(Err), OS&: llvm::errs(), ErrorBanner: "error: ");
409 } else if (Input[0] == '%') {
410 auto Err = llvm::make_error<llvm::StringError>(
411 Args: llvm::formatv(
412 Fmt: "Invalid % command \"{0}\", use \"%help\" to list commands\n",
413 Vals&: Input),
414 Args: std::error_code());
415 llvm::logAllUnhandledErrors(E: std::move(Err), OS&: llvm::errs(), ErrorBanner: "error: ");
416 } else if (auto Err = Interp->ParseAndExecute(Code: Input)) {
417 llvm::logAllUnhandledErrors(E: std::move(Err), OS&: llvm::errs(), ErrorBanner: "error: ");
418 }
419
420 Input = "";
421 LE.setPrompt("clang-repl> ");
422 }
423 }
424
425 // Our error handler depends on the Diagnostics object, which we're
426 // potentially about to delete. Uninstall the handler now so that any
427 // later errors use the default handling behavior instead.
428 llvm::remove_fatal_error_handler();
429
430 return checkDiagErrors(CI: Interp->getCompilerInstance(), HasError);
431}
432