xref: /llvm-project/clang/tools/clang-repl/ClangRepl.cpp (revision 61b0f12d6b9cdcb6bb3dd679e3a3c36fa94daeae)
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 
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>
26 
27 static llvm::cl::opt<bool> CudaEnabled("cuda", llvm::cl::Hidden);
28 static llvm::cl::opt<std::string> CudaPath("cuda-path", llvm::cl::Hidden);
29 static llvm::cl::opt<std::string> OffloadArch("offload-arch", llvm::cl::Hidden);
30 
31 static llvm::cl::list<std::string>
32     ClangArgs("Xcc",
33               llvm::cl::desc("Argument to pass to the CompilerInvocation"),
34               llvm::cl::CommaSeparated);
35 static llvm::cl::opt<bool> OptHostSupportsJit("host-supports-jit",
36                                               llvm::cl::Hidden);
37 static llvm::cl::list<std::string> OptInputs(llvm::cl::Positional,
38                                              llvm::cl::desc("[code to run]"));
39 
40 static void LLVMErrorHandler(void *UserData, const char *Message,
41                              bool GenCrashDiag) {
42   auto &Diags = *static_cast<clang::DiagnosticsEngine *>(UserData);
43 
44   Diags.Report(clang::diag::err_fe_error_backend) << Message;
45 
46   // Run the interrupt handlers to make sure any special cleanups get done, in
47   // particular that we remove files registered with RemoveFileOnSignal.
48   llvm::sys::RunInterruptHandlers();
49 
50   // We cannot recover from llvm errors.  When reporting a fatal error, exit
51   // with status 70 to generate crash diagnostics.  For BSD systems this is
52   // defined as an internal software error. Otherwise, exit with status 1.
53 
54   exit(GenCrashDiag ? 70 : 1);
55 }
56 
57 // If we are running with -verify a reported has to be returned as unsuccess.
58 // This is relevant especially for the test suite.
59 static int checkDiagErrors(const clang::CompilerInstance *CI, bool HasError) {
60   unsigned Errs = CI->getDiagnostics().getClient()->getNumErrors();
61   if (CI->getDiagnosticOpts().VerifyDiagnostics) {
62     // If there was an error that came from the verifier we must return 1 as
63     // an exit code for the process. This will make the test fail as expected.
64     clang::DiagnosticConsumer *Client = CI->getDiagnostics().getClient();
65     Client->EndSourceFile();
66     Errs = Client->getNumErrors();
67 
68     // The interpreter expects BeginSourceFile/EndSourceFiles to be balanced.
69     Client->BeginSourceFile(CI->getLangOpts(), &CI->getPreprocessor());
70   }
71   return (Errs || HasError) ? EXIT_FAILURE : EXIT_SUCCESS;
72 }
73 
74 struct ReplListCompleter {
75   clang::IncrementalCompilerBuilder &CB;
76   clang::Interpreter &MainInterp;
77   ReplListCompleter(clang::IncrementalCompilerBuilder &CB,
78                     clang::Interpreter &Interp)
79       : CB(CB), MainInterp(Interp){};
80 
81   std::vector<llvm::LineEditor::Completion> operator()(llvm::StringRef Buffer,
82                                                        size_t Pos) const;
83   std::vector<llvm::LineEditor::Completion>
84   operator()(llvm::StringRef Buffer, size_t Pos, llvm::Error &ErrRes) const;
85 };
86 
87 std::vector<llvm::LineEditor::Completion>
88 ReplListCompleter::operator()(llvm::StringRef Buffer, size_t Pos) const {
89   auto Err = llvm::Error::success();
90   auto res = (*this)(Buffer, Pos, Err);
91   if (Err)
92     llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
93   return res;
94 }
95 
96 std::vector<llvm::LineEditor::Completion>
97 ReplListCompleter::operator()(llvm::StringRef Buffer, size_t Pos,
98                               llvm::Error &ErrRes) const {
99   std::vector<llvm::LineEditor::Completion> Comps;
100   std::vector<std::string> Results;
101 
102   auto CI = CB.CreateCpp();
103   if (auto Err = CI.takeError()) {
104     ErrRes = std::move(Err);
105     return {};
106   }
107 
108   size_t Lines =
109       std::count(Buffer.begin(), std::next(Buffer.begin(), Pos), '\n') + 1;
110   auto Interp = clang::Interpreter::create(std::move(*CI));
111 
112   if (auto Err = Interp.takeError()) {
113     // log the error and returns an empty vector;
114     ErrRes = std::move(Err);
115 
116     return {};
117   }
118 
119   codeComplete(
120       const_cast<clang::CompilerInstance *>((*Interp)->getCompilerInstance()),
121       Buffer, Lines, Pos + 1, MainInterp.getCompilerInstance(), Results);
122 
123   size_t space_pos = Buffer.rfind(" ");
124   llvm::StringRef Prefix;
125   if (space_pos == llvm::StringRef::npos) {
126     Prefix = Buffer;
127   } else {
128     Prefix = Buffer.substr(space_pos + 1);
129   }
130 
131   for (auto c : Results) {
132     if (c.find(Prefix) == 0)
133       Comps.push_back(llvm::LineEditor::Completion(c.substr(Prefix.size()), c));
134   }
135   return Comps;
136 }
137 
138 llvm::ExitOnError ExitOnErr;
139 int main(int argc, const char **argv) {
140   ExitOnErr.setBanner("clang-repl: ");
141   llvm::cl::ParseCommandLineOptions(argc, argv);
142 
143   llvm::llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
144 
145   std::vector<const char *> ClangArgv(ClangArgs.size());
146   std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(),
147                  [](const std::string &s) -> const char * { return s.data(); });
148   // Initialize all targets (required for device offloading)
149   llvm::InitializeAllTargetInfos();
150   llvm::InitializeAllTargets();
151   llvm::InitializeAllTargetMCs();
152   llvm::InitializeAllAsmPrinters();
153 
154   if (OptHostSupportsJit) {
155     auto J = llvm::orc::LLJITBuilder().create();
156     if (J)
157       llvm::outs() << "true\n";
158     else {
159       llvm::consumeError(J.takeError());
160       llvm::outs() << "false\n";
161     }
162     return 0;
163   }
164 
165   clang::IncrementalCompilerBuilder CB;
166   CB.SetCompilerArgs(ClangArgv);
167 
168   std::unique_ptr<clang::CompilerInstance> DeviceCI;
169   if (CudaEnabled) {
170     if (!CudaPath.empty())
171       CB.SetCudaSDK(CudaPath);
172 
173     if (OffloadArch.empty()) {
174       OffloadArch = "sm_35";
175     }
176     CB.SetOffloadArch(OffloadArch);
177 
178     DeviceCI = ExitOnErr(CB.CreateCudaDevice());
179   }
180 
181   // FIXME: Investigate if we could use runToolOnCodeWithArgs from tooling. It
182   // can replace the boilerplate code for creation of the compiler instance.
183   std::unique_ptr<clang::CompilerInstance> CI;
184   if (CudaEnabled) {
185     CI = ExitOnErr(CB.CreateCudaHost());
186   } else {
187     CI = ExitOnErr(CB.CreateCpp());
188   }
189 
190   // Set an error handler, so that any LLVM backend diagnostics go through our
191   // error handler.
192   llvm::install_fatal_error_handler(LLVMErrorHandler,
193                                     static_cast<void *>(&CI->getDiagnostics()));
194 
195   // Load any requested plugins.
196   CI->LoadRequestedPlugins();
197   if (CudaEnabled)
198     DeviceCI->LoadRequestedPlugins();
199 
200   std::unique_ptr<clang::Interpreter> Interp;
201 
202   if (CudaEnabled) {
203     Interp = ExitOnErr(
204         clang::Interpreter::createWithCUDA(std::move(CI), std::move(DeviceCI)));
205 
206     if (CudaPath.empty()) {
207       ExitOnErr(Interp->LoadDynamicLibrary("libcudart.so"));
208     } else {
209       auto CudaRuntimeLibPath = CudaPath + "/lib/libcudart.so";
210       ExitOnErr(Interp->LoadDynamicLibrary(CudaRuntimeLibPath.c_str()));
211     }
212   } else
213     Interp = ExitOnErr(clang::Interpreter::create(std::move(CI)));
214 
215   for (const std::string &input : OptInputs) {
216     if (auto Err = Interp->ParseAndExecute(input))
217       llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
218   }
219 
220   bool HasError = false;
221 
222   if (OptInputs.empty()) {
223     llvm::LineEditor LE("clang-repl");
224     std::string Input;
225     LE.setListCompleter(ReplListCompleter(CB, *Interp));
226     while (std::optional<std::string> Line = LE.readLine()) {
227       llvm::StringRef L = *Line;
228       L = L.trim();
229       if (L.endswith("\\")) {
230         // FIXME: Support #ifdef X \ ...
231         Input += L.drop_back(1);
232         LE.setPrompt("clang-repl...   ");
233         continue;
234       }
235 
236       Input += L;
237       if (Input == R"(%quit)") {
238         break;
239       }
240       if (Input == R"(%undo)") {
241         if (auto Err = Interp->Undo()) {
242           llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
243           HasError = true;
244         }
245       } else if (Input.rfind("%lib ", 0) == 0) {
246         if (auto Err = Interp->LoadDynamicLibrary(Input.data() + 5)) {
247           llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
248           HasError = true;
249         }
250       } else if (auto Err = Interp->ParseAndExecute(Input)) {
251         llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
252         HasError = true;
253       }
254 
255       Input = "";
256       LE.setPrompt("clang-repl> ");
257     }
258   }
259 
260   // Our error handler depends on the Diagnostics object, which we're
261   // potentially about to delete. Uninstall the handler now so that any
262   // later errors use the default handling behavior instead.
263   llvm::remove_fatal_error_handler();
264 
265   return checkDiagErrors(Interp->getCompilerInstance(), HasError);
266 }
267