Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions lib/CppInterOp/CppInterOp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -61,6 +66,7 @@
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <deque>
#include <iterator>
Expand Down Expand Up @@ -91,6 +97,34 @@
#include <unistd.h>
#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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable '__ci_newtag' can be made static or moved into an anonymous namespace to enforce internal linkage [misc-use-internal-linkage]

lib/CppInterOp/CppInterOp.cpp:91:

- struct __clang_Interpreter_NewTag {
+ static struct __clang_Interpreter_NewTag {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable '__ci_newtag' can be made static or moved into an anonymous namespace to enforce internal linkage [misc-use-internal-linkage]

lib/CppInterOp/CppInterOp.cpp:96:

- struct __clang_Interpreter_NewTag {
+ static struct __clang_Interpreter_NewTag {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable '__ci_newtag' can be made static or moved into an anonymous namespace to enforce internal linkage [misc-use-internal-linkage]

lib/CppInterOp/CppInterOp.cpp:100:

- struct __clang_Interpreter_NewTag {
+ static struct __clang_Interpreter_NewTag {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable '__ci_newtag' can be made static or moved into an anonymous namespace to enforce internal linkage [misc-use-internal-linkage]

lib/CppInterOp/CppInterOp.cpp:102:

- struct __clang_Interpreter_NewTag {
+ static struct __clang_Interpreter_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;
Expand Down Expand Up @@ -3175,6 +3209,29 @@
}

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(),

Check warning on line 3227 in lib/CppInterOp/CppInterOp.cpp

View check run for this annotation

Codecov / codecov/patch

lib/CppInterOp/CppInterOp.cpp#L3227

Added line #L3227 was not covered by tests
"DefineAbsoluteSymbol error: ");
return true;

Check warning on line 3229 in lib/CppInterOp/CppInterOp.cpp

View check run for this annotation

Codecov / codecov/patch

lib/CppInterOp/CppInterOp.cpp#L3229

Added line #L3229 was not covered by tests
}
return false;
}
#endif

static std::string MakeResourcesPath() {
StringRef Dir;
#ifdef LLVM_BINARY_DIR
Expand Down Expand Up @@ -3280,6 +3337,88 @@

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<uint64_t>(&__ci_newtag));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: do not use reinterpret_cast [cppcoreguidelines-pro-type-reinterpret-cast]

                       reinterpret_cast<uint64_t>(&__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<uint64_t>(&__clang_Interpreter_SetValueWithAlloc));
#else
// obtain mangled name
auto* D = static_cast<clang::Decl*>(
Cpp::GetNamed("__clang_Interpreter_SetValueWithAlloc"));
if (auto* FD = llvm::dyn_cast<FunctionDecl>(D)) {
auto GD = GlobalDecl(FD);
std::string mangledName;
compat::maybeMangleDeclName(GD, mangledName);
DefineAbsoluteSymbol(
*I, mangledName.c_str(),
reinterpret_cast<uint64_t>(&__clang_Interpreter_SetValueWithAlloc));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: do not use reinterpret_cast [cppcoreguidelines-pro-type-reinterpret-cast]

        reinterpret_cast<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<TCppFunction_t> 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<std::vector<Cpp::TemplateArgInfo>> 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<void*> func_pointers = {
reinterpret_cast<void*>(
static_cast<FP0>(&__clang_Interpreter_SetValueNoAlloc)),
reinterpret_cast<void*>(
static_cast<FP1>(&__clang_Interpreter_SetValueNoAlloc)),
reinterpret_cast<void*>(
static_cast<FP2>(&__clang_Interpreter_SetValueNoAlloc)),
reinterpret_cast<void*>(
static_cast<FP3>(&__clang_Interpreter_SetValueNoAlloc)),
reinterpret_cast<void*>(
static_cast<FP4>(&__clang_Interpreter_SetValueNoAlloc)),
reinterpret_cast<void*>(
static_cast<FP5>(&__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<clang::Decl*>(
Cpp::BestOverloadFunctionMatch(Methods, {}, a_params[i]));
if (auto* fd = llvm::dyn_cast<clang::FunctionDecl>(decl)) {
auto gd = clang::GlobalDecl(fd);
compat::maybeMangleDeclName(gd, mangledName);
DefineAbsoluteSymbol(*I, mangledName.c_str(),
reinterpret_cast<uint64_t>(func_pointers[i]));
}
}
#else
DefineAbsoluteSymbol(
*I, "__clang_Interpreter_SetValueNoAlloc",
reinterpret_cast<uint64_t>(&__clang_Interpreter_SetValueNoAlloc));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: do not use reinterpret_cast [cppcoreguidelines-pro-type-reinterpret-cast]

      reinterpret_cast<uint64_t>(&__clang_Interpreter_SetValueNoAlloc));
      ^

#endif
#endif
return I;
}

Expand Down
16 changes: 15 additions & 1 deletion unittests/CppInterOp/InterpreterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Loading