我是否可以将现有方法绑定到LLVM函数*,并从JIT编译的代码中使用它?

2022-03-21 00:00:00 jit llvm c++

我正在研究LLVM C++API。我希望JIT编译代码并运行它。

但是,我需要从上述JIT编译的代码中调用C++方法。通常,LLVM将方法调用视为将对象指针作为第一个参数传递的函数调用,因此调用应该不是问题。真正问题是将该函数放入LLVM。

据我所知,对函数使用外部链接并按其名称获取是可能的。问题是,由于它是一个C++方法,它的名称将会被损坏,所以我认为这样做不是一个好主意。

创建FunctionType对象非常简单。但从那里,我如何才能将我的方法通知LLVM并为其获取Function对象呢?


解决方案

llvm邮件列表中的人是helpful enough to provide a better solution。他们没有说如何将指针从方法获取到函数,但是我已经弄清楚了这一部分,所以没关系。

编辑一种简单的方法就是将您的方法包装到一个函数中:

int Foo_Bar(Foo* foo)
{
    return foo->bar();
}

然后使用Foo_Bar的地址,而不是尝试获取Foo::bar的地址。使用llvm::ExecutionEngine::addGlobalMapping添加映射,如下所示。

和往常一样,最简单的解决方案有一些有趣的好处。例如,它可以毫不费力地使用虚拟函数。(但它的娱乐性要差得多。答案的睡觉是为了历史目的而保留的,主要是因为我在探究我的C++运行时的内部结构时获得了很多乐趣。还要注意,它是不可移植的。)


您需要以下内容来确定方法的地址(请注意,这是一个肮脏的技巧,可能只与Itanium ABI兼容):

template<typename T>
const void* void_cast(const T& object)
{
    union Retyper
    {
        const T object;
        void* pointer;
        Retyper(T obj) : object(obj) { }
    };

    return Retyper(object).pointer;
}

template<typename T, typename M>
const void* getMethodPointer(const T* object, M method) // will work for virtual methods
{
    union MethodEntry
    {
        intptr_t offset;
        void* function;
    };

    const MethodEntry* entry = static_cast<const MethodEntry*>(void_cast(&method));

    if (entry->offset % sizeof(intptr_t) == 0) // looks like that's how the runtime guesses virtual from static
        return getMethodPointer(method);

    const void* const* const vtable = *reinterpret_cast<const void* const* const* const>(object);
    return vtable[(entry->offset - 1) / sizeof(void*)];
}

template<typename M>
const void* getMethodPointer(M method) // will only work with non-virtual methods
{
    union MethodEntry
    {
        intptr_t offset;
        void* function;
    };

    return static_cast<const MethodEntry*>(void_cast(&method))->function;
}

然后使用llvm::ExecutionEngine::addGlobalMapping将函数映射到您获得的地址。要调用它,请将其作为第一个参数传递给您的对象,并照常传递睡觉。这里有一个快速示例。

class Foo
{
    void Bar();
    virtual void Baz();
};

class FooFoo : public Foo
{
    virtual void Baz();
};

Foo* foo = new FooFoo;

const void* barMethodPointer = getMethodPointer(&Foo::Bar);
const void* bazMethodPointer = getMethodPointer(foo, &Foo::Baz); // will get FooFoo::Baz

llvm::ExecutionEngine* engine = llvm::EngineBuilder(module).Create();

llvm::Function* bar = llvm::Function::Create(/* function type */, Function::ExternalLinkage, "foo", module);
llvm::Function* baz = llvm::Function::Create(/* function type */, Function::ExternalLinkage, "baz", module);
engine->addGlobalMapping(bar, const_cast<void*>(barMethodPointer)); // LLVM always takes non-const pointers
engine->addGlobalMapping(baz, const_cast<void*>(bazMethodPointer));

相关文章