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; 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: 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> StaticDataMemberCache; + llvm::DenseMap 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 &EltTys); + SmallVectorImpl &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 &EltTys) { + SmallVectorImpl &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(I->second.get()); + + // Get the name of the vftable and it's offset. + auto &Mangler = + cast(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 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(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: 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: 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"}