Quuxplusone / LLVMBugzillaTest

0 stars 0 forks source link

[CodeView] Misssing LF_NESTTYPE with nested templates (structure/class) #45364

Open Quuxplusone opened 4 years ago

Quuxplusone commented 4 years ago
Bugzilla Link PR46394
Status CONFIRMED
Importance P enhancement
Reported by Carlos Alberto Enciso (international.phantom@gmail.com)
Reported on 2020-06-18 22:11:53 -0700
Last modified on 2020-09-09 12:45:52 -0700
Version trunk
Hardware PC All
CC dblaikie@gmail.com, greg.bedwell@sony.com, jdevlieghere@apple.com, jeremy.morse.llvm@gmail.com, keith.walker@arm.com, llvm-bugs@lists.llvm.org, orlando.hyams@sony.com, paul_robinson@playstation.sony.com, rnk@google.com
Fixed by commit(s)
Attachments
Blocks
Blocked by
See also
Given the following test case:

//---------------------------------------------------------

template <typename T> struct Template_A {
  template <typename U> struct Template_B { U Member_TB; };
  Template_B<char> Member_TA;
};

Template_A<int> MyTemplate;

struct Struct_A {
  struct Struct_B {
    char Member_SB;
  };
  Struct_B Member_SA;
};

Struct_A MyClass;

//---------------------------------------------------------

Using the command line to generate debug info CodeView:

clang -c -g -O0 test.cpp -o test-cv.o -gcodeview --target=x86_64-windows

The output generated by llvm-pdbutil, shows that Clang does emit the
LF_NESTTYPE for nested structure/class. But it does not emit it if the
structure/class is a template.

The LF_STRUCTURE in the case of templates, does not have the 'contains nested
class' flag.

----------
`Struct_A`
----------
0x100C | LF_STRUCTURE `Struct_A`
         field list: 0x100B
         options: contains nested class     ** Nested Info **

0x100B | LF_FIELDLIST
         - LF_MEMBER [name = `Member_SA`, Type = 0x100A]
         - LF_NESTTYPE [name = `Struct_B`, parent = 0x100A]

-----------------
`Template_A<int>`
-----------------
0x1003 | LF_STRUCTURE `Template_A<int>`
         field list: 0x1002
         options:                           ** Missing Nested Info **

0x1002 | LF_FIELDLIST
         - LF_MEMBER [name = `Member_TA`, Type = 0x1001]

MSVC emits the LF_NESTTYPE for all cases (templates and non-templates).

----------
`Struct_A`
----------
0x1009 | LF_STRUCTURE `Struct_A`
         field list: 0x1008
         options: contains nested class     ** Nested Info **

0x1008 | LF_FIELDLIST
         - LF_NESTTYPE [name = `Struct_B`, parent = 0x1007]
         - LF_MEMBER [name = `Member_SA`, Type = 0x1007]

-----------------
`Template_A<int>`
-----------------
0x1003 | LF_STRUCTURE `Template_A<int>`
         field list: 0x1002
         options: contains nested class     ** Nested Info **

0x1002 | LF_FIELDLIST
         - LF_NESTTYPE [name = `?$Template_B@D`, parent = 0x1001]
         - LF_MEMBER [name = `Member_TA`, Type = 0x1001]
Quuxplusone commented 4 years ago
This is the output from llvm-diva (Sony's tool under development).

llvm-diva --print=scopes,symbols,types test_dw.c test_cv.o test_ms.o --
attribute=level,format

DWARF Logical View: (Clang)
---------------------------
Logical View:
[000]           {File} 'test_dw.o' -> elf64-x86-64
[001]             {CompileUnit} 'test.cpp'
[002]               {Producer} 'clang version 11.0.0'
[002]    13         {Struct} 'Struct_A'
[003]    17           {Member} public 'Member_SA' -> 'Struct_B'
[003]    14           {Struct} 'Struct_B'
[004]    15             {Member} public 'Member_SB' -> 'char'
[002]     6         {Struct} 'Template_A<int>'
[003]     8           {Member} public 'Member_TA' -> 'Template_B<char>'
[003]     7           {Struct} 'Template_B<char>'
[004]     7             {Member} public 'Member_TB' -> 'char'
[002]    20         {Variable} extern 'MyClass' -> 'Struct_A'
[002]    11         {Variable} extern 'MyTemplate' -> 'Template_A<int>'

CodeView Logical View: (Clang)
------------------------------
Logical View:
[000]           {File} 'test_cv.o' -> COFF-x86-64
[001]             {CompileUnit} 'test.cpp'
[002]               {Producer} 'clang version 11.0.0'
[002]    13         {Struct} 'Struct_A'
[003]                 {Member} public 'Member_SA' -> 'Struct_B'
[003]    14           {Struct} 'Struct_B'
[004]                   {Member} public 'Member_SB' -> 'char'
[002]     6         {Struct} 'Template_A<int>'
[003]                 {Member} public 'Member_TA' ->
'Template_A<int>::Template_B<char>'
[002]               {Variable} extern 'MyClass' -> 'Struct_A'
[002]               {Variable} extern 'MyTemplate' -> 'Template_A<int>'

CodeView Logical View: (MSVC)
-----------------------------
Logical View:
[000]           {File} 'test_ms.o' -> COFF-i386
[001]             {CompileUnit} 'test.cpp'
[002]               {Producer} 'Microsoft (R) Optimizing Compiler'
[002]    13         {Struct} 'Struct_A'
[003]                 {Member} public 'Member_SA' -> 'Struct_B'
[003]    14           {Struct} 'Struct_B'
[004]                   {Member} public 'Member_SB' -> 'char'
[002]    11         {Struct} 'Template_A<int>'
[003]                 {Member} public 'Member_TA' -> 'Template_B<char>'
[003]     8           {Struct} 'Template_B<char>'
[004]                   {Member} public 'Member_TB' -> 'char'
[002]               {Variable} extern 'MyClass' -> 'Struct_A'
[002]               {Variable} extern 'MyTemplate' -> 'Template_A<int>'
Quuxplusone commented 4 years ago
DWARF Logical View: (Clang)
---------------------------
Logical View:
[002]     6         {Struct} 'Template_A<int>'
[003]     7           {Struct} 'Template_B<char>'

CodeView Logical View: (Clang)
------------------------------
[002]     6         {Struct} 'Template_A<int>'

CodeView Logical View: (MSVC)
-----------------------------
[002]    11         {Struct} 'Template_A<int>'
[003]     8           {Struct} 'Template_B<char>'

The logical view for Clang (Codeview) does not show the nested
'Template_B<char>'.
Quuxplusone commented 4 years ago
Thanks. This raises an interesting issue. If we include template instantiations
of any kind in the list of members of a class, the class definition may be
different across different TUs. Consider:

struct Foo {
  template <typename U> struct Template_B { U Member_TB; };
  Template_B<char> Member_TA;
};
Foo MyTemplate;
// Only in one TU
Foo::Template_B<int> MyInnerThing;

Some TUs will produce a field list containing Foo::Template_B<int> and some
will not. I suppose MSVC does this, and Visual Studio copes with it just fine.

We shouldn't change the IR to include the nested template in the member list.
We should do what DWARF does in the backend.
Quuxplusone commented 4 years ago
Some context:

Clang doesn't put any TU-variable members in the class's member list in the
LLVM IR debug info metadata. Those members still refer to the class as their
scope - so they can be found/attached to the class, but they don't come in the
member list. This keeps class definitions identical across translation units
with no merging requirements (this makes LLVM IR linking simpler, and makes
DWARF type units similarly simpler to implement).

Things that can vary:

1) template members (static member variable templates, static and non-static
member function templates, etc)
2) nested types of any kind (template or otherwise) - because they may be only
declared sometimes but declared-and-defined in other TUs, etc
3) implicit special members (copy/move ctor/assignment operator/dtor, default
ctor)

Those are the cases I'm aware of/there might be others, might be bugs/places
where it's not implemented that way. (I think maybe on the Windows side the
implicit special members have already had different handling applied? Some
attempt to instantiate them even in TUs that don't cause an instantiation in
their source?)