I am working on a JIT that uses LLVM. The language has a small run-time written in C++ which I compile down to LLVM IR using clang
clang++ runtime.cu --cuda-gpu-arch=sm_50 -c -emit-llvm
and then load the *.bc files, generate additional IR, and execute on the fly. The reason for the CUDA stuff is that I want to add some GPU acceleration to the runtime. However, this introduces CUDA specific external functions which gives errors such as:
LLVM ERROR: Program used external function 'cudaSetupArgument' which could not be resolved!
As discussed here, this is usually solved by including the appropriate libraries when compiling the program:
g++ main.c cmal.o -L/usr/local/cuda/lib64 -lcudart
However, I am not sure how to include libraries in JITed modules using LLVM. I found this question which suggested that is used to be possible to add libraries to modules in the JIT like this:
[your module]->addLibrary("m");
Unfortunately, this has been deprecated. Can anyone tell me the best way to do this now? Let me know if I need to provide more information!
Furthermore, I am not really sure if this is the best way to be incorporating GPU offloading into my JIT, so if anyone can point me to a better method then please do :)
Thanks!
EDIT: I am using LLVM 5.0 and the JIT engine I am using is from llvm/ExecutionEngine/ExecutionEngine.h
, more specifically I create it like this:
EngineBuilder EB(std::move(module));
ExecutionEngine *EE = EB.create(targetMachine);
You need to teach your JIT engine about other symbols explicitly.
If they are in a dynamic library (dylib
, so
, dll
) then you can just call
sys::DynamicLibrary::LoadLibraryPermanently("path_to_some.dylib")
with a path to the dynamic library.
If the symbols are in an object file or an archive, then it requires a bit more work: you would need to load them into memory and add to the ExecutionEngine
using its APIs.
Here is an example for an object file:
std::string objectFileName("some_object_file.o");
ErrorOr<std::unique_ptr<MemoryBuffer>> buffer =
MemoryBuffer::getFile(objectFileName.c_str());
if (!buffer) {
// handle error
}
Expected<std::unique_ptr<ObjectFile>> objectOrError =
ObjectFile::createObjectFile(buffer.get()->getMemBufferRef());
if (!objectOrError) {
// handle error
}
std::unique_ptr<ObjectFile> objectFile(std::move(objectOrError.get()));
auto owningObject = OwningBinary<ObjectFile>(std::move(objectFile),
std::move(buffer.get()));
executionEngine.addObjectFile(std::move(owningObject));
For archives replace template types ObjectFile
with Archive
, and call
executionEngine.addArchive(std::move(owningArchive));
at the end.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With