5#ifndef CPPINTEROP_COMPATIBILITY_H
6#define CPPINTEROP_COMPATIBILITY_H
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"
15#include "clang/Basic/OffloadArch.h"
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"
26#include "clang/Options/Options.h"
28#include "clang/Frontend/TextDiagnosticBuffer.h"
29#include "clang/Sema/Sema.h"
31#include "llvm/ADT/IntrusiveRefCntPtr.h"
32#include "llvm/ADT/SmallVector.h"
33#include "llvm/Support/FileSystem.h"
43#if CLANG_VERSION_MAJOR < 22
44#define clang_driver_options clang::driver::options
46#define clang_driver_options clang::options
49#if CLANG_VERSION_MAJOR < 22
50#define Suppress_Elab SuppressElaboration
52#define Suppress_Elab FullyQualifiedName
55#if CLANG_VERSION_MAJOR < 22
56#define Get_Tag_Type getTagDeclType
58#define Get_Tag_Type getCanonicalTagType
68static inline char*
GetEnv(
const char* Var_Name) {
72 getenv_s(&sz, Env, sz, Var_Name);
75 return getenv(Var_Name);
79#if CLANG_VERSION_MAJOR < 21
80#define Print_Canonical_Types PrintCanonicalTypes
82#define Print_Canonical_Types PrintAsCanonical
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
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
96#define STRINGIFY(s) STRINGIFY_X(s)
97#define STRINGIFY_X(...) #__VA_ARGS__
99#include "clang/Interpreter/CodeCompletion.h"
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"
111#include "llvm/Support/Regex.h"
113#ifdef CPPINTEROP_USE_CLING
115#include "cling/Interpreter/DynamicLibraryManager.h"
116#include "cling/Interpreter/Interpreter.h"
117#include "cling/Interpreter/Transaction.h"
118#include "cling/Interpreter/Value.h"
120#include "cling/Utils/AST.h"
126namespace utils = cling::utils;
133class SynthesizingCodeRAII :
public Interpreter::PushTransactionRAII {
139 std::string& mangledName) {
140 cling::utils::Analyze::maybeMangleDeclName(GD, mangledName);
149 return I.getExecutionEngine();
152inline llvm::Expected<llvm::JITTargetAddress>
154 if (
void* Addr = I.getAddressOfGlobal(IRName))
155 return (llvm::JITTargetAddress)Addr;
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(),
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);
173 llvm::Error Err = llvm::Error::success();
175 llvm::Regex removeDefinition(
"\\[\\#.*\\#\\]");
176 llvm::Regex removeVariableName(
"(\\ |\\*)+(\\w+)(\\#\\>)");
177 llvm::Regex removeTrailingSpace(
"\\ *(\\#\\>)");
178 llvm::Regex removeTags(
"\\<\\#([^#>]*)\\#\\>");
181 for (
auto& r : results) {
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");
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");
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");
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");
219 if (r.find(code) == 0)
220 Results.push_back(r);
222 llvm::consumeError(std::move(Err));
229#ifndef CPPINTEROP_USE_CLING
232#include "clang/AST/Mangle.h"
233#include "clang/Frontend/CompilerInstance.h"
234#include "clang/Interpreter/Interpreter.h"
235#include "clang/Interpreter/Value.h"
237#include "llvm/Support/DynamicLibrary.h"
238#include "llvm/Support/Error.h"
239#include "llvm/TargetParser/Host.h"
241#if LLVM_VERSION_MAJOR > 21
242#include "clang/Basic/Version.h"
243#include "clang/Interpreter/IncrementalExecutor.h"
245#include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupport.h"
246#include "llvm/Support/FileSystem.h"
247#include "llvm/Support/Path.h"
250#if LLVM_VERSION_MAJOR > 21 && !defined(_WIN32)
264 std::string& CudaPath) {
266 std::string TT = llvm::sys::getProcessTriple();
267 llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(
268 new clang::DiagnosticIDs());
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);
276 clang::DiagnosticOptions DiagOpts;
277 clang::DiagnosticsEngine Diags(DiagID, DiagOpts, DiagsBuffer);
280 clang::driver::Driver D(
"clang", TT, Diags);
281 D.setCheckInputsExist(
false);
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)
294 std::unique_ptr<clang::driver::Compilation> C(D.BuildCompilation(Argv));
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;
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();
340 auto Lib = llvm::sys::DynamicLibrary::getPermanentLibrary(
350 using cuInit_t = int (*)(unsigned);
351 using cuDeviceGet_t = int (*)(uint32_t*, int);
352 using cuDeviceGetAttribute_t = int (*)(
int*, int, uint32_t);
355 auto cuInit =
reinterpret_cast<cuInit_t
>(Lib.getAddressOfSymbol(
"cuInit"));
357 reinterpret_cast<cuDeviceGet_t
>(Lib.getAddressOfSymbol(
"cuDeviceGet"));
358 auto cuDeviceGetAttribute =
reinterpret_cast<cuDeviceGetAttribute_t
>(
359 Lib.getAddressOfSymbol(
"cuDeviceGetAttribute"));
362 if (!cuInit || !cuDeviceGet || !cuDeviceGetAttribute)
367 if (cuInit(0) || cuDeviceGet(&dev, 0) ||
368 cuDeviceGetAttribute(&maj, 75, dev) ||
369 cuDeviceGetAttribute(&min, 76, dev)) {
370 Arch = clang::OffloadArchToString(clang::OffloadArch::CudaDefault);
373 Arch =
"sm_" + std::to_string(maj) + std::to_string(min);
377#if LLVM_VERSION_MAJOR > 21
382inline std::string findOwnLibraryDir() {
385 if (!dladdr(
reinterpret_cast<const void*
>(&findOwnLibraryDir), &info) ||
386 !info.dli_fname || !*info.dli_fname)
388 llvm::SmallString<256> P(info.dli_fname);
389 llvm::sys::path::remove_filename(P);
390 return std::string(P.str());
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());
420#if defined(CPPINTEROP_RUNTIME_BUILD_DIR)
421 Candidates.emplace_back(CPPINTEROP_RUNTIME_BUILD_DIR);
423#if defined(CPPINTEROP_RUNTIME_INSTALL_DIR)
424 Candidates.emplace_back(CPPINTEROP_RUNTIME_INSTALL_DIR);
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))
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();
444inline std::unique_ptr<clang::Interpreter>
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") {
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="));
461 CompilerArgs.push_back(arg);
468 clang::IncrementalCompilerBuilder CB;
469 CB.SetCompilerArgs(CompilerArgs);
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";
479 std::unique_ptr<clang::IncrementalExecutorBuilder> OutOfProcessConfig;
481 OutOfProcessConfig = std::make_unique<clang::IncrementalExecutorBuilder>();
482 OutOfProcessConfig->IsOutOfProcess =
true;
483 if (configureBundledOOPRuntime(*OutOfProcessConfig)) {
485 CB.SetDriverCompilationCallback(
486 OutOfProcessConfig->UpdateOrcRuntimePathCB);
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();
499 std::unique_ptr<clang::CompilerInstance> DeviceCI;
501 if (OffloadArch.empty())
504 if (CudaPath.empty())
507 CB.SetOffloadArch(OffloadArch);
508 if (!CudaPath.empty())
509 CB.SetCudaSDK(CudaPath);
510 auto devOrErr = CB.CreateCudaDevice();
512 llvm::logAllUnhandledErrors(devOrErr.takeError(), llvm::errs(),
513 "Failed to create device compiler:");
516 DeviceCI = std::move(*devOrErr);
518 auto ciOrErr = CudaEnabled ? CB.CreateCudaHost() : CB.CreateCpp();
520 llvm::logAllUnhandledErrors(ciOrErr.takeError(), llvm::errs(),
521 "Failed to build Incremental compiler:");
524 (*ciOrErr)->LoadRequestedPlugins();
526 DeviceCI->LoadRequestedPlugins();
528#if LLVM_VERSION_MAJOR > 21 && !defined(_WIN32)
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);
545 CudaEnabled ? clang::Interpreter::createWithCUDA(std::move(*ciOrErr),
547 : clang::Interpreter::create(
549 outOfProcess ? std::move(OutOfProcessConfig) :
nullptr);
552 CudaEnabled ? clang::Interpreter::createWithCUDA(std::move(*ciOrErr),
554 : clang::Interpreter::create(std::move(*ciOrErr));
557 llvm::logAllUnhandledErrors(innerOrErr.takeError(), llvm::errs(),
558 "Failed to build Interpreter:");
562 if (
auto Err = (*innerOrErr)->LoadDynamicLibrary(
"libcudart.so")) {
563 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
564 "Failed load libcudart.so runtime:");
569 return std::move(*innerOrErr);
573 std::string& mangledName) {
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();
587 llvm::raw_string_ostream RawStr(mangledName);
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();
597 mangleCtx->mangleName(GD, RawStr);
604#if CLANG_VERSION_MAJOR < 22
605 auto* engine = &llvm::cantFail(I.getExecutionEngine());
606 return const_cast<llvm::orc::LLJIT*
>(engine);
610 struct OrcIncrementalExecutor :
public clang::IncrementalExecutor {
611 std::unique_ptr<llvm::orc::LLJIT> Jit;
614 auto& engine =
static_cast<OrcIncrementalExecutor&
>(
615 llvm::cantFail(I.getExecutionEngine()));
616 return engine.Jit.get();
620inline llvm::Expected<llvm::JITTargetAddress>
623 auto AddrOrErr = I.getSymbolAddress(IRName);
624 if (llvm::Error Err = AddrOrErr.takeError())
625 return std::move(Err);
626 return AddrOrErr->getValue();
629inline llvm::Expected<llvm::JITTargetAddress>
631 std::string MangledName;
636inline llvm::Expected<llvm::JITTargetAddress>
638 llvm::StringRef LinkerName) {
640 char GlobalPrefix = DL.getGlobalPrefix();
641 std::string LinkerNameTmp(LinkerName);
642 if (GlobalPrefix !=
'\0') {
643 LinkerNameTmp = std::string(1, GlobalPrefix) + LinkerNameTmp;
645 auto AddrOrErr = I.getSymbolAddressFromLinkerName(LinkerNameTmp);
646 if (llvm::Error Err = AddrOrErr.takeError())
647 return std::move(Err);
648 return AddrOrErr->getValue();
651inline llvm::Error
Undo(clang::Interpreter& I,
unsigned N = 1) {
656 clang::Interpreter& I,
const char* code,
657 unsigned complete_line = 1U,
658 unsigned complete_column = 1U) {
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: ");
667 auto Interp = clang::Interpreter::create(std::move(*CI));
668 if (
auto Err = Interp.takeError()) {
669 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
"error: ");
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());
704#ifdef CPPINTEROP_USE_CLING
705using Value = cling::Value;
711#ifdef CPPINTEROP_USE_CLING
712template <
typename T>
inline T
convertTo(cling::Value V) {
713 return V.castAs<T>();
716template <
typename T>
inline T
convertTo(clang::Value V) {
717 return V.convertTo<T>();
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.");
734 std::atomic<unsigned>
rc;
739 static_cast<ValueRefCount*
>(p)->
rc.fetch_add(1, std::memory_order_relaxed);
743 if (
rc->rc.fetch_sub(1, std::memory_order_acq_rel) == 1)
758 Interpreter& interp, clang::ClassTemplateSpecializationDecl* CTSD) {
759#ifdef CPPINTEROP_USE_CLING
760 cling::Interpreter::PushTransactionRAII RAII(&interp);
762 interp.
getSema().InstantiateClassTemplateSpecialization(
763 clang::SourceLocation::getFromRawEncoding(1), CTSD,
764 clang::TemplateSpecializationKind::TSK_ExplicitInstantiationDefinition,
static char * GetEnv(const char *Var_Name)
clang::Sema & getSema() const
static Box AdoptObject(void *obj, const ObjectOps *ops, void *type) noexcept
Object-payload construction.
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)
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.
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