5#ifndef CPPINTEROP_COMPATIBILITY_H
6#define CPPINTEROP_COMPATIBILITY_H
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"
16#include "clang/Basic/OffloadArch.h"
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"
27#include "clang/Options/Options.h"
29#include "clang/Frontend/TextDiagnosticBuffer.h"
30#include "clang/Sema/Sema.h"
32#include "llvm/ADT/IntrusiveRefCntPtr.h"
33#include "llvm/ADT/SmallVector.h"
34#include "llvm/Support/FileSystem.h"
44#if CLANG_VERSION_MAJOR < 22
45#define clang_driver_options clang::driver::options
47#define clang_driver_options clang::options
50#if CLANG_VERSION_MAJOR < 22
51#define Suppress_Elab SuppressElaboration
53#define Suppress_Elab FullyQualifiedName
63static inline char*
GetEnv(
const char* Var_Name) {
67 getenv_s(&sz, Env, sz, Var_Name);
70 return getenv(Var_Name);
74#if CLANG_VERSION_MAJOR < 21
75#define Print_Canonical_Types PrintCanonicalTypes
77#define Print_Canonical_Types PrintAsCanonical
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
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
91#define STRINGIFY(s) STRINGIFY_X(s)
92#define STRINGIFY_X(...) #__VA_ARGS__
94#include "clang/Interpreter/CodeCompletion.h"
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"
106#include "llvm/Support/Regex.h"
108#ifdef CPPINTEROP_USE_CLING
110#include "cling/Interpreter/DynamicLibraryManager.h"
111#include "cling/Interpreter/Interpreter.h"
112#include "cling/Interpreter/Transaction.h"
113#include "cling/Interpreter/Value.h"
115#include "cling/Utils/AST.h"
121namespace utils = cling::utils;
128class SynthesizingCodeRAII :
public Interpreter::PushTransactionRAII {
134 std::string& mangledName) {
135 cling::utils::Analyze::maybeMangleDeclName(GD, mangledName);
144 return I.getExecutionEngine();
147inline llvm::Expected<llvm::JITTargetAddress>
149 if (
void* Addr = I.getAddressOfGlobal(IRName))
150 return (llvm::JITTargetAddress)Addr;
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(),
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);
168 llvm::Error Err = llvm::Error::success();
170 llvm::Regex removeDefinition(
"\\[\\#.*\\#\\]");
171 llvm::Regex removeVariableName(
"(\\ |\\*)+(\\w+)(\\#\\>)");
172 llvm::Regex removeTrailingSpace(
"\\ *(\\#\\>)");
173 llvm::Regex removeTags(
"\\<\\#([^#>]*)\\#\\>");
176 for (
auto& r : results) {
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");
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");
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");
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");
214 if (r.find(code) == 0)
215 Results.push_back(r);
217 llvm::consumeError(std::move(Err));
224#ifndef CPPINTEROP_USE_CLING
227#include "clang/AST/Mangle.h"
228#include "clang/Frontend/CompilerInstance.h"
229#include "clang/Interpreter/Interpreter.h"
230#include "clang/Interpreter/Value.h"
232#include "llvm/Support/DynamicLibrary.h"
233#include "llvm/Support/Error.h"
234#include "llvm/TargetParser/Host.h"
236#if LLVM_VERSION_MAJOR > 21
237#include "clang/Basic/Version.h"
238#include "clang/Interpreter/IncrementalExecutor.h"
240#include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupport.h"
241#include "llvm/Support/FileSystem.h"
242#include "llvm/Support/Path.h"
245#if LLVM_VERSION_MAJOR > 21 && !defined(_WIN32)
259 std::string& CudaPath) {
261 std::string TT = llvm::sys::getProcessTriple();
262 llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(
263 new clang::DiagnosticIDs());
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);
271 clang::DiagnosticOptions DiagOpts;
272 clang::DiagnosticsEngine Diags(DiagID, DiagOpts, DiagsBuffer);
275 clang::driver::Driver D(
"clang", TT, Diags);
276 D.setCheckInputsExist(
false);
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)
289 std::unique_ptr<clang::driver::Compilation> C(D.BuildCompilation(Argv));
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;
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();
335 auto Lib = llvm::sys::DynamicLibrary::getPermanentLibrary(
345 using cuInit_t = int (*)(unsigned);
346 using cuDeviceGet_t = int (*)(uint32_t*, int);
347 using cuDeviceGetAttribute_t = int (*)(
int*, int, uint32_t);
350 auto cuInit =
reinterpret_cast<cuInit_t
>(Lib.getAddressOfSymbol(
"cuInit"));
352 reinterpret_cast<cuDeviceGet_t
>(Lib.getAddressOfSymbol(
"cuDeviceGet"));
353 auto cuDeviceGetAttribute =
reinterpret_cast<cuDeviceGetAttribute_t
>(
354 Lib.getAddressOfSymbol(
"cuDeviceGetAttribute"));
357 if (!cuInit || !cuDeviceGet || !cuDeviceGetAttribute)
362 if (cuInit(0) || cuDeviceGet(&dev, 0) ||
363 cuDeviceGetAttribute(&maj, 75, dev) ||
364 cuDeviceGetAttribute(&min, 76, dev)) {
365 Arch = clang::OffloadArchToString(clang::OffloadArch::CudaDefault);
368 Arch =
"sm_" + std::to_string(maj) + std::to_string(min);
372#if LLVM_VERSION_MAJOR > 21
377inline std::string findOwnLibraryDir() {
380 if (!dladdr(
reinterpret_cast<const void*
>(&findOwnLibraryDir), &info) ||
381 !info.dli_fname || !*info.dli_fname)
383 llvm::SmallString<256> P(info.dli_fname);
384 llvm::sys::path::remove_filename(P);
385 return std::string(P.str());
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());
415#if defined(CPPINTEROP_RUNTIME_BUILD_DIR)
416 Candidates.emplace_back(CPPINTEROP_RUNTIME_BUILD_DIR);
418#if defined(CPPINTEROP_RUNTIME_INSTALL_DIR)
419 Candidates.emplace_back(CPPINTEROP_RUNTIME_INSTALL_DIR);
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))
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();
439inline std::unique_ptr<clang::Interpreter>
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") {
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="));
456 CompilerArgs.push_back(arg);
463 clang::IncrementalCompilerBuilder CB;
464 CB.SetCompilerArgs(CompilerArgs);
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";
474 std::unique_ptr<clang::IncrementalExecutorBuilder> OutOfProcessConfig;
476 OutOfProcessConfig = std::make_unique<clang::IncrementalExecutorBuilder>();
477 OutOfProcessConfig->IsOutOfProcess =
true;
478 if (configureBundledOOPRuntime(*OutOfProcessConfig)) {
480 CB.SetDriverCompilationCallback(
481 OutOfProcessConfig->UpdateOrcRuntimePathCB);
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();
494 std::unique_ptr<clang::CompilerInstance> DeviceCI;
496 if (OffloadArch.empty())
499 if (CudaPath.empty())
502 CB.SetOffloadArch(OffloadArch);
503 if (!CudaPath.empty())
504 CB.SetCudaSDK(CudaPath);
505 auto devOrErr = CB.CreateCudaDevice();
507 llvm::logAllUnhandledErrors(devOrErr.takeError(), llvm::errs(),
508 "Failed to create device compiler:");
511 DeviceCI = std::move(*devOrErr);
513 auto ciOrErr = CudaEnabled ? CB.CreateCudaHost() : CB.CreateCpp();
515 llvm::logAllUnhandledErrors(ciOrErr.takeError(), llvm::errs(),
516 "Failed to build Incremental compiler:");
519 (*ciOrErr)->LoadRequestedPlugins();
521 DeviceCI->LoadRequestedPlugins();
523#if LLVM_VERSION_MAJOR > 21 && !defined(_WIN32)
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);
540 CudaEnabled ? clang::Interpreter::createWithCUDA(std::move(*ciOrErr),
542 : clang::Interpreter::create(
544 outOfProcess ? std::move(OutOfProcessConfig) :
nullptr);
547 CudaEnabled ? clang::Interpreter::createWithCUDA(std::move(*ciOrErr),
549 : clang::Interpreter::create(std::move(*ciOrErr));
552 llvm::logAllUnhandledErrors(innerOrErr.takeError(), llvm::errs(),
553 "Failed to build Interpreter:");
557 if (
auto Err = (*innerOrErr)->LoadDynamicLibrary(
"libcudart.so")) {
558 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
559 "Failed load libcudart.so runtime:");
564 return std::move(*innerOrErr);
568 std::string& mangledName) {
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();
582 llvm::raw_string_ostream RawStr(mangledName);
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();
592 mangleCtx->mangleName(GD, RawStr);
599#if CLANG_VERSION_MAJOR < 22
600 auto* engine = &llvm::cantFail(I.getExecutionEngine());
601 return const_cast<llvm::orc::LLJIT*
>(engine);
605 struct OrcIncrementalExecutor :
public clang::IncrementalExecutor {
606 std::unique_ptr<llvm::orc::LLJIT> Jit;
609 auto& engine =
static_cast<OrcIncrementalExecutor&
>(
610 llvm::cantFail(I.getExecutionEngine()));
611 return engine.Jit.get();
615inline llvm::Expected<llvm::JITTargetAddress>
618 auto AddrOrErr = I.getSymbolAddress(IRName);
619 if (llvm::Error Err = AddrOrErr.takeError())
620 return std::move(Err);
621 return AddrOrErr->getValue();
624inline llvm::Expected<llvm::JITTargetAddress>
626 std::string MangledName;
631inline llvm::Expected<llvm::JITTargetAddress>
633 llvm::StringRef LinkerName) {
635 char GlobalPrefix = DL.getGlobalPrefix();
636 std::string LinkerNameTmp(LinkerName);
637 if (GlobalPrefix !=
'\0') {
638 LinkerNameTmp = std::string(1, GlobalPrefix) + LinkerNameTmp;
640 auto AddrOrErr = I.getSymbolAddressFromLinkerName(LinkerNameTmp);
641 if (llvm::Error Err = AddrOrErr.takeError())
642 return std::move(Err);
643 return AddrOrErr->getValue();
646inline llvm::Error
Undo(clang::Interpreter& I,
unsigned N = 1) {
651 clang::Interpreter& I,
const char* code,
652 unsigned complete_line = 1U,
653 unsigned complete_column = 1U) {
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: ");
662 auto Interp = clang::Interpreter::create(std::move(*CI));
663 if (
auto Err = Interp.takeError()) {
664 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
"error: ");
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());
703 return TD->getASTContext().getTypeDeclType(TD);
706#ifdef CPPINTEROP_USE_CLING
707using Value = cling::Value;
713#ifdef CPPINTEROP_USE_CLING
714template <
typename T>
inline T
convertTo(cling::Value V) {
715 return V.castAs<T>();
718template <
typename T>
inline T
convertTo(clang::Value V) {
719 return V.convertTo<T>();
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.");
736 std::atomic<unsigned>
rc;
741 static_cast<ValueRefCount*
>(p)->
rc.fetch_add(1, std::memory_order_relaxed);
745 if (
rc->rc.fetch_sub(1, std::memory_order_acq_rel) == 1)
760 Interpreter& interp, clang::ClassTemplateSpecializationDecl* CTSD) {
761#ifdef CPPINTEROP_USE_CLING
762 cling::Interpreter::PushTransactionRAII RAII(&interp);
764 interp.
getSema().InstantiateClassTemplateSpecialization(
765 clang::SourceLocation::getFromRawEncoding(1), CTSD,
766 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)
clang::QualType GetTypeFromDecl(const clang::TypeDecl *TD)
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