Master LLVM GEP
主要参考<< LLVM Essentials>> 结果发现这书内容太老了,无法编译。
然后某种情况下写对了,各种崩溃。
丝滑解决:
1. 先从C来看起来
如果一个函数参数是指针意味着下面:
#include <iostream> struct Foo{ int f{0}; }; void access_pointer(Foo *foo){ std::cout << &(foo->f) << std::endl; std::cout << &(foo[0].f) << std::endl; // 这个魔幻的0,很关键,也就是你的array就是一个单独的平整的一个维度,这个永远是0. } int main() { Foo *foo = new Foo; foo->f = 100; access_pointer(foo); return 0; }
00000188B8096AA0
00000188B8096AA0
函数参数意味着:
*foo 代表有可能是个数组(LLVM就要用这个方法来调用,也就是作为数组 &(foo[0].f)这个方法来操作。 )
*foo 也可能就是一个单纯的Foo类型值指针
2. 需要2个参数的原因
这是一段c代码,来自官方:
struct munger_struct { int f1; int f2; }; void munge(struct munger_struct *P) { // 注意这里传进来的p是个数组,所以比如先要解开数组!!!!即P[idx] P[0].f1 = P[1].f1 + P[2].f2; } ... struct munger_struct Array[3]; ... munge(Array);
翻译成LLVM:
define void @munge(ptr %P) { entry: %tmp = getelementptr %struct.munger_struct, ptr %P, i32 1, i32 0 ; 访问数组P的: P[1]的0,也就是P[1].f1 的地址 %tmp1 = load i32, ptr %tmp ; int tmp1 = P[1].f1; %tmp2 = getelementptr %struct.munger_struct, ptr %P, i32 2, i32 1 ; 访问数组P的: P[2]的1,也就是P[2].f2 的地址 %tmp3 = load i32, ptr %tmp2 ; int tmp3 = P[2].f2; %tmp4 = add i32 %tmp3, %tmp1 ; int tmp4 = tmp3 + tmp1; %tmp5 = getelementptr %struct.munger_struct, ptr %P, i32 0, i32 0 ; 访问数组P的: P[0]的0,也就是P[0].f1 的地址 store i32 %tmp4, ptr %tmp5 ; P[0].f1 = tmp4; ret void }
所以2个参数第一个用来确定一块内存的ID,第二个参数用来偏移其中的位置,精确到一个元素上。
3. LLVM 获取一个函数array参数的 "index=2" 的元素
实现C代码类似:
int test(int *array){ return arry[2]; }
对应的LLVM:
define i32 @foo(<3 x i32>* %array) { entry: %a_0 = getelementptr <3 x i32>, <3 x i32>* %array, i32 0, i32 2 ; 这里是关键,第一个参数是解第一层数组,然后出来才是真正的array,然后接着第二个参数就是2,也就是访问数组参数array[2](即数组第三个元素) %ret = load i32, i32* %a_0, align 4 ret i32 %ret }
Source:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <llvm/IR/IRBuilder.h> #include <llvm/IR/LLVMContext.h> #include <llvm/IR/Module.h> #include "llvm/ExecutionEngine/Orc/LLJIT.h" #include <llvm/ExecutionEngine/ExecutionEngine.h> #include "llvm/Support/InitLLVM.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/raw_ostream.h" llvm::Value *getArg(llvm::Function *function, int idx){ return function->getArg(idx); } void setArgName(llvm::Function *func, const std::string &name, int idx){ auto *arg = func->getArg(idx); arg->setName(name); } llvm::BasicBlock *createBlock(llvm::Function *func, const std::string &blockName){ return llvm::BasicBlock::Create( func->getContext(),blockName, func); } auto *createFunction(llvm::IRBuilder<> &builder, const std::string &funcName, llvm::Module &m){ llvm::Function *ret{nullptr}; auto *array_type= builder.getInt32Ty(); auto *vec_type = llvm::VectorType::get(array_type, 3,false); auto *ptr = vec_type->getPointerTo(0); auto *funcType = llvm::FunctionType::get(builder.getInt32Ty(), ptr, false); ret = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, funcName, m); setArgName(ret, "array", 0 ); return ret; } int main(int argc, char **argv) { llvm::ExitOnError ExitOnErr; llvm::InitLLVM give_me_a_name(argc,argv); llvm::InitializeNativeTarget(); llvm::InitializeNativeTargetAsmPrinter(); auto context = std::make_unique<llvm::LLVMContext>(); auto mod = std::make_unique<llvm::Module>("test foo module", *context); auto &contextRef = *context; auto &modRef = *mod; llvm::IRBuilder<> builder(contextRef); auto func = createFunction(builder, "foo", modRef); auto *entry_block = createBlock(func, "entry"); builder.SetInsertPoint(entry_block); llvm::Value *base = getArg(func, 0); base->getType()->dump(); base->getType()->getScalarType()->dump(); base->getType()->getScalarType()->getNonOpaquePointerElementType()->dump(); std::vector<llvm::Value*> indices(2); indices[0] = builder.getInt32(0); indices[1] = builder.getInt32(0); llvm::Value* indexList[2] = {builder.getInt32(0), builder.getInt32(2)}; //builder.CreateAlloca(builder.getInt32Ty(), builder.getInt32(5), "test_array"); auto *gep_0 = builder.CreateGEP(base->getType()->getScalarType()->getNonOpaquePointerElementType(), base, indexList, "a_0" ); auto *ret = builder.CreateLoad(builder.getInt32Ty(), gep_0, "ret"); builder.CreateRet(ret); mod->dump(); // create execution engine auto J = ExitOnErr(llvm::orc::LLJITBuilder().create()); auto threadSafeMod = llvm::orc::ThreadSafeModule(std::move(mod), std::move(context)); J->addIRModule(std::move(threadSafeMod)); int arr[] = {24, 12, 32}; // Look up the JIT'd function, cast it to a function pointer, then call it. auto addSymbol = ExitOnErr(J->lookup("foo")); auto fooFunc= (int (*)(int*))addSymbol.getAddress(); int Result1 = fooFunc(arr); std::cout << Result1 << std::endl; return 0; }
int arr[] = {24, 12, 32}; // Look up the JIT'd function, cast it to a function pointer, then call it. auto addSymbol = ExitOnErr(J->lookup("foo")); auto fooFunc= (int (*)(int*))addSymbol.getAddress(); int Result1 = fooFunc(arr); std::cout << Result1 << std::endl;
可以看到console输出了32;
关键就是这一句:
auto *gep_0 = builder.CreateGEP(base->getType()->getScalarType()->getNonOpaquePointerElementType(), base, indexList, "a_0" );
llvm::Value *base = getArg(func, 0); base->getType()->dump(); // 输出<3 x i32>* base->getType()->getScalarType()->dump(); // 输出 <3 x i32>* base->getType()->getScalarType()->getNonOpaquePointerElementType()->dump(); // 只有这个是输出<3 x i32>
LLVM getelementptr:看第一个参数类型必须是array里的确定类型才可以。
%a_0 = getelementptr <3 x i32>, <3 x i32>* %array, i32 0, i32 2 ;
4. 数组求和:
对应的C代码:
int foo(int *array){ int ret=0; for(int i=0;i < 3;i++){ ret = ret + array[i]; } return ret; }
对应的LLVM:
; ModuleID = 'test foo module' source_filename = "test foo module" define i32 @foo(<3 x i32>* %array) { entry: %ret_alloc = alloca i32, align 4 store i32 0, i32* %ret_alloc, align 4 ; 相当于int ret = 0; br label %loop loop: ; preds = %loop_body, %entry %phi_i = phi i32 [ 0, %entry ], [ %next_val, %loop_body ] %cmp = icmp slt i32 %phi_i, 3 br i1 %cmp, label %loop_body, label %after_loop loop_body: ; preds = %loop %next_val = add i32 %phi_i, 1 %gep_addr = getelementptr <3 x i32>, <3 x i32>* %array, i32 0, i32 %phi_i %gep_load = load i32, i32* %gep_addr, align 4 %ret_temp_load_value = load i32, i32* %ret_alloc, align 4 %added = add i32 %ret_temp_load_value, %gep_load store i32 %added, i32* %ret_alloc, align 4 br label %loop after_loop: ; preds = %loop %ret = load i32, i32* %ret_alloc, align 4 ret i32 %ret }
source:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <llvm/IR/IRBuilder.h> #include <llvm/IR/LLVMContext.h> #include <llvm/IR/Module.h> #include "llvm/ExecutionEngine/Orc/LLJIT.h" #include <llvm/ExecutionEngine/ExecutionEngine.h> #include "llvm/Support/InitLLVM.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/raw_ostream.h" constexpr int array_size = 3; llvm::Value *getArg(llvm::Function *function, int idx){ return function->getArg(idx); } void setArgName(llvm::Function *func, const std::string &name, int idx){ auto *arg = func->getArg(idx); arg->setName(name); } llvm::BasicBlock *createBlock(llvm::Function *func, const std::string &blockName){ return llvm::BasicBlock::Create( func->getContext(),blockName, func); } auto *createFunction(llvm::IRBuilder<> &builder, const std::string &funcName, llvm::Module &m){ llvm::Function *ret{nullptr}; auto *array_type= builder.getInt32Ty(); auto *vec_type = llvm::VectorType::get(array_type, array_size,false); auto *ptr = vec_type->getPointerTo(0); auto *funcType = llvm::FunctionType::get(builder.getInt32Ty(), ptr, false); ret = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, funcName, m); setArgName(ret, "array", 0 ); return ret; } int main(int argc, char **argv) { llvm::ExitOnError ExitOnErr; llvm::InitLLVM give_me_a_name(argc,argv); llvm::InitializeNativeTarget(); llvm::InitializeNativeTargetAsmPrinter(); auto context = std::make_unique<llvm::LLVMContext>(); auto mod = std::make_unique<llvm::Module>("test foo module", *context); auto &contextRef = *context; auto &modRef = *mod; llvm::IRBuilder<> builder(contextRef); auto func = createFunction(builder, "foo", modRef); auto *entry_block = createBlock(func, "entry"); auto *loop_block = createBlock(func, "loop"); auto *loop_body_block = createBlock(func, "loop_body"); auto *after_loop_block = createBlock(func, "after_loop"); builder.SetInsertPoint(entry_block); llvm::Value *base = getArg(func, 0); base->getType()->dump(); base->getType()->getScalarType()->dump(); base->getType()->getScalarType()->getNonOpaquePointerElementType()->dump(); auto *ret_alloc = builder.CreateAlloca(builder.getInt32Ty(),nullptr, "ret_alloc"); builder.CreateStore(builder.getInt32(0), ret_alloc); builder.CreateBr(loop_block); // loop block builder.SetInsertPoint(loop_block); auto *phi_i = builder.CreatePHI(builder.getInt32Ty(), 2, "phi_i"); phi_i->addIncoming(builder.getInt32(0), entry_block); auto *cmp = builder.CreateICmpSLT(phi_i, builder.getInt32(array_size ) , "cmp"); // i < 3 builder.CreateCondBr(cmp, loop_body_block, after_loop_block); // loop_body builder.SetInsertPoint(loop_body_block); auto *next_value = builder.CreateAdd(phi_i, builder.getInt32(1), "next_val"); phi_i->addIncoming(next_value, loop_body_block); llvm::Value* indices[] = {builder.getInt32(0), phi_i}; auto *gep = builder.CreateGEP(base->getType()->getScalarType()->getNonOpaquePointerElementType(), base, indices, "gep_addr" ); auto *gep_value = builder.CreateLoad(builder.getInt32Ty(), gep, "gep_load"); auto *ret_temp_load_value = builder.CreateLoad(builder.getInt32Ty(),ret_alloc, "ret_temp_load_value"); auto *added = builder.CreateAdd(ret_temp_load_value, gep_value, "added"); builder.CreateStore(added,ret_alloc); builder.CreateBr(loop_block); // after loop body builder.SetInsertPoint(after_loop_block); builder.CreateRet(builder.CreateLoad(builder.getInt32Ty(),ret_alloc, "ret")); mod->dump(); // create execution engine auto J = ExitOnErr(llvm::orc::LLJITBuilder().create()); auto threadSafeMod = llvm::orc::ThreadSafeModule(std::move(mod), std::move(context)); J->addIRModule(std::move(threadSafeMod)); int arr[] = {1, 2, 3}; // Look up the JIT'd function, cast it to a function pointer, then call it. auto addSymbol = ExitOnErr(J->lookup("foo")); auto fooFunc= (int (*)(int*))addSymbol.getAddress(); int Result1 = fooFunc(arr); std::cout << Result1 << std::endl; return 0; }
关键就看这一句:
%gep_addr = getelementptr <3 x i32>, <3 x i32>* %array, i32 0, i32 %phi_i
第一个参数一定是<3 x i32>。
5. 数组求和2 CreateGEP使用一个参数+extractelement指令
有必要看下CreateGEP中使用一个值的偏移(off)情况,顺便配合extractelement实现一样的求和
注意这里我使用LLVM git 版本是12,有个函数是改了,上面的都是LLVM14
base->getType()->getScalarType()->getNonOpaquePointerElementType()
改成了:
base->getType()->getPointerElementType()->dump();
看看两个参数和一个参数 区别:
llvm::Value *gep = builder.CreateGEP( base, {builder.getInt32(0), builder.getInt32(0)}, "gep_addr" ); gep->getType()->dump(); // i32* auto *load1 = builder.CreateLoad(gep, "load_grep"); load1->getType()->dump(); // i32 llvm::Value *gep2 = builder.CreateGEP( base, builder.getInt32(0), "gep_addr" ); // 注意这里,只有一个参数。 gep2->getType()->dump(); // <3 x i32> * // 指向数组。 auto *load2 = builder.CreateLoad(gep2, "load_grep"); load2->getType()->dump(); // <3 x i32> // 注意,这个时候load2是数组。
非常符合预期。相当于指向数组。
接着对load2 来一次extract element 指令就可以得到<3 x i32>元素的值
auto *element = builder.CreateExtractElement(load2, phi_i, "extract");
源码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <llvm/IR/IRBuilder.h> #include <llvm/IR/LLVMContext.h> #include <llvm/IR/Module.h> #include "llvm/ExecutionEngine/Orc/LLJIT.h" #include <llvm/ExecutionEngine/ExecutionEngine.h> #include "llvm/Support/InitLLVM.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/raw_ostream.h" constexpr int array_size = 3; llvm::Value *getArg(llvm::Function *function, int idx){ return function->getArg(idx); } void setArgName(llvm::Function *func, const std::string &name, int idx){ auto *arg = func->getArg(idx); arg->setName(name); } llvm::BasicBlock *createBlock(llvm::Function *func, const std::string &blockName){ return llvm::BasicBlock::Create( func->getContext(),blockName, func); } auto *createFunction(llvm::IRBuilder<> &builder, const std::string &funcName, llvm::Module &m){ llvm::Function *ret{nullptr}; auto *array_type= builder.getInt32Ty(); auto *vec_type = llvm::VectorType::get(array_type, array_size, false); auto *ptr = vec_type->getPointerTo(0); auto *funcType = llvm::FunctionType::get(builder.getInt32Ty(), ptr, false); ret = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, funcName, m); setArgName(ret, "array", 0 ); return ret; } int main(int argc, char **argv) { llvm::ExitOnError ExitOnErr; llvm::InitLLVM give_me_a_name(argc,argv); llvm::InitializeNativeTarget(); llvm::InitializeNativeTargetAsmPrinter(); auto context = std::make_unique<llvm::LLVMContext>(); auto mod = std::make_unique<llvm::Module>("test foo module", *context); auto &contextRef = *context; auto &modRef = *mod; llvm::IRBuilder<> builder(contextRef); auto func = createFunction(builder, "foo", modRef); auto *entry_block = createBlock(func, "entry"); auto *loop_block = createBlock(func, "loop"); auto *loop_body_block = createBlock(func, "loop_body"); auto *after_loop_block = createBlock(func, "after_loop"); builder.SetInsertPoint(entry_block); llvm::Value *base = getArg(func, 0); base->getType()->dump(); base->getType()->getScalarType()->dump(); base->getType()->getPointerElementType()->dump(); auto *ret_alloc = builder.CreateAlloca(builder.getInt32Ty(),nullptr, "ret_alloc"); builder.CreateStore(builder.getInt32(0), ret_alloc); builder.CreateBr(loop_block); // loop block builder.SetInsertPoint(loop_block); auto *phi_i = builder.CreatePHI(builder.getInt32Ty(), 2, "phi_i"); phi_i->addIncoming(builder.getInt32(0), entry_block); auto *cmp = builder.CreateICmpSLT(phi_i, builder.getInt32(array_size ) , "cmp"); // i < 3 builder.CreateCondBr(cmp, loop_body_block, after_loop_block); // loop_body builder.SetInsertPoint(loop_body_block); auto *next_value = builder.CreateAdd(phi_i, builder.getInt32(1), "next_val"); phi_i->addIncoming(next_value, loop_body_block); llvm::Value* indices[] = {builder.getInt32(0), phi_i}; // base->getType()->getPointerElementType()->dump(); /* llvm::Value *gep = builder.CreateGEP( base, {builder.getInt32(0), builder.getInt32(0)}, "gep_addr" ); gep->getType()->dump(); // i32* auto *load1 = builder.CreateLoad(gep, "load_grep"); load1->getType()->dump(); // i32 */ llvm::Value *gep2 = builder.CreateGEP( base, builder.getInt32(0), "gep_addr" ); gep2->getType()->dump(); // <3 x i32> * auto *load2 = builder.CreateLoad(gep2, "load_grep"); load2->getType()->dump(); // <3 x i32> auto *element = builder.CreateExtractElement(load2, phi_i, "extract"); auto *ret_temp_load_value = builder.CreateLoad(builder.getInt32Ty(), ret_alloc, "ret_temp_load_value"); auto *added = builder.CreateAdd(ret_temp_load_value, element, "added"); builder.CreateStore(added,ret_alloc); builder.CreateBr(loop_block); // after loop body builder.SetInsertPoint(after_loop_block); builder.CreateRet(builder.CreateLoad(builder.getInt32Ty(),ret_alloc, "ret")); /* USE GEP WITH indices ARG{} auto *gep = builder.CreateGEP(base->getType()->getPointerElementType(), base, indices, "gep_addr" ); auto *gep_value = builder.CreateLoad(builder.getInt32Ty(), gep, "gep_load"); auto *ret_temp_load_value = builder.CreateLoad(builder.getInt32Ty(),ret_alloc, "ret_temp_load_value"); auto *added = builder.CreateAdd(ret_temp_load_value, gep_value, "added"); builder.CreateStore(added,ret_alloc); builder.CreateBr(loop_block); // after loop body builder.SetInsertPoint(after_loop_block); builder.CreateRet(builder.CreateLoad(builder.getInt32Ty(),ret_alloc, "ret"));*/ mod->dump(); // create execution engine auto J = ExitOnErr(llvm::orc::LLJITBuilder().create()); auto threadSafeMod = llvm::orc::ThreadSafeModule(std::move(mod), std::move(context)); J->addIRModule(std::move(threadSafeMod)); int arr[] = {1, 2, 3}; // Look up the JIT'd function, cast it to a function pointer, then call it. auto addSymbol = ExitOnErr(J->lookup("foo")); auto fooFunc= (int (*)(int*))addSymbol.getAddress(); int Result1 = fooFunc(arr); std::cout << Result1 << std::endl; std::cin.get(); return 0; }
6:数组求和3 LLVM15 API 又尼玛的改了
下面是用LLVM15, LLVM15 的API我感觉是正常人可用了。
注意看LLVM 14 IR函数参数是
define i32 @foo(<3 x i32>* %array) { }
然后我手动把c->LLVM IR 试了试
extern "C" int foo(int array[]){ // 参数也可以是int * return array[2]; } int main(){ int a[3] = {100,200,300}; foo(a); return 0; }
clang -emit-llvm -c -S const_array.cpp
@__const.main.a = private unnamed_addr constant [3 x i32] [i32 100, i32 200, i32 300], align 4 ; Function Attrs: mustprogress noinline nounwind optnone uwtable define dso_local i32 @foo(ptr noundef %0) #0 { // 注意这个参数。是ptr %var,以前是<3 x i32>* %array %2 = alloca ptr, align 8 store ptr %0, ptr %2, align 8 %3 = load ptr, ptr %2, align 8 %4 = getelementptr inbounds i32, ptr %3, i64 2 // 注意这里,直接就是getelementptr, indices其实就是一个了,也就是符合C语言这个风格。 %5 = load i32, ptr %4, align 4 ret i32 %5 } ; Function Attrs: mustprogress noinline norecurse nounwind optnone uwtable define dso_local noundef i32 @main() #1 { %1 = alloca i32, align 4 %2 = alloca [3 x i32], align 4 store i32 0, ptr %1, align 4 call void @llvm.memcpy.p0.p0.i64(ptr align 4 %2, ptr align 4 @__const.main.a, i64 12, i1 false) %3 = getelementptr inbounds [3 x i32], ptr %2, i64 0, i64 0 %4 = call i32 @foo(ptr noundef %3) ret i32 0 }
可以看到参数完全变了,这个时候其实请好。
所以 对于我这边的源码改了:
全部源码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <llvm/IR/IRBuilder.h> #include <llvm/IR/LLVMContext.h> #include <llvm/IR/Module.h> #include "llvm/ExecutionEngine/Orc/LLJIT.h" #include <llvm/ExecutionEngine/ExecutionEngine.h> #include "llvm/Support/InitLLVM.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/raw_ostream.h" constexpr int array_size = 3; llvm::Value *getArg(llvm::Function *function, int idx){ return function->getArg(idx); } void setArgName(llvm::Function *func, const std::string &name, int idx){ auto *arg = func->getArg(idx); arg->setName(name); } llvm::BasicBlock *createBlock(llvm::Function *func, const std::string &blockName){ return llvm::BasicBlock::Create( func->getContext(),blockName, func); } auto *createFunction(llvm::IRBuilder<> &builder, const std::string &funcName, llvm::Module &m){ llvm::Function *ret{nullptr}; auto *array_type= builder.getInt32Ty(); auto *vec_type = llvm::VectorType::get(array_type, array_size, false); auto *ptr = vec_type->getPointerTo(0); auto *funcType = llvm::FunctionType::get(builder.getInt32Ty(), ptr, false); ret = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, funcName, m); setArgName(ret, "array", 0 ); return ret; } int main(int argc, char **argv) { llvm::ExitOnError ExitOnErr; llvm::InitLLVM give_me_a_name(argc,argv); llvm::InitializeNativeTarget(); llvm::InitializeNativeTargetAsmPrinter(); auto context = std::make_unique<llvm::LLVMContext>(); auto mod = std::make_unique<llvm::Module>("test foo module", *context); auto &contextRef = *context; auto &modRef = *mod; llvm::IRBuilder<> builder(contextRef); auto func = createFunction(builder, "foo", modRef); auto *entry_block = createBlock(func, "entry"); auto *loop_block = createBlock(func, "loop"); auto *loop_body_block = createBlock(func, "loop_body"); auto *after_loop_block = createBlock(func, "after_loop"); builder.SetInsertPoint(entry_block); llvm::Value *base = getArg(func, 0); base->getType()->dump(); base->getType()->getScalarType()->dump(); base->dump(); llvm::PointerType::get(builder.getInt32Ty(), base->getType()->getPointerAddressSpace())->dump(); auto *ret_alloc = builder.CreateAlloca(builder.getInt32Ty(),nullptr, "ret_alloc"); builder.CreateStore(builder.getInt32(0), ret_alloc); builder.CreateBr(loop_block); // loop block builder.SetInsertPoint(loop_block); auto *phi_i = builder.CreatePHI(builder.getInt32Ty(), 2, "phi_i"); phi_i->addIncoming(builder.getInt32(0), entry_block); auto *cmp = builder.CreateICmpSLT(phi_i, builder.getInt32(array_size ) , "cmp"); // i < 3 builder.CreateCondBr(cmp, loop_body_block, after_loop_block); // loop_body builder.SetInsertPoint(loop_body_block); auto *next_value = builder.CreateAdd(phi_i, builder.getInt32(1), "next_val"); phi_i->addIncoming(next_value, loop_body_block); // this is works //auto *vec_type = llvm::VectorType::get(builder.getInt32Ty(), array_size, false); //vec_type->dump(); //builder.CreateGEP(vec_type, base, {builder.getInt32(0), builder.getInt32(0)}, "gep_addr" ); auto *gep = builder.CreateGEP(builder.getInt32Ty(), base, phi_i, "gep_addr" ); gep->getType()->dump(); auto *load = builder.CreateLoad(builder.getInt32Ty(), gep, "load_grep"); auto *ret_temp_load_value = builder.CreateLoad(builder.getInt32Ty(), ret_alloc, "ret_temp_load_value"); auto *added = builder.CreateAdd(ret_temp_load_value, load, "added"); builder.CreateStore(added,ret_alloc); builder.CreateBr(loop_block); // after loop body builder.SetInsertPoint(after_loop_block); builder.CreateRet(builder.CreateLoad(builder.getInt32Ty(),ret_alloc, "ret")); mod->dump(); // create execution engine auto J = ExitOnErr(llvm::orc::LLJITBuilder().create()); auto threadSafeMod = llvm::orc::ThreadSafeModule(std::move(mod), std::move(context)); J->addIRModule(std::move(threadSafeMod)); int arr[] = {1, 2, 3}; // Look up the JIT'd function, cast it to a function pointer, then call it. auto addSymbol = ExitOnErr(J->lookup("foo")); auto fooFunc = addSymbol.toPtr<int(int*)>(); //auto fooFunc= (int (*)(int*))addSymbol.getAddress(); int Result1 = fooFunc(arr); std::cout << Result1 << std::endl; std::cin.get(); return 0; }
参考:
https://mukulrathi.com/create-your-own-programming-language/llvm-ir-cpp-api-tutorial/