rnk created this revision.
rnk added reviewers: amccarth, aprantl, dblaikie, dexonsmith.
rnk added a subscriber: cfe-commits.
rnk added a dependency: D22884: [codeview] Emit vftable records.

MSVC emits codeview records describing all the methods contained in a
vftable. This is observable in windbg with 'dt -v'.

Depends on D22884

https://reviews.llvm.org/D22920

Files:
  include/clang/AST/VTableBuilder.h
  lib/AST/VTableBuilder.cpp
  lib/CodeGen/CGDebugInfo.cpp
  lib/CodeGen/CGDebugInfo.h
  test/CodeGenCXX/debug-info-ms-abi.cpp
  test/CodeGenCXX/debug-info-ms-vftables.cpp

Index: test/CodeGenCXX/debug-info-ms-vftables.cpp
===================================================================
--- /dev/null
+++ test/CodeGenCXX/debug-info-ms-vftables.cpp
@@ -0,0 +1,57 @@
+// RUN: %clang_cc1 %s -triple=i686-pc-windows-msvc -debug-info-kind=limited -gcodeview -emit-llvm -o - | FileCheck %s
+
+namespace diamond {
+struct A { virtual void f(); };
+struct B : virtual A { virtual void g(); };
+struct C : virtual A { virtual void h(); };
+struct D : B, C { virtual void j(); };
+D d;
+}
+
+// CHECK: distinct !DICompositeType(tag: DW_TAG_structure_type, name: "D",
+// CHECK-SAME:    elements: ![[elements:[0-9]+]],
+// CHECK-SAME:    identifier: ".?AUD@diamond@@")
+
+// CHECK: ![[elements]] = !{!{{[0-9]+}}, !{{[0-9]+}}, ![[vt_D_B:[0-9]+]], ![[vt_D_A:[0-9]+]], ![[vt_D_C:[0-9]+]], !{{[0-9]+}}}
+
+// CHECK: ![[vt_D_B]] = !DIDerivedType(tag: DW_TAG_LLVM_msvftable, name: "??_7D@diamond@@6BB@1@@",
+// CHECK: ![[vt_D_A]] = !DIDerivedType(tag: DW_TAG_LLVM_msvftable, name: "??_7D@diamond@@6BA@1@@",
+// CHECK: ![[vt_D_C]] = !DIDerivedType(tag: DW_TAG_LLVM_msvftable, name: "??_7D@diamond@@6BC@1@@",
+
+// This test tries to exercise all four of the possible MS ABI manglings for
+// C++ virtual methods:
+// - normal methods
+// - virtual dtors
+// - virtual dtor thunks
+// - virtual method thunks
+namespace thunks {
+struct A {
+  virtual ~A();
+  virtual void f();
+};
+struct B {
+  virtual ~B();
+  virtual void f();
+};
+struct C : A, B {
+  virtual ~C();
+  virtual void f();
+};
+C c;
+}
+
+// CHECK: ![[C_thunks:[0-9]+]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "C",
+// CHECK-SAME:    elements: ![[elements:[0-9]+]],
+// CHECK-SAME:    identifier: ".?AUC@thunks@@")
+
+// CHECK: ![[elements]] = !{!{{[0-9]+}}, !{{[0-9]+}}, ![[vt_C_A:[0-9]+]], ![[vt_C_B:[0-9]+]], !{{[0-9]+}}, !{{[0-9]+}}}
+
+// CHECK: ![[vt_C_A]] = !DIDerivedType(tag: DW_TAG_LLVM_msvftable, name: "??_7C@thunks@@6BA@1@@",
+// CHECK-SAME:   extraData: ![[vt_C_A_methods:[0-9]+]])
+
+// CHECK: ![[vt_C_A_methods]] = !{!"??_GC@thunks@@UAEPAXI@Z", !"?f@C@thunks@@UAEXXZ"}
+
+// CHECK: ![[vt_C_B]] = !DIDerivedType(tag: DW_TAG_LLVM_msvftable, name: "??_7C@thunks@@6BB@1@@",
+// CHECK-SAME:   extraData: ![[vt_C_B_methods:[0-9]+]])
+
+// CHECK: ![[vt_C_B_methods]] = !{!"??_EC@thunks@@W3AEPAXI@Z", !"?f@C@thunks@@W3AEXXZ"}
Index: test/CodeGenCXX/debug-info-ms-abi.cpp
===================================================================
--- test/CodeGenCXX/debug-info-ms-abi.cpp
+++ test/CodeGenCXX/debug-info-ms-abi.cpp
@@ -15,7 +15,11 @@
 // CHECK-SAME: elements: ![[elements:[0-9]+]]
 // CHECK-SAME: identifier: ".?AUFoo@@"
 
-// CHECK: ![[elements]] = !{![[vptr:[0-9]+]], ![[Nested:[0-9]+]], ![[f:[0-9]+]], ![[g:[0-9]+]], ![[h:[0-9]+]]}
+// CHECK: ![[elements]] = !{![[vftable:[0-9]+]], ![[vptr:[0-9]+]], ![[Nested:[0-9]+]], ![[f:[0-9]+]], ![[g:[0-9]+]], ![[h:[0-9]+]]}
+
+// CHECK: ![[vftable]] = !DIDerivedType(tag: DW_TAG_LLVM_msvftable, name: "??_7Foo@@6B@", scope: ![[Foo]], baseType: null, extraData: ![[methodlist:[0-9]+]])
+
+// CHECK: ![[methodlist]] = !{!"?f@Foo@@UAEXXZ", !"?g@Foo@@UAEXXZ", !"?h@Foo@@UAEXXZ"}
 
 // CHECK: ![[Nested]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Nested",
 // CHECK-SAME: identifier: ".?AUNested@Foo@@"
Index: lib/CodeGen/CGDebugInfo.h
===================================================================
--- lib/CodeGen/CGDebugInfo.h
+++ lib/CodeGen/CGDebugInfo.h
@@ -22,6 +22,7 @@
 #include "clang/Frontend/CodeGenOptions.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/Optional.h"
+#include "llvm/ADT/TinyPtrVector.h"
 #include "llvm/IR/DIBuilder.h"
 #include "llvm/IR/DebugInfo.h"
 #include "llvm/IR/ValueHandle.h"
@@ -40,6 +41,7 @@
 class ObjCIvarDecl;
 class UsingDecl;
 class VarDecl;
+struct VPtrInfo;
 
 namespace CodeGen {
 class CodeGenModule;
@@ -128,6 +130,7 @@
       NamespaceAliasCache;
   llvm::DenseMap<const Decl *, llvm::TypedTrackingMDRef<llvm::DIDerivedType>>
       StaticDataMemberCache;
+  llvm::DenseMap<const VPtrInfo *, llvm::TrackingMDRef> MSVFTableCache;
 
   /// Helper functions for getOrCreateType.
   /// @{
@@ -263,7 +266,13 @@
   /// If the C++ class has vtable info then insert appropriate debug
   /// info entry in EltTys vector.
   void CollectVTableInfo(const CXXRecordDecl *Decl, llvm::DIFile *F,
-                         SmallVectorImpl<llvm::Metadata *> &EltTys);
+                         SmallVectorImpl<llvm::Metadata *> &EltTys,
+                         llvm::DIType *RecordTy);
+
+  /// Get the DW_TAG_LLVM_msvftable type for this vptr.
+  llvm::DIType *getMSVFTableType(const VPtrInfo *VPI, const CXXRecordDecl *RD,
+                                 llvm::DIType *RecordTy);
+
   /// @}
 
   /// Create a new lexical block node and push it on the stack.
Index: lib/CodeGen/CGDebugInfo.cpp
===================================================================
--- lib/CodeGen/CGDebugInfo.cpp
+++ lib/CodeGen/CGDebugInfo.cpp
@@ -1546,9 +1546,19 @@
 }
 
 void CGDebugInfo::CollectVTableInfo(const CXXRecordDecl *RD, llvm::DIFile *Unit,
-                                    SmallVectorImpl<llvm::Metadata *> &EltTys) {
+                                    SmallVectorImpl<llvm::Metadata *> &EltTys,
+                                    llvm::DIType *RecordTy) {
   const ASTRecordLayout &RL = CGM.getContext().getASTRecordLayout(RD);
 
+  // Emit information about MS ABI vftables, primarily for the benefit of
+  // codeview.
+  if (CGM.getTarget().getCXXABI().isMicrosoft() && RD->isDynamicClass()) {
+    const VPtrInfoVector &VFPtrs =
+        CGM.getMicrosoftVTableContext().getVFPtrOffsets(RD);
+    for (const VPtrInfo *VPI : VFPtrs)
+      EltTys.push_back(getMSVFTableType(VPI, RD, RecordTy));
+  }
+
   // If there is a primary base then it will hold vtable info.
   if (RL.getPrimaryBase())
     return;
@@ -1564,6 +1574,88 @@
   EltTys.push_back(VPTR);
 }
 
+llvm::DIType *CGDebugInfo::getMSVFTableType(const VPtrInfo *VPI,
+                                            const CXXRecordDecl *RD,
+                                            llvm::DIType *RecordTy) {
+  assert(CGM.getTarget().getCXXABI().isMicrosoft());
+
+  // Check the cache to see if we've built vftable info already.
+  auto I = MSVFTableCache.find(VPI);
+  if (I != MSVFTableCache.end())
+    return cast<llvm::DIType>(I->second.get());
+
+  // Get the name of the vftable and it's offset.
+  auto &Mangler =
+      cast<MicrosoftMangleContext>(CGM.getCXXABI().getMangleContext());
+  SmallString<256> VFTableName;
+  llvm::raw_svector_ostream VFTableNameOS(VFTableName);
+  Mangler.mangleCXXVFTable(RD, VPI->MangledPath, VFTableNameOS);
+  uint64_t VFPtrOffsetInBits = CGM.getContext().toBits(VPI->FullOffsetInMDC);
+
+  // Get the overridden vftable type.
+  llvm::DIType *OverriddenVFTable = nullptr;
+  if (VPI->OverriddenVPtr) {
+    const CXXRecordDecl *Base = VPI->DirectBase;
+    llvm::DIType *BaseTy = getOrCreateRecordType(
+        CGM.getContext().getRecordType(Base), Base->getLocation());
+    OverriddenVFTable = getMSVFTableType(VPI->OverriddenVPtr, Base, BaseTy);
+  }
+
+  // Compute the mangled names used to fill out the vftable. Keep in sync with
+  // CodeGenVTables::CreateVTableInitializer.
+  const VTableLayout &VL = CGM.getMicrosoftVTableContext().getVFTableLayout(
+      RD, VPI->FullOffsetInMDC);
+  const VTableComponent *VComponents = VL.vtable_component_begin();
+  unsigned NumComponents = VL.getNumVTableComponents();
+  const auto *VTableThunks = VL.vtable_thunk_begin();
+  unsigned NumVTableThunks = VL.getNumVTableThunks();
+  unsigned NextVTableThunkIndex = 0;
+  SmallVector<llvm::Metadata *, 4> MethodNames;
+  for (unsigned I = 0; I < NumComponents; I++) {
+    const VTableComponent &VC = VComponents[I];
+    // Skip RTTI and other Itanium stuff.
+    if (!VC.isFunctionPointerKind())
+      continue;
+
+    const CXXMethodDecl *MD = VC.getFunctionDecl();
+    SmallString<128> MethodName;
+    if (MD->isPure()) {
+      MethodName = CGM.getCXXABI().GetPureVirtualCallName();
+    } else if (MD->isDeleted()) {
+      MethodName = CGM.getCXXABI().GetDeletedVirtualCallName();
+    } else {
+      // Figure out if this is a thunk vtable slot.
+      llvm::raw_svector_ostream MethodNameOS(MethodName);
+      if (NextVTableThunkIndex < NumVTableThunks &&
+          VTableThunks[NextVTableThunkIndex].first == I) {
+        const ThunkInfo &Thunk = VTableThunks[NextVTableThunkIndex].second;
+        NextVTableThunkIndex++;
+        if (VC.isDestructorKind())
+          Mangler.mangleCXXDtorThunk(VC.getDestructorDecl(), Dtor_Deleting,
+                                     Thunk.This, MethodNameOS);
+        else
+          Mangler.mangleThunk(MD, Thunk, MethodNameOS);
+      } else {
+        if (VC.isDestructorKind())
+          Mangler.mangleCXXDtor(VC.getDestructorDecl(), Dtor_Deleting,
+                                MethodNameOS);
+        else
+          Mangler.mangleName(MD, MethodNameOS);
+      }
+    }
+    MethodNames.push_back(
+        llvm::MDString::get(CGM.getLLVMContext(),
+                            llvm::GlobalValue::getRealLinkageName(MethodName)));
+  }
+
+  llvm::DIType *VFTy = DBuilder.createMSVFTable(
+      RecordTy, llvm::GlobalValue::getRealLinkageName(VFTableName),
+      VFPtrOffsetInBits, OverriddenVFTable,
+      llvm::MDTuple::get(CGM.getLLVMContext(), MethodNames), 0);
+  MSVFTableCache[VPI].reset(VFTy);
+  return VFTy;
+}
+
 llvm::DIType *CGDebugInfo::getOrCreateRecordType(QualType RTy,
                                                  SourceLocation Loc) {
   assert(DebugKind >= codegenoptions::LimitedDebugInfo);
@@ -1747,7 +1839,7 @@
   const auto *CXXDecl = dyn_cast<CXXRecordDecl>(RD);
   if (CXXDecl) {
     CollectCXXBases(CXXDecl, DefUnit, EltTys, FwdDecl);
-    CollectVTableInfo(CXXDecl, DefUnit, EltTys);
+    CollectVTableInfo(CXXDecl, DefUnit, EltTys, FwdDecl);
   }
 
   // Collect data fields (including static variables and any initializers).
Index: lib/AST/VTableBuilder.cpp
===================================================================
--- lib/AST/VTableBuilder.cpp
+++ lib/AST/VTableBuilder.cpp
@@ -3303,6 +3303,10 @@
       // Copy the path and adjust it as necessary.
       VPtrInfo *P = new VPtrInfo(*BaseInfo);
 
+      // Remember the vptr we overrode.
+      P->OverriddenVPtr = BaseInfo;
+      P->DirectBase = Base;
+
       // We mangle Base into the path if the path would've been ambiguous and it
       // wasn't already extended with Base.
       if (P->MangledPath.empty() || P->MangledPath.back() != Base)
Index: include/clang/AST/VTableBuilder.h
===================================================================
--- include/clang/AST/VTableBuilder.h
+++ include/clang/AST/VTableBuilder.h
@@ -409,6 +409,13 @@
   /// methods or virtual bases.
   const CXXRecordDecl *IntroducingObject;
 
+  /// Link to the vptr we are overriding from our base, or null the vptr was
+  /// introduced in this class.
+  const VPtrInfo *OverriddenVPtr = nullptr;
+
+  /// The direct base that we got this vptr from.
+  const CXXRecordDecl *DirectBase = nullptr;
+
   /// IntroducingObject is at this offset from its containing complete object or
   /// virtual base.
   CharUnits NonVirtualOffset;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to