Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to track down LLVM verifyFunction error "Expected no forward declarations!"?

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?

like image 390
Matthew Sanders Avatar asked Dec 12 '15 03:12

Matthew Sanders


Video Answer


1 Answers

I finally got around to looking into Clang and noticed that they do not appear to utilize verifyFunction at all and verifyModule is optionally added as a pass.

CODEGENOPT(VerifyModule      , 1, 1) ///< Control whether the module should be run
                                     ///< through the LLVM Verifier.

It would appear from what I can see that my issues may not even be valid as it passes verifyModule in the end. I'm opting for the "do as Clang does" here for now.

I'm still open for suggestions.

like image 69
Matthew Sanders Avatar answered Sep 19 '22 04:09

Matthew Sanders