Я могу связать существующий метод с Функцией LLVM* и использовать его от скомпилированного кода JIT?

Я играю вокруг с C++ LLVM API. Я хотел бы к коду компиляции JIT и выполнил бы его.

Однако я должен назвать метод C++ от упомянутого скомпилированного кода JIT. Обычно, LLVM рассматривает вызовы метода как вызовы функции с объектным указателем, переданным как первый аргумент, таким образом звонить не должно быть проблемой. Настоящая проблема состоит в том, чтобы получить ту функцию в LLVM.

Насколько я вижу, возможно использовать внешнюю связь для функций и получить его ее именем. Проблема, так как это - метод C++, его имя будет искаженным, таким образом, я не думаю, что это - хорошая идея пойти тем путем.

Создание FunctionType объект достаточно легок. Но оттуда, как я могу сообщить LLVM своего метода и получить a Function объект для него?

19
задан zneak 11 July 2013 в 06:59
поделиться

3 ответа

Парни из Список рассылки LLVM оказался достаточно полезным, чтобы предложить лучшее решение . Они не сказали, как получить указатель от метода к функции, но я уже разобрался с этой частью, так что все в порядке.

ИЗМЕНИТЬ Простой способ сделать это - просто превратить ваш метод в функцию:

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

Затем используйте адрес Foo_Bar вместо попытки получить Foo :: bar s. Используйте llvm :: ExecutionEngine :: addGlobalMapping , чтобы добавить отображение, как показано ниже.

Как обычно, у простейшего решения есть некоторые интересные преимущества. Например, он без сбоев работает с виртуальными функциями. (Но это гораздо менее увлекательно. Остальная часть ответа сохранена для исторических целей, в основном потому, что я очень повеселился, копаясь во внутренностях моей среды выполнения C ++. Также обратите внимание, что она непереносима.)


You ' мне понадобится что-то в этом роде, чтобы вычислить адрес метода (предупреждаем, это грязный взлом, который, вероятно, будет совместим только с 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));
15
ответ дан 30 November 2019 в 04:20
поделиться

Один из способов - это оболочка C вокруг желаемого метода, то есть

extern "C" {
  void wrapped_foo(bar *b, int arg1, int arg2) {
    b->foo(arg1, arg2);
  }
}

бит extern «C» заставляет функцию использовать соглашения о вызовах C и предотвращает любое искажение имени. См. http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html#faq-32.6 для получения подробной информации о взаимодействии C / C ++, включая extern "C "

Вы также, вероятно, должны иметь возможность получить адрес функции в вашем коде C ++, а затем сохранить этот адрес в глобальном, известном LLVM.

8
ответ дан 30 November 2019 в 04:20
поделиться

Ха, используя нестандартный dladdr и смехотворно запутанный и небезопасный способ преобразования указателей методов в указатели на недействительные, похоже, есть способ получить имя метод из его указателя.

Это, безусловно, опаснее огнестрельного оружия. Не делайте этого дома (или на работе, если на то пошло).

C ++ запрещает преобразовывать указатели методов в void * (что требуется для работы dladdr ) даже со всемогущим приведением C, но вы можете обмануть это.

#include <string>
#include <dlfcn.h>

template<typename T>
static void* voidify(T method)
{
    asm ("movq %rdi, %rax"); // should work on x86_64 ABI compliant platforms
}

template<typename T>
const char* getMethodName(T method)
{
    Dl_info info;
    if (dladdr(voidify(method), &info))
        return info.dli_sname;
    return "";
}

Отсюда:

int main()
{
    std::cout << getMethodName(&Foo::bar) << std::endl;
    // prints something like "_ZN3Foo3barEv"
}

... аааи вы сможете использовать это имя символа с LLVM. Но с виртуальными методами он работать не будет (еще одна веская причина не использовать его).

РЕДАКТИРОВАТЬ Взламывая гораздо глубже, как обрабатываются указатели виртуальных методов, я собрал более сложную функцию, которая работает и для них. Только самые смелые из вас должны перейти по этой ссылке .

4
ответ дан 30 November 2019 в 04:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: