2010-06-23 10 views
19

Ich spiele mit der LLVM C++ API herum. Ich möchte JIT kompilieren und ausführen.Kann ich eine vorhandene Methode an eine LLVM-Funktion * binden und sie aus JIT-kompiliertem Code verwenden?

Allerdings muss ich eine C++ - Methode aus dem JIT-kompilierten Code aufrufen. Normalerweise behandelt LLVM Methodenaufrufe als Funktionsaufrufe, wobei der Objektzeiger als erstes Argument übergeben wird. Daher sollte das Aufrufen kein Problem darstellen. Das eigentliche Problem ist, diese Funktion in LLVM zu bekommen.

Soweit ich sehen kann, ist es möglich, externe Verknüpfung für Funktionen zu verwenden und es durch seinen Namen zu erhalten. Das Problem ist, da es sich um eine C++ - Methode handelt, wird der Name verfälscht, daher denke ich nicht, dass es eine gute Idee ist, so zu gehen.

Das Objekt FunctionType zu machen ist einfach genug. Aber von dort, wie kann ich LLVM über meine Methode informieren und ein Function Objekt dafür erhalten?

Antwort

15

Die Leute von der LLVM-Mailingliste waren helpful enough to provide a better solution. Sie haben nicht gesagt, wie man den Zeiger von der Methode zur Funktion bringt, aber ich habe diesen Teil bereits herausgefunden, also ist es in Ordnung.

EDIT Eine saubere Art und Weise, dies zu tun ist einfach Ihre Methode in eine Funktion zu wickeln:

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

Dann Foo_Bar ‚s-Adresse anstatt zu versuchen, Foo::bar zu bekommen‘ verwenden s. Verwenden Sie llvm::ExecutionEngine::addGlobalMapping, um die Zuordnung wie unten gezeigt hinzuzufügen.

Wie üblich, hat die einfachste Lösung einige interessante Vorteile. Zum Beispiel funktioniert es mit virtuellen Funktionen ohne einen Schluckauf. (Aber es ist so viel weniger unterhaltsam. Der Rest der Antwort wird für historische Zwecke gehalten, vor allem weil ich viel Spaß beim Stöbern auf die Interna meiner C++ Laufzeit hatte.)


Sie werden etwas in dieser Richtung müssen die Adresse einer Methode Abbildung (seien Sie gewarnt, dass ist ein schmutziger Hack, der wahrscheinlich nur mit dem Itanium ABI kompatibel sein wird):

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; 
} 

Dann nutzen llvm::ExecutionEngine::addGlobalMapping ein zur Karte Funktion zu der Adresse, die Sie bekommen haben. Um es aufzurufen, geben Sie Ihr Objekt als ersten Parameter und den Rest wie gewohnt weiter. Hier ist ein kurzes Beispiel.

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)); 
+0

Würde es Ihnen etwas ausmachen, uns zu zeigen, wie Sie das Problem am Ende gelöst haben? Ich kämpfe mit dem gleichen Problem. – FFox

+0

@FFox: sicher. Ich habe die Antwort bearbeitet, um ein hilfreiches Beispiel zu geben. – zneak

+0

@zneak, im Funktionstyp übergeben Sie 'llvm :: Function :: Create' welchen Typ haben Sie für das erste Argument (den Objektzeiger) verwendet? hast du void * benutzt? – lurscher

8

Eine Möglichkeit ist ein C-Wrapper um die gewünschten Verfahren, d.h.

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

Die extern "C" Bit macht die Funktionsaufrufkonventionen C und verhindert jeden Namen Mangeln. Siehe http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html#faq-32.6 Einzelheiten zu C/C++ Interop einschließlich extern "C"

sollten Sie auch wahrscheinlich in der Lage sein, die Adresse der Funktion in C++ Code zu bekommen und dann speichern Sie diese Adresse in einem globalen LLVM bekannt.

+0

Ich dachte, es könnte einen einfacheren Weg geben. – zneak

3

Huh, mit der nicht-Standard dladdr und eine lächerlich gewundene und unsichere Möglichkeit, Methodenzeiger auf void-Zeiger zu werfen, scheint es eine Möglichkeit zu geben, den Namen einer Methode von seinem Zeiger zu erhalten.

Dies ist sicherlich gefährlicher als Feuerwaffen. Mach das nicht zu Hause (oder bei der Arbeit).

C++ verbietet es, Methodenzeiger auf void * (was von dladdr erforderlich ist) zu arbeiten, sogar mit dem allmächtigen C-Cast, aber Sie können das betrügen.

#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 ""; 
} 

Von dort:

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

... uuuuund Sie sollten diese Symbolnamen mit LLVM verwenden können. Aber es wird nicht mit virtuellen Methoden funktionieren (ein weiterer guter Grund, es nicht zu benutzen).

BEARBEITEN Hacking viel, viel tiefer in wie virtuelle Methode Zeiger behandelt werden, habe ich eine ausgefeiltere Funktion zusammengestellt, die auch für sie funktioniert. Nur die mutigsten von Ihnen sollten follow this link.

+0

Voidify, wie. Nett. –

+0

@Paul Nathan: Es hat jedoch ein paar Warnungen ausgegeben ("Kontrolle erreicht das Ende der nicht-void-Funktion"). Ich habe _beautified_ es besser mit C++ zu integrieren. – zneak

Verwandte Themen