DebugInfo: Correct/improve type formatting (pointers to function types especially)
authorDavid Blaikie <dblaikie@gmail.com>
Fri, 3 Sep 2021 19:18:13 +0000 (12:18 -0700)
committerDavid Blaikie <dblaikie@gmail.com>
Fri, 3 Sep 2021 19:22:28 +0000 (12:22 -0700)
This does add some extra superfluous whitespace (eg: "int *") intended
to make the Simplified Template Names work easier - this makes the
DIE-based names match more exactly the clang-generated names, so it's
easier to identify cases that don't generate matching names.

(arguably we could change clang to skip that whitespace or add some
fuzzy matching to accommodate differences in certain whitespace - but
this seemed easier and fairly low-impact)

llvm/lib/DebugInfo/DWARF/DWARFDie.cpp
llvm/test/DebugInfo/MSP430/dwarf-basics.ll
llvm/test/DebugInfo/X86/dwarfdump-allocatedVar.ll
llvm/test/DebugInfo/X86/dwarfdump-associatedVar.ll
llvm/test/DebugInfo/X86/tu-to-non-named-type.ll
llvm/test/MC/WebAssembly/dwarfdump.ll
llvm/test/MC/WebAssembly/dwarfdump64.ll
llvm/test/tools/dsymutil/X86/dwarf5.test
llvm/test/tools/llvm-dwarfdump/X86/DW_AT_object_pointer.s
llvm/test/tools/llvm-dwarfdump/X86/prettyprint_types.s

index b3d361b..338f58d 100644 (file)
@@ -161,43 +161,114 @@ static void dumpArrayType(raw_ostream &OS, const DWARFDie &D) {
     }
 }
 
-/// Recursively dump the DIE type name when applicable.
-static void dumpTypeName(raw_ostream &OS, const DWARFDie &D) {
-  if (!D.isValid())
-    return;
-
+static void dumpTypeName(raw_ostream &OS, const DWARFDie &D, bool SkipFirstParamIfArtificial = false);
+static DWARFDie dumpTypeNameBefore(raw_ostream &OS, DWARFDie D, bool *Word = nullptr);
+
+static void dumpPointerLikeTypeBefore(raw_ostream &OS, DWARFDie D, DWARFDie Inner, StringRef Ptr, bool *Word) {
+    bool SubWord;
+    dumpTypeNameBefore(OS, Inner, &SubWord);
+    bool NeedsParens =
+        Inner && (Inner.getTag() == llvm::dwarf::DW_TAG_subroutine_type ||
+                  Inner.getTag() == llvm::dwarf::DW_TAG_array_type);
+    if (NeedsParens)
+      OS << '(';
+    else if (SubWord)
+      OS << ' ';
+    OS << Ptr;
+    if (Word)
+      *Word = false;
+}
+
+static DWARFDie dumpTypeNameBefore(raw_ostream &OS, DWARFDie D, bool *Word) {
+  if (!D) {
+    OS << "void";
+    if (Word)
+      *Word = true;
+    return DWARFDie();
+  }
   if (const char *Name = D.getName(DINameKind::LinkageName)) {
     OS << Name;
-    return;
+    if (Word)
+      *Word = true;
+    return DWARFDie();
   }
 
-  // FIXME: We should have pretty printers per language. Currently we print
-  // everything as if it was C++ and fall back to the TAG type name.
+  DWARFDie Inner = D.getAttributeValueAsReferencedDie(DW_AT_type);
   const dwarf::Tag T = D.getTag();
   switch (T) {
-  case DW_TAG_array_type:
-  case DW_TAG_pointer_type:
-  case DW_TAG_ptr_to_member_type:
+  case DW_TAG_pointer_type: {
+    dumpPointerLikeTypeBefore(OS, D, Inner, "*", Word);
+    break;
+  }
+  case DW_TAG_subroutine_type: {
+    bool SubWord;
+    dumpTypeNameBefore(OS, Inner, &SubWord);
+    if (SubWord) {
+      OS << ' ';
+    }
+    if (Word)
+      *Word = false;
+    break;
+  }
+  case DW_TAG_array_type: {
+    bool SubWord;
+    dumpTypeNameBefore(OS, Inner, &SubWord);
+    if (SubWord)
+      OS << ' ';
+    if (Word)
+      *Word = false;
+    break;
+  }
   case DW_TAG_reference_type:
+    dumpPointerLikeTypeBefore(OS, D, Inner, "&", Word);
+    break;
   case DW_TAG_rvalue_reference_type:
-  case DW_TAG_subroutine_type:
+    dumpPointerLikeTypeBefore(OS, D, Inner, "&&", Word);
+    break;
+  case DW_TAG_ptr_to_member_type: {
+    bool SubWord;
+    dumpTypeNameBefore(OS, Inner, &SubWord);
+    bool NeedsParens =
+        Inner && (Inner.getTag() == llvm::dwarf::DW_TAG_subroutine_type ||
+                  Inner.getTag() == llvm::dwarf::DW_TAG_array_type);
+    if (NeedsParens)
+      OS << '(';
+    else if (SubWord)
+      OS << ' ';
+    if (DWARFDie Cont =
+            D.getAttributeValueAsReferencedDie(DW_AT_containing_type)) {
+      dumpTypeName(OS, Cont);
+      OS << "::";
+    }
+    OS << "*";
+    if (Word)
+      *Word = false;
     break;
+  }
   default:
     dumpTypeTagName(OS, T);
+    dumpTypeNameBefore(OS, Inner);
+    break;
   }
+  return Inner;
+}
 
-  // Follow the DW_AT_type if possible.
-  DWARFDie TypeDie = D.getAttributeValueAsReferencedDie(DW_AT_type);
-  dumpTypeName(OS, TypeDie);
-
-  switch (T) {
+static void dumpTypeNameAfter(raw_ostream &OS, DWARFDie D, DWARFDie Inner,
+                              bool SkipFirstParamIfArtificial = false) {
+  if (!D)
+    return;
+  switch(D.getTag()) {
   case DW_TAG_subroutine_type: {
-    if (!TypeDie)
-      OS << "void";
     OS << '(';
     bool First = true;
+    bool RealFirst = true;
     for (const DWARFDie &C : D.children()) {
       if (C.getTag() == DW_TAG_formal_parameter) {
+        if (SkipFirstParamIfArtificial && RealFirst &&
+            C.find(DW_AT_artificial)) {
+          RealFirst = false;
+          continue;
+        }
         if (!First)
           OS << ", ";
         First = false;
@@ -211,28 +282,36 @@ static void dumpTypeName(raw_ostream &OS, const DWARFDie &D) {
     dumpArrayType(OS, D);
     break;
   }
-  case DW_TAG_pointer_type:
-    OS << '*';
-    break;
   case DW_TAG_ptr_to_member_type:
-    if (DWARFDie Cont =
-            D.getAttributeValueAsReferencedDie(DW_AT_containing_type)) {
-      dumpTypeName(OS << ' ', Cont);
-      OS << "::";
-    }
-    OS << '*';
-    break;
   case DW_TAG_reference_type:
-    OS << '&';
-    break;
   case DW_TAG_rvalue_reference_type:
-    OS << "&&";
+  case DW_TAG_pointer_type: {
+    bool NeedsParens =
+        Inner && (Inner.getTag() == llvm::dwarf::DW_TAG_subroutine_type ||
+                  Inner.getTag() == llvm::dwarf::DW_TAG_array_type);
+    if (NeedsParens)
+      OS << ')';
+    dumpTypeNameAfter(OS, Inner, D.getAttributeValueAsReferencedDie(DW_AT_type),
+                      /*SkipFirstParamIfArtificial=*/D.getTag() ==
+                          DW_TAG_ptr_to_member_type);
     break;
+  }
   default:
     break;
   }
 }
 
+/// Recursively dump the DIE type name when applicable.
+static void dumpTypeName(raw_ostream &OS, const DWARFDie &D, bool SkipFirstParamIfArtificial) {
+  if (!D.isValid() || D.isNULL())
+    return;
+
+  // FIXME: We should have pretty printers per language. Currently we print
+  // everything as if it was C++ and fall back to the TAG type name.
+  DWARFDie Inner = dumpTypeNameBefore(OS, D);
+  dumpTypeNameAfter(OS, D, Inner, SkipFirstParamIfArtificial);
+}
+
 static void dumpAttribute(raw_ostream &OS, const DWARFDie &Die,
                           const DWARFAttribute &AttrValue, unsigned Indent,
                           DIDumpOptions DumpOpts) {
index d324472..592e3a5 100644 (file)
@@ -56,7 +56,7 @@
 ; CHECK:         DW_AT_name    ("p")
 ; CHECK:         DW_AT_decl_file       ("/tmp{{[/\\]}}dwarf-basics.c")
 ; CHECK:         DW_AT_decl_line       (5)
-; CHECK:         DW_AT_type    (0x{{.*}} "X*")
+; CHECK:         DW_AT_type    (0x{{.*}} "X *")
 
 ; CHECK:       NULL
 
@@ -81,7 +81,7 @@
 
 ; CHECK:       DW_TAG_member
 ; CHECK:         DW_AT_name    ("a")
-; CHECK:         DW_AT_type    (0x{{.*}} "*")
+; CHECK:         DW_AT_type    (0x{{.*}} "void *")
 ; CHECK:         DW_AT_decl_file       ("/tmp{{[/\\]}}dwarf-basics.c")
 ; CHECK:         DW_AT_decl_line       (2)
 ; CHECK:         DW_AT_data_member_location    (0)
index 5636151..4a65078 100644 (file)
@@ -8,7 +8,7 @@
 ; CHECK: [[ALCDIE:0x.+]]:       DW_TAG_variable
 ; CHECK:                          DW_AT_type    ({{0x[0-9]+}} "logical")
 ; CHECK: [[LOCDIE:0x.+]]:       DW_TAG_variable
-; CHECK:                          DW_AT_type    ({{0x[0-9]+}} "integer*")
+; CHECK:                          DW_AT_type    ({{0x[0-9]+}} "integer *")
 ; CHECK:                          DW_AT_artificial    (true)
 ; CHECK:  DW_TAG_variable
 ; CHECK:    DW_AT_name  ("arr")
index eda7852..1039fad 100644 (file)
@@ -8,7 +8,7 @@
 ; CHECK: [[ALCDIE:0x.+]]:       DW_TAG_variable
 ; CHECK:                          DW_AT_type    ({{0x[0-9]+}} "logical")
 ; CHECK: [[LOCDIE:0x.+]]:       DW_TAG_variable
-; CHECK:                          DW_AT_type    ({{0x[0-9]+}} "integer*")
+; CHECK:                          DW_AT_type    ({{0x[0-9]+}} "integer *")
 ; CHECK:                          DW_AT_artificial    (true)
 ; CHECK:  DW_TAG_variable
 ; CHECK:    DW_AT_name  ("arr")
index 883bab1..095509f 100644 (file)
@@ -11,7 +11,7 @@
 ; CHECK-NOT: {{DW_TAG|NULL}}
 ; CHECK:     DW_TAG_member
 ; CHECK-NEXT:       DW_AT_name    ("x")
-; CHECK-NEXT:       DW_AT_type    ({{.*}} "int[1]"
+; CHECK-NEXT:       DW_AT_type    ({{.*}} "int [1]"
 
 ; But make sure we still use a type unit for an anonymous type that still has a
 ; name for linkage purposes (due to being defined in a typedef).
index 5383646..cb2e3e6 100644 (file)
@@ -17,7 +17,7 @@
 
 ; CHECK: 0x00000026:   DW_TAG_variable
 ; CHECK-NEXT:                DW_AT_name        ("foo")
-; CHECK-NEXT:                DW_AT_type        (0x00000037 "int*")
+; CHECK-NEXT:                DW_AT_type        (0x00000037 "int *")
 ; CHECK-NEXT:                DW_AT_external    (true)
 ; CHECK-NEXT:                DW_AT_decl_file   ("/usr/local/google/home/sbc/dev/wasm/simple{{[/\\]}}test.c")
 ; CHECK-NEXT:                DW_AT_decl_line   (4)
 
 ; CHECK: 0x00000043:   DW_TAG_variable
 ; CHECK-NEXT:                DW_AT_name        ("ptr2")
-; CHECK-NEXT:                DW_AT_type        (0x00000054 "void()*")
+; CHECK-NEXT:                DW_AT_type        (0x00000054 "void (*)()")
 ; CHECK-NEXT:                DW_AT_external    (true)
 ; CHECK-NEXT:                DW_AT_decl_file   ("/usr/local/google/home/sbc/dev/wasm/simple{{[/\\]}}test.c")
 ; CHECK-NEXT:                DW_AT_decl_line   (5)
 ; CHECK-NEXT:                DW_AT_location    (DW_OP_addr 0x4)
 
 ; CHECK: 0x00000054:   DW_TAG_pointer_type
-; CHECK-NEXT:                DW_AT_type        (0x00000059 "void()")
+; CHECK-NEXT:                DW_AT_type        (0x00000059 "void ()")
 
 ; CHECK: 0x00000059:   DW_TAG_subroutine_type
 ; CHECK-NEXT:                DW_AT_prototyped  (true)
@@ -70,7 +70,7 @@
 
 ; SPLIT:      0x00000019:   DW_TAG_variable
 ; SPLIT-NEXT:                 DW_AT_name      ("foo")
-; SPLIT-NEXT:                 DW_AT_type      (0x00000024 "int*")
+; SPLIT-NEXT:                 DW_AT_type      (0x00000024 "int *")
 ; SPLIT-NEXT:                 DW_AT_external  (true)
 ; SPLIT-NEXT:                 DW_AT_decl_file (0x01)
 ; SPLIT-NEXT:                 DW_AT_decl_line (4)
 
 ; SPLIT:      0x0000002d:   DW_TAG_variable
 ; SPLIT-NEXT:                 DW_AT_name      ("ptr2")
-; SPLIT-NEXT:                 DW_AT_type      (0x00000038 "void()*")
+; SPLIT-NEXT:                 DW_AT_type      (0x00000038 "void (*)()")
 ; SPLIT-NEXT:                 DW_AT_external  (true)
 ; SPLIT-NEXT:                 DW_AT_decl_file (0x01)
 ; SPLIT-NEXT:                 DW_AT_decl_line (5)
 ; SPLIT-NEXT:                 DW_AT_location  (DW_OP_GNU_addr_index 0x1)
 
 ; SPLIT:      0x00000038:   DW_TAG_pointer_type
-; SPLIT-NEXT:                 DW_AT_type      (0x0000003d "void()")
+; SPLIT-NEXT:                 DW_AT_type      (0x0000003d "void ()")
 
 ; SPLIT:      0x0000003d:   DW_TAG_subroutine_type
 ; SPLIT-NEXT:                 DW_AT_prototyped        (true)
index ff711ed..603f610 100644 (file)
@@ -16,7 +16,7 @@
 
 ; CHECK: 0x0000002a:   DW_TAG_variable
 ; CHECK-NEXT:                DW_AT_name [DW_FORM_strp]       ("foo")
-; CHECK-NEXT:                DW_AT_type [DW_FORM_ref4]       (0x0000003f "int*")
+; CHECK-NEXT:                DW_AT_type [DW_FORM_ref4]       (0x0000003f "int *")
 ; CHECK-NEXT:                DW_AT_external [DW_FORM_flag_present]   (true)
 ; CHECK-NEXT:                DW_AT_decl_file [DW_FORM_data1] ("/usr/local/google/home/sbc/dev/wasm/simple{{[/\\]}}test.c")
 ; CHECK-NEXT:                DW_AT_decl_line [DW_FORM_data1] (4)
 
 ; CHECK: 0x0000004b:   DW_TAG_variable
 ; CHECK-NEXT:                DW_AT_name [DW_FORM_strp]       ("ptr2")
-; CHECK-NEXT:                DW_AT_type [DW_FORM_ref4]       (0x00000060 "void()*")
+; CHECK-NEXT:                DW_AT_type [DW_FORM_ref4]       (0x00000060 "void (*)()")
 ; CHECK-NEXT:                DW_AT_external [DW_FORM_flag_present]   (true)
 ; CHECK-NEXT:                DW_AT_decl_file [DW_FORM_data1] ("/usr/local/google/home/sbc/dev/wasm/simple{{[/\\]}}test.c")
 ; CHECK-NEXT:                DW_AT_decl_line [DW_FORM_data1] (5)
 ; CHECK-NEXT:                DW_AT_location [DW_FORM_exprloc]        (DW_OP_addr 0x8)
 
 ; CHECK: 0x00000060:   DW_TAG_pointer_type
-; CHECK-NEXT:                DW_AT_type [DW_FORM_ref4]       (0x00000065 "void()")
+; CHECK-NEXT:                DW_AT_type [DW_FORM_ref4]       (0x00000065 "void ()")
 
 ; CHECK: 0x00000065:   DW_TAG_subroutine_type
 ; CHECK-NEXT:                DW_AT_prototyped [DW_FORM_flag_present] (true)
index 3c36aac..a37b4f3 100644 (file)
@@ -56,4 +56,4 @@ DWARF:     DW_TAG_formal_parameter
 DWARF:       DW_AT_name    ("argv")
 DWARF:       DW_AT_decl_file       (0x00)
 DWARF:       DW_AT_decl_line       (7)
-DWARF:       DW_AT_type    (0x0000009d "char**")
+DWARF:       DW_AT_type    (0x0000009d "char **")
index d9d212c..d94a377 100644 (file)
@@ -1,7 +1,7 @@
 # RUN: llvm-mc %s -filetype obj -triple x86_64-apple-darwin -o %t.o
 # RUN: llvm-dwarfdump -diff %t.o | FileCheck %s
 
-# CHECK: DW_AT_type ("A*")
+# CHECK: DW_AT_type ("A *")
 # CHECK: DW_AT_specification ("A")
 # CHECK: DW_AT_object_pointer ()
 
index afeee4c..74e52c2 100644 (file)
@@ -7,19 +7,22 @@
 # CHECK:   DW_AT_type{{.*}}"int"
 
 # pointer_type
-# CHECK:   DW_AT_type{{.*}}"int*"
+# CHECK:   DW_AT_type{{.*}}"int *"
 
 # reference_type
-# CHECK:   DW_AT_type{{.*}}"int&"
+# CHECK:   DW_AT_type{{.*}}"int &"
 
 # rvalue_reference_type
-# CHECK:   DW_AT_type{{.*}}"int&&"
+# CHECK:   DW_AT_type{{.*}}"int &&"
 
 # ptr_to_member_type
 # CHECK:   DW_AT_type{{.*}}"int foo::*"
 
+# ptr_to_member_type to a member function
+# CHECK:   DW_AT_type{{.*}}"void (foo::*)(int)"
+
 # array_type
-# CHECK:   DW_AT_type{{.*}}"int
+# CHECK:   DW_AT_type{{.*}}"int{{ }}
 # Testing with a default lower bound of 0 and the following explicit bounds:
 #   lower_bound(1)
 # CHECK-NOT: {{.}}
 
 
 # subroutine types
-# CHECK:   DW_AT_type{{.*}}"int()"
-# CHECK:   DW_AT_type{{.*}}"void(int)"
-# CHECK:   DW_AT_type{{.*}}"void(int, int)"
+# CHECK:   DW_AT_type{{.*}}"int ()"
+# CHECK:   DW_AT_type{{.*}}"void (int)"
+# CHECK:   DW_AT_type{{.*}}"void (int, int)"
+# CHECK:   DW_AT_type{{.*}}"void (*)(foo *, int)"
 
 # array_type with a language with a default lower bound of 1 instead of 0 and
 # an upper bound of 2. This describes an array with 2 elements (whereas with a
 # default lower bound of 0 it would be an array of 3 elements)
-# CHECK: DW_AT_type{{.*}}"int[2]"
+# CHECK: DW_AT_type{{.*}}"int [2]"
 
        .section        .debug_str,"MS",@progbits,1
 .Lint_name:
        .byte   0                       # DW_CHILDREN_no
        .byte   0                       # EOM(1)
        .byte   0                       # EOM(2)
+       .byte   19                      # Abbreviation Code
+       .byte   0x5                     # DW_TAG_formal_parameter
+       .byte   0                       # DW_CHILDREN_no
+       .byte   73                      # DW_AT_type
+       .byte   19                      # DW_FORM_ref4
+       .byte   0x34                    # DW_AT_artificial
+       .byte   0x19                    # DW_FORM_flag_present
+       .byte   0                       # EOM(1)
+       .byte   0                       # EOM(2)
        .byte   0                       # EOM(3)
        .section        .debug_info,"",@progbits
 .Lcu_begin:
 .Lstruct_type:
        .byte   14                      # DW_TAG_structure_type
        .long   .Lfoo_name              #   DW_AT_name
+.Lstruct_ptr_type:
+       .byte   4                       # DW_TAG_pointer_type
+       .long   .Lstruct_type - .Lcu_begin #   DW_AT_type
 .Lptr_to_member_type:
        .byte   7                       # DW_TAG_ptr_to_member_type
        .long   .Lint_type - .Lcu_begin #   DW_AT_type
        .long   .Lstruct_type - .Lcu_begin #   DW_AT_containing_type
+.Lptr_to_member_type_function:
+       .byte   7                       # DW_TAG_ptr_to_member_type
+       .long   .Lsub_void_foo_int_type - .Lcu_begin #   DW_AT_type
+       .long   .Lstruct_type - .Lcu_begin #   DW_AT_containing_type
 .Larray_type:
        .byte   8                       # DW_TAG_array_type
        .long   .Lint_type - .Lcu_begin #   DW_AT_type
        .byte   17                       #   DW_TAG_formal_parameter
        .long   .Lint_type - .Lcu_begin #     DW_AT_type
        .byte   0                       # End Of Children Mark
+.Lsub_void_foo_int_type:
+       .byte   16                      # DW_TAG_subroutine_type
+       .byte   19                      #   DW_TAG_formal_parameter
+       .long   .Lstruct_ptr_type - .Lcu_begin #     DW_AT_type
+       .byte   17                      #   DW_TAG_formal_parameter
+       .long   .Lint_type - .Lcu_begin #     DW_AT_type
+       .byte   0                       # End Of Children Mark
+.Lpointer_to_function_type:
+       .byte   4                       # DW_TAG_pointer_type
+       .long   .Lsub_void_foo_int_type - .Lcu_begin #   DW_AT_type
 
        .byte   3                       # DW_TAG_variable
        .long   .Lint_type - .Lcu_begin #   DW_AT_type
        .byte   3                       # DW_TAG_variable
        .long   .Lptr_to_member_type - .Lcu_begin #   DW_AT_type
        .byte   3                       # DW_TAG_variable
+       .long   .Lptr_to_member_type_function - .Lcu_begin #   DW_AT_type
+       .byte   3                       # DW_TAG_variable
        .long   .Larray_type - .Lcu_begin #   DW_AT_type
        .byte   3                       # DW_TAG_variable
        .long   .Lsub_int_empty_type - .Lcu_begin #   DW_AT_type
        .long   .Lsub_void_int_type - .Lcu_begin #   DW_AT_type
        .byte   3                       # DW_TAG_variable
        .long   .Lsub_void_int_int_type - .Lcu_begin #   DW_AT_type
+       .byte   3                       # DW_TAG_variable
+       .long   .Lpointer_to_function_type - .Lcu_begin #   DW_AT_type
        .byte   0                       # End Of Children Mark
 .Lunit_end:
 .Lcu2_begin: