[DEBUG]CreateGEP中的索引问题
本来按照LLVM essential书上关于计算内存地址的方法写了一段用GEP
函数获取内存的示例代码
这个代码是用来获取函数参数列表的第二个参数的
FunArgs.push_back ( "a" );
static IRBuilder<> Builder ( Context );
Function* fooFunc = createFunc ( Builder, "foo" );
setFuncArgs ( fooFunc, FunArgs );
Value* Base = fooFunc->arg_begin();
......
Value* gep = getGEP(Builder, Base, ConstantInt::get(Context, APInt(32, 1)));
Value* load = getLoad ( Builder, gep );
getGEP
函数其实是个wrapper
Value *getGEP(IRBuilder<> &Builder, Value *Base, Value *Offset) {
return Builder.CreateGEP(
Builder.getInt32Ty(),
Base, Offset, "a1");
}
但是运行的时候在CreateGEP
的时候出现了类型不匹配的断言错误。
Assertion failed: PointeeType == cast<PointerType> ( Ptr->getType()->getScalarType() )->getElementType(), file C:\Users\pcy19\Desktop\llvm-8.0.1.src\include\llvm/IR/Instructions.h, line 954
然后在解决问题的时候尝试了很多方法思路。
首先我尝试dump不同参数的Type,然后发现Base
(函数参数列表)是<2 x i32>*
类型的,Base->getType()->getScalarType()
得到的是<2 x i32>*
,但是cast<PointerType> ( Base->getType()->getScalarType() )->getElementType()->dump();
得到的居然还是<2 x i32>
.
后来翻到LLVM文档中关于GEP困惑的一篇很好的文章
http://llvm.org/docs/GetElementPtr.html
里面就提到了extra 0 index
的问题,就是
GEP需要额外的Indexing,第一个索引解开第一层指针,这时取出是你的vector数组本身,其余的索引才是索引这个结构体/数组/vector内部具体的元素。
然后我也找到了CreateGEP不同的重载函数,由于需要一个索引列表(表示两次索引),故用如下的重载函数
Value *CreateGEP(Type *Ty, Value *Ptr, ArrayRef<Value *> IdxList,
const Twine &Name = "")
发现这个第三个参数需要ArrayRef<Value *>类型,于是又老老实实地创建了这个ArrayRef
ArrayRef<Value*> listOffset ( {ConstantInt::get ( Context, APInt ( 32, 0 ) ),
ConstantInt::get ( Context, APInt ( 32, 0 ) )
} );
而后去翻ArrayRef的源码,发现它也不过是一个在小量数据时优化过的数组,这么手动创建略显麻烦,直接写入数组也是可以的。(这个主要是在GitHub搜GEP代码时发现的)
然后就去索引把这个本身的vector解引用出来,再获取vector中元素的值,下面的代码在LLVM8.0.1中测试通过。
outs() << "Now create first GEP\n";
Value *vector_self = Builder.CreateGEP(
cast<PointerType>(Base->getType()->getScalarType())->getElementType(),
Base,
{
ConstantInt::get(Context, APInt(32, 0)),
ConstantInt::get(Context, APInt(32, 1)),
},
"a");
outs() << "Now create second GEP\n";
Value *gep = Builder.CreateGEP(Builder.getInt32Ty(), vector_self,
ConstantInt::get(Context, APInt(32, 0)), "a2");
gep->getType()->dump();
Value *load = getLoad(Builder, gep);
Builder.CreateRet(load);
运行结果
Now create first GEP
Now create second GEP
i32* // This is the gep type
; ModuleID = 'my compiler'
source_filename = "my compiler"
define i32 @foo(<2 x i32>* %a) {
entry:
%a1 = getelementptr <2 x i32>, <2 x i32>* %a, i32 0, i32 1
%a2 = getelementptr i32, i32* %a1, i32 0
%load = load i32, i32* %a2
ret i32 %load
}
Something Important
思索良久,发现其实只要修改getGEP这个wrapper就可以了。
现在的做法代码如下,这样动态获取类型,然后只要给offset传入数组即可。
Value* getGEP ( IRBuilder<>& Builder, Value* Base, ArrayRef<Value*>Offset ) {
return Builder.CreateGEP (
cast<PointerType> ( Base->getType()->getScalarType() )->getElementType(),
Base, Offset, "a" );
}
这样调用的时候代码很简洁
Value *gep = getGEP(Builder, Base, {Builder.getInt32(0), Builder.getInt32(1)});
gep = getGEP(Builder, gep, {Builder.getInt32(0)});
Value *load = getLoad(Builder, gep);
补充(非常重要)
第三天又发现重载函数中不需要传入第一个Type参数也是可以的
(这个其实是在翻c1interpreter的源码的时候看到的用法)
例如
Value *gep = Builder.CreateGEP(Base, {Builder.getInt32(0) ,Builder.getInt32(0)});
这样出来的ir就是
; ModuleID = 'my compiler'
source_filename = "my compiler"
define i32 @foo(<2 x i32>* %a) {
entry:
%0 = getelementptr <2 x i32>, <2 x i32>* %a, i32 0, i32 1
%load = load i32, i32* %0
ret i32 %load
}
不必手动指定类型,不仅不用受类型不匹配之苦,还写起来简洁。
murmur
这里非常感谢 @mayuyu 大佬在我困惑整整一天后点拨我,让我有了解决思路。
原书上的API用法可能仅在旧版本有效,但是书上对于vector的元素取法应该是错误的。
整个调试花了整整两天,(虽然有段时间溜达去了),但是查阅了无数资料最后能解决的感觉是挺好的。
书上的多少有些过时,写出来的不兼容(填坑三天了),还是官方文档好。
一个vector的写法
std::vector<Value *> ind_list;
auto Zero = ConstantInt::get(Type::getInt32Ty(Context), 0, true);
auto index = ConstantInt::get(Type::getInt32Ty(Context), 1, true);
ind_list.push_back(Zero);
ind_list.push_back(index);
完整的代码
实现了arg_vector[1]的load
简洁版
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include <vector>
using namespace llvm;
static LLVMContext Context;
static Module *ModuleOb = new Module("my compiler", Context);
static std::vector<std::string> FunArgs;
Function *createFunc(IRBuilder<> &Builder, std::string Name) {
Type *u32Ty = Type::getInt32Ty(Context);
Type *vecTy = VectorType::get(u32Ty, 2);
Type *ptrTy = vecTy->getPointerTo(0);
FunctionType *funcType =
FunctionType::get(Builder.getInt32Ty(), ptrTy, false);
Function *fooFunc =
Function::Create(funcType, Function::ExternalLinkage, Name, ModuleOb);
return fooFunc;
}
void setFuncArgs(Function *fooFunc, std::vector<std::string> FunArgs) {
unsigned Idx = 0;
Function::arg_iterator AI, AE;
for (AI = fooFunc->arg_begin(), AE = fooFunc->arg_end(); AI != AE;
++AI, ++Idx)
AI->setName(FunArgs[Idx]);
}
BasicBlock *createBB(Function *fooFunc, std::string Name) {
return BasicBlock::Create(Context, Name, fooFunc);
}
Value *getLoad(IRBuilder<> &Builder, Value *Address) {
return Builder.CreateLoad(Address, "load");
}
int main(int argc, char *argv[]) {
FunArgs.push_back("a");
static IRBuilder<> Builder(Context);
Function *fooFunc = createFunc(Builder, "foo");
setFuncArgs(fooFunc, FunArgs);
Value *Base = fooFunc->arg_begin();
BasicBlock *entry = createBB(fooFunc, "entry");
Builder.SetInsertPoint(entry);
Value *gep = Builder.CreateGEP(Base, {Builder.getInt32(0) ,Builder.getInt32(0)});
Value *load = getLoad(Builder, gep);
Builder.CreateRet(load);
verifyFunction(*fooFunc);
ModuleOb->dump();
return 0;
}
原始版
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include <vector>
using namespace llvm;
static LLVMContext Context;
static Module *ModuleOb = new Module("my compiler", Context);
static std::vector<std::string> FunArgs;
Function *createFunc(IRBuilder<> &Builder, std::string Name) {
Type *u32Ty = Type::getInt32Ty(Context);
Type *vecTy = VectorType::get(u32Ty, 2);
Type *ptrTy = vecTy->getPointerTo(0);
FunctionType *funcType =
FunctionType::get(Builder.getInt32Ty(), ptrTy, false);
Function *fooFunc =
Function::Create(funcType, Function::ExternalLinkage, Name, ModuleOb);
return fooFunc;
}
void setFuncArgs(Function *fooFunc, std::vector<std::string> FunArgs) {
unsigned Idx = 0;
Function::arg_iterator AI, AE;
for (AI = fooFunc->arg_begin(), AE = fooFunc->arg_end(); AI != AE;
++AI, ++Idx)
AI->setName(FunArgs[Idx]);
}
BasicBlock *createBB(Function *fooFunc, std::string Name) {
return BasicBlock::Create(Context, Name, fooFunc);
}
Value *getLoad(IRBuilder<> &Builder, Value *Address) {
return Builder.CreateLoad(Address, "load");
}
int main(int argc, char *argv[]) {
FunArgs.push_back("a");
static IRBuilder<> Builder(Context);
Function *fooFunc = createFunc(Builder, "foo");
setFuncArgs(fooFunc, FunArgs);
Value *Base = fooFunc->arg_begin();
BasicBlock *entry = createBB(fooFunc, "entry");
Builder.SetInsertPoint(entry);
Value *vector_self = Builder.CreateGEP(
cast<PointerType>(Base->getType()->getScalarType())->getElementType(),
Base,
{
ConstantInt::get(Context, APInt(32, 0)),
ConstantInt::get(Context, APInt(32, 1)),
},
"a1");
Value *gep = Builder.CreateGEP(Builder.getInt32Ty(), vector_self,
ConstantInt::get(Context, APInt(32, 0)), "a2");
Value *load = getLoad(Builder, gep);
Builder.CreateRet(load);
verifyFunction(*fooFunc);
ModuleOb->dump();
return 0;
}