我正在 LLVM 中为我的一种新语言开发编译器,但在生成调试信息时遇到了问题。
我还没有找到很多关于如何使用 DIBuilder 实际生成调试信息的文档。所以我很可能做错了很多事。
我一直在看 Kaleidoscope示例,因为它是我发现的唯一一个使用调试信息的示例。我还没有打开 Clang 来查看他们是如何使用它的,但我很想听听已经使用过的人的意见。
我已经能够使用一些更复杂的示例来编译和运行我的语言,但是我从一些基础知识开始重新添加调试支持。这是我要编译的简单脚本:
double my_main()
{
return 0.0;
}
这是我从 verifyFunction、verifyModule 和转储模块的输出。
编辑:在下面的编辑中注意,我指出转储是在正确删除临时文件的最终确定调用之后进行的。
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)
在 LLVM 代码库中搜索错误消息可在 Verifier.cpp 中找到源代码:
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);
我的调试函数创建逻辑在示例的另一个调用中使用了这种类型:
// the argument is of type: SmallVector<Metadata *, 8> returnPlusParams;
dbuilder->createSubroutineType(dbuilder->getOrCreateTypeArray(returnPlusParams));
我还在 IRBuilder 上设置了行号和列号来 self 的 AST 节点:
_mBuilder->SetCurrentDebugLocation(DebugLoc::get(node->line, node->column, currentDebugScope()));
我也在阅读 SourceLevelDebugging 上的页面,但这并没有像调试 IR 格式那样谈论 LLVM 的 C++ API。
如果有人注意到我的模块转储中有明显的问题或有任何进一步的建议,我将不胜感激。
编辑:添加示例 IR
我做了更多的测试,并想使用以下命令发布 Clang 输出的类似函数的输出:
clang -cc1 hello_llvm.c -emit-llvm
编辑:我还找到了 this发布以将调试信息添加到输出。
这段代码:
double main() {
return 0.0;
}
编译为:
; 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)"}
它给出了将 main
的返回类型更改为 int 的明显警告。我还制作了一个生成 alloca 的 int 版本:
; 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)"}
注意: 在我的示例中,double
被选为任意返回类型,但 int
也失败了。在我的一些初始测试中,我实际上用带有 argv/argc 的适当 main
包装了 my_main,并且能够从终端编译和运行。
编辑 2:“完成”
我在之前的 IR 中没有看到任何太明显的东西,所以我决定在模块 finalize
调用之后运行 verifyModule。这是成功的,并反射(reflect)在我们上面看到的 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)
所以我想问题是...
在验证清理此临时文件之前是否必须执行某些操作?还是我只是错误地创建了类型?哪些操作会隐式创建临时对象?
最佳答案
我终于开始研究 Clang 并注意到它们似乎根本没有使用 verifyFunction
并且可以选择添加 verifyModule
作为传递。
CODEGENOPT(VerifyModule , 1, 1) ///< Control whether the module should be run
///< through the LLVM Verifier.
据我所知,我的问题可能甚至无效,因为它最终通过了 verifyModule
。我现在选择“像 Clang 那样做”。
我仍然愿意接受建议。
关于c++ - 如何追踪 LLVM verifyFunction 错误 "Expected no forward declarations!"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34236034/