Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / clang / tools / clang-repl / ClangRepl.cpp
blob5663c2c5a6c9285450cbc850741e4e45afc11b49
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.
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"
25 #include <optional>
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; }
33 #endif
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>
40 ClangArgs("Xcc",
41 llvm::cl::desc("Argument to pass to the CompilerInvocation"),
42 llvm::cl::CommaSeparated);
43 static llvm::cl::opt<bool> OptHostSupportsJit("host-supports-jit",
44 llvm::cl::Hidden);
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,
49 bool GenCrashDiag) {
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,
90 size_t Pos) const;
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);
99 if (Err)
100 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
101 return res;
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);
113 return {};
116 size_t Lines =
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);
124 return {};
127 codeComplete(
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) {
134 Prefix = Buffer;
135 } else {
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));
143 return Comps;
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();
164 if (J)
165 llvm::outs() << "true\n";
166 else {
167 llvm::consumeError(J.takeError());
168 llvm::outs() << "false\n";
170 return 0;
173 clang::IncrementalCompilerBuilder CB;
174 CB.SetCompilerArgs(ClangArgv);
176 std::unique_ptr<clang::CompilerInstance> DeviceCI;
177 if (CudaEnabled) {
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;
192 if (CudaEnabled) {
193 CI = ExitOnErr(CB.CreateCudaHost());
194 } else {
195 CI = ExitOnErr(CB.CreateCpp());
198 // Set an error handler, so that any LLVM backend diagnostics go through our
199 // error handler.
200 llvm::install_fatal_error_handler(LLVMErrorHandler,
201 static_cast<void *>(&CI->getDiagnostics()));
203 // Load any requested plugins.
204 CI->LoadRequestedPlugins();
205 if (CudaEnabled)
206 DeviceCI->LoadRequestedPlugins();
208 std::unique_ptr<clang::Interpreter> Interp;
210 if (CudaEnabled) {
211 Interp = ExitOnErr(
212 clang::Interpreter::createWithCUDA(std::move(CI), std::move(DeviceCI)));
214 if (CudaPath.empty()) {
215 ExitOnErr(Interp->LoadDynamicLibrary("libcudart.so"));
216 } else {
217 auto CudaRuntimeLibPath = CudaPath + "/lib/libcudart.so";
218 ExitOnErr(Interp->LoadDynamicLibrary(CudaRuntimeLibPath.c_str()));
220 } else
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");
232 std::string Input;
233 LE.setListCompleter(ReplListCompleter(CB, *Interp));
234 while (std::optional<std::string> Line = LE.readLine()) {
235 llvm::StringRef L = *Line;
236 L = L.trim();
237 if (L.endswith("\\")) {
238 // FIXME: Support #ifdef X \ ...
239 Input += L.drop_back(1);
240 LE.setPrompt("clang-repl... ");
241 continue;
244 Input += L;
245 if (Input == R"(%quit)") {
246 break;
248 if (Input == R"(%undo)") {
249 if (auto Err = Interp->Undo()) {
250 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
251 HasError = true;
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: ");
256 HasError = true;
258 } else if (auto Err = Interp->ParseAndExecute(Input)) {
259 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
260 HasError = true;
263 Input = "";
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);