2010-06-23 28 views
19

Tôi đang đùa giỡn với API LLVM C++. Tôi muốn JIT biên dịch mã và chạy nó.Tôi có thể liên kết một phương thức hiện có với một hàm LLVM * và sử dụng nó từ mã được biên dịch JIT không?

Tuy nhiên, tôi cần gọi phương thức C++ từ mã được JIT biên dịch. Thông thường, LLVM xử lý các cuộc gọi phương thức như các cuộc gọi hàm với con trỏ đối tượng được chuyển làm đối số đầu tiên, vì vậy việc gọi điện không phải là một vấn đề. Vấn đề thực sự là lấy hàm đó vào LLVM.

Theo như tôi thấy, có thể sử dụng liên kết bên ngoài cho các chức năng và nhận được nó theo tên của nó. Vấn đề là, vì nó là một phương pháp C++, tên của nó sẽ bị xáo trộn, vì vậy tôi không nghĩ rằng đó là một ý tưởng tốt để đi theo cách đó.

Làm cho đối tượng FunctionType đủ dễ dàng. Nhưng từ đó, làm thế nào tôi có thể thông báo cho LLVM về phương pháp của tôi và nhận được một đối tượng Function cho nó?

Trả lời

15

Các dudes từ danh sách gửi thư của LLVM là helpful enough to provide a better solution. Họ đã không nói làm thế nào để có được con trỏ từ phương pháp này đến hàm, nhưng tôi đã tìm ra phần này để nó ổn.

EDIT Một cách sạch để làm điều này chỉ đơn giản là quấn phương pháp của bạn thành một chức năng:

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

Sau đó sử dụng 'địa chỉ s thay vì cố gắng để có được Foo::bar' Foo_Bar s. Sử dụng llvm::ExecutionEngine::addGlobalMapping để thêm ánh xạ như được hiển thị bên dưới.

Như thường lệ, giải pháp đơn giản nhất có một số lợi ích thú vị. Ví dụ, nó hoạt động với các chức năng ảo mà không có trục trặc. (Nhưng nó ít giải trí hơn nhiều. Phần còn lại của câu trả lời được giữ cho mục đích lịch sử, chủ yếu là vì tôi đã có rất nhiều niềm vui poking tại internals của thời gian chạy C++ của tôi. Cũng lưu ý rằng nó không di động.)


Bạn sẽ cần một cái gì đó dọc theo những dòng để tìm địa chỉ của một phương pháp (được cảnh báo, đó là một bẩn hack mà có lẽ sẽ chỉ tương thích với các bộ xử lý 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; 
} 

Sau đó sử dụng llvm::ExecutionEngine::addGlobalMapping để ánh xạ một đến địa chỉ bạn đã nhận được. Để gọi nó, chuyển đối tượng của bạn làm tham số đầu tiên và phần còn lại như bình thường. Đây là một ví dụ nhanh.

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

Bạn có thể cho chúng tôi biết cách bạn giải quyết vấn đề cuối cùng không. Tôi đang đấu tranh với cùng một vấn đề. – FFox

+0

@FFox: chắc chắn rồi. Tôi đã chỉnh sửa câu trả lời để cung cấp một ví dụ hữu ích. – zneak

+0

@zneak, trong kiểu hàm bạn chuyển đến 'llvm :: Function :: Create' bạn đã sử dụng kiểu nào cho đối số đầu tiên (đối tượng con trỏ)? bạn đã sử dụng void *? – lurscher

8

Một cách là một wrapper C xung quanh phương pháp mong muốn, ví dụ:

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

Bit extern "C" làm cho các chức năng công ước gọi sử dụng C và ngăn chặn bất kỳ tên mangling. Xem http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html#faq-32.6 để biết chi tiết về liên lạc C/C++ bao gồm extern "C"

Bạn cũng nên có thể lấy địa chỉ của hàm trong mã C++ và sau đó lưu địa chỉ đó vào LLVM toàn cục.

+0

Tôi nghĩ có thể có cách đơn giản hơn. – zneak

3

Huh, sử dụng phi tiêu chuẩn dladdr và cách thức phức tạp và không an toàn để đúc con trỏ phương thức làm mất con trỏ, có vẻ như là cách để lấy tên của phương thức từ con trỏ của nó.

Điều này chắc chắn nguy hiểm hơn súng cầm tay. Đừng làm điều này ở nhà (hoặc tại nơi làm việc, cho vấn đề đó).

C++ cấm gán các con trỏ phương thức để hủy * (được yêu cầu bởi dladdr để hoạt động) ngay cả với phép đúc C toàn năng, nhưng bạn có thể lừa nó.

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

Từ đó:

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

... aaaand bạn sẽ có thể sử dụng tên biểu tượng với LLVM. Nhưng nó sẽ không làm việc với các phương pháp ảo (một lý do tốt để không sử dụng nó).

EDIT Hacking nhiều, sâu hơn vào cách xử lý con trỏ ảo được xử lý, tôi đã đặt cùng một hàm phức tạp hơn cũng hoạt động cho chúng. Chỉ có can đảm nhất của bạn nên follow this link.

+0

Voidify, eh. Tốt đẹp. –

+0

@Paul Nathan: nó đã phát ra một vài cảnh báo, mặc dù ("điều khiển đến hết chức năng không có hiệu lực"). Tôi đã _beautified_ nó tích hợp tốt hơn với C++. – zneak

Các vấn đề liên quan