I'm developing a compiler for a new language of mine in LLVM and have run into an issue while generating debug information.
I have not yet found much documentation on how to actually generate the debug information using the DIBuilder so it is very possible I'm doing something terribly wrong.
I have been mostly looking at the Kaleidoscope example as it is the only one I have found that uses debug information. I have yet to crack open Clang to look at how they have been using it, but I would love to hear from someone who has.
I have been able to compile and run my language with some more complex examples, but I started back at some basics for adding the debugging support. Here is the simple script I am trying to compile:
double my_main()
{
return 0.0;
}
Here is my output from verifyFunction, verifyModule, and dumping the module.
Edit: note in edits below I point out that the dump is after a call to finalize which correctly removes the temporary.
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)
Searching for the error message in the LLVM codebase reveals the source in 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); }
I'm assuming by the assertion that I have some metadata that is still considered "Temporary", but I would like to know how to track down what is creating it.
I'm creating my types much like how the example does:
// 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);
My debug function creation logic uses this type, among others, in another call from the example:
// the argument is of type: SmallVector<Metadata *, 8> returnPlusParams;
dbuilder->createSubroutineType(dbuilder->getOrCreateTypeArray(returnPlusParams));
I am also setting line and column numbers on my IRBuilder from my AST nodes:
_mBuilder->SetCurrentDebugLocation(DebugLoc::get(node->line, node->column, currentDebugScope()));
I was also reading the page on SourceLevelDebugging, but this doesn't talk about the C++ API to LLVM as much as the debug IR format.
If anyone notices something obvious in my module dump or has any further suggestions I would appreciate it greatly.
Edit: Adding example IR
I did a few more tests and wanted to post the output of a similar function output from Clang with the following command:
clang -cc1 hello_llvm.c -emit-llvm
Edit: I also found this post to add debug info to the output.
This code:
double main() {
return 0.0;
}
Compiles to this:
; 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)"}
It gives the obvious warn to change return type of main
to int. I produced an int version as well which produces an 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)"}
NOTE: In my example double
was chosen as an arbitrary return type, but int
also fails. In some of my initial tests I actually wrap my_main with a proper main
with argv/argc and was able to compile and run from the terminal.
Edit 2: 'finalize'
I didn't see anything too obvious in the previous IR so I decided to run verifyModule after the module finalize
call. This was successful and reflected in the IR dump we see above.
I then decided to dump the module prior to finalize. This time you can see the temp it was complaining about.
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)
So I suppose the question is...
Is there something that must be done prior to verify to clean up this temporary? or am I just creating the type incorrectly? What operations implicitly create temporaries?