-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[clang][DebugInfo] Add symbol for debugger with VTable information. #130255
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
4bd0c48
e811ab7
dc84180
8356bb7
75357fa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2518,6 +2518,57 @@ StringRef CGDebugInfo::getVTableName(const CXXRecordDecl *RD) { | |
| return internString("_vptr$", RD->getNameAsString()); | ||
| } | ||
|
|
||
| // Emit symbol for the debugger that points to the vtable address for | ||
| // the given class. The symbol is named as '_vtable$'. | ||
| // The debugger does not need to know any details about the contents of the | ||
| // vtable as it can work this out using its knowledge of the ABI and the | ||
| // existing information in the DWARF. The type is assumed to be 'void *'. | ||
| void CGDebugInfo::emitVTableSymbol(llvm::GlobalVariable *VTable, | ||
| const CXXRecordDecl *RD) { | ||
| if (!CGM.getTarget().getCXXABI().isItaniumFamily()) | ||
| return; | ||
|
|
||
| ASTContext &Context = CGM.getContext(); | ||
| SmallString<64> Buffer; | ||
| StringRef SymbolName = "_vtable$"; | ||
| const DeclContext *DC = static_cast<const DeclContext *>(RD); | ||
|
||
| SourceLocation Loc; | ||
| QualType VoidPtr = Context.getPointerType(Context.VoidTy); | ||
|
|
||
| // We deal with two different contexts: | ||
| // - The type for the variable, which is part of the class that has the | ||
| // vtable, is placed in the context of the DICompositeType metadata. | ||
| // - The DIGlobalVariable for the vtable is put in the DICompileUnitScope. | ||
|
|
||
| // The created non-member should be mark as 'artificial'. It will be | ||
| // placed inside the scope of the C++ class/structure. | ||
| llvm::DIScope *DContext = getContextDescriptor(cast<Decl>(DC), TheCU); | ||
| auto *Ctxt = cast<llvm::DICompositeType>(DContext); | ||
| llvm::DIFile *Unit = getOrCreateFile(Loc); | ||
| llvm::DIType *VTy = getOrCreateType(VoidPtr, Unit); | ||
| llvm::DINode::DIFlags Flags = getAccessFlag(AccessSpecifier::AS_private, RD) | | ||
| llvm::DINode::FlagArtificial; | ||
| auto Tag = CGM.getCodeGenOpts().DwarfVersion >= 5 | ||
| ? llvm::dwarf::DW_TAG_variable | ||
| : llvm::dwarf::DW_TAG_member; | ||
| llvm::DIDerivedType *DT = DBuilder.createStaticMemberType( | ||
| Ctxt, SymbolName, Unit, /*LineNumber=*/0, VTy, Flags, | ||
| /*Val=*/nullptr, Tag); | ||
|
|
||
| // Use the same vtable pointer to global alignment for the symbol. | ||
| unsigned PAlign = CGM.getGlobalVarAlignment(); | ||
|
|
||
| // The global variable is in the CU scope, and links back to the type it's | ||
| // "within" via the declaration field. | ||
| llvm::DIGlobalVariableExpression *GVE = | ||
| DBuilder.createGlobalVariableExpression( | ||
| TheCU, SymbolName, VTable->getName(), Unit, /*LineNo=*/0, | ||
| getOrCreateType(VoidPtr, Unit), VTable->hasLocalLinkage(), | ||
| /*isDefined=*/true, nullptr, DT, /*TemplateParameters=*/nullptr, | ||
| PAlign); | ||
| VTable->addDebugInfo(GVE); | ||
| } | ||
|
|
||
| StringRef CGDebugInfo::getDynamicInitializerName(const VarDecl *VD, | ||
| DynamicInitKind StubKind, | ||
| llvm::Function *InitFn) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1816,6 +1816,15 @@ class CodeGenModule : public CodeGenTypeCache { | |
| void requireVectorDestructorDefinition(const CXXRecordDecl *RD); | ||
| bool classNeedsVectorDestructor(const CXXRecordDecl *RD); | ||
|
|
||
| // Helper to get the alignment for a variable. | ||
| unsigned getGlobalVarAlignment(const VarDecl *D = nullptr) { | ||
| LangAS AS = GetGlobalVarAddressSpace(D); | ||
| unsigned PAlign = getItaniumVTableContext().isRelativeLayout() | ||
| ? 32 | ||
| : getTarget().getPointerAlign(AS); | ||
| return PAlign; | ||
| } | ||
|
|
||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It makes sense to refactor this out; I feel the name of the function should contain "vtable" somewhere though, it's fundamentally tied to producing vtable information as there's a call to
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed function name to |
||
| private: | ||
| bool shouldDropDLLAttribute(const Decl *D, const llvm::GlobalValue *GV) const; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| #include "vtable-debug-info-inheritance-simple-base.h" | ||
|
|
||
| void NSP::CBase::zero() {} | ||
| int NSP::CBase::one() { return 1; } | ||
| int NSP::CBase::two() { return 2; }; | ||
| int NSP::CBase::three() { return 3; } | ||
|
|
||
| #ifdef SYMBOL_AT_FILE_SCOPE | ||
| static NSP::CBase Base; | ||
| #else | ||
| void fooBase() { | ||
| NSP::CBase Base; | ||
| } | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| #ifndef BASE_H | ||
| #define BASE_H | ||
|
|
||
| namespace NSP { | ||
| struct CBase { | ||
| unsigned B = 1; | ||
| virtual void zero(); | ||
| virtual int one(); | ||
| virtual int two(); | ||
| virtual int three(); | ||
| }; | ||
| } | ||
|
|
||
| extern void fooBase(); | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| #include "vtable-debug-info-inheritance-simple-derived.h" | ||
|
|
||
| void CDerived::zero() {} | ||
| int CDerived::two() { return 22; }; | ||
| int CDerived::three() { return 33; } | ||
|
|
||
| #ifdef SYMBOL_AT_FILE_SCOPE | ||
| static CDerived Derived; | ||
| #else | ||
| void fooDerived() { | ||
| CDerived Derived; | ||
| } | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| #include "vtable-debug-info-inheritance-simple-base.h" | ||
|
|
||
| #ifndef DERIVED_H | ||
| #define DERIVED_H | ||
|
|
||
| struct CDerived : NSP::CBase { | ||
| unsigned D = 2; | ||
| void zero() override; | ||
| int two() override; | ||
| int three() override; | ||
| }; | ||
|
|
||
| extern void fooDerived(); | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| // REQUIRES: target={{x86_64.*-linux.*}} | ||
|
|
||
| // Diamond inheritance case: | ||
| // For CBase, CLeft, CRight and CDerived we check: | ||
| // - Generation of their vtables (including attributes). | ||
| // - Generation of their '_vtable$' data members: | ||
| // * Correct scope and attributes | ||
|
|
||
| namespace NSP { | ||
| struct CBase { | ||
| int B = 0; | ||
| virtual char fooBase() { return 'B'; } | ||
| }; | ||
| } | ||
|
|
||
| namespace NSP_1 { | ||
| struct CLeft : NSP::CBase { | ||
| int M1 = 1; | ||
| char fooBase() override { return 'O'; }; | ||
| virtual int fooLeft() { return 1; } | ||
| }; | ||
| } | ||
|
|
||
| namespace NSP_2 { | ||
| struct CRight : NSP::CBase { | ||
| int M2 = 2; | ||
| char fooBase() override { return 'T'; }; | ||
| virtual int fooRight() { return 2; } | ||
| }; | ||
| } | ||
|
|
||
| struct CDerived : NSP_1::CLeft, NSP_2::CRight { | ||
| int D = 3; | ||
| char fooBase() override { return 'D'; }; | ||
| int fooDerived() { return 3; }; | ||
| }; | ||
|
|
||
| int main() { | ||
| NSP::CBase Base; | ||
| NSP_1::CLeft Left; | ||
| NSP_2::CRight Right; | ||
| CDerived Derived; | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| // RUN: %clang --target=x86_64-linux -Xclang -disable-O0-optnone -Xclang -disable-llvm-passes -emit-llvm -S -g %s -o - | FileCheck %s | ||
|
|
||
| // CHECK: $_ZTVN3NSP5CBaseE = comdat any | ||
| // CHECK: $_ZTVN5NSP_15CLeftE = comdat any | ||
| // CHECK: $_ZTVN5NSP_26CRightE = comdat any | ||
| // CHECK: $_ZTV8CDerived = comdat any | ||
|
|
||
| // CHECK: @_ZTVN3NSP5CBaseE = linkonce_odr {{dso_local|hidden}} unnamed_addr constant {{.*}}, comdat, align 8, !dbg [[BASE_VTABLE_VAR:![0-9]*]] | ||
| // CHECK: @_ZTVN5NSP_15CLeftE = linkonce_odr {{dso_local|hidden}} unnamed_addr constant {{.*}}, comdat, align 8, !dbg [[LEFT_VTABLE_VAR:![0-9]*]] | ||
| // CHECK: @_ZTVN5NSP_26CRightE = linkonce_odr {{dso_local|hidden}} unnamed_addr constant {{.*}}, comdat, align 8, !dbg [[RIGHT_VTABLE_VAR:![0-9]*]] | ||
| // CHECK: @_ZTV8CDerived = linkonce_odr {{dso_local|hidden}} unnamed_addr constant {{.*}}, comdat, align 8, !dbg [[DERIVED_VTABLE_VAR:![0-9]*]] | ||
|
|
||
| // CHECK: [[BASE_VTABLE_VAR]] = !DIGlobalVariableExpression(var: [[BASE_VTABLE:![0-9]*]], expr: !DIExpression()) | ||
| // CHECK-NEXT: [[BASE_VTABLE]] = distinct !DIGlobalVariable(name: "_vtable$", linkageName: "_ZTVN3NSP5CBaseE" | ||
|
|
||
| // CHECK: [[LEFT_VTABLE_VAR]] = !DIGlobalVariableExpression(var: [[LEFT_VTABLE:![0-9]*]], expr: !DIExpression()) | ||
| // CHECK-NEXT: [[LEFT_VTABLE]] = distinct !DIGlobalVariable(name: "_vtable$", linkageName: "_ZTVN5NSP_15CLeftE" | ||
|
|
||
| // CHECK: [[TYPE:![0-9]*]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) | ||
|
|
||
| // CHECK: !DIDerivedType(tag: DW_TAG_variable, name: "_vtable$", scope: [[LEFT:![0-9]*]], file: {{.*}}, baseType: [[TYPE]], flags: DIFlagPrivate | DIFlagArtificial | DIFlagStaticMember) | ||
|
|
||
| // CHECK: [[LEFT]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "CLeft" | ||
|
|
||
| // CHECK: [[BASE:![0-9]*]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "CBase" | ||
|
|
||
| // CHECK: [[RIGHT_VTABLE_VAR]] = !DIGlobalVariableExpression(var: [[RIGHT_VTABLE:![0-9]*]], expr: !DIExpression()) | ||
| // CHECK-NEXT: [[RIGHT_VTABLE]] = distinct !DIGlobalVariable(name: "_vtable$", linkageName: "_ZTVN5NSP_26CRightE" | ||
|
|
||
| // CHECK: !DIDerivedType(tag: DW_TAG_variable, name: "_vtable$", scope: [[RIGHT:![0-9]*]], file: {{.*}}, baseType: [[TYPE]], flags: DIFlagPrivate | DIFlagArtificial | DIFlagStaticMember) | ||
|
|
||
| // CHECK: [[RIGHT]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "CRight" | ||
|
|
||
| // CHECK: [[DERIVED_VTABLE_VAR]] = !DIGlobalVariableExpression(var: [[DERIVED_VTABLE:![0-9]*]], expr: !DIExpression()) | ||
| // CHECK-NEXT: [[DERIVED_VTABLE]] = distinct !DIGlobalVariable(name: "_vtable$", linkageName: "_ZTV8CDerived" | ||
|
|
||
| // CHECK: !DIDerivedType(tag: DW_TAG_variable, name: "_vtable$", scope: [[DERIVED:![0-9]*]], file: {{.*}}, baseType: [[TYPE]], flags: DIFlagPrivate | DIFlagArtificial | DIFlagStaticMember) | ||
|
|
||
| // CHECK: [[DERIVED]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "CDerived" | ||
|
|
||
| // CHECK: !DIDerivedType(tag: DW_TAG_variable, name: "_vtable$", scope: [[BASE]], file: {{.*}}, baseType: [[TYPE]], flags: DIFlagPrivate | DIFlagArtificial | DIFlagStaticMember) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unused?