GEP 分段错误 LLVM C++ API
GEP segmentation fault LLVM C++ API
我确定这真的很简单,但是,我已经尝试了一个多小时,但我无法弄清楚。
下面的代码给我一个分段错误:
Value *newArray = mBuilder.CreateGEP(alloca, value); // alloca is a `StructType`
但这不
Value *newArray = mBuilder.CreateGEP(alloca, ConstantInt::get(mContext, APInt(32, 0)));
value
的值
%bar1 = load double, double* %bar
%3 = fptoui double %bar1 to i32
调试
当我使用 lldb 调试它时,我得到:
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
frame #0: 0x00000001000b9e6e a.out`llvm::PointerType::get(llvm::Type*, unsigned int) + 20
a.out`llvm::PointerType::get:
-> 0x1000b9e6e <+20>: movq (%rdi), %rax
问题
为什么会出现分段错误,我该如何解决?
如何重现问题?
以下代码重现了问题:
#include <vector>
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/IR/Value.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/APInt.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"
using namespace llvm;
static LLVMContext mContext;
static IRBuilder<> mBuilder(mContext);
static std::unique_ptr<Module> mModule = make_unique<Module>("example", mContext);
static Module *M = mModule.get();
static Type *dType = Type::getDoubleTy(mContext);
static Type *i32 = IntegerType::get(mContext, 32);
// helper functions
static AllocaInst *entryCreateBlockAllocaType(Function *func, std::string name, Type* type) {
IRBuilder<> tmpBuilder(&func->getEntryBlock(), func->getEntryBlock().begin());
return tmpBuilder.CreateAlloca(type, nullptr, name);
}
static ArrayRef<Value *> PrefixZero (Value *index) {
std::vector<Value *> out;
out.push_back(ConstantInt::get(mContext, APInt(32, 0)));
out.push_back(index);
return ArrayRef<Value *>(out);
}
static AllocaInst *createVariable () {
auto *func = mBuilder.GetInsertBlock()->getParent();
auto *initValue = ConstantInt::get(mContext, APInt(32, 0));
auto *alloca = entryCreateBlockAllocaType(func, "var", initValue->getType());
mBuilder.CreateStore(initValue, alloca);
return alloca;
}
static std::vector<Type *> elementTypes (3, dType);
static AllocaInst *createStruct () {
auto *func = mBuilder.GetInsertBlock()->getParent();
auto *mStructType = StructType::get(mContext, elementTypes);
return entryCreateBlockAllocaType(func, "str", mStructType);
}
int main () {
// create a main function
auto *FT = FunctionType::get(i32, std::vector<Type *>(), false);
auto *f = Function::Create(FT, Function::ExternalLinkage, "main", M);
// set insert point for out below code
auto *bb = BasicBlock::Create(mContext, "entry", f);
mBuilder.SetInsertPoint(bb);
// Create a variable
auto *variable = createVariable();
// create a struct
auto *mStruct = createStruct();
// Create a GEP with the loaded index
auto *loadedVar = mBuilder.CreateLoad(variable, "loaded_index");
// This is where the problem is.
// If `PrefixZero` is changed to `ConstantInt::get(mContext, APInt(32, 0))` this works
auto *elementPtr = mBuilder.CreateGEP(mStruct, PrefixZero(loadedVar));
mBuilder.CreateRet(ConstantInt::get(mContext, APInt(32, 0)));
f->print(errs()); // print out the function
return 1;
}
您的代码有两个问题:
static ArrayRef<Value *> PrefixZero (Value *index) {
std::vector<Value *> out;
out.push_back(ConstantInt::get(mContext, APInt(32, 0)));
out.push_back(index);
return ArrayRef<Value *>(out);
}
This class does not own the underlying data, it is expected to be used in situations where the data resides in some other buffer, whose lifetime extends past that of the ArrayRef.
换句话说,将 ArrayRef
返回给局部变量是非法的,就像返回指向局部变量的指针一样。在内部 ArrayRef
只存储 out
的 data
指针,一旦 out
超出范围(即在 PrefixZero
的末尾),data
被释放并且 ArrayRef
现在包含指向释放内存的指针。
在结构上使用 getelementptr
时,表示成员访问的索引(即您的情况下的第二个索引)必须是常量。如果您考虑一下,否则就不可能对指令进行类型检查(请记住,通常结构的成员并不都具有相同的类型)。加上计算给定非常量索引的指针偏移量基本上必须生成整个查找 table 并且指针算术指令生成那么多代码是违反直觉的。您可以将结构上的 GEP 视为等同于 C 中的 the_struct.member_name
并且您也不能将 member_name
替换为变量。
请注意,如果在您的 LLVM 构建中启用了断言,第二个问题应该会导致断言失败 "Invalid GetElementPtrInst indices for type!",虽然没有完全告诉您您需要知道的一切(比如以何种方式索引无效),确实比 "segmentation fault" 更能为您指明正确的方向。因此,如果您没有收到该消息,请确保您已启用断言,以便下次您 运行 遇到问题时可以从断言消息中受益。
我确定这真的很简单,但是,我已经尝试了一个多小时,但我无法弄清楚。
下面的代码给我一个分段错误:
Value *newArray = mBuilder.CreateGEP(alloca, value); // alloca is a `StructType`
但这不
Value *newArray = mBuilder.CreateGEP(alloca, ConstantInt::get(mContext, APInt(32, 0)));
value
的值
%bar1 = load double, double* %bar
%3 = fptoui double %bar1 to i32
调试
当我使用 lldb 调试它时,我得到:
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
frame #0: 0x00000001000b9e6e a.out`llvm::PointerType::get(llvm::Type*, unsigned int) + 20
a.out`llvm::PointerType::get:
-> 0x1000b9e6e <+20>: movq (%rdi), %rax
问题
为什么会出现分段错误,我该如何解决?
如何重现问题?
以下代码重现了问题:
#include <vector>
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/IR/Value.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/APInt.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"
using namespace llvm;
static LLVMContext mContext;
static IRBuilder<> mBuilder(mContext);
static std::unique_ptr<Module> mModule = make_unique<Module>("example", mContext);
static Module *M = mModule.get();
static Type *dType = Type::getDoubleTy(mContext);
static Type *i32 = IntegerType::get(mContext, 32);
// helper functions
static AllocaInst *entryCreateBlockAllocaType(Function *func, std::string name, Type* type) {
IRBuilder<> tmpBuilder(&func->getEntryBlock(), func->getEntryBlock().begin());
return tmpBuilder.CreateAlloca(type, nullptr, name);
}
static ArrayRef<Value *> PrefixZero (Value *index) {
std::vector<Value *> out;
out.push_back(ConstantInt::get(mContext, APInt(32, 0)));
out.push_back(index);
return ArrayRef<Value *>(out);
}
static AllocaInst *createVariable () {
auto *func = mBuilder.GetInsertBlock()->getParent();
auto *initValue = ConstantInt::get(mContext, APInt(32, 0));
auto *alloca = entryCreateBlockAllocaType(func, "var", initValue->getType());
mBuilder.CreateStore(initValue, alloca);
return alloca;
}
static std::vector<Type *> elementTypes (3, dType);
static AllocaInst *createStruct () {
auto *func = mBuilder.GetInsertBlock()->getParent();
auto *mStructType = StructType::get(mContext, elementTypes);
return entryCreateBlockAllocaType(func, "str", mStructType);
}
int main () {
// create a main function
auto *FT = FunctionType::get(i32, std::vector<Type *>(), false);
auto *f = Function::Create(FT, Function::ExternalLinkage, "main", M);
// set insert point for out below code
auto *bb = BasicBlock::Create(mContext, "entry", f);
mBuilder.SetInsertPoint(bb);
// Create a variable
auto *variable = createVariable();
// create a struct
auto *mStruct = createStruct();
// Create a GEP with the loaded index
auto *loadedVar = mBuilder.CreateLoad(variable, "loaded_index");
// This is where the problem is.
// If `PrefixZero` is changed to `ConstantInt::get(mContext, APInt(32, 0))` this works
auto *elementPtr = mBuilder.CreateGEP(mStruct, PrefixZero(loadedVar));
mBuilder.CreateRet(ConstantInt::get(mContext, APInt(32, 0)));
f->print(errs()); // print out the function
return 1;
}
您的代码有两个问题:
static ArrayRef<Value *> PrefixZero (Value *index) { std::vector<Value *> out; out.push_back(ConstantInt::get(mContext, APInt(32, 0))); out.push_back(index); return ArrayRef<Value *>(out); }
This class does not own the underlying data, it is expected to be used in situations where the data resides in some other buffer, whose lifetime extends past that of the ArrayRef.
换句话说,将
ArrayRef
返回给局部变量是非法的,就像返回指向局部变量的指针一样。在内部ArrayRef
只存储out
的data
指针,一旦out
超出范围(即在PrefixZero
的末尾),data
被释放并且ArrayRef
现在包含指向释放内存的指针。在结构上使用
getelementptr
时,表示成员访问的索引(即您的情况下的第二个索引)必须是常量。如果您考虑一下,否则就不可能对指令进行类型检查(请记住,通常结构的成员并不都具有相同的类型)。加上计算给定非常量索引的指针偏移量基本上必须生成整个查找 table 并且指针算术指令生成那么多代码是违反直觉的。您可以将结构上的 GEP 视为等同于 C 中的the_struct.member_name
并且您也不能将member_name
替换为变量。
请注意,如果在您的 LLVM 构建中启用了断言,第二个问题应该会导致断言失败 "Invalid GetElementPtrInst indices for type!",虽然没有完全告诉您您需要知道的一切(比如以何种方式索引无效),确实比 "segmentation fault" 更能为您指明正确的方向。因此,如果您没有收到该消息,请确保您已启用断言,以便下次您 运行 遇到问题时可以从断言消息中受益。