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