[llvm-dev] External function resolution: MCJIT vs ORC JIT

Lang Hames via llvm-dev llvm-dev at lists.llvm.org
Wed May 18 13:52:15 PDT 2016


Hi Larry,

You're basically there, but you're hitting a couple of subtle issues:

(1) On both platforms you'll want to call
llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr) at program
startup. This makes exported symbols in the main program searchable by
RTDyldMemoryManager::getSymbolAddressInProcess (important for making 'sqr'
findable on any platform).

(2) On Linux (if I understand correctly) symbols aren't exported from the
main process by default, so even if you've called
DynamicLibrary::LoadLibraryPermanently, sqr won't show up. To fix this you
can add -Wl,export-dynamic to your link line. This will flip the default
and export symbols from the main binary, allowing getSymbolAddressInProcess
to find sqr. On MacOS this isn't necessary.

(3) On MacOS, C names are mangled by prepending an '_'. To maintain
consistency with statically linked programs, the JIT uses these mangled
names so you need to search for '_myfunc', rather than 'myfunc'. I usually
add a 'mangle' function that uses the DataLayout, then search for
'mangle(<name>)', which does the right thing on all platforms.

I've attached a new version of your code with these changes included. Hope
this helps!

- Lang.


On Tue, May 17, 2016 at 12:13 PM, Larry Gritz via llvm-dev <
llvm-dev at lists.llvm.org> wrote:

> When using ORC JIT, I'm having trouble with external function resolution
> (that is, of a function defined in the app, with C linkage).
>
> I add a declaration for the function to my IR, and when I use MCJIT, it
> finds it and all is well, But when I use ORC JIT (I *think* correctly, at
> least it closely matches what I see in the tutorial), I get an LLVM error,
> "Program used external function 'sqr' which could not be resolved."
>
> (Excuse my coming to the ORC party late, I've been stuck on an older LLVM
> for my DSL, and am only now jumping forward several versions at once, with
> some growing pains.)
>
> I've boiled it down to the minimal example below. When I build with orc=0,
> thus using MCJIT, it works fine and I get the expected output. When I build
> with orc=1, it fails as I described.
>
> I'm having this trouble with LLVM 3.7 and 3.8, on both Linux and OSX.
>
> I figure I'm probably just getting some part of the magic incantation
> wrong. Can anybody help a poor guy out and spot the error of my ways?
>
> -----
>
> #include <llvm/ADT/STLExtras.h>
> #include <llvm/Bitcode/ReaderWriter.h>
> #include <llvm/ExecutionEngine/ExecutionEngine.h>
> #include <llvm/ExecutionEngine/Orc/CompileUtils.h>
> #include <llvm/ExecutionEngine/RuntimeDyld.h>
> #include <llvm/ExecutionEngine/Orc/IRCompileLayer.h>
> #include <llvm/ExecutionEngine/Orc/LambdaResolver.h>
> #include <llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h>
> #include <llvm/IR/DataLayout.h>
> #include <llvm/IR/IRBuilder.h>
> #include <llvm/IR/LegacyPassManager.h>
> #include <llvm/IR/LLVMContext.h>
> #include <llvm/IR/Mangler.h>
> #include <llvm/IR/Module.h>
> #include <llvm/IR/Verifier.h>
> #include <llvm/Support/TargetSelect.h>
> #include <llvm/Target/TargetMachine.h>
> #include <llvm/Transforms/IPO.h>
> #include <llvm/Transforms/IPO/PassManagerBuilder.h>
> #include <llvm/Transforms/Scalar.h>
> #include <llvm/Transforms/Utils/UnifyFunctionExitNodes.h>
>
> template <typename T>
> inline std::vector<T> singletonSet (T t)
> {
>     std::vector<T> Vec;
>     Vec.push_back(std::move(t));
>     return Vec;
> }
>
>
> ///
> /// THIS is the function I want my IR to call
> ///
> extern "C" {
> float sqr (float x) { return x*x; }
> }
>
>
> void
> simple ()
> {
>     llvm::InitializeAllTargets();
>     llvm::InitializeAllTargetMCs();
>     llvm::InitializeAllAsmPrinters();
>     llvm::InitializeAllAsmParsers();
>     llvm::LLVMContext Context;
>     std::unique_ptr<llvm::TargetMachine> TM
> (llvm::EngineBuilder().selectTarget());
>     std::unique_ptr<llvm::DataLayout> DL;
>     DL.reset (new llvm::DataLayout (TM->createDataLayout()));
>     std::unique_ptr<llvm::ExecutionEngine> EE;
>     typedef llvm::orc::ObjectLinkingLayer<> ObjLayerT;
>     typedef llvm::orc::IRCompileLayer<ObjLayerT> CompileLayerT;
>     typedef CompileLayerT::ModuleSetHandleT ModuleHandleT;
>     ObjLayerT Objlayer;
>     CompileLayerT Compilelayer (Objlayer, llvm::orc::SimpleCompiler(*TM));
>     std::unique_ptr<llvm::Module> M (new llvm::Module("module", Context));
>     M->setDataLayout (*DL);
>
>     // Declare stub for external function sqr
>     auto type_float = llvm::Type::getFloatTy (Context);
>     llvm::Type* one_float[] = { type_float };
>     llvm::FunctionType *functype_ff = llvm::FunctionType::get (type_float,
> one_float, false);
>     llvm::Function::Create (functype_ff, llvm::Function::ExternalLinkage,
>                             "sqr", M.get());
>
>     // Create myfunc and generate its IR, which just calls sqr on its
> argument
>     llvm::Function *myfunc = llvm::Function::Create (functype_ff,
>
>  llvm::Function::ExternalLinkage,
>                                                      "myfunc", M.get());
>     llvm::IRBuilder<> builder (Context);
>     auto block = llvm::BasicBlock::Create (Context, "", myfunc);
>     builder.SetInsertPoint (block);
>     llvm::Value *a = llvm::cast<llvm::Value>(myfunc->arg_begin());
>     llvm::Value *asq = builder.CreateCall (M->getFunction ("sqr"), a);
>     builder.CreateRet (asq);
>
>     // Set up compilation
>     if (orc) {
>         auto Resolver = llvm::orc::createLambdaResolver(
>             // External lookup functor
>             [&](const std::string &name) {
>                 if (auto Sym = Compilelayer.findSymbol(name, true))
>                     return llvm::RuntimeDyld::SymbolInfo(Sym.getAddress(),
> Sym.getFlags());
>                 // If not found as a symbol, look up in current process.
>                 // Why doesn't this work?
>                 if (auto Addr =
> llvm::RTDyldMemoryManager::getSymbolAddressInProcess(name))
>                     return llvm::RuntimeDyld::SymbolInfo(Addr,
> llvm::JITSymbolFlags::Exported);
>                 return llvm::RuntimeDyld::SymbolInfo(nullptr);
>             },
>             // Dylib lookup functor
>             [&](const std::string &name) { return nullptr; }
>         );
>         Compilelayer.addModuleSet (singletonSet(std::move(M)),
>
>  llvm::make_unique<llvm::SectionMemoryManager>(),
>                                    std::move(Resolver));
>     } else {
>         // MCJIT
>         std::string engine_errors;
>         llvm::EngineBuilder engine_builder (std::move(M));
>         engine_builder.setEngineKind (llvm::EngineKind::JIT)
>                       .setOptLevel (llvm::CodeGenOpt::Default) //
> Aggressive?
>                       .setErrorStr (&engine_errors);
>         EE.reset (engine_builder.create());
>         EE->finalizeObject ();
>     }
>
>     // Ask for a callable function
>     typedef float (*FuncFloatFloat)(float);
>     FuncFloatFloat my_executable_function = NULL;
>     if (orc) {
>         auto ExprSymbol = Compilelayer.findSymbol ("myfunc", true);
>         my_executable_function = (FuncFloatFloat) ExprSymbol.getAddress ();
>     } else {
>         my_executable_function = (FuncFloatFloat) EE->getFunctionAddress
> ("myfunc");
>     }
>
>     assert (my_executable_function);
>     printf ("myfunc(42.0f) = %g\n", (*my_executable_function)(42.0f));
> }
>
>
>
> --
> Larry Gritz
> lg at larrygritz.com
>
>
> _______________________________________________
> LLVM Developers mailing list
> llvm-dev at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20160518/b3287981/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: larrys_jit.cpp
Type: text/x-c++src
Size: 5249 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20160518/b3287981/attachment-0001.cpp>


More information about the llvm-dev mailing list