仅当方法声明具有参数时,退出期间LLVM IR codegen SEGFULTS

解释

我使用LLVM C++ API创建了一个使用类似于YACC/BISON、Flex和LLVM工具链(LLVM 12)的C语言的编译器。我一直在Ubuntu版本20.04.3 LTS(Focal Fossa)和macOS 11.6 Big Sur上开发和测试。目前,我要处理的问题是,当方法声明具有方法参数时,退出程序时会出现程序分段故障,例如:

函数测试(x int)无效{

LLVM IR将正确打印为

;ModuleID='Test'
源文件名="测试“;
在测试时定义void(i32%x){
条目:
%x1=alloca i32,对齐4
存储i32%x,i32*%x1,对齐4
ret void
}

然后会马上出现故障

方法声明,如

func test()int{
var x int;
x=5;
返回(x);
}

不会犯错

GDB报告segfault在llvm::LLVMContextImpl::~LLVMContextImpl()期间发生。Valgrind报告执行大小为8的无效读取

编辑:与无效读取相关的Valgrind输出

==10254==大小为8的无效读取
==10254==at 0x5553C30:llvm::LLVMContextImpl::~LLVMContextImpl()(在/usr/lib/x86_64-linux-gnu/libLLVM-12.so.1中)
==10254==0x5552130:llvm::LLVMContext::~LLVMContext()(在/usr/lib/x86_64-linux-gnu/libLLVM-12.so.1中)
==10254==0xA44AA26:\u运行\u退出\u处理程序(exit.c:108)
==10254==0xA44ABDF:exit(exit.c:139)
==10254==by 0xA4280B9:(在main下面)(libc start.c:342)
==10254==地址0x0不是堆栈、malloc或(最近)空闲
==10254== 
==10254== 
==10254==进程以信号11(SIGSEGV)的默认动作终止
==10254==访问不在地址0x0的映射区域内
==10254==at 0x5553C30:llvm::LLVMContextImpl::~LLVMContextImpl()(在/usr/lib/x86_64-linux-gnu/libLLVM-12.so.1中)
==10254==0x5552130:llvm::LLVMContext::~LLVMContext()(在/usr/lib/x86_64-linux-gnu/libLLVM-12.so.1中)
==10254==0xA44AA26:\u运行\u退出\u处理程序(exit.c:108)
==10254==0xA44ABDF:exit(exit.c:139)
==10254==by 0xA4280B9:(在main下面)(libc start.c:342)
==10254==如果您认为这是由于堆栈导致的
==10254==程序主线程溢出(不太可能,但
==10254==可能),您可以尝试增大
==10254==使用--main stacksize=标志的主线程堆栈。
==10254==此运行中使用的主线程堆栈大小为8388608。

我希望通过在这里提问,我能得到一些关于如何努力解决这个问题的提示。这件事我已经坚持了好几天了

源代码片段

我的代码中与方法声明和方法参数相关的部分如下所示,我为篇幅感到抱歉:

程序的Bison语法规则

程序:外部列表decappackage
{ 
ProgramAST*prog=新的ProgramAST((decafStmtList*)1美元,(PackageAST*)2美元;
if(printAST){
cout<<getString(prog)<<endl;
}
prog->Codegen();
删除程序;
}
;

方法声明的Bison语法规则

method\u decl:T函数T\u ID T\u LPAREN参数T\u rpare方法类型方法块
{
$$=新方法(*$2,$6->str(),$4,$7);
删除2美元;
删除6美元;
}

方法参数的Bison语法规则

参数:T_ID类型{$$=new VarDef(*$1,$2->str());删除$1;删除$2;}
;

C++方法::Codegen()处理参数

llvm::Function*func=llvm::Function::Create(
llvm::FunctionType::get(returnTy、args、false),
llvm::Function::ExternalLink,
名称
模块
);
llvm::BasicBlock*BB=llvm::BasicBlock::Create(上下文“entry”,func);
生成器设置插入点(BB);
. . .
对于(auto&Arg:func->args()){
llvm::AllocaInst*Alloca=CreateEntryBlockAlloca(func,Arg.getName().str());
Builder.CreateStore(&Arg,Alloca);
sTStack->输入符号(Arg.getName().str(),Alloca);
}

C++VarDef::Codegen()

llvm::Value*Codegen(){
llvm::Type*ty=getLLVMType(Type);
llvm::AllocaInst*V=Builder.CreateAlloca(ty,0,name);
V->设置名称(名称);
sTStack->输入符号(名称,V);
返回V;
返回空ptr;
}

野牛干道

int main(){
//设置
llvm::LLVMContext&Context=上下文;
模块=新的llvm::模块(“测试”,上下文);
FPM=std::make_unique<llvm::legacy::FunctionPassManager>(模块);
FPM->添加(llvm::createPromoteMemoryToRegisterPass());
FPM->添加(llvm::CreateInstructionComponingPass());
FPM->添加(llvm::createReassociatePass());
FPM->添加(llvm::createGVNPass());
FPM->添加(llvm::createcfgsimplicationpass());
FPM->doInitialization();
int-retval=yyparse();
模块->打印(llvm::errs(),nullptr);
返回(retval>=1?退出失败:退出成功);
}

解决方案:

问题出在未包含的代码行中llvm::Function::Create需要一个llvm::FunctionType,它可以通过用llvm::Type*对象填充向量来提供。为此,我编写了一个函数:

void getLLVMTypes(vector<llvm::Type*>*v){
用于(自动*i:stmts){
llvm::Type*Type=getLLVMType(i->getType());
((llvm::Value*)(type))->setName(i->getName());//问题
v->推回(类型);
}
}

问题是将每个llvm::Type*对象强制转换为llvm::Value*,并使用llvm::Value::setName设置其名称。我这样做是为了解决我之前遇到的一个问题,即没有设置参数名。我不完全确定问题是什么,我在从源代码处编译带有调试标志的LLVM时遇到了问题,但这是一行看起来很粗糙的代码,删除它,并使用另一种方法保留方法参数名,解决了问题

谢谢大家的建议和帮助

发表评论