CppInterOp
C++ Language Interoperability Layer
Loading...
Searching...
No Matches
Compatibility.h
Go to the documentation of this file.
1//--------------------------------------------------------------------*- C++ -*-
2// CppInterOp Compatibility
3// author: Alexander Penev <alexander_penev@yahoo.com>
4//------------------------------------------------------------------------------
5#ifndef CPPINTEROP_COMPATIBILITY_H
6#define CPPINTEROP_COMPATIBILITY_H
7
8#include "clang/AST/DeclTemplate.h"
9#include "clang/AST/GlobalDecl.h"
10#include "clang/Basic/DiagnosticIDs.h"
11#include "clang/Basic/DiagnosticOptions.h"
12#if CLANG_VERSION_MAJOR < 21
13#include "clang/Basic/Cuda.h"
14#else
15#include "clang/Basic/OffloadArch.h"
16#endif
17#include "clang/Basic/SourceLocation.h"
18#include "clang/Basic/Specifiers.h"
19#include "clang/Basic/Version.h"
20#include "clang/Config/config.h"
21#include "clang/Driver/Compilation.h"
22#include "clang/Driver/Driver.h"
23#if CLANG_VERSION_MAJOR < 22
24#include "clang/Driver/Options.h"
25#else
26#include "clang/Options/Options.h"
27#endif
28#include "clang/Frontend/TextDiagnosticBuffer.h"
29#include "clang/Sema/Sema.h"
30
31#include "llvm/ADT/IntrusiveRefCntPtr.h"
32#include "llvm/ADT/SmallVector.h"
33#include "llvm/Support/FileSystem.h"
34
35#include "CppInterOp/Box.h"
36
37#include <atomic>
38#include <cstdint>
39#include <cstring>
40#include <memory>
41#include <string>
42
43#if CLANG_VERSION_MAJOR < 22
44#define clang_driver_options clang::driver::options
45#else
46#define clang_driver_options clang::options
47#endif
48
49#if CLANG_VERSION_MAJOR < 22
50#define Suppress_Elab SuppressElaboration
51#else
52#define Suppress_Elab FullyQualifiedName
53#endif
54
55#if CLANG_VERSION_MAJOR < 22
56#define Get_Tag_Type getTagDeclType
57#else
58#define Get_Tag_Type getCanonicalTagType
59#endif
60
61#ifdef _MSC_VER
62#define dup _dup
63#define dup2 _dup2
64#define close _close
65#define fileno _fileno
66#endif
67
68static inline char* GetEnv(const char* Var_Name) {
69#ifdef _MSC_VER
70 char* Env = nullptr;
71 size_t sz = 0;
72 getenv_s(&sz, Env, sz, Var_Name);
73 return Env;
74#else
75 return getenv(Var_Name);
76#endif
77}
78
79#if CLANG_VERSION_MAJOR < 21
80#define Print_Canonical_Types PrintCanonicalTypes
81#else
82#define Print_Canonical_Types PrintAsCanonical
83#endif
84
85#if CLANG_VERSION_MAJOR < 21
86#define clang_LookupResult_Found clang::LookupResult::Found
87#define clang_LookupResult_Not_Found clang::LookupResult::NotFound
88#define clang_LookupResult_Found_Overloaded clang::LookupResult::FoundOverloaded
89#else
90#define clang_LookupResult_Found clang::LookupResultKind::Found
91#define clang_LookupResult_Not_Found clang::LookupResultKind::NotFound
92#define clang_LookupResult_Found_Overloaded \
93 clang::LookupResultKind::FoundOverloaded
94#endif
95
96#define STRINGIFY(s) STRINGIFY_X(s)
97#define STRINGIFY_X(...) #__VA_ARGS__
98
99#include "clang/Interpreter/CodeCompletion.h"
100
101#include "llvm/ADT/SmallString.h"
102#include "llvm/ADT/StringRef.h"
103#include "llvm/ADT/Twine.h"
104#include "llvm/Config/llvm-config.h"
105#include "llvm/ExecutionEngine/JITSymbol.h"
106#include "llvm/ExecutionEngine/Orc/LLJIT.h"
107#include "llvm/Support/Casting.h"
108#include "llvm/Support/Path.h"
109
110// std::regex breaks pytorch's jit: pytorch/pytorch#49460
111#include "llvm/Support/Regex.h"
112
113#ifdef CPPINTEROP_USE_CLING
114
115#include "cling/Interpreter/DynamicLibraryManager.h"
116#include "cling/Interpreter/Interpreter.h"
117#include "cling/Interpreter/Transaction.h"
118#include "cling/Interpreter/Value.h"
119
120#include "cling/Utils/AST.h"
121
122#include <regex>
123#include <vector>
124
125namespace CppInternal {
126namespace utils = cling::utils;
127}
128
129namespace compat {
130
131using Interpreter = cling::Interpreter;
132
133class SynthesizingCodeRAII : public Interpreter::PushTransactionRAII {
134public:
135 SynthesizingCodeRAII(Interpreter* i) : Interpreter::PushTransactionRAII(i) {}
136};
137
138inline void maybeMangleDeclName(const clang::GlobalDecl& GD,
139 std::string& mangledName) {
140 cling::utils::Analyze::maybeMangleDeclName(GD, mangledName);
141}
142
143/// The getExecutionEngine() interface was been added for Cling based on LLVM
144/// >=18. For previous versions, the LLJIT was obtained by computing the object
145/// offsets in the cling::Interpreter instance(IncrementalExecutor):
146/// sizeof (m_Opts) + sizeof(m_LLVMContext). The IncrementalJIT and JIT itself
147/// have an offset of 0 as the first datamember.
148inline llvm::orc::LLJIT* getExecutionEngine(cling::Interpreter& I) {
149 return I.getExecutionEngine();
150}
151
152inline llvm::Expected<llvm::JITTargetAddress>
153getSymbolAddress(cling::Interpreter& I, llvm::StringRef IRName) {
154 if (void* Addr = I.getAddressOfGlobal(IRName))
155 return (llvm::JITTargetAddress)Addr;
156
157 llvm::orc::LLJIT& Jit = *compat::getExecutionEngine(I);
158 llvm::orc::SymbolNameVector Names;
159 llvm::orc::ExecutionSession& ES = Jit.getExecutionSession();
160 Names.push_back(ES.intern(IRName));
161 return llvm::make_error<llvm::orc::SymbolsNotFound>(ES.getSymbolStringPool(),
162 std::move(Names));
163}
164
165inline void codeComplete(std::vector<std::string>& Results,
166 const cling::Interpreter& I, const char* code,
167 unsigned complete_line = 1U,
168 unsigned complete_column = 1U) {
169 std::vector<std::string> results;
170 size_t column = complete_column;
171 I.codeComplete(code, column, results);
172 std::string error;
173 llvm::Error Err = llvm::Error::success();
174 // Regex patterns
175 llvm::Regex removeDefinition("\\[\\#.*\\#\\]");
176 llvm::Regex removeVariableName("(\\ |\\*)+(\\w+)(\\#\\>)");
177 llvm::Regex removeTrailingSpace("\\ *(\\#\\>)");
178 llvm::Regex removeTags("\\<\\#([^#>]*)\\#\\>");
179
180 // append cleaned results
181 for (auto& r : results) {
182 // remove the definition at the beginning (e.g., [#int#])
183 r = removeDefinition.sub("", r, &error);
184 if (!error.empty()) {
185 Err = llvm::make_error<llvm::StringError>(error,
186 llvm::inconvertibleErrorCode());
187 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
188 "Invalid substitution in CodeComplete");
189 return;
190 }
191 // remove the variable name in <#type name#>
192 r = removeVariableName.sub("$1$3", r, &error);
193 if (!error.empty()) {
194 Err = llvm::make_error<llvm::StringError>(error,
195 llvm::inconvertibleErrorCode());
196 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
197 "Invalid substitution in CodeComplete");
198 return;
199 }
200 // remove unnecessary space at the end of <#type #>
201 r = removeTrailingSpace.sub("$1", r, &error);
202 if (!error.empty()) {
203 Err = llvm::make_error<llvm::StringError>(error,
204 llvm::inconvertibleErrorCode());
205 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
206 "Invalid substitution in CodeComplete");
207 return;
208 }
209 // remove <# #> to keep only the type
210 r = removeTags.sub("$1", r, &error);
211 if (!error.empty()) {
212 Err = llvm::make_error<llvm::StringError>(error,
213 llvm::inconvertibleErrorCode());
214 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
215 "Invalid substitution in CodeComplete");
216 return;
217 }
218
219 if (r.find(code) == 0)
220 Results.push_back(r);
221 }
222 llvm::consumeError(std::move(Err));
223}
224
225} // namespace compat
226
227#endif // CPPINTEROP_USE_CLING
228
229#ifndef CPPINTEROP_USE_CLING
230
232#include "clang/AST/Mangle.h"
233#include "clang/Frontend/CompilerInstance.h"
234#include "clang/Interpreter/Interpreter.h"
235#include "clang/Interpreter/Value.h"
236
237#include "llvm/Support/DynamicLibrary.h"
238#include "llvm/Support/Error.h"
239#include "llvm/TargetParser/Host.h"
240
241#if LLVM_VERSION_MAJOR > 21
242#include "clang/Basic/Version.h"
243#include "clang/Interpreter/IncrementalExecutor.h"
244
245#include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupport.h"
246#include "llvm/Support/FileSystem.h"
247#include "llvm/Support/Path.h"
248#endif
249
250#if LLVM_VERSION_MAJOR > 21 && !defined(_WIN32)
251#include <dlfcn.h>
252#include <unistd.h>
253#endif
254
255#include <algorithm>
256
257namespace compat {
258
259/// Detect the CUDA installation path using clang::Driver
260/// \param args user-provided interpreter arguments (may contain --cuda-path).
261/// \param[out] CudaPath the detected CUDA installation path.
262/// \returns true on success, false if not found.
263inline bool detectCudaInstallPath(const std::vector<const char*>& args,
264 std::string& CudaPath) {
265 // minimal driver that runs CudaInstallationDetector internally
266 std::string TT = llvm::sys::getProcessTriple();
267 llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(
268 new clang::DiagnosticIDs());
269 // NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
270 auto* DiagsBuffer = new clang::TextDiagnosticBuffer;
271#if CLANG_VERSION_MAJOR < 21
272 llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts(
273 new clang::DiagnosticOptions());
274 clang::DiagnosticsEngine Diags(DiagID, DiagOpts, DiagsBuffer);
275#else
276 clang::DiagnosticOptions DiagOpts;
277 clang::DiagnosticsEngine Diags(DiagID, DiagOpts, DiagsBuffer);
278#endif
279
280 clang::driver::Driver D("clang", TT, Diags);
281 D.setCheckInputsExist(false);
282
283 // construct args: clang -x cuda -c <<< inputs >>> [args]
284 llvm::SmallVector<const char*, 16> Argv;
285 Argv.push_back("clang");
286 Argv.push_back("-xcuda");
287 Argv.push_back("-c");
288 Argv.push_back("<<< inputs >>>");
289 for (const auto* arg : args)
290 Argv.push_back(arg);
291
292 // build a compilation object, which runs the driver's CUDA installation
293 // detection logic and stores the paths
294 std::unique_ptr<clang::driver::Compilation> C(D.BuildCompilation(Argv));
295 if (!C)
296 return false;
297
298 // --cuda-path was explicitly provided in user args
299 if (auto* A =
300 C->getArgs().getLastArg(clang_driver_options::OPT_cuda_path_EQ)) {
301 std::string Candidate = A->getValue();
302 if (llvm::sys::fs::is_directory(Candidate + "/include")) {
303 CudaPath = Candidate;
304 return true;
305 }
306 }
307
308 // fallback: clang tries to auto-detect the install, CudaInstallationDetector
309 // stores the path internally but doesn't expose it, so we look for
310 // "-internal-isystem <cuda-path>/include" that the driver adds for CUDA
311 // headers.
312 for (const auto& Job : C->getJobs()) {
313 if (const auto* Cmd = llvm::dyn_cast<clang::driver::Command>(&Job)) {
314 const auto& Args = Cmd->getArguments();
315 for (size_t i = 0; i + 1 < Args.size(); ++i) {
316 if (llvm::StringRef(Args[i]) == "-internal-isystem") {
317 llvm::StringRef IncDir(Args[i + 1]);
318 if (IncDir.ends_with("/include") &&
319 llvm::sys::fs::exists(IncDir.str() + "/cuda.h")) {
320 CudaPath = IncDir.drop_back(strlen("/include")).str();
321 return true;
322 }
323 }
324 }
325 }
326 }
327 return false;
328}
329
330/// Detect GPU architecture via the CUDA Driver API, tweaked from clang's
331/// nvptx-arch tool (NVPTXArch.cpp) \param[out] Arch Set to "sm_XX" on success,
332/// or clang's default fallback. \returns true on success, false on error (no
333/// CUDA driver available).
334inline bool detectNVPTXArch(std::string& Arch) {
335 std::string Err;
336 // FIXME: Use ToolChain::getSystemGPUArchs() from a minimal driver compilation
337 // instead, and unify this function with detectCudaInstallPath. Ideally we
338 // should rely on the offload-arch/nvptx-arch tool in clang, but there is no
339 // public API or library to link against.
340 auto Lib = llvm::sys::DynamicLibrary::getPermanentLibrary(
341#ifdef _WIN32
342 "nvcuda.dll",
343#else
344 "libcuda.so.1",
345#endif
346 &Err);
347 if (!Lib.isValid())
348 return false;
349
350 using cuInit_t = int (*)(unsigned);
351 using cuDeviceGet_t = int (*)(uint32_t*, int);
352 using cuDeviceGetAttribute_t = int (*)(int*, int, uint32_t);
353
354 // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast)
355 auto cuInit = reinterpret_cast<cuInit_t>(Lib.getAddressOfSymbol("cuInit"));
356 auto cuDeviceGet =
357 reinterpret_cast<cuDeviceGet_t>(Lib.getAddressOfSymbol("cuDeviceGet"));
358 auto cuDeviceGetAttribute = reinterpret_cast<cuDeviceGetAttribute_t>(
359 Lib.getAddressOfSymbol("cuDeviceGetAttribute"));
360 // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
361
362 if (!cuInit || !cuDeviceGet || !cuDeviceGetAttribute)
363 return false;
364
365 uint32_t dev;
366 int maj, min;
367 if (cuInit(0) || cuDeviceGet(&dev, 0) ||
368 cuDeviceGetAttribute(&maj, /*MAJOR*/ 75, dev) ||
369 cuDeviceGetAttribute(&min, /*MINOR*/ 76, dev)) {
370 Arch = clang::OffloadArchToString(clang::OffloadArch::CudaDefault);
371 return true;
372 }
373 Arch = "sm_" + std::to_string(maj) + std::to_string(min);
374 return true;
375}
376
377#if LLVM_VERSION_MAJOR > 21
378/// Directory containing libclangCppInterOp itself, derived via
379/// `dladdr` of an in-library function pointer. Returns empty when the
380/// platform has no self-DSO discovery (Windows -- a `GetModuleHandleEx`
381/// port can be added when Windows OOP support arrives).
382inline std::string findOwnLibraryDir() {
383#if !defined(_WIN32)
384 Dl_info info{};
385 if (!dladdr(reinterpret_cast<const void*>(&findOwnLibraryDir), &info) ||
386 !info.dli_fname || !*info.dli_fname)
387 return {};
388 llvm::SmallString<256> P(info.dli_fname);
389 llvm::sys::path::remove_filename(P);
390 return std::string(P.str());
391#else
392 return {};
393#endif
394}
395
396/// Wire CppInterOp's bundled OOP runtime parts into `B`. Probes a
397/// layered list of candidate directories, in priority order:
398/// 1. `$CPPINTEROP_RUNTIME_DIR` -- sysadmin override.
399/// 2. `<dir of libclangCppInterOp>/cppinterop-rt` -- relocatable, follows
400/// the .so wherever a package manager moved it.
401/// 3. `CPPINTEROP_RUNTIME_BUILD_DIR` -- in-tree test runs.
402/// 4. `CPPINTEROP_RUNTIME_INSTALL_DIR` -- baked install path; last
403/// resort when self-DSO discovery isn't available (e.g. static
404/// link of CppInterOp into a host binary).
405/// `UpdateOrcRuntimePathCB` is replaced with a no-op so the upstream
406/// resource-dir prefix check inside
407/// `IncrementalExecutorBuilder::UpdateOrcRuntimePath`
408/// (`clang/lib/Interpreter/IncrementalExecutor.cpp`, the
409/// `consume_front(parent_path(D.Dir))` guard) doesn't run -- our
410/// runtime lives outside the host's clang resource tree.
411inline bool configureBundledOOPRuntime(clang::IncrementalExecutorBuilder& B) {
412 llvm::SmallVector<std::string, 4> Candidates;
413 if (const char* Env = std::getenv("CPPINTEROP_RUNTIME_DIR"))
414 Candidates.emplace_back(Env);
415 if (std::string OwnDir = findOwnLibraryDir(); !OwnDir.empty()) {
416 llvm::SmallString<256> P(OwnDir);
417 llvm::sys::path::append(P, "cppinterop-rt");
418 Candidates.emplace_back(P.str());
419 }
420#if defined(CPPINTEROP_RUNTIME_BUILD_DIR)
421 Candidates.emplace_back(CPPINTEROP_RUNTIME_BUILD_DIR);
422#endif
423#if defined(CPPINTEROP_RUNTIME_INSTALL_DIR)
424 Candidates.emplace_back(CPPINTEROP_RUNTIME_INSTALL_DIR);
425#endif
426 for (const std::string& Dir : Candidates) {
427 llvm::SmallString<256> OrcRT(Dir);
428 llvm::sys::path::append(OrcRT, "liborc_rt.a");
429 llvm::SmallString<256> Exec(Dir);
430 llvm::sys::path::append(Exec, "llvm-jitlink-executor");
431 if (!llvm::sys::fs::exists(OrcRT) || !llvm::sys::fs::exists(Exec))
432 continue;
433 B.OrcRuntimePath = std::string(OrcRT.str());
434 B.OOPExecutor = std::string(Exec.str());
435 B.UpdateOrcRuntimePathCB = [](const clang::driver::Compilation&) {
436 return llvm::Error::success();
437 };
438 return true;
439 }
440 return false;
441}
442#endif // LLVM_VERSION_MAJOR > 21
443
444inline std::unique_ptr<clang::Interpreter>
445createClangInterpreter(std::vector<const char*>& args, int stdin_fd = -1,
446 int stdout_fd = -1, int stderr_fd = -1) {
447 bool CudaEnabled = false;
448 std::string OffloadArch;
449 std::string CudaPath;
450 std::vector<const char*> CompilerArgs;
451 for (const auto* arg : args) {
452 llvm::StringRef A(arg);
453 llvm::StringRef Stripped = A.trim().ltrim('-');
454 if (Stripped == "cuda") {
455 CudaEnabled = true;
456 } else if (A.starts_with("--offload-arch=")) {
457 OffloadArch = A.substr(strlen("--offload-arch="));
458 } else if (A.starts_with("--cuda-path=")) {
459 CudaPath = A.substr(strlen("--cuda-path="));
460 } else {
461 CompilerArgs.push_back(arg);
462 }
463 }
464#ifdef __APPLE__
465 CudaEnabled = false;
466#endif
467
468 clang::IncrementalCompilerBuilder CB;
469 CB.SetCompilerArgs(CompilerArgs);
470
471#if LLVM_VERSION_MAJOR > 21 && !defined(_WIN32)
472 bool outOfProcess = false;
473 const bool oopRequested =
474 std::any_of(args.begin(), args.end(), [](const char* arg) {
475 return llvm::StringRef(arg).trim() == "--use-oop-jit";
476 });
477 // The IncrementalExecutorBuilder must outlive the IncrementalCompiler
478 // it gets attached to, so it's a unique_ptr at function scope.
479 std::unique_ptr<clang::IncrementalExecutorBuilder> OutOfProcessConfig;
480 if (oopRequested) {
481 OutOfProcessConfig = std::make_unique<clang::IncrementalExecutorBuilder>();
482 OutOfProcessConfig->IsOutOfProcess = true;
483 if (configureBundledOOPRuntime(*OutOfProcessConfig)) {
484 outOfProcess = true;
485 CB.SetDriverCompilationCallback(
486 OutOfProcessConfig->UpdateOrcRuntimePathCB);
487 } else {
488 llvm::errs()
489 << "[CreateClangInterpreter]: --use-oop-jit requested but the "
490 "bundled OOP runtime "
491 "(<libdir>/cppinterop-rt/{liborc_rt.a,llvm-jitlink-executor}) "
492 "is missing from CppInterOp's build/install tree. Falling "
493 "back to in-process JIT.\n";
494 OutOfProcessConfig.reset();
495 }
496 }
497#endif
498
499 std::unique_ptr<clang::CompilerInstance> DeviceCI;
500 if (CudaEnabled) {
501 if (OffloadArch.empty())
502 detectNVPTXArch(OffloadArch);
503
504 if (CudaPath.empty())
505 detectCudaInstallPath(CompilerArgs, CudaPath);
506
507 CB.SetOffloadArch(OffloadArch);
508 if (!CudaPath.empty())
509 CB.SetCudaSDK(CudaPath);
510 auto devOrErr = CB.CreateCudaDevice();
511 if (!devOrErr) {
512 llvm::logAllUnhandledErrors(devOrErr.takeError(), llvm::errs(),
513 "Failed to create device compiler:");
514 return nullptr;
515 }
516 DeviceCI = std::move(*devOrErr);
517 }
518 auto ciOrErr = CudaEnabled ? CB.CreateCudaHost() : CB.CreateCpp();
519 if (!ciOrErr) {
520 llvm::logAllUnhandledErrors(ciOrErr.takeError(), llvm::errs(),
521 "Failed to build Incremental compiler:");
522 return nullptr;
523 }
524 (*ciOrErr)->LoadRequestedPlugins();
525 if (CudaEnabled)
526 DeviceCI->LoadRequestedPlugins();
527
528#if LLVM_VERSION_MAJOR > 21 && !defined(_WIN32)
529 if (outOfProcess) {
530 // OrcRuntimePath and OOPExecutor were populated by
531 // configureBundledOOPRuntime() above; UpdateOrcRuntimePathCB was
532 // replaced with a no-op there too, so the upstream auto-discovery
533 // safety check doesn't run.
534 OutOfProcessConfig->UseSharedMemory = false;
535 OutOfProcessConfig->SlabAllocateSize = 0;
536 OutOfProcessConfig->CustomizeFork = [stdin_fd, stdout_fd, stderr_fd]() {
537 dup2(stdin_fd, STDIN_FILENO);
538 dup2(stdout_fd, STDOUT_FILENO);
539 dup2(stderr_fd, STDERR_FILENO);
540 setvbuf(fdopen(stdout_fd, "w+"), nullptr, _IONBF, 0);
541 setvbuf(fdopen(stderr_fd, "w+"), nullptr, _IONBF, 0);
542 };
543 }
544 auto innerOrErr =
545 CudaEnabled ? clang::Interpreter::createWithCUDA(std::move(*ciOrErr),
546 std::move(DeviceCI))
547 : clang::Interpreter::create(
548 std::move(*ciOrErr),
549 outOfProcess ? std::move(OutOfProcessConfig) : nullptr);
550#else
551 auto innerOrErr =
552 CudaEnabled ? clang::Interpreter::createWithCUDA(std::move(*ciOrErr),
553 std::move(DeviceCI))
554 : clang::Interpreter::create(std::move(*ciOrErr));
555#endif
556 if (!innerOrErr) {
557 llvm::logAllUnhandledErrors(innerOrErr.takeError(), llvm::errs(),
558 "Failed to build Interpreter:");
559 return nullptr;
560 }
561 if (CudaEnabled) {
562 if (auto Err = (*innerOrErr)->LoadDynamicLibrary("libcudart.so")) {
563 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
564 "Failed load libcudart.so runtime:");
565 return nullptr;
566 }
567 }
568
569 return std::move(*innerOrErr);
570}
571
572inline void maybeMangleDeclName(const clang::GlobalDecl& GD,
573 std::string& mangledName) {
574 // copied and adapted from CodeGen::CodeGenModule::getMangledName
575
576 clang::NamedDecl* D =
577 llvm::cast<clang::NamedDecl>(const_cast<clang::Decl*>(GD.getDecl()));
578 std::unique_ptr<clang::MangleContext> mangleCtx;
579 mangleCtx.reset(D->getASTContext().createMangleContext());
580 if (!mangleCtx->shouldMangleDeclName(D)) {
581 clang::IdentifierInfo* II = D->getIdentifier();
582 assert(II && "Attempt to mangle unnamed decl.");
583 mangledName = II->getName().str();
584 return;
585 }
586
587 llvm::raw_string_ostream RawStr(mangledName);
588
589#if defined(_WIN32)
590 // MicrosoftMangle.cpp:954 calls llvm_unreachable when mangling Dtor_Comdat
591 if (llvm::isa<clang::CXXDestructorDecl>(GD.getDecl()) &&
592 GD.getDtorType() == clang::Dtor_Comdat) {
593 if (const clang::IdentifierInfo* II = D->getIdentifier())
594 RawStr << II->getName();
595 } else
596#endif
597 mangleCtx->mangleName(GD, RawStr);
598 RawStr.flush();
599}
600
601// Clang 18 - Add new Interpreter methods: CodeComplete
602
603inline llvm::orc::LLJIT* getExecutionEngine(clang::Interpreter& I) {
604#if CLANG_VERSION_MAJOR < 22
605 auto* engine = &llvm::cantFail(I.getExecutionEngine());
606 return const_cast<llvm::orc::LLJIT*>(engine);
607#else
608 // FIXME: Remove the need of exposing the low-level execution engine and kill
609 // this horrible hack.
610 struct OrcIncrementalExecutor : public clang::IncrementalExecutor {
611 std::unique_ptr<llvm::orc::LLJIT> Jit;
612 };
613
614 auto& engine = static_cast<OrcIncrementalExecutor&>(
615 llvm::cantFail(I.getExecutionEngine()));
616 return engine.Jit.get();
617#endif
618}
619
620inline llvm::Expected<llvm::JITTargetAddress>
621getSymbolAddress(clang::Interpreter& I, llvm::StringRef IRName) {
622
623 auto AddrOrErr = I.getSymbolAddress(IRName);
624 if (llvm::Error Err = AddrOrErr.takeError())
625 return std::move(Err);
626 return AddrOrErr->getValue();
627}
628
629inline llvm::Expected<llvm::JITTargetAddress>
630getSymbolAddress(clang::Interpreter& I, clang::GlobalDecl GD) {
631 std::string MangledName;
632 compat::maybeMangleDeclName(GD, MangledName);
633 return getSymbolAddress(I, llvm::StringRef(MangledName));
634}
635
636inline llvm::Expected<llvm::JITTargetAddress>
637getSymbolAddressFromLinkerName(clang::Interpreter& I,
638 llvm::StringRef LinkerName) {
639 const auto& DL = getExecutionEngine(I)->getDataLayout();
640 char GlobalPrefix = DL.getGlobalPrefix();
641 std::string LinkerNameTmp(LinkerName);
642 if (GlobalPrefix != '\0') {
643 LinkerNameTmp = std::string(1, GlobalPrefix) + LinkerNameTmp;
644 }
645 auto AddrOrErr = I.getSymbolAddressFromLinkerName(LinkerNameTmp);
646 if (llvm::Error Err = AddrOrErr.takeError())
647 return std::move(Err);
648 return AddrOrErr->getValue();
649}
650
651inline llvm::Error Undo(clang::Interpreter& I, unsigned N = 1) {
652 return I.Undo(N);
653}
654
655inline void codeComplete(std::vector<std::string>& Results,
656 clang::Interpreter& I, const char* code,
657 unsigned complete_line = 1U,
658 unsigned complete_column = 1U) {
659 // FIXME: We should match the invocation arguments of the main interpreter.
660 // That can affect the returned completion results.
661 auto CB = clang::IncrementalCompilerBuilder();
662 auto CI = CB.CreateCpp();
663 if (auto Err = CI.takeError()) {
664 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
665 return;
666 }
667 auto Interp = clang::Interpreter::create(std::move(*CI));
668 if (auto Err = Interp.takeError()) {
669 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
670 return;
671 }
672
673 std::vector<std::string> results;
674 clang::CompilerInstance* MainCI = (*Interp)->getCompilerInstance();
675 auto CC = clang::ReplCodeCompleter();
676 CC.codeComplete(MainCI, code, complete_line, complete_column,
677 I.getCompilerInstance(), results);
678 for (llvm::StringRef r : results)
679 if (r.find(CC.Prefix) == 0)
680 Results.push_back(r.str());
681}
682
683} // namespace compat
684
686
687namespace compat {
689
691private:
692 [[maybe_unused]] Interpreter* m_Interpreter;
693
694public:
695 SynthesizingCodeRAII(Interpreter* i) : m_Interpreter(i) {}
696 // ~SynthesizingCodeRAII() {} // TODO: implement
697};
698} // namespace compat
699
700#endif // CPPINTEROP_USE_REPL
701
702namespace compat {
703
704#ifdef CPPINTEROP_USE_CLING
705using Value = cling::Value;
706#else
707using Value = clang::Value;
708#endif
709
710// Clang >= 16 (=16 with Value patch) change castAs to convertTo
711#ifdef CPPINTEROP_USE_CLING
712template <typename T> inline T convertTo(cling::Value V) {
713 return V.castAs<T>();
714}
715#else // CLANG_REPL
716template <typename T> inline T convertTo(clang::Value V) {
717 return V.convertTo<T>();
718}
719#endif // CPPINTEROP_USE_CLING
720
721// Refcount-shared payload wrapping a `compat::Value` for Cpp::Box's
722// K_PtrOrObj slot. Boxing-via-copy (not move): clang::Value's move ctor
723// releases its own storage on construction -- fixed upstream by
724// llvm/llvm-project#200888. The copy ctor correctly retains.
725// FIXME(llvm 23): static_assert below fails the build once the minimum
726// LLVM crosses 23, prompting the move-semantics cleanup.
727static_assert(LLVM_VERSION_MAJOR < 23,
728 "clang::Value::Value(Value&&) was fixed upstream in "
729 "llvm/llvm-project#200888; switch ValueRefCount to move "
730 "semantics and drop this workaround.");
731
732namespace detail {
734 std::atomic<unsigned> rc;
736 explicit ValueRefCount(const Value& V) noexcept : rc(1), v(V) {}
737
738 static void retain(void* p) noexcept {
739 static_cast<ValueRefCount*>(p)->rc.fetch_add(1, std::memory_order_relaxed);
740 }
741 static void release(void* p) noexcept {
742 auto* rc = static_cast<ValueRefCount*>(p);
743 if (rc->rc.fetch_sub(1, std::memory_order_acq_rel) == 1)
744 delete rc;
745 }
746 static constexpr Cpp::Box::ObjectOps Ops{&retain, &release};
747};
748} // namespace detail
749
750/// Wrap a compat::Value into a refcount-shared K_PtrOrObj Cpp::Box.
751/// `qt` is the opaque QualType (clang::QualType::getAsOpaquePtr()).
752inline Cpp::Box MakeValueBox(const Value& V, void* qt) noexcept {
755}
756
758 Interpreter& interp, clang::ClassTemplateSpecializationDecl* CTSD) {
759#ifdef CPPINTEROP_USE_CLING
760 cling::Interpreter::PushTransactionRAII RAII(&interp);
761#endif
762 interp.getSema().InstantiateClassTemplateSpecialization(
763 clang::SourceLocation::getFromRawEncoding(1), CTSD,
764 clang::TemplateSpecializationKind::TSK_ExplicitInstantiationDefinition,
765 /*Complain=*/true,
766 /*PrimaryHasMatchedPackOnParmToNonPackOnArg=*/false);
767}
768} // namespace compat
769
770#endif // CPPINTEROP_COMPATIBILITY_H
static char * GetEnv(const char *Var_Name)
CppInterOp Interpreter.
clang::Sema & getSema() const
Definition Box.h:96
static Box AdoptObject(void *obj, const ObjectOps *ops, void *type) noexcept
Object-payload construction.
Definition Box.h:225
SynthesizingCodeRAII(Interpreter *i)
llvm::Expected< llvm::JITTargetAddress > getSymbolAddressFromLinkerName(clang::Interpreter &I, llvm::StringRef LinkerName)
llvm::Error Undo(clang::Interpreter &I, unsigned N=1)
void InstantiateClassTemplateSpecialization(Interpreter &interp, clang::ClassTemplateSpecializationDecl *CTSD)
clang::Value Value
T convertTo(clang::Value V)
void maybeMangleDeclName(const clang::GlobalDecl &GD, std::string &mangledName)
CppInternal::Interpreter Interpreter
void codeComplete(std::vector< std::string > &Results, clang::Interpreter &I, const char *code, unsigned complete_line=1U, unsigned complete_column=1U)
bool detectCudaInstallPath(const std::vector< const char * > &args, std::string &CudaPath)
Detect the CUDA installation path using clang::Driver.
std::unique_ptr< clang::Interpreter > createClangInterpreter(std::vector< const char * > &args, int stdin_fd=-1, int stdout_fd=-1, int stderr_fd=-1)
llvm::orc::LLJIT * getExecutionEngine(clang::Interpreter &I)
Cpp::Box MakeValueBox(const Value &V, void *qt) noexcept
Wrap a compat::Value into a refcount-shared K_PtrOrObj Cpp::Box.
bool detectNVPTXArch(std::string &Arch)
Detect GPU architecture via the CUDA Driver API, tweaked from clang's nvptx-arch tool (NVPTXArch....
llvm::Expected< llvm::JITTargetAddress > getSymbolAddress(clang::Interpreter &I, llvm::StringRef IRName)
Operations vtable for a K_PtrOrObj payload.
Definition Box.h:118
ValueRefCount(const Value &V) noexcept
static constexpr Cpp::Box::ObjectOps Ops
std::atomic< unsigned > rc
static void release(void *p) noexcept
static void retain(void *p) noexcept