DebugInfo: Correct/improve type formatting (pointers to function types especially)

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)
This commit is contained in:
David Blaikie 2021-09-03 12:18:13 -07:00
parent 6df09d6ccb
commit 40f1593558
10 changed files with 170 additions and 57 deletions

View 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_reference_type:
case DW_TAG_rvalue_reference_type:
case DW_TAG_subroutine_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:
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) {

View 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)

View 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")

View 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")

View 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).

View 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)
@ -33,14 +33,14 @@
; 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)
@ -86,14 +86,14 @@
; 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)

View 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)
@ -32,14 +32,14 @@
; 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)

View 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 **")

View 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 ()

View 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: {{.}}
@ -45,14 +48,15 @@
# 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:
@ -188,6 +192,15 @@
.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:
@ -213,10 +226,17 @@
.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
@ -252,6 +272,16 @@
.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
@ -264,6 +294,8 @@
.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
@ -271,6 +303,8 @@
.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: