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>
34LLVM_ATTRIBUTE_USED int __lsan_is_turned_off() { return 1; }
35#endif
36
37static llvm::cl::opt<bool> CudaEnabled("cuda", llvm::cl::Hidden);
38static llvm::cl::opt<std::string> CudaPath("cuda-path", llvm::cl::Hidden);
39static llvm::cl::opt<std::string> OffloadArch("offload-arch", llvm::cl::Hidden);
40
41static llvm::cl::list<std::string>
42 ClangArgs("Xcc",
43 llvm::cl::desc("Argument to pass to the CompilerInvocation"),
44 llvm::cl::CommaSeparated);
45static llvm::cl::opt<bool> OptHostSupportsJit("host-supports-jit",
46 llvm::cl::Hidden);
47static llvm::cl::list<std::string> OptInputs(llvm::cl::Positional,
48 llvm::cl::desc("[code to run]"));
49
50static 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(status: 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.
69static 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(LangOpts: CI->getLangOpts(), PP: &CI->getPreprocessor());
80 }
81 return (Errs || HasError) ? EXIT_FAILURE : EXIT_SUCCESS;
82}
83
84struct ReplListCompleter {
85 clang::IncrementalCompilerBuilder &CB;
86 clang::Interpreter &MainInterp;
87 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
97std::vector<llvm::LineEditor::Completion>
98ReplListCompleter::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(E: std::move(Err), OS&: llvm::errs(), ErrorBanner: "error: ");
103 return res;
104}
105
106std::vector<llvm::LineEditor::Completion>
107ReplListCompleter::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(first: Buffer.begin(), last: std::next(x: Buffer.begin(), n: Pos), value: '\n') + 1;
120 auto Interp = clang::Interpreter::create(CI: 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(InterpCI: MainCI, Content: Buffer, Line: Lines, Col: Pos + 1,
131 ParentCI: MainInterp.getCompilerInstance(), CCResults&: Results);
132 for (auto c : Results) {
133 if (c.find(str: CC.Prefix) == 0)
134 Comps.push_back(
135 x: llvm::LineEditor::Completion(c.substr(pos: CC.Prefix.size()), c));
136 }
137 return Comps;
138}
139
140llvm::ExitOnError ExitOnErr;
141int main(int argc, const char **argv) {
142 llvm::sys::PrintStackTraceOnErrorSignal(Argv0: argv[0]);
143
144 ExitOnErr.setBanner("clang-repl: ");
145 llvm::cl::ParseCommandLineOptions(argc, argv);
146
147 llvm::llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
148
149 std::vector<const char *> ClangArgv(ClangArgs.size());
150 std::transform(first: ClangArgs.begin(), last: ClangArgs.end(), result: ClangArgv.begin(),
151 unary_op: [](const std::string &s) -> const char * { return s.data(); });
152 // Initialize all targets (required for device offloading)
153 llvm::InitializeAllTargetInfos();
154 llvm::InitializeAllTargets();
155 llvm::InitializeAllTargetMCs();
156 llvm::InitializeAllAsmPrinters();
157 llvm::InitializeAllAsmParsers();
158
159 if (OptHostSupportsJit) {
160 auto J = llvm::orc::LLJITBuilder().create();
161 if (J)
162 llvm::outs() << "true\n";
163 else {
164 llvm::consumeError(Err: J.takeError());
165 llvm::outs() << "false\n";
166 }
167 return 0;
168 }
169
170 clang::IncrementalCompilerBuilder CB;
171 CB.SetCompilerArgs(ClangArgv);
172
173 std::unique_ptr<clang::CompilerInstance> DeviceCI;
174 if (CudaEnabled) {
175 if (!CudaPath.empty())
176 CB.SetCudaSDK(CudaPath);
177
178 if (OffloadArch.empty()) {
179 OffloadArch = "sm_35";
180 }
181 CB.SetOffloadArch(OffloadArch);
182
183 DeviceCI = ExitOnErr(CB.CreateCudaDevice());
184 }
185
186 // FIXME: Investigate if we could use runToolOnCodeWithArgs from tooling. It
187 // can replace the boilerplate code for creation of the compiler instance.
188 std::unique_ptr<clang::CompilerInstance> CI;
189 if (CudaEnabled) {
190 CI = ExitOnErr(CB.CreateCudaHost());
191 } else {
192 CI = ExitOnErr(CB.CreateCpp());
193 }
194
195 // Set an error handler, so that any LLVM backend diagnostics go through our
196 // error handler.
197 llvm::install_fatal_error_handler(handler: LLVMErrorHandler,
198 user_data: static_cast<void *>(&CI->getDiagnostics()));
199
200 // Load any requested plugins.
201 CI->LoadRequestedPlugins();
202 if (CudaEnabled)
203 DeviceCI->LoadRequestedPlugins();
204
205 std::unique_ptr<clang::Interpreter> Interp;
206
207 if (CudaEnabled) {
208 Interp = ExitOnErr(
209 clang::Interpreter::createWithCUDA(CI: std::move(CI), DCI: std::move(DeviceCI)));
210
211 if (CudaPath.empty()) {
212 ExitOnErr(Interp->LoadDynamicLibrary(name: "libcudart.so"));
213 } else {
214 auto CudaRuntimeLibPath = CudaPath + "/lib/libcudart.so";
215 ExitOnErr(Interp->LoadDynamicLibrary(name: CudaRuntimeLibPath.c_str()));
216 }
217 } else
218 Interp = ExitOnErr(clang::Interpreter::create(CI: std::move(CI)));
219
220 bool HasError = false;
221
222 for (const std::string &input : OptInputs) {
223 if (auto Err = Interp->ParseAndExecute(Code: input)) {
224 llvm::logAllUnhandledErrors(E: std::move(Err), OS&: llvm::errs(), ErrorBanner: "error: ");
225 HasError = true;
226 }
227 }
228
229 if (OptInputs.empty()) {
230 llvm::LineEditor LE("clang-repl");
231 std::string Input;
232 LE.setListCompleter(ReplListCompleter(CB, *Interp));
233 while (std::optional<std::string> Line = LE.readLine()) {
234 llvm::StringRef L = *Line;
235 L = L.trim();
236 if (L.ends_with(Suffix: "\\")) {
237 Input += L.drop_back(N: 1);
238 // If it is a preprocessor directive, new lines matter.
239 if (L.starts_with(Prefix: '#'))
240 Input += "\n";
241 LE.setPrompt("clang-repl... ");
242 continue;
243 }
244
245 Input += L;
246 if (Input == R"(%quit)") {
247 break;
248 }
249 if (Input == R"(%undo)") {
250 if (auto Err = Interp->Undo())
251 llvm::logAllUnhandledErrors(E: std::move(Err), OS&: llvm::errs(), ErrorBanner: "error: ");
252 } else if (Input.rfind(s: "%lib ", pos: 0) == 0) {
253 if (auto Err = Interp->LoadDynamicLibrary(name: Input.data() + 5))
254 llvm::logAllUnhandledErrors(E: std::move(Err), OS&: llvm::errs(), ErrorBanner: "error: ");
255 } else if (auto Err = Interp->ParseAndExecute(Code: Input)) {
256 llvm::logAllUnhandledErrors(E: std::move(Err), OS&: llvm::errs(), ErrorBanner: "error: ");
257 }
258
259 Input = "";
260 LE.setPrompt("clang-repl> ");
261 }
262 }
263
264 // Our error handler depends on the Diagnostics object, which we're
265 // potentially about to delete. Uninstall the handler now so that any
266 // later errors use the default handling behavior instead.
267 llvm::remove_fatal_error_handler();
268
269 return checkDiagErrors(CI: Interp->getCompilerInstance(), HasError);
270}
271

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of clang/tools/clang-repl/ClangRepl.cpp