Index: clang/lib/CodeGen/CGDebugInfo.cpp =================================================================== --- clang/lib/CodeGen/CGDebugInfo.cpp +++ clang/lib/CodeGen/CGDebugInfo.cpp @@ -1607,8 +1607,31 @@ ContainingType = RecordTy; } + // We're checking for deleted C++ special member functions + // [Ctors,Dtors, Copy/Move] + auto checkAttrDeleted = [&SPFlags](const auto *Method) { + if (Method->getCanonicalDecl()->isDeleted()) + SPFlags |= llvm::DISubprogram::SPFlagDeleted; + }; + + switch (Method->getKind()) { + + case Decl::CXXConstructor: + case Decl::CXXDestructor: + checkAttrDeleted(Method); + break; + case Decl::CXXMethod: + if (Method->isCopyAssignmentOperator() || + Method->isMoveAssignmentOperator()) + checkAttrDeleted(Method); + break; + default: + break; + } + if (Method->isNoReturn()) Flags |= llvm::DINode::FlagNoReturn; + if (Method->isStatic()) Flags |= llvm::DINode::FlagStaticMember; if (Method->isImplicit()) Index: clang/test/CodeGenCXX/debug-info-deleted.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/debug-info-deleted.cpp @@ -0,0 +1,31 @@ +// Test for debug info for C++11 deleted member functions + +//Supported: -O0, standalone DI +// RUN: %clang_cc1 -emit-llvm -triple x86_64-linux-gnu %s -o - \ +// RUN: -O0 -disable-llvm-passes \ +// RUN: -debug-info-kind=standalone \ +// RUN: | FileCheck %s -check-prefix=ATTR + +// ATTR: DISubprogram(name: "deleted", {{.*}}, flags: DIFlagPublic | DIFlagPrototyped, +// ATTR: DISubprogram(name: "deleted", {{.*}}, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagDeleted +// ATTR: DISubprogram(name: "operator=", linkageName: "_ZN7deletedaSERKS_", {{.*}}, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagDeleted +// ATTR: DISubprogram(name: "deleted", {{.*}}, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagDeleted +// ATTR: DISubprogram(name: "operator=", linkageName: "_ZN7deletedaSEOS_", {{.*}}, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagDeleted +// ATTR: DISubprogram(name: "~deleted", {{.*}}, flags: DIFlagPublic | DIFlagPrototyped, +class deleted { +public: + // Defaulted on purpose, so as to facilitate object creation + deleted() = default; + + deleted(const deleted &) = delete; + deleted &operator=(const deleted &) = delete; + + deleted(deleted &&) = delete; + deleted &operator=(deleted &&) = delete; + + ~deleted() = default; +}; + +void foo() { + deleted obj1; +} Index: llvm/docs/SourceLevelDebugging.rst =================================================================== --- llvm/docs/SourceLevelDebugging.rst +++ llvm/docs/SourceLevelDebugging.rst @@ -989,6 +989,39 @@ ... } +C++ specific debug information +============================== + +C++ special member functions information +---------------------------------------- + +DWARF v5 introduces attributes defined to enhance debugging information of C++ programs. LLVM can generate (or omit) these appropriate DWARF attributes. In C++ a special member function Ctors, Dtors, Copy/Move Ctors, assignment operators can be declared with C++11 keyword deleted. This is represented in LLVM using spFlags value DISPFlagDeleted. + +Given a class declaration with copy constructor declared as deleted: + +.. code-block:: c + + class foo { + public: + foo(const foo&) = deleted; + }; + +A C++ frontend would generate follwing: + +.. code-block:: text + + !17 = !DISubprogram(name: "foo", scope: !11, file: !1, line: 5, type: !18, scopeLine: 5, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagDeleted) + +and this will produce an additional DWARF attibute as: + +.. code-block:: text + + DW_TAG_subprogram [7] * + DW_AT_name [DW_FORM_strx1] (indexed (00000006) string = "foo") + DW_AT_decl_line [DW_FORM_data1] (5) + ... + DW_AT_deleted [DW_FORM_flag_present] (true) + Fortran specific debug information ================================== Index: llvm/include/llvm/BinaryFormat/Dwarf.h =================================================================== --- llvm/include/llvm/BinaryFormat/Dwarf.h +++ llvm/include/llvm/BinaryFormat/Dwarf.h @@ -458,6 +458,7 @@ StringRef DecimalSignString(unsigned Sign); StringRef EndianityString(unsigned Endian); StringRef AccessibilityString(unsigned Access); +StringRef DefaultedMemberString(unsigned DefaultedEncodings); StringRef VisibilityString(unsigned Visibility); StringRef VirtualityString(unsigned Virtuality); StringRef LanguageString(unsigned Language); Index: llvm/include/llvm/IR/DebugInfoFlags.def =================================================================== --- llvm/include/llvm/IR/DebugInfoFlags.def +++ llvm/include/llvm/IR/DebugInfoFlags.def @@ -88,11 +88,14 @@ HANDLE_DISP_FLAG((1u << 6), Elemental) HANDLE_DISP_FLAG((1u << 7), Recursive) HANDLE_DISP_FLAG((1u << 8), MainSubprogram) +// May also utilize this Flag in future, when adding support +// for defaulted functions +HANDLE_DISP_FLAG((1u << 9), Deleted) #ifdef DISP_FLAG_LARGEST_NEEDED // Intended to be used with ADT/BitmaskEnum.h. // NOTE: Always must be equal to largest flag, check this when adding new flags. -HANDLE_DISP_FLAG((1 << 8), Largest) +HANDLE_DISP_FLAG((1 << 9), Largest) #undef DISP_FLAG_LARGEST_NEEDED #endif Index: llvm/include/llvm/IR/DebugInfoMetadata.h =================================================================== --- llvm/include/llvm/IR/DebugInfoMetadata.h +++ llvm/include/llvm/IR/DebugInfoMetadata.h @@ -1759,6 +1759,12 @@ bool isElemental() const { return getSPFlags() & SPFlagElemental; } bool isRecursive() const { return getSPFlags() & SPFlagRecursive; } + /// Check if this is deleted member function. + /// + /// Return true if this subprogram is a C++11 special + /// member function declared deleted. + bool isDeleted() const { return getSPFlags() & SPFlagDeleted; } + /// Check if this is reference-qualified. /// /// Return true if this subprogram is a C++11 reference-qualified non-static Index: llvm/lib/BinaryFormat/Dwarf.cpp =================================================================== --- llvm/lib/BinaryFormat/Dwarf.cpp +++ llvm/lib/BinaryFormat/Dwarf.cpp @@ -274,6 +274,19 @@ return StringRef(); } +StringRef llvm::dwarf::DefaultedMemberString(unsigned DefaultedEncodings) { + switch (DefaultedEncodings) { + // Defaulted Member Encodings codes + case DW_DEFAULTED_no: + return "DW_DEFAULTED_no"; + case DW_DEFAULTED_in_class: + return "DW_DEFAULTED_in_class"; + case DW_DEFAULTED_out_of_class: + return "DW_DEFAULTED_out_of_class"; + } + return StringRef(); +} + StringRef llvm::dwarf::VisibilityString(unsigned Visibility) { switch (Visibility) { case DW_VIS_local: @@ -615,6 +628,8 @@ return ArrayOrderString(Val); case DW_AT_APPLE_runtime_class: return LanguageString(Val); + case DW_AT_defaulted: + return DefaultedMemberString(Val); } return StringRef(); Index: llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp +++ llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp @@ -1307,6 +1307,9 @@ addFlag(SPDie, dwarf::DW_AT_elemental); if (SP->isRecursive()) addFlag(SPDie, dwarf::DW_AT_recursive); + + if (DD->getDwarfVersion() >= 5 && SP->isDeleted()) + addFlag(SPDie, dwarf::DW_AT_deleted); } void DwarfUnit::constructSubrangeDIE(DIE &Buffer, const DISubrange *SR, Index: llvm/test/DebugInfo/X86/DW_AT_deleted.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/X86/DW_AT_deleted.ll @@ -0,0 +1,110 @@ +; RUN: llc < %s -filetype=obj -o %t +; RUN: llvm-dwarfdump -v %t | FileCheck %s + +; C++ source to regenerate: +; class deleted { +; public: +; // Defaulted on purpose, so as to facilitate object creation +; deleted() = default; +; +; deleted(const deleted &) = delete; +; deleted &operator=(const deleted &) = delete; +; +; deleted(deleted &&) = delete; +; deleted &operator=(deleted &&) = delete; +; +; ~deleted() = default; +; }; +; +; void foo() { +; deleted obj1; +; } +; $ clang++ -O0 -g -gdwarf-5 debug-info-deleted.cpp -c + + +; CHECK: .debug_abbrev contents: + +; CHECK: [7] DW_TAG_subprogram DW_CHILDREN_yes +; CHECK: DW_AT_deleted DW_FORM_flag_present +; CHECK: [9] DW_TAG_subprogram DW_CHILDREN_yes +; CHECK: DW_AT_deleted DW_FORM_flag_present + +; CHECK: .debug_info contents: + +; CHECK: DW_TAG_subprogram [7] +; CHECK-NEXT: DW_AT_name [DW_FORM_strx1] (indexed (00000006) string = "deleted") +; CHECK: DW_AT_deleted [DW_FORM_flag_present] (true) + +; CHECK: DW_TAG_subprogram [9] +; CHECK-NEXT: DW_AT_linkage_name [DW_FORM_strx1] (indexed (00000007) string = "_ZN7deletedaSERKS_") +; CHECK: DW_AT_deleted [DW_FORM_flag_present] (true) + +; CHECK: DW_TAG_subprogram [7] +; CHECK-NEXT: DW_AT_name [DW_FORM_strx1] (indexed (00000006) string = "deleted") +; CHECK: DW_AT_deleted [DW_FORM_flag_present] (true) + +; CHECK: DW_TAG_subprogram [9] +; CHECK-NEXT: DW_AT_linkage_name [DW_FORM_strx1] (indexed (00000009) string = "_ZN7deletedaSEOS_") +; CHECK-NEXT: DW_AT_name [DW_FORM_strx1] (indexed (00000008) string = "operator=") +; CHECK: DW_AT_deleted [DW_FORM_flag_present] (true) + +; ModuleID = 'debug-info-deleted.cpp' +source_filename = "debug-info-deleted.cpp" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%class.deleted = type { i8 } + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @_Z3foov() #0 !dbg !7 { + %1 = alloca %class.deleted, align 1 + call void @llvm.dbg.declare(metadata %class.deleted* %1, metadata !10, metadata !DIExpression()), !dbg !34 + ret void, !dbg !35 +} + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +attributes #0 = { noinline nounwind optnone uwtable } +attributes #1 = { nounwind readnone speculatable willreturn } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 10.0.0 (https://github.com/llvm/llvm-project.git 715c47d5de9aa8860050992a7aaf27dca53f7f4a)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) +!1 = !DIFile(filename: "debug-info-deleted.cpp", directory: "/home/sourabh/work/dwarf/c_c++/c++11", checksumkind: CSK_MD5, checksum: "49dc56907586479c64634558b060292d") +!2 = !{} +!3 = !{i32 2, !"Dwarf Version", i32 5} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{!"clang version 10.0.0 (https://github.com/llvm/llvm-project.git 715c47d5de9aa8860050992a7aaf27dca53f7f4a)"} +!7 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !1, file: !1, line: 14, type: !8, scopeLine: 14, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!8 = !DISubroutineType(types: !9) +!9 = !{null} +!10 = !DILocalVariable(name: "obj1", scope: !7, file: !1, line: 15, type: !11) +!11 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "deleted", file: !1, line: 1, size: 8, flags: DIFlagTypePassByReference, elements: !12, identifier: "_ZTS7deleted") +!12 = !{!13, !17, !22, !26, !30, !33} +!13 = !DISubprogram(name: "deleted", scope: !11, file: !1, line: 3, type: !14, scopeLine: 3, flags: DIFlagPublic | DIFlagPrototyped, spFlags: 0) +!14 = !DISubroutineType(types: !15) +!15 = !{null, !16} +!16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !11, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!17 = !DISubprogram(name: "deleted", scope: !11, file: !1, line: 5, type: !18, scopeLine: 5, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagDeleted) +!18 = !DISubroutineType(types: !19) +!19 = !{null, !16, !20} +!20 = !DIDerivedType(tag: DW_TAG_reference_type, baseType: !21, size: 64) +!21 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !11) +!22 = !DISubprogram(name: "operator=", linkageName: "_ZN7deletedaSERKS_", scope: !11, file: !1, line: 6, type: !23, scopeLine: 6, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagDeleted) +!23 = !DISubroutineType(types: !24) +!24 = !{!25, !16, !20} +!25 = !DIDerivedType(tag: DW_TAG_reference_type, baseType: !11, size: 64) +!26 = !DISubprogram(name: "deleted", scope: !11, file: !1, line: 8, type: !27, scopeLine: 8, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagDeleted) +!27 = !DISubroutineType(types: !28) +!28 = !{null, !16, !29} +!29 = !DIDerivedType(tag: DW_TAG_rvalue_reference_type, baseType: !11, size: 64) +!30 = !DISubprogram(name: "operator=", linkageName: "_ZN7deletedaSEOS_", scope: !11, file: !1, line: 9, type: !31, scopeLine: 9, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagDeleted) +!31 = !DISubroutineType(types: !32) +!32 = !{!25, !16, !29} +!33 = !DISubprogram(name: "~deleted", scope: !11, file: !1, line: 11, type: !14, scopeLine: 11, flags: DIFlagPublic | DIFlagPrototyped, spFlags: 0) +!34 = !DILocation(line: 15, column: 13, scope: !7) +!35 = !DILocation(line: 16, column: 3, scope: !7)