c++ - 用于生成目标代码的 LLVM 通行证导致分段错误
问题描述
我正在使用 LLVM C++ API 编写一个 C 子集编译器,并使用 pass 来生成目标代码。但是当源文件包含 if-else 块时,目标代码生成步骤会给出段错误。以下是我正在使用的代码
用于生成 if-else 块的编译器代码
llvm::Value *Conditional::generateCode(CodeKit &kit) {
auto *cond = condition->generateCode(kit);
if (cond == nullptr) {
return nullptr;
}
llvm::Function *func = kit.builder.GetInsertBlock()->getParent();
auto *ifBlock = llvm::BasicBlock::Create(kit.context, "if", func);
auto *elseBlock = llvm::BasicBlock::Create(kit.context, "else");
auto *mergeBlock = llvm::BasicBlock::Create(kit.context, "ifelsemerge");
kit.builder.CreateCondBr(cond, ifBlock, elseBlock);
kit.builder.SetInsertPoint(ifBlock);
if (ifstmt != nullptr) {
kit.symbolTable.enterScope();
ifstmt->generateCode(kit);
kit.symbolTable.exitScope();
}
kit.builder.CreateBr(mergeBlock);
ifBlock = kit.builder.GetInsertBlock();
func->getBasicBlockList().push_back(elseBlock);
kit.builder.SetInsertPoint(elseBlock);
if (elsestmt != nullptr) {
kit.symbolTable.enterScope();
elsestmt->generateCode(kit);
kit.symbolTable.exitScope();
}
kit.builder.CreateBr(mergeBlock);
elseBlock = kit.builder.GetInsertBlock();
func->getBasicBlockList().push_back(mergeBlock);
kit.builder.SetInsertPoint(mergeBlock);
return nullptr;
从 llvm 模块生成目标代码的代码。
void emitCode(CodeKit &kit) {
kit.module.print(llvm::outs(), nullptr);
auto irFile = "output.bc";
error_code ec;
llvm::raw_fd_ostream irFileStream(irFile, ec, llvm::sys::fs::F_None);
llvm::WriteBitcodeToFile(kit.module, irFileStream);
irFileStream.flush();
auto targetTriple = llvm::sys::getDefaultTargetTriple();
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargets();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllAsmParsers();
llvm::InitializeAllAsmPrinters();
string error;
auto target = llvm::TargetRegistry::lookupTarget(targetTriple, error);
auto cpu = "generic";
auto features = "";
llvm::TargetOptions opt;
auto rm = llvm::Optional<llvm::Reloc::Model>();
auto targetMachine =
target->createTargetMachine(targetTriple, cpu, features, opt, rm);
kit.module.setDataLayout(targetMachine->createDataLayout());
kit.module.setTargetTriple(targetTriple);
auto objectFile = "output.o";
llvm::raw_fd_ostream objectFileStream(objectFile, ec,
llvm::sys::fs::OF_None);
llvm::legacy::PassManager pass;
auto fileType = llvm::CGFT_ObjectFile;
targetMachine->addPassesToEmitFile(pass, objectFileStream, nullptr,
fileType);
pass.run(kit.module);
objectFileStream.flush();
}
我目前正在测试的源文件
int factorial(int n) {
if (n <= 0)
return 1;
else
return n * factorial(n - 1);
}
生成的 IR 代码
; ModuleID = 'bootleg c compiler'
source_filename = "bootleg c compiler"
define i32 @factorial(i32 %n) {
entry:
%n1 = alloca i32
store i32 %n, i32* %n1
%n2 = load i32, i32* %n1
%"Less or Equal" = icmp sle i32 %n2, 0
br i1 %"Less or Equal", label %if, label %else
if: ; preds = %entry
ret i32 1
br label %ifelsemerge
else: ; preds = %entry
%n3 = load i32, i32* %n1
%n4 = load i32, i32* %n1
%Subtract = sub i32 %n4, 1
%call = call i32 @factorial(i32 %Subtract)
%Multiply = mul i32 %n3, %call
ret i32 %Multiply
br label %ifelsemerge
ifelsemerge: ; preds = %else, %if
}
段错误的 GDB 堆栈跟踪
#0 0x00007ffff4386bc0 in llvm::Instruction::getNumSuccessors() const () from /lib/x86_64-linux-gnu/libLLVM-10.so.1
#1 0x00007ffff4fd643c in llvm::BranchProbabilityInfo::computePostDominatedByUnreachable(llvm::Function const&, llvm::PostDominatorTree*) ()
from /lib/x86_64-linux-gnu/libLLVM-10.so.1
#2 0x00007ffff4fdb175 in llvm::BranchProbabilityInfo::calculate(llvm::Function const&, llvm::LoopInfo const&, llvm::TargetLibraryInfo const*) ()
from /lib/x86_64-linux-gnu/libLLVM-10.so.1
#3 0x00007ffff4fdb9c4 in llvm::BranchProbabilityInfoWrapperPass::runOnFunction(llvm::Function&) () from /lib/x86_64-linux-gnu/libLLVM-10.so.1
#4 0x00007ffff43a7d76 in llvm::FPPassManager::runOnFunction(llvm::Function&) () from /lib/x86_64-linux-gnu/libLLVM-10.so.1
#5 0x00007ffff43a7ff3 in llvm::FPPassManager::runOnModule(llvm::Module&) () from /lib/x86_64-linux-gnu/libLLVM-10.so.1
#6 0x00007ffff43a84a0 in llvm::legacy::PassManagerImpl::run(llvm::Module&) () from /lib/x86_64-linux-gnu/libLLVM-10.so.1
#7 0x0000555555565b73 in emitCode (kit=...) at c.ast.cpp:88
#8 0x0000555555579957 in generateCode (ast=...) at cc.cpp:25
#9 0x0000555555579a83 in main (argc=2, argv=0x7fffffffdfe8) at cc.cpp:41
解决方案
ret i32 1
br label %ifelsemerge
是无效的 IR,您br
必须ret
在BasicBlock
. 您可以通过在插入分支之前检查终止指令来避免这种情况,
if (ifBlock->size() == 0 || !ifBlock->back().isTerminator()) {
kit.builder.CreateBr(mergeBlock);
}
// ...
if (elseBlock->size() == 0 || !elseBlock->back().isTerminator()) {
kit.builder.CreateBr(mergeBlock);
}
请注意,空块也是无效的,因此如果条件的两个分支都退出函数,您可能不应该生成mergeBlock
. AFAIK,如果您没有指向空块的分支,则可以将其留在其中。
llvm::verifyFunction
通常,您可以通过添加验证通行证或使用or来在前端捕获这些类型的错误llvm::verifyModule
。使用生成的 IR 代码运行llc
也会给出更详细的错误消息。
推荐阅读
- python - 重新创建没有迁移文件夹的 django 迁移
- vim - MacVim GUI 不使用 XDG vimrc
- python - 什么时候在 Python 中创建子进程?
- r - ggplot facet_wrap 多个面板
- git - 获取 Cherry-Pick 冲突
- android - Android Studio 编辑文本 - 键盘未出现在应用程序中 | 科特林
- html - HTML5 移动页面 - 禁用缩放
- java - DateTimeFormatter 问题
- javascript - Javascript函数确定具有最小可能差异的数字组合
- arrays - 使用C将二维数组导出到新文件中