[PATCH] D54667: [CodeView] Emit proper debug info for ref-qualified member functions

Zachary Turner via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Sat Nov 17 08:57:34 PST 2018


zturner created this revision.
zturner added a reviewer: rnk.
Herald added subscribers: JDevlieghere, hiraditya, aprantl.

When you have a member function with a ref-qualifier, for example:

  struct Foo {
    void Func() &;
    void Func2() &&;
  };

clang-cl was not emitting this information.  Doing so is a bit awkward, because it's not a property of the `LF_MFUNCTION` type, which is what you'd expect.  Instead, it's a property of the `this` pointer which is actually an `LF_POINTER`.  This record has an attributes bitmask on it, and our handling of this bitmask was all wrong.  We had some parts of the bitmask defined incorrectly, but importantly for this bug, we didn't know about these extra 2 bits that represent the ref qualifier at all.  We can see this in the reference implementation here:

https://github.com/Microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L1524-L1525

And we can also observe that clang-cl and cl emit different records by compiling a simple program and using `llvm-pdbutil` to dump the results of each one.

  // cat foo.cpp
  struct A {
    int NoRefQual();
  
    int RefQual() &;
    int RefQual() &&;
  
    int LValueRef() &;
  
    int RValueRef() &&;
  };
  
  int main(int argc, char **argv) {
    A *GenericPtr = nullptr;
    A a;
    return 0;
  }

With clang:

  $ clang-cl.exe /Z7 /GS- /GR- foo.cpp /o foo-clang.exe
  
  $ llvm-pdbutil.exe dump -types -type-index=0x1009 -dependents -type-data foo-clang.pdb
  
  
                       Types (TPI Stream)
  ============================================================
    Showing 1 records and their dependents (7 records total)
     0x1003 | LF_STRUCTURE [size = 32] `A`
              unique name: `.?AUA@@`
              vtable: <no type>, base list: <no type>, field list: <no type>
              options: forward ref (-> 0x1009) | has unique name, sizeof 0
             Bytes (
               0000: 1E000515 00008002 00000000 00000000 00000000 00004100 2E3F4155 41404000  |......................A..?AUA@@.|
             )
     0x1004 | LF_POINTER [size = 12]
              referent = 0x1003, mode = pointer, opts = None, kind = ptr64
             Bytes (
               0000: 0A000210 03100000 0C000100                                               |............|
             )
     0x1005 | LF_ARGLIST [size = 8]
             Bytes (
               0000: 06000112 00000000                                                        |........|
             )
     0x1006 | LF_MFUNCTION [size = 28]
              return type = 0x0074 (int), # args = 0, param list = 0x1005
              class type = 0x1003, this type = 0x1004, this adjust = 0
              calling conv = cdecl, options = None
             Bytes (
               0000: 1A000910 74000000 03100000 04100000 00000000 05100000 00000000           |....t.......................|
             )
     0x1007 | LF_METHODLIST [size = 20]
              - Method [type = 0x1006, vftable offset = -1, attrs = public]
              - Method [type = 0x1006, vftable offset = -1, attrs = public]
             Bytes (
               0000: 12000612 03000000 06100000 03000000 06100000                             |....................|
             )
     0x1008 | LF_FIELDLIST [size = 80]
              - LF_ONEMETHOD [name = `NoRefQual`]
                type = 0x1006, vftable offset = -1, attrs = public
                Bytes (
                  0000: 03000610 00004E6F 52656651 75616C00                                      |......NoRefQual.|
                )
              - LF_METHOD [name = `RefQual`, # overloads = 2, overload list = 0x1007]
                Bytes (
                  0000: 02000710 00005265 66517561 6C00                                          |......RefQual.|
                )
              - LF_ONEMETHOD [name = `LValueRef`]
                type = 0x1006, vftable offset = -1, attrs = public
                Bytes (
                  0000: 03000610 00004C56 616C7565 52656600                                      |......LValueRef.|
                )
              - LF_ONEMETHOD [name = `RValueRef`]
                type = 0x1006, vftable offset = -1, attrs = public
                Bytes (
                  0000: 03000610 00005256 616C7565 52656600                                      |......RValueRef.|
                )
             Bytes (
               0000: 4E000312 11150300 06100000 4E6F5265 66517561 6C00F2F1 0F150200 07100000  |N...........NoRefQual...........|
               0020: 52656651 75616C00 11150300 06100000 4C56616C 75655265 6600F2F1 11150300  |RefQual.........LValueRef.......|
               0040: 06100000 5256616C 75655265 6600F2F1                                      |....RValueRef...|
             )
     0x1009 | LF_STRUCTURE [size = 32] `A`
              unique name: `.?AUA@@`
              vtable: <no type>, base list: <no type>, field list: 0x1008
              options: has unique name, sizeof 1
             Bytes (
               0000: 1E000515 05000002 08100000 00000000 00000000 01004100 2E3F4155 41404000  |......................A..?AUA@@.|
             )

If we follow the type indices here, we can see that `RValueRef`, `LValueRef`, both overloads of `RefQual` and also `NoRefQual` all have the same `LF_MFUNCTION` type, which is 0x1006.  Since they have the same `LF_MFUNCTION`, they also have the same `LF_POINTER` for the this pointer since it is part of that record.  For the sake of comparison later, the dump of this pointer record is:

  referent = 0x1003, mode = pointer, opts = None, kind = ptr64  0000: 0A000210 03100000 0C000100

With MSVC

  $ cl.exe /Z7 /GS- /GR- foo.cpp /o foo-cl.exe
  
  $ llvm-pdbutil.exe dump -types -type-index=0x100E -dependents -type-data foo-cl.pdb
  
  
                       Types (TPI Stream)
  ============================================================
    Showing 1 records and their dependents (11 records total)
     0x1003 | LF_STRUCTURE [size = 32] `A`
              unique name: `.?AUA@@`
              vtable: <no type>, base list: <no type>, field list: <no type>
              options: forward ref (-> 0x100E) | has unique name, sizeof 0
             Bytes (
               0000: 1E000515 00008002 00000000 00000000 00000000 00004100 2E3F4155 41404000  |......................A..?AUA@@.|
             )
     0x1005 | LF_POINTER [size = 12]
              referent = 0x1003, mode = pointer, opts = const, kind = ptr64
             Bytes (
               0000: 0A000210 03100000 0C040100                                               |............|
             )
     0x1006 | LF_ARGLIST [size = 8]
             Bytes (
               0000: 06000112 00000000                                                        |........|
             )
     0x1007 | LF_MFUNCTION [size = 28]
              return type = 0x0074 (int), # args = 0, param list = 0x1006
              class type = 0x1003, this type = 0x1005, this adjust = 0
              calling conv = cdecl, options = None
             Bytes (
               0000: 1A000910 74000000 03100000 05100000 00000000 06100000 00000000           |....t.......................|
             )
     0x1008 | LF_POINTER [size = 12]
              referent = 0x1003, mode = pointer, opts = const, kind = ptr64
             Bytes (
               0000: 0A000210 03100000 0C042100                                               |..........!.|
             )
     0x1009 | LF_MFUNCTION [size = 28]
              return type = 0x0074 (int), # args = 0, param list = 0x1006
              class type = 0x1003, this type = 0x1008, this adjust = 0
              calling conv = cdecl, options = None
             Bytes (
               0000: 1A000910 74000000 03100000 08100000 00000000 06100000 00000000           |....t.......................|
             )
     0x100A | LF_POINTER [size = 12]
              referent = 0x1003, mode = pointer, opts = const, kind = ptr64
             Bytes (
               0000: 0A000210 03100000 0C041100                                               |............|
             )
     0x100B | LF_MFUNCTION [size = 28]
              return type = 0x0074 (int), # args = 0, param list = 0x1006
              class type = 0x1003, this type = 0x100A, this adjust = 0
              calling conv = cdecl, options = None
             Bytes (
               0000: 1A000910 74000000 03100000 0A100000 00000000 06100000 00000000           |....t.......................|
             )
     0x100C | LF_METHODLIST [size = 20]
              - Method [type = 0x1009, vftable offset = -1, attrs = public]
              - Method [type = 0x100B, vftable offset = -1, attrs = public]
             Bytes (
               0000: 12000612 03000000 09100000 03000000 0B100000                             |....................|
             )
     0x100D | LF_FIELDLIST [size = 80]
              - LF_ONEMETHOD [name = `NoRefQual`]
                type = 0x1007, vftable offset = -1, attrs = public
                Bytes (
                  0000: 03000710 00004E6F 52656651 75616C00                                      |......NoRefQual.|
                )
              - LF_METHOD [name = `RefQual`, # overloads = 2, overload list = 0x100C]
                Bytes (
                  0000: 02000C10 00005265 66517561 6C00                                          |......RefQual.|
                )
              - LF_ONEMETHOD [name = `LValueRef`]
                type = 0x100B, vftable offset = -1, attrs = public
                Bytes (
                  0000: 03000B10 00004C56 616C7565 52656600                                      |......LValueRef.|
                )
              - LF_ONEMETHOD [name = `RValueRef`]
                type = 0x1009, vftable offset = -1, attrs = public
                Bytes (
                  0000: 03000910 00005256 616C7565 52656600                                      |......RValueRef.|
                )
             Bytes (
               0000: 4E000312 11150300 07100000 4E6F5265 66517561 6C00F2F1 0F150200 0C100000  |N...........NoRefQual...........|
               0020: 52656651 75616C00 11150300 0B100000 4C56616C 75655265 6600F2F1 11150300  |RefQual.........LValueRef.......|
               0040: 09100000 5256616C 75655265 6600F2F1                                      |....RValueRef...|
             )
     0x100E | LF_STRUCTURE [size = 32] `A`
              unique name: `.?AUA@@`
              vtable: <no type>, base list: <no type>, field list: 0x100D
              options: has unique name, sizeof 1
             Bytes (
               0000: 1E000515 05000002 0D100000 00000000 00000000 01004100 2E3F4155 41404000  |......................A..?AUA@@.|
             )

Here we see several different `LF_MFUNCTION` records in use.

0x1009: Used by `Foo::RValueRef` and one overload of `Foo::RefQual`
0x100B: Used by `Foo::LValueRef` and one overload of `Foo::RefQual`
0x1007: Used by `Foo::NoRefQual`

And the only difference between any of them is the "this type" field.  So for this pointer types, we have:

0x1008: Used by `Foo::RValueRef` and one overload of `Foo::RefQual`
0x100A: Used by `Foo::LValueRef` and one overload of `Foo::RefQual`
0x1005: Used by `Foo::NoRefQual`

And if we then go look at the record data of each one, we have:

  0x1008: referent = 0x1003, mode = pointer, opts = const, kind = ptr64  0000: 0A000210 03100000 0C042100
  0x100A: referent = 0x1003, mode = pointer, opts = const, kind = ptr64  0000: 0A000210 03100000 0C041100
  0x1005: referent = 0x1003, mode = pointer, opts = const, kind = ptr64  0000: 0A000210 03100000 0C040100

The data that llvm-pdbutil looks identical for each one, but if we look closer at the record bytes we can see that there is 1 bit difference in each of them.

After this patch, clang-cl emits compatible debug info for this case.

  0x1009: referent = 0x1003, mode = pointer, opts = None, kind = ptr64  0000: 0A000210 03100000 0C002100
  0x1007: referent = 0x1003, mode = pointer, opts = None, kind = ptr64  0000: 0A000210 03100000 0C001100
  0x1004: referent = 0x1003, mode = pointer, opts = None, kind = ptr64  0000: 0A000210 03100000 0C000100


https://reviews.llvm.org/D54667

Files:
  lldb/lit/SymbolFile/NativePDB/member-function-types.cpp
  llvm/include/llvm/DebugInfo/CodeView/CodeView.h
  llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h
  llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
  llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h
  llvm/lib/DebugInfo/CodeView/TypeDumpVisitor.cpp
  llvm/lib/ObjectYAML/CodeViewYAMLTypes.cpp
  llvm/test/DebugInfo/COFF/defer-complete-type.ll
  llvm/test/DebugInfo/COFF/globals.ll
  llvm/test/DebugInfo/COFF/type-quals.ll
  llvm/test/DebugInfo/COFF/types-array-advanced.ll
  llvm/test/DebugInfo/COFF/types-basic.ll
  llvm/test/DebugInfo/COFF/types-calling-conv.ll
  llvm/test/DebugInfo/COFF/types-data-members.ll
  llvm/test/DebugInfo/COFF/types-method-ref-qualifiers.ll
  llvm/test/DebugInfo/COFF/types-non-virtual-methods.ll
  llvm/test/DebugInfo/COFF/types-ptr-to-member.ll
  llvm/test/DebugInfo/COFF/types-recursive-struct.ll
  llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp

-------------- next part --------------
A non-text attachment was scrubbed...
Name: D54667.174507.patch
Type: text/x-patch
Size: 33980 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20181117/7370eadb/attachment.bin>


More information about the llvm-commits mailing list