Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cast a function pointer to a LLVM value so I can call it in my IR?

I am working on a JIT like thing. I have the following code:

Obj doSomething(Obj o, Selector sel){
    ...
}

And I have a pointer to this function, my question is how can I wrap the pointer up into a LLVM:Value so that I can insert it into my IR, using IRBuilder.CreateCall, What do I need to do?

like image 639
Bob Fang Avatar asked Dec 31 '25 09:12

Bob Fang


1 Answers

I was struggling with something similar. My toy program is equivalent to the following C program, but generating the function boo() at run time:

#include <stdio.h>
typedef int (*callback)(const char*);
static int boo(callback print, const char *str) { return print(str); }
int main() { return boo(puts, "hello world"); }

I believe that my solution should work at least with LLVM 9 through 14, based on testing with those two versions.

#include "llvm/ExecutionEngine/Orc/LLJIT.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/raw_ostream.h"

int main()
{
  llvm::InitializeNativeTarget();
  llvm::InitializeNativeTargetAsmPrinter();

  auto C = std::make_unique<llvm::LLVMContext>();
  auto M = std::make_unique<llvm::Module>("boomodule", *C);

  const auto ArgType = llvm::Type::getInt8Ty(*C)->getPointerTo();
  std::vector<llvm::Type *> PutsArgs{ArgType};
  llvm::FunctionType *PutsType =
    llvm::FunctionType::get(llvm::Type::getInt32Ty(*C), PutsArgs, false);
  llvm::FunctionType *FT =
    llvm::FunctionType::get(llvm::Type::getInt32Ty(*C),
                            {PutsType->getPointerTo(), ArgType}, false);

  {
    llvm::Function *TheFunction =
      llvm::Function::Create(FT, llvm::Function::ExternalLinkage,
                             "boo", M.get());
    {
      llvm::IRBuilder<> builder(llvm::BasicBlock::Create(*C, "entry",
                                                         TheFunction));
      auto Arg = TheFunction->arg_begin();
      llvm::FunctionCallee FC{PutsType, Arg++};
      builder.CreateRet(builder.CreateCall(FC, Arg));
    }
    assert(!llvm::verifyFunction(*TheFunction, &llvm::errs()));
    // TheFunction->dump();
  }

  llvm::ExitOnError ExitOnErr;
  auto J = ExitOnErr(llvm::orc::LLJITBuilder().create());
  ExitOnErr(J->addIRModule
            (llvm::orc::ThreadSafeModule(std::move(M), std::move(C))));
  auto BooAddr = ExitOnErr(J->lookup("boo"));
  typedef int (*callback)(const char*);
  auto boo =
    reinterpret_cast<int(*)(callback, const char*)>(BooAddr.getAddress());
  return boo(puts, "hello world");
}
// c++ lljit.cc $(llvm-config --cxxflags --ldflags --system-libs --libs core)

I found some other example where CreateAlloca() was used for storing and loading a function pointer. In my intended application, I will be passing an array of function pointers via a parameter to the generated function.

like image 195
Marko Mäkelä Avatar answered Jan 05 '26 23:01

Marko Mäkelä



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!