解释
我使用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时遇到了问题,但这是一行看起来很粗糙的代码,删除它,并使用另一种方法保留方法参数名,解决了问题
谢谢大家的建议和帮助