Mat*_*ers 5 c++ compiler-construction debugging llvm llvm-ir
我正在为LLVM中的一种新语言开发编译器,并在生成调试信息时遇到问题.
我还没有找到很多关于如何使用DIBuilder实际生成调试信息的文档,所以我很可能做了一些非常错误的事情.
我一直在看Kaleidoscope示例,因为它是我发现的唯一使用调试信息的示例.我还没有打开Clang来看看他们是如何使用它的,但我很乐意听到有人的声音.
我已经能够使用一些更复杂的示例来编译和运行我的语言,但我在一些基础知识中重新开始添加调试支持.这是我正在尝试编译的简单脚本:
double my_main()
{
return 0.0;
}
Run Code Online (Sandbox Code Playgroud)
这是来自verifyFunction,verifyModule和转储模块的输出.
编辑:注意在下面的编辑中我指出转储是在调用finalize之后正确删除临时.
Failed To Verify Function: my_main error: Expected no forward declarations!
!8 = <temporary!> !{}
Failed To Verify Module: test.str error: Expected no forward declarations!
!8 = <temporary!> !{}
; ModuleID = 'test.str'
define double @my_main() !dbg !6 {
entry:
br label %block, !dbg !10
block: ; preds = %entry
ret double 0.000000e+00, !dbg !10
}
!llvm.module.flags = !{!0, !1}
!llvm.dbg.cu = !{!2}
!0 = !{i32 2, !"Debug Info Version", i32 3}
!1 = !{i32 2, !"Dwarf Version", i32 2}
!2 = distinct !DICompileUnit(language: DW_LANG_C, file: !3, producer: "Test Compiler", isOptimized: false, runtimeVersion: 0, emissionKind: 1, enums: !4, subprograms: !5)
!3 = !DIFile(filename: "test.str", directory: ".")
!4 = !{}
!5 = !{!6}
!6 = distinct !DISubprogram(name: "my_main", scope: !3, file: !3, line: 10, type: !7, isLocal: false, isDefinition: true, scopeLine: 10, isOptimized: false, variables: !4)
!7 = !DISubroutineType(types: !8)
!8 = !{!9}
!9 = !DIBasicType(name: "double", size: 64, align: 8, encoding: DW_ATE_float)
!10 = !DILocation(line: 9, column: 11, scope: !6)
Run Code Online (Sandbox Code Playgroud)
在LLVM代码库中搜索错误消息会显示Verifier.cpp中的源代码:
Run Code Online (Sandbox Code Playgroud)void Verifier::visitMDNode(const MDNode &MD) { // Only visit each node once. Metadata can be mutually recursive, so this // avoids infinite recursion here, as well as being an optimization. if (!MDNodes.insert(&MD).second) return; switch (MD.getMetadataID()) { default: llvm_unreachable("Invalid MDNode subclass"); case Metadata::MDTupleKind: break; #define HANDLE_SPECIALIZED_MDNODE_LEAF(CLASS) \ case Metadata::CLASS##Kind: \ visit##CLASS(cast<CLASS>(MD)); \ break; #include "llvm/IR/Metadata.def" } for (unsigned i = 0, e = MD.getNumOperands(); i != e; ++i) { Metadata *Op = MD.getOperand(i); if (!Op) continue; Assert(!isa<LocalAsMetadata>(Op), "Invalid operand for global metadata!", &MD, Op); if (auto *N = dyn_cast<MDNode>(Op)) { visitMDNode(*N); continue; } if (auto *V = dyn_cast<ValueAsMetadata>(Op)) { visitValueAsMetadata(*V, nullptr); continue; } } // Check these last, so we diagnose problems in operands first. Assert(!MD.isTemporary(), "Expected no forward declarations!", &MD); Assert(MD.isResolved(), "All nodes should be resolved!", &MD); }
我假设我有一些仍然被认为是"临时"的元数据,但我想知道如何追踪创建它的内容.
我正在创建我的类型,就像示例一样:
// here dbuilder is a DIBuilder* and alignment is coming from
// my Module's getDataLayout().getABITypeAlignment(t);
// where t is Type::getDoubleTy(context.getLLVMContext());
// the context object is my own type
dbuilder->createBasicType("double", 64, alignment, dwarf::DW_ATE_float);
Run Code Online (Sandbox Code Playgroud)
我的调试函数创建逻辑使用此类型,以及来自示例的另一个调用:
// the argument is of type: SmallVector<Metadata *, 8> returnPlusParams;
dbuilder->createSubroutineType(dbuilder->getOrCreateTypeArray(returnPlusParams));
Run Code Online (Sandbox Code Playgroud)
我还从我的AST节点在我的IRBuilder上设置行号和列号:
_mBuilder->SetCurrentDebugLocation(DebugLoc::get(node->line, node->column, currentDebugScope()));
Run Code Online (Sandbox Code Playgroud)
我也在阅读关于SourceLevelDebugging的页面,但这并没有像调试IR格式那样谈论LLVM的C++ API.
如果有人注意到我的模块转储中有明显的东西或有任何进一步的建议,我会非常感激.
我做了一些测试,并希望使用以下命令发布Clang类似函数输出的输出:
clang -cc1 hello_llvm.c -emit-llvm
Run Code Online (Sandbox Code Playgroud)
编辑:我还发现这篇文章将调试信息添加到输出中.
这段代码:
double main() {
return 0.0;
}
Run Code Online (Sandbox Code Playgroud)
编译到:
; ModuleID = 'hello_llvm.c'
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-darwin15.0.0"
; Function Attrs: nounwind
define double @main() #0 {
entry:
ret double 0.000000e+00
}
attributes #0 = { nounwind "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-features"="+mmx,+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }
!llvm.ident = !{!0}
!0 = !{!"clang version 3.8.0 (http://llvm.org/git/clang.git 80803f026ba7160f7cfa122c7ef829ab42abc3bf) (http://llvm.org/git/llvm.git 1bb03c5884405c428c3ab54631c0528b6cedeb54)"}
Run Code Online (Sandbox Code Playgroud)
它给出了明显的警告,将返回类型更改main为int.我也生成了一个int版本,它产生了一个alloca:
; ModuleID = 'hello_llvm.c'
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-darwin15.0.0"
; Function Attrs: nounwind
define i32 @main() #0 {
entry:
%retval = alloca i32, align 4
store i32 0, i32* %retval, align 4
ret i32 0
}
attributes #0 = { nounwind "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-features"="+mmx,+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }
!llvm.ident = !{!0}
!0 = !{!"clang version 3.8.0 (http://llvm.org/git/clang.git 80803f026ba7160f7cfa122c7ef829ab42abc3bf) (http://llvm.org/git/llvm.git 1bb03c5884405c428c3ab54631c0528b6cedeb54)"}
Run Code Online (Sandbox Code Playgroud)
注意:在我的示例中double被选为任意返回类型,但int也失败了.在我的一些初始测试中,我实际上用一个适当main的argv/argc包装my_main,并且能够从终端编译和运行.
我在之前的IR中没有看到任何太明显的东西,所以我决定在模块finalize调用后运行verifyModule .这很成功,并反映在我们上面看到的IR转储中.
然后我决定在完成之前转储模块.这次你可以看到它抱怨的温度.
Failed To Verify Module: test.str error: Expected no forward declarations!
!8 = <temporary!> !{}
; ModuleID = 'test.str'
define i32 @my_main() !dbg !4 {
entry:
br label %block, !dbg !9
block: ; preds = %entry
ret i32 0, !dbg !9
}
!llvm.module.flags = !{!0, !1}
!llvm.dbg.cu = !{!2}
!0 = !{i32 2, !"Debug Info Version", i32 3}
!1 = !{i32 2, !"Dwarf Version", i32 2}
!2 = distinct !DICompileUnit(language: DW_LANG_C, file: !3, producer: "Test Compiler", isOptimized: false, runtimeVersion: 0, emissionKind: 1)
!3 = !DIFile(filename: "test.str", directory: ".")
!4 = distinct !DISubprogram(name: "my_main", scope: !3, file: !3, line: 10, type: !5, isLocal: false, isDefinition: true, scopeLine: 10, isOptimized: false, variables: !8)
!5 = !DISubroutineType(types: !6)
!6 = !{!7}
!7 = !DIBasicType(name: "int32", size: 32, align: 4, encoding: DW_ATE_signed)
!8 = <temporary!> !{}
!9 = !DILocation(line: 9, column: 11, scope: !4)
Run Code Online (Sandbox Code Playgroud)
所以我想问题是......
在验证之前是否必须要做一些事情来清理这个临时的?或者我只是错误地创建类型?什么操作隐含地创造临时工?
我终于抽出时间研究 Clang,发现它们似乎verifyFunction根本没有使用,并且verifyModule可以选择添加为通行证。
CODEGENOPT(VerifyModule , 1, 1) ///< Control whether the module should be run
///< through the LLVM Verifier.
Run Code Online (Sandbox Code Playgroud)
从我看来,我的问题最终可能会变得无效verifyModule。我现在选择“像 Clang 那样做”。
我仍然愿意接受建议。