将对象文件添加到 JIT 并从 IR 代码调用它

问题描述 投票:0回答:2

我创建了

HowToUseJIT.cpp
的修改版本(llvm 版本 11.x),它使用
IRBuilder
类来构建一个调用共享对象文件中定义的外部函数。

当外部具有

int
参数和返回值时,此示例工作正常(在我的系统上),但当参数和返回值是
double
时,它会失败。

int 案例的来源如下。此外,源代码顶部有说明,用于将其转换为双壳。

这个例子的双重版本有什么问题?

/*
This file is a modified version of the llvm 11.x example HowToUseJIT.cpp:

The file callee.c contains the following text:
    int callee(int arg)
    {   return arg + 1; }

The shared library callee.so is created from callee.c as follows:
    clang -shared callee.c -o callee.so

This example calls the funciton callee from a function that is generated using 
the IRBuilder class. It links callee by loading callee.so into its LLJIT.

This works on my sytesm where the progam output is
    add1(42) = 43
which is correct.

If I change the type of the function callee from "int (*)(int)" to 
"double (*)(double)", the program output is
    add1(42) = 4.200000e+01
which is incorrect.

I use following command to change callee.c so that it uses double:
    sed -i callee.c \
        -e 's|int callee(int arg)|double callee(double arg)|' \
        -e 's|return arg + 1;|return arg + 1.0;|'

I use the following command to change this file so that it should porperly 
link to the double version of callee:
    sed -i add_obj2jit.cpp \
        -e '30,$s|"int"|"double"|' \
        -e '30,$s|getInt32Ty|getDoubleTy|g' \
        -e '/getAddress/s|int|double|g' \
        -e 's|int Result = Add1(42);|double Result = Add1(42.0);|

What is wrong with the double version of this example ?
*/
#include "llvm/ExecutionEngine/Orc/LLJIT.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/raw_ostream.h"

using namespace llvm;
using namespace llvm::orc;

ExitOnError ExitOnErr;

// --------------------------------------------------------------------------
void add_obj2jit(LLJIT* jit, const std::string filename)
{   // load object file into memory_buffer
    ErrorOr< std::unique_ptr<MemoryBuffer> > error_or_buffer =
        MemoryBuffer::getFile(filename);
    std::error_code std_error_code = error_or_buffer.getError();
    if( std_error_code )
    {   std::string msg = "add_obj2jit: " + filename + "\n";
        msg            += std_error_code.message();   
        std::fprintf(stderr, "%s\n", msg.c_str() );
        std::exit( std_error_code.value() );
    }
    std::unique_ptr<MemoryBuffer> memory_buffer( 
        std::move( error_or_buffer.get() )
    );
    // move object file into jit
    Error error = jit->addObjectFile( std::move(memory_buffer) );
    if( error )
    {   std::fprintf(stderr, "Can't load object file %s", filename.c_str());
        std::exit(1);
    }
}
// --------------------------------------------------------------------------
ThreadSafeModule createDemoModule() {
  auto Context = std::make_unique<LLVMContext>();
  auto M = std::make_unique<Module>("test", *Context);

  // functiont_t
  // function has a return type of "int" and take an argument of "int".
  FunctionType* function_t = FunctionType::get(
    Type::getInt32Ty(*Context), {Type::getInt32Ty(*Context)}, false
  );

  // declare the callee function
  AttributeList empty_attributes;
  FunctionCallee callee = M->getOrInsertFunction(
    "callee", function_t, empty_attributes
  );

  // Create the add1 function entry and insert this entry into module M.  
  Function *Add1F = Function::Create(
    function_t, Function::ExternalLinkage, "add1", M.get()
  );

  // Add a basic block to the function. As before, it automatically inserts
  // because of the last argument.
  BasicBlock *BB = BasicBlock::Create(*Context, "EntryBlock", Add1F);

  // Create a basic block builder with default parameters.  The builder will
  // automatically append instructions to the basic block `BB'.
  IRBuilder<> builder(BB);

  // Get pointers to the integer argument of the add1 function...
  assert(Add1F->arg_begin() +1 == Add1F->arg_end()); // Make sure there's an arg
  Argument *ArgX = &*Add1F->arg_begin();          // Get the arg
  ArgX->setName("AnArg"); // Give it a nice symbolic name for fun.

  // Create the call instruction, inserting it into the end of BB.
  Value *Add = builder.CreateCall( callee, {ArgX}, "Add=callee(ArgX)" );

  // Create the return instruction and add it to the basic block
  builder.CreateRet(Add);

  return ThreadSafeModule(std::move(M), std::move(Context));
}
// --------------------------------------------------------------------------
int main(int argc, char *argv[]) {
  // Initialize LLVM.
  InitLLVM X(argc, argv);

  InitializeNativeTarget();
  InitializeNativeTargetAsmPrinter();

  cl::ParseCommandLineOptions(argc, argv, "add_obj2jit");
  ExitOnErr.setBanner(std::string(argv[0]) + ": ");

  // Create an LLJIT instance.
  auto J = ExitOnErr(LLJITBuilder().create());
  auto M = createDemoModule();

  ExitOnErr(J->addIRModule(std::move(M)));

  add_obj2jit(J.get(), "callee.so");

  // Look up the JIT'd function, cast it to a function pointer, then call it.
  auto Add1Sym = ExitOnErr(J->lookup("add1"));
  int (*Add1)(int) = (int (*)(int))Add1Sym.getAddress();

  int Result = Add1(42);
  outs() << "add1(42) = " << Result << "\n";

  // return error number
  if( Result != 43 )
    return 1;
  return 0;
}
c++ jit llvm-ir
2个回答
0
投票

安德里亚:

感谢您要求查看红外输出。更改示例代码行

   // llvm::outs() << *M;

到线

   lvm::outs() << *M;

生成此输出。

查看输出,我很清楚第二个 sed 命令失败了。 这是因为它最后缺少一个单引号。

当我解决这个问题时,双壳就起作用了。以下是

int
情况的输出,包括 IR:

; ModuleID = 'test'
source_filename = "test"

declare i32 @callee(i32)

define i32 @add1(i32 %AnArg) {
EntryBlock:
  %0 = call i32 @callee(i32 %AnArg)
  ret i32 %0
}
add1(42) = 43

这是

double
情况的输出:

; ModuleID = 'test'
source_filename = "test"

declare double @callee(double)

define double @add1(double %AnArg) {
EntryBlock:
  %0 = call double @callee(double %AnArg)
  ret double %0
}
add1(42) = 4.300000e+01

0
投票

代码显示您加载了动态库

"callee.so"
而不是目标文件。

加载动态库,使用此API代替调用

add_obj2jit
:

J->loadPlatformDynamicLibrary("callee.so");
© www.soinside.com 2019 - 2024. All rights reserved.