diff --git a/lib/CppInterOp/CppInterOp.cpp b/lib/CppInterOp/CppInterOp.cpp index f1527b4a8..e08992565 100755 --- a/lib/CppInterOp/CppInterOp.cpp +++ b/lib/CppInterOp/CppInterOp.cpp @@ -90,6 +90,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; @@ -3330,6 +3358,30 @@ 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 @@ -3442,6 +3494,85 @@ TInterp_t CreateInterpreter(const std::vector& Args /*={}*/, sInterpreters->back().get()}); assert(sInterpreters->size() == sInterpreterASTMap->size()); + +// Define runtime symbols in the JIT dylib for clang-repl +#if !defined(CPPINTEROP_USE_CLING) && !defined(EMSCRIPTEN) + DefineAbsoluteSymbol(*I, "__ci_newtag", (uint64_t)&__ci_newtag); +// llvm > 22 has this defined as a C symbol that does not require mangling +#if CLANG_VERSION_MAJOR >= 22 + DefineAbsoluteSymbol(*I, "__clang_Interpreter_SetValueWithAlloc", + (uint64_t)&__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(), + (uint64_t)&__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", + (uint64_t)&__clang_Interpreter_SetValueNoAlloc); +#endif +#endif return I; } diff --git a/unittests/CppInterOp/InterpreterTest.cpp b/unittests/CppInterOp/InterpreterTest.cpp index 5800fe64e..b1713cc51 100644 --- a/unittests/CppInterOp/InterpreterTest.cpp +++ b/unittests/CppInterOp/InterpreterTest.cpp @@ -84,6 +84,38 @@ TEST(InterpreterTest, Evaluate) { EXPECT_FALSE(HadError) ; } +TEST(InterpreterTest, EvaluateExtensive) { +#ifdef EMSCRIPTEN + GTEST_SKIP() << "Test fails for Emscipten builds"; +#endif +#ifdef _WIN32 + GTEST_SKIP() << "Disabled on Windows. Needs fixing."; +#endif + if (llvm::sys::RunningOnValgrind()) + GTEST_SKIP() << "XFAIL due to Valgrind report"; + Cpp::CreateInterpreter(); + EXPECT_TRUE(Cpp::Evaluate("__cplusplus") == 201402); + + bool HadError; + EXPECT_TRUE(Cpp::Evaluate("#error", &HadError) == (intptr_t)~0UL); + EXPECT_TRUE(HadError); +// for llvm < 19 this tests all different overloads of __clang_Interpreter_SetValueNoAlloc + EXPECT_EQ(Cpp::Evaluate("int i = 11; ++i", &HadError), 12); + 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) ; +} + TEST(InterpreterTest, DeleteInterpreter) { auto* I1 = Cpp::CreateInterpreter(); auto* I2 = Cpp::CreateInterpreter();