Index: clang/lib/CodeGen/CGDebugInfo.cpp =================================================================== --- clang/lib/CodeGen/CGDebugInfo.cpp +++ clang/lib/CodeGen/CGDebugInfo.cpp @@ -1789,23 +1789,37 @@ ContainingType = RecordTy; } - // We're checking for deleted C++ special member functions - // [Ctors,Dtors, Copy/Move] - auto checkAttrDeleted = [&](const auto *Method) { - if (Method->getCanonicalDecl()->isDeleted()) + // We're checking for deleted/defaulted C++ special member functions + // [Ctors, Dtors, Copy/Move]. + auto checkDeletedOrDefaulted = [&](const auto *Method) { + // '= delete' must be on the first declaration, so checking the + // canonical decl is sufficient. + if (Method->getCanonicalDecl()->isDeleted()) { SPFlags |= llvm::DISubprogram::SPFlagDeleted; + return; + } + // '= default' can be on any decl. + for (auto *Redecl : Method->redecls()) { + if (Redecl->isExplicitlyDefaulted()) { + if (Redecl->isOutOfLine()) + SPFlags |= llvm::DISubprogram::SPFlagDefaultedOutOfClass; + else + SPFlags |= llvm::DISubprogram::SPFlagDefaultedInClass; + return; + } + } }; switch (Method->getKind()) { case Decl::CXXConstructor: case Decl::CXXDestructor: - checkAttrDeleted(Method); + checkDeletedOrDefaulted(Method); break; case Decl::CXXMethod: if (Method->isCopyAssignmentOperator() || Method->isMoveAssignmentOperator()) - checkAttrDeleted(Method); + checkDeletedOrDefaulted(Method); break; default: break; Index: clang/test/CodeGenCXX/debug-info-deleted-defaulted.cpp =================================================================== --- clang/test/CodeGenCXX/debug-info-deleted-defaulted.cpp +++ clang/test/CodeGenCXX/debug-info-deleted-defaulted.cpp @@ -1,4 +1,4 @@ -// Test for debug info for C++11 deleted member functions +// Test for debug info for C++11 deleted/defaulted member functions. //Supported: -O0, standalone DI // RUN: %clang_cc1 -emit-llvm -triple x86_64-linux-gnu %s -o - \ @@ -6,12 +6,12 @@ // 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: DISPFlagDefaultedInClass // 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, +// ATTR: DISubprogram(name: "~deleted", {{.*}}, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagDefaultedOutOfClass class deleted { public: // Defaulted on purpose, so as to facilitate object creation @@ -23,9 +23,11 @@ deleted(deleted &&) = delete; deleted &operator=(deleted &&) = delete; - ~deleted() = default; + ~deleted(); }; +deleted::~deleted() = default; + void foo() { deleted obj1; } Index: llvm/include/llvm/IR/DebugInfoFlags.def =================================================================== --- llvm/include/llvm/IR/DebugInfoFlags.def +++ llvm/include/llvm/IR/DebugInfoFlags.def @@ -87,9 +87,12 @@ 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 +// The Deleted/Defaulted states are mutually exclusive and folded into a +// two-bit field with these four states: none(0), deleted(1), +// defaulted in-class(2), defaulted out-of-class(3). +// Unfortunately the values do not match the DW_DEFAULTED_* constants. HANDLE_DISP_FLAG((1u << 9), Deleted) +HANDLE_DISP_FLAG((1u << 10), DefaultedInClass) HANDLE_DISP_FLAG((1u << 11), ObjCDirect) #ifdef DISP_FLAG_LARGEST_NEEDED Index: llvm/include/llvm/IR/DebugInfoMetadata.h =================================================================== --- llvm/include/llvm/IR/DebugInfoMetadata.h +++ llvm/include/llvm/IR/DebugInfoMetadata.h @@ -1838,6 +1838,8 @@ #include "llvm/IR/DebugInfoFlags.def" SPFlagNonvirtual = SPFlagZero, SPFlagVirtuality = SPFlagVirtual | SPFlagPureVirtual, + SPFlagDeletedOrDefaulted = SPFlagDeleted | SPFlagDefaultedInClass, + SPFlagDefaultedOutOfClass = SPFlagDeletedOrDefaulted, LLVM_MARK_AS_BITMASK_ENUM(SPFlagLargest) }; @@ -1991,11 +1993,17 @@ bool isRecursive() const { return getSPFlags() & SPFlagRecursive; } bool isObjCDirect() const { return getSPFlags() & SPFlagObjCDirect; } + /// Return a value indicating whether this function is deleted or + /// defaulted. + DISPFlags getDeletedOrDefaulted() const { + return getSPFlags() & SPFlagDeletedOrDefaulted; + } + /// 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; } + bool isDeleted() const { return getDeletedOrDefaulted() == SPFlagDeleted; } /// Check if this is reference-qualified. /// Index: llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp +++ llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp @@ -1320,8 +1320,27 @@ if (SP->isRecursive()) addFlag(SPDie, dwarf::DW_AT_recursive); - if (DD->getDwarfVersion() >= 5 && SP->isDeleted()) - addFlag(SPDie, dwarf::DW_AT_deleted); + if (DD->getDwarfVersion() >= 5) { + switch (SP->getDeletedOrDefaulted()) { + case DISubprogram::SPFlagDeleted: + addFlag(SPDie, dwarf::DW_AT_deleted); + break; + case DISubprogram::SPFlagDefaultedInClass: + addUInt(SPDie, dwarf::DW_AT_defaulted, dwarf::DW_FORM_data1, + dwarf::DW_DEFAULTED_in_class); + break; + case DISubprogram::SPFlagDefaultedOutOfClass: + addUInt(SPDie, dwarf::DW_AT_defaulted, dwarf::DW_FORM_data1, + dwarf::DW_DEFAULTED_out_of_class); + break; + case DISubprogram::SPFlagZero: + // Older bitcode doesn't record the defaulted kind, so for backward + // compatibility, emit nothing instead of saying not-defaulted. + break; + default: + llvm_unreachable("Invalid value from getDefaultedOrDeleted()"); + } + } } void DwarfUnit::constructSubrangeDIE(DIE &Buffer, const DISubrange *SR, Index: llvm/lib/IR/DebugInfoMetadata.cpp =================================================================== --- llvm/lib/IR/DebugInfoMetadata.cpp +++ llvm/lib/IR/DebugInfoMetadata.cpp @@ -826,8 +826,10 @@ DISubprogram::DISPFlags DISubprogram::getFlag(StringRef Flag) { return StringSwitch(Flag) #define HANDLE_DISP_FLAG(ID, NAME) .Case("DISPFlag" #NAME, SPFlag##NAME) + // Handle the one multi-bit special case. + HANDLE_DISP_FLAG(Ignored, DefaultedOutOfClass) #include "llvm/IR/DebugInfoFlags.def" - .Default(SPFlagZero); + .Default(SPFlagZero); } StringRef DISubprogram::getFlagString(DISPFlags Flag) { @@ -838,6 +840,8 @@ #define HANDLE_DISP_FLAG(ID, NAME) \ case SPFlag##NAME: \ return "DISPFlag" #NAME; + // Handle the one multi-bit special case. + HANDLE_DISP_FLAG(Ignored, DefaultedOutOfClass) #include "llvm/IR/DebugInfoFlags.def" } return ""; @@ -846,14 +850,16 @@ DISubprogram::DISPFlags DISubprogram::splitFlags(DISPFlags Flags, SmallVectorImpl &SplitFlags) { - // Multi-bit fields can require special handling. In our case, however, the - // only multi-bit field is virtuality, and all its values happen to be + // Multi-bit fields can require special handling. In our case, there + // are two: DeletedOrDefaulted, and Virtuality. We special-case one + // value for DeletedOrDefaulted, but everything else happens to have // single-bit values, so the right behavior just falls out. #define HANDLE_DISP_FLAG(ID, NAME) \ if (DISPFlags Bit = Flags & SPFlag##NAME) { \ SplitFlags.push_back(Bit); \ Flags &= ~Bit; \ } + HANDLE_DISP_FLAG(Ignored, DefaultedOutOfClass) #include "llvm/IR/DebugInfoFlags.def" return Flags; } Index: llvm/test/DebugInfo/X86/DW_AT_defaulted.s =================================================================== --- /dev/null +++ llvm/test/DebugInfo/X86/DW_AT_defaulted.s @@ -0,0 +1,43 @@ +## Demonstrate dumping DW_AT_defaulted. +## Any ELF-target triple will work. +# RUN: llvm-mc -triple=x86_64--linux -filetype=obj < %s | \ +# RUN: llvm-dwarfdump -v - | FileCheck %s + +# CHECK: .debug_abbrev contents: +# CHECK: DW_AT_defaulted DW_FORM_data1 +# CHECK: .debug_info contents: +# CHECK: DW_AT_defaulted [DW_FORM_data1] (DW_DEFAULTED_no) +# CHECK: DW_AT_defaulted [DW_FORM_data1] (DW_DEFAULTED_in_class) +# CHECK: DW_AT_defaulted [DW_FORM_data1] (DW_DEFAULTED_out_of_class) + + .section .debug_abbrev,"",@progbits + .byte 1 # Abbreviation Code + .byte 17 # DW_TAG_compile_unit + .byte 1 # DW_CHILDREN_yes + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 2 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 0 # DW_CHILDREN_no + .byte 0x8b, 1 # DW_AT_defaulted (ULEB) + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + + .section .debug_info,"",@progbits + .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit +.Ldebug_info_start0: + .short 5 # DWARF version number + .byte 1 # Unit type + .byte 8 # Address Size (in bytes) + .long .debug_abbrev # Offset Into Abbrev. Section + .byte 1 # Abbrev [1] DW_TAG_compile_unit + .byte 2 # Abbrev [2] DW_TAG_subprogram + .byte 0 # DW_DEFAULTED_no + .byte 2 # Abbrev [2] DW_TAG_subprogram + .byte 1 # DW_DEFAULTED_in_class + .byte 2 # Abbrev [2] DW_TAG_subprogram + .byte 2 # DW_DEFAULTED_out_of_class + .byte 0 # End Of Children Mark +.Ldebug_info_end0: Index: llvm/test/DebugInfo/X86/DW_AT_deleted_defaulted.ll =================================================================== --- llvm/test/DebugInfo/X86/DW_AT_deleted_defaulted.ll +++ llvm/test/DebugInfo/X86/DW_AT_deleted_defaulted.ll @@ -1,5 +1,5 @@ -; RUN: llc < %s -filetype=obj -o %t -; RUN: llvm-dwarfdump -v %t | FileCheck %s +;; Test emitting DW_AT_deleted and DW_AT_defaulted. +; RUN: llc < %s -filetype=obj | llvm-dwarfdump -v - | FileCheck %s ; C++ source to regenerate: ; class deleted { @@ -13,9 +13,11 @@ ; deleted(deleted &&) = delete; ; deleted &operator=(deleted &&) = delete; ; -; ~deleted() = default; +; ~deleted(); ; }; ; +; deleted::~deleted() = default; +; ; void foo() { ; deleted obj1; ; } @@ -24,6 +26,8 @@ ; CHECK: .debug_abbrev contents: +; CHECK: [5] DW_TAG_subprogram DW_CHILDREN_yes +; CHECK: DW_AT_defaulted DW_FORM_data1 ; CHECK: [7] DW_TAG_subprogram DW_CHILDREN_yes ; CHECK: DW_AT_deleted DW_FORM_flag_present ; CHECK: [9] DW_TAG_subprogram DW_CHILDREN_yes @@ -31,6 +35,10 @@ ; CHECK: .debug_info contents: +; CHECK: DW_TAG_subprogram [5] +; CHECK-NEXT: DW_AT_name [DW_FORM_strx1] (indexed (00000006) string = "deleted") +; CHECK: DW_AT_defaulted [DW_FORM_data1] (DW_DEFAULTED_in_class) + ; 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) @@ -48,6 +56,10 @@ ; CHECK-NEXT: DW_AT_name [DW_FORM_strx1] (indexed (00000008) string = "operator=") ; CHECK: DW_AT_deleted [DW_FORM_flag_present] (true) +; CHECK: DW_TAG_subprogram [5] +; CHECK-NEXT: DW_AT_name [DW_FORM_strx1] (indexed (0000000a) string = "~deleted") +; CHECK: DW_AT_defaulted [DW_FORM_data1] (DW_DEFAULTED_out_of_class) + ; 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" @@ -85,7 +97,7 @@ !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) +!13 = !DISubprogram(name: "deleted", scope: !11, file: !1, line: 3, type: !14, scopeLine: 3, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagDefaultedInClass) !14 = !DISubroutineType(types: !15) !15 = !{null, !16} !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !11, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) @@ -105,6 +117,6 @@ !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) +!33 = !DISubprogram(name: "~deleted", scope: !11, file: !1, line: 11, type: !14, scopeLine: 11, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagDefaultedOutOfClass) !34 = !DILocation(line: 15, column: 13, scope: !7) !35 = !DILocation(line: 16, column: 3, scope: !7)