1 //===--- tools/clang-repl/ClangRepl.cpp - clang-repl - the Clang REPL -----===//
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
7 //===----------------------------------------------------------------------===//
9 // This file implements a REPL tool on top of clang.
11 //===----------------------------------------------------------------------===//
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"
19 #include "llvm/ExecutionEngine/Orc/LLJIT.h"
20 #include "llvm/LineEditor/LineEditor.h"
21 #include "llvm/Support/CommandLine.h"
22 #include "llvm/Support/ManagedStatic.h" // llvm_shutdown
23 #include "llvm/Support/Signals.h"
24 #include "llvm/Support/TargetSelect.h"
27 // Disable LSan for this test.
28 // FIXME: Re-enable once we can assume GCC 13.2 or higher.
29 // https://llvm.org/github.com/llvm/llvm-project/issues/67586.
30 #if LLVM_ADDRESS_SANITIZER_BUILD || LLVM_HWADDRESS_SANITIZER_BUILD
31 #include <sanitizer/lsan_interface.h>
32 LLVM_ATTRIBUTE_USED
int __lsan_is_turned_off() { return 1; }
35 static llvm::cl::opt
<bool> CudaEnabled("cuda", llvm::cl::Hidden
);
36 static llvm::cl::opt
<std::string
> CudaPath("cuda-path", llvm::cl::Hidden
);
37 static llvm::cl::opt
<std::string
> OffloadArch("offload-arch", llvm::cl::Hidden
);
39 static llvm::cl::list
<std::string
>
41 llvm::cl::desc("Argument to pass to the CompilerInvocation"),
42 llvm::cl::CommaSeparated
);
43 static llvm::cl::opt
<bool> OptHostSupportsJit("host-supports-jit",
45 static llvm::cl::list
<std::string
> OptInputs(llvm::cl::Positional
,
46 llvm::cl::desc("[code to run]"));
48 static void LLVMErrorHandler(void *UserData
, const char *Message
,
50 auto &Diags
= *static_cast<clang::DiagnosticsEngine
*>(UserData
);
52 Diags
.Report(clang::diag::err_fe_error_backend
) << Message
;
54 // Run the interrupt handlers to make sure any special cleanups get done, in
55 // particular that we remove files registered with RemoveFileOnSignal.
56 llvm::sys::RunInterruptHandlers();
58 // We cannot recover from llvm errors. When reporting a fatal error, exit
59 // with status 70 to generate crash diagnostics. For BSD systems this is
60 // defined as an internal software error. Otherwise, exit with status 1.
62 exit(GenCrashDiag
? 70 : 1);
65 // If we are running with -verify a reported has to be returned as unsuccess.
66 // This is relevant especially for the test suite.
67 static int checkDiagErrors(const clang::CompilerInstance
*CI
, bool HasError
) {
68 unsigned Errs
= CI
->getDiagnostics().getClient()->getNumErrors();
69 if (CI
->getDiagnosticOpts().VerifyDiagnostics
) {
70 // If there was an error that came from the verifier we must return 1 as
71 // an exit code for the process. This will make the test fail as expected.
72 clang::DiagnosticConsumer
*Client
= CI
->getDiagnostics().getClient();
73 Client
->EndSourceFile();
74 Errs
= Client
->getNumErrors();
76 // The interpreter expects BeginSourceFile/EndSourceFiles to be balanced.
77 Client
->BeginSourceFile(CI
->getLangOpts(), &CI
->getPreprocessor());
79 return (Errs
|| HasError
) ? EXIT_FAILURE
: EXIT_SUCCESS
;
82 struct ReplListCompleter
{
83 clang::IncrementalCompilerBuilder
&CB
;
84 clang::Interpreter
&MainInterp
;
85 ReplListCompleter(clang::IncrementalCompilerBuilder
&CB
,
86 clang::Interpreter
&Interp
)
87 : CB(CB
), MainInterp(Interp
){};
89 std::vector
<llvm::LineEditor::Completion
> operator()(llvm::StringRef Buffer
,
91 std::vector
<llvm::LineEditor::Completion
>
92 operator()(llvm::StringRef Buffer
, size_t Pos
, llvm::Error
&ErrRes
) const;
95 std::vector
<llvm::LineEditor::Completion
>
96 ReplListCompleter::operator()(llvm::StringRef Buffer
, size_t Pos
) const {
97 auto Err
= llvm::Error::success();
98 auto res
= (*this)(Buffer
, Pos
, Err
);
100 llvm::logAllUnhandledErrors(std::move(Err
), llvm::errs(), "error: ");
104 std::vector
<llvm::LineEditor::Completion
>
105 ReplListCompleter::operator()(llvm::StringRef Buffer
, size_t Pos
,
106 llvm::Error
&ErrRes
) const {
107 std::vector
<llvm::LineEditor::Completion
> Comps
;
108 std::vector
<std::string
> Results
;
110 auto CI
= CB
.CreateCpp();
111 if (auto Err
= CI
.takeError()) {
112 ErrRes
= std::move(Err
);
117 std::count(Buffer
.begin(), std::next(Buffer
.begin(), Pos
), '\n') + 1;
118 auto Interp
= clang::Interpreter::create(std::move(*CI
));
120 if (auto Err
= Interp
.takeError()) {
121 // log the error and returns an empty vector;
122 ErrRes
= std::move(Err
);
128 const_cast<clang::CompilerInstance
*>((*Interp
)->getCompilerInstance()),
129 Buffer
, Lines
, Pos
+ 1, MainInterp
.getCompilerInstance(), Results
);
131 size_t space_pos
= Buffer
.rfind(" ");
132 llvm::StringRef Prefix
;
133 if (space_pos
== llvm::StringRef::npos
) {
136 Prefix
= Buffer
.substr(space_pos
+ 1);
139 for (auto c
: Results
) {
140 if (c
.find(Prefix
) == 0)
141 Comps
.push_back(llvm::LineEditor::Completion(c
.substr(Prefix
.size()), c
));
146 llvm::ExitOnError ExitOnErr
;
147 int main(int argc
, const char **argv
) {
148 ExitOnErr
.setBanner("clang-repl: ");
149 llvm::cl::ParseCommandLineOptions(argc
, argv
);
151 llvm::llvm_shutdown_obj Y
; // Call llvm_shutdown() on exit.
153 std::vector
<const char *> ClangArgv(ClangArgs
.size());
154 std::transform(ClangArgs
.begin(), ClangArgs
.end(), ClangArgv
.begin(),
155 [](const std::string
&s
) -> const char * { return s
.data(); });
156 // Initialize all targets (required for device offloading)
157 llvm::InitializeAllTargetInfos();
158 llvm::InitializeAllTargets();
159 llvm::InitializeAllTargetMCs();
160 llvm::InitializeAllAsmPrinters();
162 if (OptHostSupportsJit
) {
163 auto J
= llvm::orc::LLJITBuilder().create();
165 llvm::outs() << "true\n";
167 llvm::consumeError(J
.takeError());
168 llvm::outs() << "false\n";
173 clang::IncrementalCompilerBuilder CB
;
174 CB
.SetCompilerArgs(ClangArgv
);
176 std::unique_ptr
<clang::CompilerInstance
> DeviceCI
;
178 if (!CudaPath
.empty())
179 CB
.SetCudaSDK(CudaPath
);
181 if (OffloadArch
.empty()) {
182 OffloadArch
= "sm_35";
184 CB
.SetOffloadArch(OffloadArch
);
186 DeviceCI
= ExitOnErr(CB
.CreateCudaDevice());
189 // FIXME: Investigate if we could use runToolOnCodeWithArgs from tooling. It
190 // can replace the boilerplate code for creation of the compiler instance.
191 std::unique_ptr
<clang::CompilerInstance
> CI
;
193 CI
= ExitOnErr(CB
.CreateCudaHost());
195 CI
= ExitOnErr(CB
.CreateCpp());
198 // Set an error handler, so that any LLVM backend diagnostics go through our
200 llvm::install_fatal_error_handler(LLVMErrorHandler
,
201 static_cast<void *>(&CI
->getDiagnostics()));
203 // Load any requested plugins.
204 CI
->LoadRequestedPlugins();
206 DeviceCI
->LoadRequestedPlugins();
208 std::unique_ptr
<clang::Interpreter
> Interp
;
212 clang::Interpreter::createWithCUDA(std::move(CI
), std::move(DeviceCI
)));
214 if (CudaPath
.empty()) {
215 ExitOnErr(Interp
->LoadDynamicLibrary("libcudart.so"));
217 auto CudaRuntimeLibPath
= CudaPath
+ "/lib/libcudart.so";
218 ExitOnErr(Interp
->LoadDynamicLibrary(CudaRuntimeLibPath
.c_str()));
221 Interp
= ExitOnErr(clang::Interpreter::create(std::move(CI
)));
223 for (const std::string
&input
: OptInputs
) {
224 if (auto Err
= Interp
->ParseAndExecute(input
))
225 llvm::logAllUnhandledErrors(std::move(Err
), llvm::errs(), "error: ");
228 bool HasError
= false;
230 if (OptInputs
.empty()) {
231 llvm::LineEditor
LE("clang-repl");
233 LE
.setListCompleter(ReplListCompleter(CB
, *Interp
));
234 while (std::optional
<std::string
> Line
= LE
.readLine()) {
235 llvm::StringRef L
= *Line
;
237 if (L
.endswith("\\")) {
238 // FIXME: Support #ifdef X \ ...
239 Input
+= L
.drop_back(1);
240 LE
.setPrompt("clang-repl... ");
245 if (Input
== R
"(%quit)") {
248 if (Input
== R
"(%undo)") {
249 if (auto Err
= Interp
->Undo()) {
250 llvm::logAllUnhandledErrors(std::move(Err
), llvm::errs(), "error: ");
253 } else if (Input
.rfind("%lib ", 0) == 0) {
254 if (auto Err
= Interp
->LoadDynamicLibrary(Input
.data() + 5)) {
255 llvm::logAllUnhandledErrors(std::move(Err
), llvm::errs(), "error: ");
258 } else if (auto Err
= Interp
->ParseAndExecute(Input
)) {
259 llvm::logAllUnhandledErrors(std::move(Err
), llvm::errs(), "error: ");
264 LE
.setPrompt("clang-repl> ");
268 // Our error handler depends on the Diagnostics object, which we're
269 // potentially about to delete. Uninstall the handler now so that any
270 // later errors use the default handling behavior instead.
271 llvm::remove_fatal_error_handler();
273 return checkDiagErrors(Interp
->getCompilerInstance(), HasError
);