llvm interpreter does not find function defined by addGlobalMapping

Hi,

I want to use a function defined in c(++)-code from code generated by llvm. For this I use ExecutionEngine.addGlobalMapping(). I started with the JIT execution engine and everything worked, then I switched to the interpreter engine and it stopped working, but only if compiled on a Linux system. More precisely it works if I use

    llvm 3.8.1 + gcc (Linux) + JIT
    llvm 3.8.0 + mingw-gcc (Windows) + JIT
    llvm 3.8.0 + mingw-gcc (Windows) + Interpreter

But it does not work for llvm 3.8.1 + gcc (Linux) + Interpreter. In that case I get the error message "LLVM ERROR: Tried to execute an unknown external function: testFunction." Am I using ExecutionEngine.addGlobalMapping() in the wrong way, even it works in three out of four scenarios?

#include <llvm/Support/TargetSelect.h>
#include <llvm/Analysis/Passes.h>
#include <llvm/Transforms/Scalar.h>
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
#include <llvm/Transforms/IPO.h>
#include <llvm/IR/Verifier.h>
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/ExecutionEngine/Interpreter.h>
#include <llvm/ExecutionEngine/MCJIT.h>
#include <llvm/ExecutionEngine/GenericValue.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/IR/IRBuilder.h>

extern "C" double testFunction(){
  return 42.0;
}

int main() {
  // initialization needed for JIT
  llvm::InitializeNativeTarget();
  llvm::InitializeNativeTargetAsmPrinter();
  llvm::InitializeNativeTargetAsmParser();

  llvm::LLVMContext &context(llvm::getGlobalContext());
  llvm::Module *pModule = new llvm::Module("a module", context);

  // Here the EngineKind-flag decides if JIT or interpreter is used.
  auto pExecutionEngine = llvm::EngineBuilder(std::unique_ptr<llvm::Module>(pModule)
    ).setEngineKind(llvm::EngineKind::Interpreter).create();
  pModule->setDataLayout(pExecutionEngine->getDataLayout());

  // declaration of the c function.
  std::vector<llvm::Type *> noArgTypes;
  llvm::FunctionType* ft = llvm::FunctionType::get(llvm::Type::getDoubleTy(context),noArgTypes, false);
  auto pFunction = llvm::Function::Create(ft, llvm::Function::ExternalLinkage, "testFunction",pModule);
  pExecutionEngine->addGlobalMapping(pFunction,reinterpret_cast<void*>(&testFunction));

  // generation of llvm code
  auto pBlock = llvm::BasicBlock::Create(context, "evaluation");
  llvm::IRBuilder<> builder(context);
  builder.SetInsertPoint(pBlock);

  // code for call of the c function.
  auto pFunction2 = pModule->getFunction("testFunction");
  auto temp = builder.CreateCall(pFunction2, std::vector<llvm::Value*>(), "calltmp");
  builder.CreateRet(temp);

  // generation of the llvm function calling the c function
  llvm::FunctionType* ftWrapper = llvm::FunctionType::get(llvm::Type::getDoubleTy(context),noArgTypes, false);
  auto pWrapperFunction = llvm::Function::Create(ftWrapper, llvm::Function::ExternalLinkage, "AFunction",pModule);
  pWrapperFunction->getBasicBlockList().push_back(pBlock);

  // calling the generated llvm function
  pExecutionEngine->finalizeObject();
  pExecutionEngine->runFunction(pWrapperFunction,std::vector<llvm::GenericValue>());
}

Thanks for any help,
Markus

Hi Markus,

What happens if you set the data layout on pModule before creating the engine?

  • Lang.

Hi Lang,

What happens if you set the data layout on pModule before creating the
engine?

The output of pExecutionEngine->getDataLayout().getStringRepresentation() is an empty string if I do not set the layout on pModule before creating the interpreter engine. If I set the layout before, the output is the specified layout. But unfortunately, the error is still there.

Markus

Hi Markus,

I don’t know the Interpreter code well (and it’s very poorly maintained) but from a quick inspection it doesn’t seem to be inspecting the global mapping during external function resolution at all. The reason the code works on windows anyway is that (I would guess) it’s an exported symbol in your program, and the Interpreter does look for those. On Linux, where symbols are not exported from the main binary by default, this fails.

So: global mappings do not work in the interpreter at the moment.

Is there any particular reason for you to use the interpreter? Given that it is poorly maintained, I would encourage you to use MCJIT instead (or, even better, ORC: http://llvm.org/docs/tutorial/BuildingAJIT1.html).

Cheers,
Lang.