仅当方法声明具有参数时,LLVM IR codegen 才会在退出期间出现段错误
LLVM IR codegen segfaults during exit only when method declarations have parameters
说明
我正在使用 yacc/bison、flex 和使用 LLVM C++ API 的 LLVM 工具链 (LLVM 12) 创建类 C 语言的编译器。
我一直在 Ubuntu 版本 20.04.3 LTS (Focal Fossa) 和 macOS 11.6 Big Sur 上进行开发和测试。
目前,问题是当方法声明具有方法参数时退出程序时程序段错误,例如:
func test(x int) void {}
LLVM IR 将正确打印为
; ModuleID = 'Test'
source_filename = "Test"
define void @test(i32 %x) {
entry:
%x1 = alloca i32, align 4
store i32 %x, i32* %x1, align 4
ret void
}
之后会立即发生段错误。
像
这样的方法声明
func test() int {
var x int;
x = 5;
return (x);
}
不会出现段错误。
GDB 报告段错误发生在 llvm::LLVMContextImpl::~LLVMContextImpl()
期间。 Valgrind 报告 ~LLVMContextImpl()
进行大小为 8 的无效读取。
编辑:与无效读取相关的 Valgrind 输出
==10254== Invalid read of size 8
==10254== at 0x5553C30: llvm::LLVMContextImpl::~LLVMContextImpl() (in /usr/lib/x86_64-linux-gnu/libLLVM-12.so.1)
==10254== by 0x5552130: llvm::LLVMContext::~LLVMContext() (in /usr/lib/x86_64-linux-gnu/libLLVM-12.so.1)
==10254== by 0xA44AA26: __run_exit_handlers (exit.c:108)
==10254== by 0xA44ABDF: exit (exit.c:139)
==10254== by 0xA4280B9: (below main) (libc-start.c:342)
==10254== Address 0x0 is not stack'd, malloc'd or (recently) free'd
==10254==
==10254==
==10254== Process terminating with default action of signal 11 (SIGSEGV)
==10254== Access not within mapped region at address 0x0
==10254== at 0x5553C30: llvm::LLVMContextImpl::~LLVMContextImpl() (in /usr/lib/x86_64-linux-gnu/libLLVM-12.so.1)
==10254== by 0x5552130: llvm::LLVMContext::~LLVMContext() (in /usr/lib/x86_64-linux-gnu/libLLVM-12.so.1)
==10254== by 0xA44AA26: __run_exit_handlers (exit.c:108)
==10254== by 0xA44ABDF: exit (exit.c:139)
==10254== by 0xA4280B9: (below main) (libc-start.c:342)
==10254== If you believe this happened as a result of a stack
==10254== overflow in your program's main thread (unlikely but
==10254== possible), you can try to increase the size of the
==10254== main thread stack using the --main-stacksize= flag.
==10254== The main thread stack size used in this run was 8388608.
我希望通过在这里提问,我可以得到一些关于如何努力解决这个问题的提示。我已经坚持了好几天了。
源代码片段
我的代码中与方法声明和方法参数相关的部分如下,对于长度,我深表歉意:
程序的 Bison 语法规则
program: extern_list decafpackage
{
ProgramAST *prog = new ProgramAST((decafStmtList*), (PackageAST*));
if (printAST) {
cout << getString(prog) << endl;
}
prog->Codegen();
delete prog;
}
;
方法声明的Bison语法规则
method_decl: T_FUNC T_ID T_LPAREN params T_RPAREN method_type method_block
{
$$ = new Method(*, ->str(), , );
delete ;
delete ;
}
方法参数的 Bison 语法规则
param: T_ID type { $$ = new VarDef(*, ->str()); delete ; delete ; }
;
C++ Method::Codegen() 参数处理
llvm::Function *func = llvm::Function::Create(
llvm::FunctionType::get(returnTy, args, false),
llvm::Function::ExternalLinkage,
name,
TheModule
);
llvm::BasicBlock *BB = llvm::BasicBlock::Create(TheContext, "entry", func);
Builder.SetInsertPoint(BB);
. . .
for (auto &Arg : func->args()) {
llvm::AllocaInst* Alloca = CreateEntryBlockAlloca(func, Arg.getName().str());
Builder.CreateStore(&Arg, Alloca);
sTStack->enter_symtbl(Arg.getName().str(), Alloca);
}
C++ VarDef::Codegen()
llvm::Value *Codegen() {
llvm::Type* ty = getLLVMType(type);
llvm::AllocaInst* V = Builder.CreateAlloca(ty, 0, name);
V->setName(name);
sTStack->enter_symtbl(name, V);
return V;
return nullptr;
}
野牛主菜
int main() {
// Setup
llvm::LLVMContext &Context = TheContext;
TheModule = new llvm::Module("Test", Context);
FPM = std::make_unique<llvm::legacy::FunctionPassManager>(TheModule);
FPM->add(llvm::createPromoteMemoryToRegisterPass());
FPM->add(llvm::createInstructionCombiningPass());
FPM->add(llvm::createReassociatePass());
FPM->add(llvm::createGVNPass());
FPM->add(llvm::createCFGSimplificationPass());
FPM->doInitialization();
int retval = yyparse();
TheModule->print(llvm::errs(), nullptr);
return(retval >= 1 ? EXIT_FAILURE : EXIT_SUCCESS);
}
解法:
问题出在未包含的代码行中。 llvm::Function::Create
需要 llvm::FunctionType
可以通过用 llvm::Type*
对象填充向量来提供。我写了一个函数来做到这一点:
void getLLVMTypes(vector<llvm::Type*>* v) {
for (auto* i : stmts) {
llvm::Type* type = getLLVMType(i->getType());
((llvm::Value*)(type))->setName(i->getName()); // Problem
v->push_back(type);
}
}
问题是将每个 llvm::Type*
对象转换为 llvm::Value*
并使用 llvm::Value::setName
设置其名称。我这样做是为了解决我之前遇到的未设置参数名称的问题。我不完全确定问题出在哪里,我在使用调试标志从源代码编译 LLVM 时遇到了问题,但这是一行看起来很粗糙的代码并将其删除,同时使用另一种方法来保留方法参数名称,解决了这个问题。
说明
我正在使用 yacc/bison、flex 和使用 LLVM C++ API 的 LLVM 工具链 (LLVM 12) 创建类 C 语言的编译器。 我一直在 Ubuntu 版本 20.04.3 LTS (Focal Fossa) 和 macOS 11.6 Big Sur 上进行开发和测试。 目前,问题是当方法声明具有方法参数时退出程序时程序段错误,例如:
func test(x int) void {}
LLVM IR 将正确打印为
; ModuleID = 'Test'
source_filename = "Test"
define void @test(i32 %x) {
entry:
%x1 = alloca i32, align 4
store i32 %x, i32* %x1, align 4
ret void
}
之后会立即发生段错误。
像
这样的方法声明func test() int {
var x int;
x = 5;
return (x);
}
不会出现段错误。
GDB 报告段错误发生在 llvm::LLVMContextImpl::~LLVMContextImpl()
期间。 Valgrind 报告 ~LLVMContextImpl()
进行大小为 8 的无效读取。
编辑:与无效读取相关的 Valgrind 输出
==10254== Invalid read of size 8
==10254== at 0x5553C30: llvm::LLVMContextImpl::~LLVMContextImpl() (in /usr/lib/x86_64-linux-gnu/libLLVM-12.so.1)
==10254== by 0x5552130: llvm::LLVMContext::~LLVMContext() (in /usr/lib/x86_64-linux-gnu/libLLVM-12.so.1)
==10254== by 0xA44AA26: __run_exit_handlers (exit.c:108)
==10254== by 0xA44ABDF: exit (exit.c:139)
==10254== by 0xA4280B9: (below main) (libc-start.c:342)
==10254== Address 0x0 is not stack'd, malloc'd or (recently) free'd
==10254==
==10254==
==10254== Process terminating with default action of signal 11 (SIGSEGV)
==10254== Access not within mapped region at address 0x0
==10254== at 0x5553C30: llvm::LLVMContextImpl::~LLVMContextImpl() (in /usr/lib/x86_64-linux-gnu/libLLVM-12.so.1)
==10254== by 0x5552130: llvm::LLVMContext::~LLVMContext() (in /usr/lib/x86_64-linux-gnu/libLLVM-12.so.1)
==10254== by 0xA44AA26: __run_exit_handlers (exit.c:108)
==10254== by 0xA44ABDF: exit (exit.c:139)
==10254== by 0xA4280B9: (below main) (libc-start.c:342)
==10254== If you believe this happened as a result of a stack
==10254== overflow in your program's main thread (unlikely but
==10254== possible), you can try to increase the size of the
==10254== main thread stack using the --main-stacksize= flag.
==10254== The main thread stack size used in this run was 8388608.
我希望通过在这里提问,我可以得到一些关于如何努力解决这个问题的提示。我已经坚持了好几天了。
源代码片段
我的代码中与方法声明和方法参数相关的部分如下,对于长度,我深表歉意:
程序的 Bison 语法规则
program: extern_list decafpackage
{
ProgramAST *prog = new ProgramAST((decafStmtList*), (PackageAST*));
if (printAST) {
cout << getString(prog) << endl;
}
prog->Codegen();
delete prog;
}
;
方法声明的Bison语法规则
method_decl: T_FUNC T_ID T_LPAREN params T_RPAREN method_type method_block
{
$$ = new Method(*, ->str(), , );
delete ;
delete ;
}
方法参数的 Bison 语法规则
param: T_ID type { $$ = new VarDef(*, ->str()); delete ; delete ; }
;
C++ Method::Codegen() 参数处理
llvm::Function *func = llvm::Function::Create(
llvm::FunctionType::get(returnTy, args, false),
llvm::Function::ExternalLinkage,
name,
TheModule
);
llvm::BasicBlock *BB = llvm::BasicBlock::Create(TheContext, "entry", func);
Builder.SetInsertPoint(BB);
. . .
for (auto &Arg : func->args()) {
llvm::AllocaInst* Alloca = CreateEntryBlockAlloca(func, Arg.getName().str());
Builder.CreateStore(&Arg, Alloca);
sTStack->enter_symtbl(Arg.getName().str(), Alloca);
}
C++ VarDef::Codegen()
llvm::Value *Codegen() {
llvm::Type* ty = getLLVMType(type);
llvm::AllocaInst* V = Builder.CreateAlloca(ty, 0, name);
V->setName(name);
sTStack->enter_symtbl(name, V);
return V;
return nullptr;
}
野牛主菜
int main() {
// Setup
llvm::LLVMContext &Context = TheContext;
TheModule = new llvm::Module("Test", Context);
FPM = std::make_unique<llvm::legacy::FunctionPassManager>(TheModule);
FPM->add(llvm::createPromoteMemoryToRegisterPass());
FPM->add(llvm::createInstructionCombiningPass());
FPM->add(llvm::createReassociatePass());
FPM->add(llvm::createGVNPass());
FPM->add(llvm::createCFGSimplificationPass());
FPM->doInitialization();
int retval = yyparse();
TheModule->print(llvm::errs(), nullptr);
return(retval >= 1 ? EXIT_FAILURE : EXIT_SUCCESS);
}
解法:
问题出在未包含的代码行中。 llvm::Function::Create
需要 llvm::FunctionType
可以通过用 llvm::Type*
对象填充向量来提供。我写了一个函数来做到这一点:
void getLLVMTypes(vector<llvm::Type*>* v) {
for (auto* i : stmts) {
llvm::Type* type = getLLVMType(i->getType());
((llvm::Value*)(type))->setName(i->getName()); // Problem
v->push_back(type);
}
}
问题是将每个 llvm::Type*
对象转换为 llvm::Value*
并使用 llvm::Value::setName
设置其名称。我这样做是为了解决我之前遇到的未设置参数名称的问题。我不完全确定问题出在哪里,我在使用调试标志从源代码编译 LLVM 时遇到了问题,但这是一行看起来很粗糙的代码并将其删除,同时使用另一种方法来保留方法参数名称,解决了这个问题。