From 68249329a6ecccfc33123fb7f5740b976627bddb Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Wed, 3 Dec 2025 15:39:59 +0100 Subject: [PATCH 1/3] Add static DefineAbsoluteSymbol to define injected symbols --- lib/CppInterOp/CppInterOp.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lib/CppInterOp/CppInterOp.cpp b/lib/CppInterOp/CppInterOp.cpp index 46f2fe226..0ac065c6e 100755 --- a/lib/CppInterOp/CppInterOp.cpp +++ b/lib/CppInterOp/CppInterOp.cpp @@ -48,6 +48,11 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Demangle/Demangle.h" +#if CLANG_VERSION_MAJOR >= 20 +#include "llvm/ExecutionEngine/Orc/AbsoluteSymbols.h" +#include "llvm/ExecutionEngine/Orc/CoreContainers.h" +#endif +#include "llvm/ExecutionEngine/Orc/Core.h" #include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" #include "llvm/IR/GlobalValue.h" #include "llvm/Support/Casting.h" @@ -3175,6 +3180,29 @@ CPPINTEROP_API JitCall MakeFunctionCallable(TCppConstFunction_t func) { } namespace { +#if !defined(CPPINTEROP_USE_CLING) && !defined(EMSCRIPTEN) +bool DefineAbsoluteSymbol(compat::Interpreter& I, + const char* linker_mangled_name, uint64_t address) { + using namespace llvm; + using namespace llvm::orc; + + llvm::orc::LLJIT& Jit = *compat::getExecutionEngine(I); + llvm::orc::ExecutionSession& ES = Jit.getExecutionSession(); + JITDylib& DyLib = *Jit.getProcessSymbolsJITDylib().get(); + + llvm::orc::SymbolMap InjectedSymbols{ + {ES.intern(linker_mangled_name), + ExecutorSymbolDef(ExecutorAddr(address), JITSymbolFlags::Exported)}}; + + if (Error Err = DyLib.define(absoluteSymbols(InjectedSymbols))) { + logAllUnhandledErrors(std::move(Err), errs(), + "DefineAbsoluteSymbol error: "); + return true; + } + return false; +} +#endif + static std::string MakeResourcesPath() { StringRef Dir; #ifdef LLVM_BINARY_DIR From f126d76b4dcb66e6cb0de372092ef626ea585aca Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Wed, 3 Dec 2025 15:42:18 +0100 Subject: [PATCH 2/3] Declare clang-repl value printing symbols, mangle and define to the JIT Supports LLVM 18-21 --- lib/CppInterOp/CppInterOp.cpp | 111 ++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/lib/CppInterOp/CppInterOp.cpp b/lib/CppInterOp/CppInterOp.cpp index 0ac065c6e..e88aae744 100755 --- a/lib/CppInterOp/CppInterOp.cpp +++ b/lib/CppInterOp/CppInterOp.cpp @@ -66,6 +66,7 @@ #include #include #include +#include #include #include #include @@ -96,6 +97,34 @@ #include #endif // WIN32 +// Runtime symbols required if the library using JIT (Cpp::Evaluate) does not +// link to llvm +#if !defined(CPPINTEROP_USE_CLING) && !defined(EMSCRIPTEN) +struct __clang_Interpreter_NewTag { +} __ci_newtag; +#if CLANG_VERSION_MAJOR > 21 +extern "C" void* __clang_Interpreter_SetValueWithAlloc(void* This, void* OutVal, + void* OpaqueType) +#else +void* __clang_Interpreter_SetValueWithAlloc(void* This, void* OutVal, + void* OpaqueType); +#endif + +#if CLANG_VERSION_MAJOR > 18 + extern "C" void __clang_Interpreter_SetValueNoAlloc(void* This, + void* OutVal, + void* OpaqueType, ...); +#else +void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*); +void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, void*); +void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, float); +void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, double); +void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, long double); +void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, + unsigned long long); +#endif +#endif // CPPINTEROP_USE_CLING + namespace Cpp { using namespace clang; @@ -3308,6 +3337,88 @@ TInterp_t CreateInterpreter(const std::vector& Args /*={}*/, sInterpreters->emplace_back(I, /*Owned=*/true); +// Define runtime symbols in the JIT dylib for clang-repl +#if !defined(CPPINTEROP_USE_CLING) && !defined(EMSCRIPTEN) + DefineAbsoluteSymbol(*I, "__ci_newtag", + reinterpret_cast(&__ci_newtag)); +// llvm >= 21 has this defined as a C symbol that does not require mangling +#if CLANG_VERSION_MAJOR >= 21 + DefineAbsoluteSymbol( + *I, "__clang_Interpreter_SetValueWithAlloc", + reinterpret_cast(&__clang_Interpreter_SetValueWithAlloc)); +#else + // obtain mangled name + auto* D = static_cast( + Cpp::GetNamed("__clang_Interpreter_SetValueWithAlloc")); + if (auto* FD = llvm::dyn_cast(D)) { + auto GD = GlobalDecl(FD); + std::string mangledName; + compat::maybeMangleDeclName(GD, mangledName); + DefineAbsoluteSymbol( + *I, mangledName.c_str(), + reinterpret_cast(&__clang_Interpreter_SetValueWithAlloc)); + } +#endif +// llvm < 19 has multiple overloads of __clang_Interpreter_SetValueNoAlloc +#if CLANG_VERSION_MAJOR < 19 + // obtain all 6 candidates, and obtain the correct Decl for each overload + // using BestOverloadFunctionMatch. We then map the decl to the correct + // function pointer (force the compiler to find the right declarion by casting + // to the corresponding function pointer signature) and then register it. + const std::vector Methods = Cpp::GetFunctionsUsingName( + Cpp::GetGlobalScope(), "__clang_Interpreter_SetValueNoAlloc"); + std::string mangledName; + ASTContext& Ctxt = I->getSema().getASTContext(); + auto* TAI = Ctxt.VoidPtrTy.getAsOpaquePtr(); + + // possible parameter lists for __clang_Interpreter_SetValueNoAlloc overloads + // in LLVM 18 + const std::vector> a_params = { + {TAI, TAI, TAI}, + {TAI, TAI, TAI, TAI}, + {TAI, TAI, TAI, Ctxt.FloatTy.getAsOpaquePtr()}, + {TAI, TAI, TAI, Ctxt.DoubleTy.getAsOpaquePtr()}, + {TAI, TAI, TAI, Ctxt.LongDoubleTy.getAsOpaquePtr()}, + {TAI, TAI, TAI, Ctxt.UnsignedLongLongTy.getAsOpaquePtr()}}; + + using FP0 = void (*)(void*, void*, void*); + using FP1 = void (*)(void*, void*, void*, void*); + using FP2 = void (*)(void*, void*, void*, float); + using FP3 = void (*)(void*, void*, void*, double); + using FP4 = void (*)(void*, void*, void*, long double); + using FP5 = void (*)(void*, void*, void*, unsigned long long); + + const std::vector func_pointers = { + reinterpret_cast( + static_cast(&__clang_Interpreter_SetValueNoAlloc)), + reinterpret_cast( + static_cast(&__clang_Interpreter_SetValueNoAlloc)), + reinterpret_cast( + static_cast(&__clang_Interpreter_SetValueNoAlloc)), + reinterpret_cast( + static_cast(&__clang_Interpreter_SetValueNoAlloc)), + reinterpret_cast( + static_cast(&__clang_Interpreter_SetValueNoAlloc)), + reinterpret_cast( + static_cast(&__clang_Interpreter_SetValueNoAlloc))}; + + // these symbols are not externed, so we need to mangle their names + for (size_t i = 0; i < a_params.size(); ++i) { + auto* decl = static_cast( + Cpp::BestOverloadFunctionMatch(Methods, {}, a_params[i])); + if (auto* fd = llvm::dyn_cast(decl)) { + auto gd = clang::GlobalDecl(fd); + compat::maybeMangleDeclName(gd, mangledName); + DefineAbsoluteSymbol(*I, mangledName.c_str(), + reinterpret_cast(func_pointers[i])); + } + } +#else + DefineAbsoluteSymbol( + *I, "__clang_Interpreter_SetValueNoAlloc", + reinterpret_cast(&__clang_Interpreter_SetValueNoAlloc)); +#endif +#endif return I; } From ecb9bb68b8b8abc9fdbc2b5ccc438361099d9eb2 Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Wed, 3 Dec 2025 15:44:07 +0100 Subject: [PATCH 3/3] Add extensive Interpreter tests for Cpp::Evaluate --- unittests/CppInterOp/InterpreterTest.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/unittests/CppInterOp/InterpreterTest.cpp b/unittests/CppInterOp/InterpreterTest.cpp index 14ed2619d..6e5446cfd 100644 --- a/unittests/CppInterOp/InterpreterTest.cpp +++ b/unittests/CppInterOp/InterpreterTest.cpp @@ -79,8 +79,22 @@ TYPED_TEST(CppInterOpTest, InterpreterTestEvaluate) { bool HadError; EXPECT_TRUE(Cpp::Evaluate("#error", &HadError) == (intptr_t)~0UL); EXPECT_TRUE(HadError); + // for llvm < 19 this tests different overloads of + // __clang_Interpreter_SetValueNoAlloc EXPECT_EQ(Cpp::Evaluate("int i = 11; ++i", &HadError), 12); - EXPECT_FALSE(HadError) ; + EXPECT_FALSE(HadError); + EXPECT_EQ(Cpp::Evaluate("double a = 12.; a", &HadError), 12.); + EXPECT_FALSE(HadError); + EXPECT_EQ(Cpp::Evaluate("float b = 13.; b", &HadError), 13.); + EXPECT_FALSE(HadError); + EXPECT_EQ(Cpp::Evaluate("long double c = 14.; c", &HadError), 14.); + EXPECT_FALSE(HadError); + EXPECT_EQ(Cpp::Evaluate("long double d = 15.; d", &HadError), 15.); + EXPECT_FALSE(HadError); + EXPECT_EQ(Cpp::Evaluate("unsigned long long e = 16; e", &HadError), 16); + EXPECT_FALSE(HadError); + EXPECT_NE(Cpp::Evaluate("struct S{} s; s", &HadError), (intptr_t)~0UL); + EXPECT_FALSE(HadError); } TYPED_TEST(CppInterOpTest, InterpreterTestDeleteInterpreter) {