Index: clang/lib/CodeGen/CGDebugInfo.h =================================================================== --- clang/lib/CodeGen/CGDebugInfo.h +++ clang/lib/CodeGen/CGDebugInfo.h @@ -580,6 +580,12 @@ unsigned LineNo, StringRef LinkageName, llvm::GlobalVariable *Var, llvm::DIScope *DContext); + + /// Return a copy of \p Flags with call site-related attributes set, provided + /// they are supported and enabled. + llvm::DINode::DIFlags + setCallSiteRelatedAttrs(llvm::DINode::DIFlags Flags) const; + /// Get the printing policy for producing names for debug info. PrintingPolicy getPrintingPolicy() const; Index: clang/lib/CodeGen/CGDebugInfo.cpp =================================================================== --- clang/lib/CodeGen/CGDebugInfo.cpp +++ clang/lib/CodeGen/CGDebugInfo.cpp @@ -3096,6 +3096,7 @@ QualType FnType = CGM.getContext().getFunctionType( FD->getReturnType(), ArgTypes, FunctionProtoType::ExtProtoInfo(CC)); if (Stub) { + Flags = setCallSiteRelatedAttrs(Flags); return DBuilder.createFunction( DContext, Name, LinkageName, Unit, Line, getOrCreateFunctionType(GD.getDecl(), FnType, Unit), @@ -3333,6 +3334,8 @@ if (CurFuncIsThunk) Flags |= llvm::DINode::FlagThunk; + llvm::DINode::DIFlags FlagsForDef = setCallSiteRelatedAttrs(Flags); + unsigned LineNo = getLineNumber(Loc); unsigned ScopeLine = getLineNumber(ScopeLoc); @@ -3344,7 +3347,7 @@ llvm::DISubprogram *SP = DBuilder.createFunction( FDContext, Name, LinkageName, Unit, LineNo, getOrCreateFunctionType(D, FnType, Unit), Fn->hasLocalLinkage(), - true /*definition*/, ScopeLine, Flags, CGM.getLangOpts().Optimize, + true /*definition*/, ScopeLine, FlagsForDef, CGM.getLangOpts().Optimize, TParamsArray.get(), getFunctionDeclaration(D)); Fn->setSubprogram(SP); // We might get here with a VarDecl in the case we're generating @@ -4318,3 +4321,23 @@ llvm::MDNode *Scope = LexicalBlockStack.back(); return llvm::DebugLoc::get(getLineNumber(Loc), getColumnNumber(Loc), Scope); } + +llvm::DINode::DIFlags +CGDebugInfo::setCallSiteRelatedAttrs(llvm::DINode::DIFlags Flags) const { + // Call site-related attributes are only useful in optimized programs, and + // should only be emitted when full debug info is desired. + if (!CGM.getLangOpts().Optimize || + DebugKind < codegenoptions::FullDebugInfo) + return Flags; + + // Call site-related attributes are available in DWARF v5. Some debuggers, + // while not fully DWARF v5-compliant, may accept these attributes as if they + // were part of DWARF v4. + bool SupportsDWARFv4Ext = + CGM.getCodeGenOpts().DwarfVersion == 4 && + CGM.getCodeGenOpts().getDebuggerTuning() == llvm::DebuggerKind::LLDB; + if (CGM.getCodeGenOpts().DwarfVersion < 5 && !SupportsDWARFv4Ext) + return Flags; + + return Flags | llvm::DINode::FlagAllCallsDescribed; +} Index: clang/test/CodeGenCXX/dbg-info-all-calls-described.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/dbg-info-all-calls-described.cpp @@ -0,0 +1,67 @@ +// RUN: %clang_cc1 -emit-llvm -triple %itanium_abi_triple %s -o - \ +// RUN: -O1 -disable-llvm-passes \ +// RUN: -dwarf-column-info -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: | FileCheck %s -check-prefix=HAS-ATTR \ +// RUN: -implicit-check-not=DISubprogram -implicit-check-not=DIFlagAllCallsDescribed + +// RUN: %clang_cc1 -emit-llvm -triple %itanium_abi_triple %s -o - \ +// RUN: -O1 -disable-llvm-passes \ +// RUN: -debugger-tuning=lldb \ +// RUN: -dwarf-column-info -debug-info-kind=standalone -dwarf-version=4 \ +// RUN: | FileCheck %s -check-prefix=HAS-ATTR \ +// RUN: -implicit-check-not=DISubprogram -implicit-check-not=DIFlagAllCallsDescribed + +// RUN: %clang_cc1 -emit-llvm -triple %itanium_abi_triple %s -o - \ +// RUN: -O0 \ +// RUN: -dwarf-column-info -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: | FileCheck %s -check-prefix=NO-ATTR + +// RUN: %clang_cc1 -emit-llvm -triple %itanium_abi_triple %s -o - \ +// RUN: -O1 -disable-llvm-passes \ +// RUN: -dwarf-column-info -debug-info-kind=standalone -dwarf-version=4 \ +// RUN: | FileCheck %s -check-prefix=NO-ATTR + +// RUN: %clang_cc1 -emit-llvm -triple %itanium_abi_triple %s -o - \ +// RUN: -O1 -disable-llvm-passes \ +// RUN: -dwarf-column-info -debug-info-kind=limited -dwarf-version=5 \ +// RUN: | FileCheck %s -check-prefix=NO-ATTR + +// NO-ATTR-NOT: FlagAllCallsDescribed + +// HAS-ATTR-DAG: DISubprogram(name: "declaration2", {{.*}}, isDefinition: true, {{.*}}, flags: DIFlagPrototyped | DIFlagAllCallsDescribed +// HAS-ATTR-DAG: DISubprogram(name: "struct1", {{.*}}, isDefinition: false, {{.*}}, flags: DIFlagPrototyped +// HAS-ATTR-DAG: DISubprogram(name: "struct1", {{.*}}, isDefinition: true, {{.*}}, flags: DIFlagPrototyped | DIFlagAllCallsDescribed +// HAS-ATTR-DAG: DISubprogram(name: "method1", {{.*}}, isDefinition: true, {{.*}}, flags: DIFlagPrototyped | DIFlagAllCallsDescribed +// HAS-ATTR-DAG: DISubprogram(name: "method1", {{.*}}, isDefinition: false, {{.*}}, flags: DIFlagPublic | DIFlagPrototyped +// HAS-ATTR-DAG: DISubprogram(name: "struct2", {{.*}}, isDefinition: false, {{.*}}, flags: DIFlagPrototyped +// HAS-ATTR-DAG: DISubprogram(name: "struct2", {{.*}}, isDefinition: true, {{.*}}, flags: DIFlagPrototyped | DIFlagAllCallsDescribed +// HAS-ATTR-DAG: DISubprogram(name: "struct2", {{.*}}, isDefinition: true, {{.*}}, flags: DIFlagPrototyped | DIFlagAllCallsDescribed +// HAS-ATTR-DAG: DISubprogram(name: "method2", {{.*}}, isDefinition: false, {{.*}}, flags: DIFlagPrototyped +// HAS-ATTR-DAG: DISubprogram(name: "method2", {{.*}}, isDefinition: true, {{.*}}, flags: DIFlagPrototyped | DIFlagAllCallsDescribed +// HAS-ATTR-DAG: DISubprogram(name: "force_irgen", {{.*}}, isDefinition: true, {{.*}}, flags: DIFlagPrototyped | DIFlagAllCallsDescribed + +void declaration1(); + +void declaration2(); + +void declaration2() {} + +class class1 { +public: + void method1() {} +}; + +struct struct1 { + struct1() {} + void method2() {} +}; + +struct struct2 : struct1 { + struct2() : struct1() {} +}; + +void __attribute__((optnone)) force_irgen() { + declaration1(); + class1().method1(); + struct2().method2(); +} Index: llvm/include/llvm/IR/DebugInfoFlags.def =================================================================== --- llvm/include/llvm/IR/DebugInfoFlags.def +++ llvm/include/llvm/IR/DebugInfoFlags.def @@ -48,6 +48,7 @@ HANDLE_DI_FLAG((1 << 24), FixedEnum) HANDLE_DI_FLAG((1 << 25), Thunk) HANDLE_DI_FLAG((1 << 26), Trivial) +HANDLE_DI_FLAG((1 << 27), AllCallsDescribed) // To avoid needing a dedicated value for IndirectVirtualBase, we use // the bitwise or of Virtual and FwdDecl, which does not otherwise @@ -57,7 +58,7 @@ #ifdef DI_FLAG_LARGEST_NEEDED // intended to be used with ADT/BitmaskEnum.h // NOTE: always must be equal to largest flag, check this when adding new flag -HANDLE_DI_FLAG((1 << 26), Largest) +HANDLE_DI_FLAG((1 << 27), Largest) #undef DI_FLAG_LARGEST_NEEDED #endif Index: llvm/include/llvm/IR/DebugInfoMetadata.h =================================================================== --- llvm/include/llvm/IR/DebugInfoMetadata.h +++ llvm/include/llvm/IR/DebugInfoMetadata.h @@ -1717,6 +1717,9 @@ } bool isExplicit() const { return getFlags() & FlagExplicit; } bool isPrototyped() const { return getFlags() & FlagPrototyped; } + bool areAllCallsDescribed() const { + return getFlags() & FlagAllCallsDescribed; + } bool isMainSubprogram() const { return getFlags() & FlagMainSubprogram; } /// Check if this is reference-qualified. Index: llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h +++ llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h @@ -206,6 +206,11 @@ void constructAbstractSubprogramScopeDIE(LexicalScope *Scope); + /// Construct a DIE for a call site entry describing a call within \p Scope + /// to a callee described by \p CalleeSP. + DIE &constructCallSiteEntryDIE(const DILocalScope &Scope, + const DISubprogram &CalleeSP); + /// Construct import_module DIE. DIE *constructImportedEntityDIE(const DIImportedEntity *Module); Index: llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp +++ llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp @@ -784,6 +784,25 @@ ContextCU->addDIEEntry(*AbsDef, dwarf::DW_AT_object_pointer, *ObjectPointer); } +DIE &DwarfCompileUnit::constructCallSiteEntryDIE(const DILocalScope &Scope, + const DISubprogram &CalleeSP) { + DIE *ScopeDIE = getOrCreateContextDIE(&Scope); + assert(ScopeDIE && "Local scope is not represented in some DIE"); + DIE &CallSiteDIE = + createAndAddDIE(dwarf::DW_TAG_call_site, *ScopeDIE, &Scope); + + // For the purposes of showing heuristic tail call frames in backtraces, a + // key piece of information is DW_AT_call_origin, a pointer to the callee DIE. + // + // Note that with the exception of DW_AT_call_tail_call (when applicable), a + // call site entry has no required attributes. Even though it's non-conformant + // behavior, don't emit DW_AT_call_tail_call to save space. + DIE *CalleeDIE = getOrCreateSubprogramDIE(&CalleeSP); + assert(CalleeDIE && "Could not create DIE for call site entry origin"); + addDIEEntry(CallSiteDIE, dwarf::DW_AT_call_origin, *CalleeDIE); + return CallSiteDIE; +} + DIE *DwarfCompileUnit::constructImportedEntityDIE( const DIImportedEntity *Module) { DIE *IMDie = DIE::get(DIEValueAllocator, (dwarf::Tag)Module->getTag()); Index: llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h +++ llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h @@ -345,6 +345,10 @@ /// Construct a DIE for this abstract scope. void constructAbstractSubprogramScopeDIE(DwarfCompileUnit &SrcCU, LexicalScope *Scope); + /// Construct DIEs for call site entries describing the calls in \p MF. + void constructCallSiteEntryDIEs(DwarfCompileUnit &CU, + const MachineFunction &MF); + template void addAccelNameImpl(AccelTable &AppleAccel, StringRef Name, const DIE &Die); Index: llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -46,6 +46,8 @@ #include "llvm/IR/DebugLoc.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Module.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" @@ -501,6 +503,36 @@ } } +void DwarfDebug::constructCallSiteEntryDIEs(DwarfCompileUnit &CU, + const MachineFunction &MF) { + // Emit call site entries for the first call to each direct callee visited + // from this function. This is the bare minimum needed to display heuristic + // tail-call frames in backtraces. + // + // This is non-conformant behavior. Entries are expected for each call site, + // but we do the bare minimum needed to save space. + SmallPtrSet SeenCallees; + for (const Instruction &I : instructions(MF.getFunction())) { + const auto *Call = dyn_cast(&I); + if (!Call || isa(Call) || Call->isInlineAsm()) + continue; + + DebugLoc DL = Call->getDebugLoc(); + if (!DL) + continue; + + Function *DirectCallee = Call->getCalledFunction(); + if (!DirectCallee || !DirectCallee->getSubprogram()) + continue; + + if (!SeenCallees.insert(DirectCallee).second) + continue; + + const DILocalScope &Scope = *DL.get()->getScope(); + CU.constructCallSiteEntryDIE(Scope, *DirectCallee->getSubprogram()); + } +} + void DwarfDebug::addGnuPubAttributes(DwarfCompileUnit &U, DIE &D) const { if (!U.hasDwarfPubSections()) return; @@ -1457,6 +1489,10 @@ TheCU.getCUNode()->getSplitDebugInlining()) SkelCU->constructSubprogramScopeDIE(SP, FnScope); + // Construct call site entries if they are required. + if (SP->areAllCallsDescribed()) + constructCallSiteEntryDIEs(TheCU, *MF); + // Clear debug info // Ownership of DbgVariables is a bit subtle - ScopeVariables owns all the // DbgVariables except those that are also in AbstractVariables (since they Index: llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp +++ llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp @@ -1289,6 +1289,16 @@ Language == dwarf::DW_LANG_ObjC)) addFlag(SPDie, dwarf::DW_AT_prototyped); + // Add call site-related attributes (DWARF5, Sec. 3.3.1.3). Do this only if + // the subprogram must support it. + // + // Note: Use DW_AT_call_all_calls to communicate that call site entries are + // present for both tail and non-tail calls. Avoid DW_AT_call_all_source_calls + // because call site entries for optimized-out calls are elided, even though + // entries for inlined calls are available via inlined subroutine entries. + if (SP->areAllCallsDescribed()) + addFlag(SPDie, dwarf::DW_AT_call_all_calls); + unsigned CC = 0; DITypeRefArray Args; if (const DISubroutineType *SPTy = SP->getType()) { Index: llvm/lib/IR/Verifier.cpp =================================================================== --- llvm/lib/IR/Verifier.cpp +++ llvm/lib/IR/Verifier.cpp @@ -1117,6 +1117,10 @@ AssertDI(Op && isa(Op), "invalid thrown type", &N, ThrownTypes, Op); } + + if (N.areAllCallsDescribed()) + AssertDI(N.isDefinition(), + "DIFlagAllCallsDescribed must be attached to a definition"); } void Verifier::visitDILexicalBlockBase(const DILexicalBlockBase &N) { Index: llvm/test/DebugInfo/Generic/dwarf-callsite-related-attrs.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/dwarf-callsite-related-attrs.ll @@ -0,0 +1,119 @@ +; REQUIRES: object-emission +; RUN: %llc_dwarf < %s -o - | FileCheck %s -check-prefix=ASM +; RUN: %llc_dwarf < %s -filetype=obj -o %t.o +; RUN: llvm-dwarfdump %t.o -o - | FileCheck %s -check-prefix=OBJ +; RUN: llvm-dwarfdump -verify %t.o 2>&1 | FileCheck %s -check-prefix=VERIFY + +; $ clang++ -S -emit-llvm -o - -gdwarf-5 -o - -O1 tail2.cc +; volatile int sink; +; void __attribute__((noinline)) bat() { sink++; } +; void __attribute__((noinline)) bar() { sink++; } +; void __attribute__((noinline)) foo() { +; bar(); bat(); +; bar(); bat(); +; } +; int __attribute__((disable_tail_calls)) main() { foo(); } + +; VERIFY: No errors. + +@sink = global i32 0, align 4, !dbg !0 + +; ASM: DW_TAG_subprogram +; ASM: DW_AT_call_all_calls +; OBJ: [[bat_sp:.*]]: DW_TAG_subprogram +; OBJ: DW_AT_name ("bat") +; OBJ: DW_AT_call_all_calls +; OBJ-NOT: DW_TAG_call_site +define void @_Z3batv() !dbg !13 { +entry: + %0 = load volatile i32, i32* @sink, align 4, !dbg !16, !tbaa !17 + %inc = add nsw i32 %0, 1, !dbg !16 + store volatile i32 %inc, i32* @sink, align 4, !dbg !16, !tbaa !17 + ret void, !dbg !21 +} + +; ASM: DW_TAG_subprogram +; ASM: DW_AT_call_all_calls +; OBJ: [[bar_sp:.*]]: DW_TAG_subprogram +; OBJ: DW_AT_name ("bar") +; OBJ: DW_AT_call_all_calls +; OBJ-NOT: DW_TAG_call_site +define void @_Z3barv() !dbg !22 { +entry: + %0 = load volatile i32, i32* @sink, align 4, !dbg !23, !tbaa !17 + %inc = add nsw i32 %0, 1, !dbg !23 + store volatile i32 %inc, i32* @sink, align 4, !dbg !23, !tbaa !17 + ret void, !dbg !24 +} + +; ASM: DW_TAG_subprogram +; ASM: DW_AT_call_all_calls +; OBJ: [[foo_sp:.*]]: DW_TAG_subprogram +; OBJ: DW_AT_name ("foo") +; OBJ: DW_AT_call_all_calls +; OBJ: DW_TAG_call_site +; OBJ: DW_AT_call_origin ([[bar_sp]]) +; OBJ: DW_TAG_call_site +; OBJ: DW_AT_call_origin ([[bat_sp]]) +; OBJ-NOT: DW_TAG_call_site +define void @_Z3foov() !dbg !25 { +entry: + tail call void @_Z3barv(), !dbg !26 + tail call void @_Z3batv(), !dbg !27 + tail call void @_Z3barv(), !dbg !26 + tail call void @_Z3batv(), !dbg !27 + ret void, !dbg !28 +} + +; ASM: DW_TAG_subprogram +; ASM: DW_AT_call_all_calls +; OBJ: DW_TAG_subprogram +; OBJ: DW_AT_name ("main") +; OBJ: DW_AT_call_all_calls +; OBJ: DW_TAG_call_site +; OBJ: DW_AT_call_origin ([[foo_sp]]) +; OBJ-NOT: DW_TAG_call_site +define i32 @main() !dbg !29 { +entry: + call void @_Z3foov(), !dbg !32 + ret i32 0, !dbg !33 +} + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!8, !9, !10, !11} +!llvm.ident = !{!12} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "sink", scope: !2, file: !3, line: 1, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "clang version 7.0.0 ", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5) +!3 = !DIFile(filename: "/Users/vsk/src/llvm.org-tailcall/tail2.cc", directory: "/Users/vsk/src/builds/llvm-project-tailcall-RA", checksumkind: CSK_MD5, checksum: "3b61952c21b7f657ddb7c0ad44cf5529") +!4 = !{} +!5 = !{!0} +!6 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !7) +!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!8 = !{i32 2, !"Dwarf Version", i32 5} +!9 = !{i32 2, !"Debug Info Version", i32 3} +!10 = !{i32 1, !"wchar_size", i32 4} +!11 = !{i32 7, !"PIC Level", i32 2} +!12 = !{!"clang version 7.0.0 "} +!13 = distinct !DISubprogram(name: "bat", linkageName: "_Z3batv", scope: !3, file: !3, line: 2, type: !14, isLocal: false, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, isOptimized: true, unit: !2, retainedNodes: !4) +!14 = !DISubroutineType(types: !15) +!15 = !{null} +!16 = !DILocation(line: 2, column: 44, scope: !13) +!17 = !{!18, !18, i64 0} +!18 = !{!"int", !19, i64 0} +!19 = !{!"omnipotent char", !20, i64 0} +!20 = !{!"Simple C++ TBAA"} +!21 = !DILocation(line: 2, column: 48, scope: !13) +!22 = distinct !DISubprogram(name: "bar", linkageName: "_Z3barv", scope: !3, file: !3, line: 3, type: !14, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, isOptimized: true, unit: !2, retainedNodes: !4) +!23 = !DILocation(line: 3, column: 44, scope: !22) +!24 = !DILocation(line: 3, column: 48, scope: !22) +!25 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !3, file: !3, line: 4, type: !14, isLocal: false, isDefinition: true, scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, isOptimized: true, unit: !2, retainedNodes: !4) +!26 = !DILocation(line: 5, column: 3, scope: !25) +!27 = !DILocation(line: 6, column: 3, scope: !25) +!28 = !DILocation(line: 7, column: 1, scope: !25) +!29 = distinct !DISubprogram(name: "main", scope: !3, file: !3, line: 8, type: !30, isLocal: false, isDefinition: true, scopeLine: 8, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, isOptimized: true, unit: !2, retainedNodes: !4) +!30 = !DISubroutineType(types: !31) +!31 = !{!7} +!32 = !DILocation(line: 8, column: 50, scope: !29) +!33 = !DILocation(line: 8, column: 57, scope: !29)