Skip to content

Commit e08f205

Browse files
committed
Reland (again): [DWARF] Allow cross-CU references of subprogram definitions
This is a revert-of-revert (i.e. this reverts commit 802bec8, which itself reverted fa4701e and 79daafc) with a fix folded in. The problem was that call site tags weren't emitted properly when LTO was enabled along with split-dwarf. This required a minor fix. I've added a reduced test case in test/DebugInfo/X86/fission-call-site.ll. Original commit message: This allows a call site tag in CU A to reference a callee DIE in CU B without resorting to creating an incomplete duplicate DIE for the callee inside of CU A. We already allow cross-CU references of subprogram declarations, so it doesn't seem like definitions ought to be special. This improves entry value evaluation and tail call frame synthesis in the LTO setting. During LTO, it's common for cross-module inlining to produce a call in some CU A where the callee resides in a different CU, and there is no declaration subprogram for the callee anywhere. In this case llvm would (unnecessarily, I think) emit an empty DW_TAG_subprogram in order to fill in the call site tag. That empty 'definition' defeats entry value evaluation etc., because the debugger can't figure out what it means. As a follow-up, maybe we could add a DWARF verifier check that a DW_TAG_subprogram at least has a DW_AT_name attribute. Update #1: Reland with a fix to create a declaration DIE when the declaration is missing from the CU's retainedTypes list. The declaration is left out of the retainedTypes list in two cases: 1) Re-compiling pre-r266445 bitcode (in which declarations weren't added to the retainedTypes list), and 2) Doing LTO function importing (which doesn't update the retainedTypes list). It's possible to handle (1) and (2) by modifying the retainedTypes list (in AutoUpgrade, or in the LTO importing logic resp.), but I don't see an advantage to doing it this way, as it would cause more DWARF to be emitted compared to creating the declaration DIEs lazily. Update #2: Fold in a fix for call site tag emission in the split-dwarf + LTO case. Tested with a stage2 ThinLTO+RelWithDebInfo build of clang, and with a ReleaseLTO-g build of the test suite. rdar://46577651, rdar://57855316, rdar://57840415, rdar://58888440 Differential Revision: https://reviews.llvm.org/D70350
1 parent 88c7b16 commit e08f205

10 files changed

+372
-32
lines changed

llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp

+2-3
Original file line numberDiff line numberDiff line change
@@ -977,7 +977,7 @@ DwarfCompileUnit::getDwarf5OrGNULocationAtom(dwarf::LocationAtom Loc) const {
977977
}
978978

979979
DIE &DwarfCompileUnit::constructCallSiteEntryDIE(DIE &ScopeDIE,
980-
const DISubprogram *CalleeSP,
980+
DIE *CalleeDIE,
981981
bool IsTail,
982982
const MCSymbol *PCAddr,
983983
unsigned CallReg) {
@@ -990,8 +990,7 @@ DIE &DwarfCompileUnit::constructCallSiteEntryDIE(DIE &ScopeDIE,
990990
addAddress(CallSiteDIE, getDwarf5OrGNUAttr(dwarf::DW_AT_call_target),
991991
MachineLocation(CallReg));
992992
} else {
993-
DIE *CalleeDIE = getOrCreateSubprogramDIE(CalleeSP);
994-
assert(CalleeDIE && "Could not create DIE for call site entry origin");
993+
assert(CalleeDIE && "No DIE for call site entry origin");
995994
addDIEEntry(CallSiteDIE, getDwarf5OrGNUAttr(dwarf::DW_AT_call_origin),
996995
*CalleeDIE);
997996
}

llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h

+5-4
Original file line numberDiff line numberDiff line change
@@ -240,14 +240,15 @@ class DwarfCompileUnit final : public DwarfUnit {
240240
dwarf::LocationAtom getDwarf5OrGNULocationAtom(dwarf::LocationAtom Loc) const;
241241

242242
/// Construct a call site entry DIE describing a call within \p Scope to a
243-
/// callee described by \p CalleeSP.
243+
/// callee described by \p CalleeDIE.
244+
/// \p CalleeDIE is a declaration or definition subprogram DIE for the callee.
245+
/// For indirect calls \p CalleeDIE is set to nullptr.
244246
/// \p IsTail specifies whether the call is a tail call.
245247
/// \p PCAddr points to the PC value after the call instruction.
246248
/// \p CallReg is a register location for an indirect call. For direct calls
247249
/// the \p CallReg is set to 0.
248-
DIE &constructCallSiteEntryDIE(DIE &ScopeDIE, const DISubprogram *CalleeSP,
249-
bool IsTail, const MCSymbol *PCAddr,
250-
unsigned CallReg);
250+
DIE &constructCallSiteEntryDIE(DIE &ScopeDIE, DIE *CalleeDIE, bool IsTail,
251+
const MCSymbol *PCAddr, unsigned CallReg);
251252
/// Construct call site parameter DIEs for the \p CallSiteDIE. The \p Params
252253
/// were collected by the \ref collectCallSiteParameters.
253254
/// Note: The order of parameters does not matter, since debuggers recognize

llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp

+23-8
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,14 @@ void DwarfDebug::constructAbstractSubprogramScopeDIE(DwarfCompileUnit &SrcCU,
540540
}
541541
}
542542

543+
DIE &DwarfDebug::constructSubprogramDefinitionDIE(const DISubprogram *SP) {
544+
DICompileUnit *Unit = SP->getUnit();
545+
assert(SP->isDefinition() && "Subprogram not a definition");
546+
assert(Unit && "Subprogram definition without parent unit");
547+
auto &CU = getOrCreateDwarfCompileUnit(Unit);
548+
return *CU.getOrCreateSubprogramDIE(SP);
549+
}
550+
543551
/// Try to interpret values loaded into registers that forward parameters
544552
/// for \p CallMI. Store parameters with interpreted value into \p Params.
545553
static void collectCallSiteParameters(const MachineInstr *CallMI,
@@ -771,7 +779,7 @@ void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP,
771779
continue;
772780

773781
unsigned CallReg = 0;
774-
const DISubprogram *CalleeSP = nullptr;
782+
DIE *CalleeDIE = nullptr;
775783
const Function *CalleeDecl = nullptr;
776784
if (CalleeOp.isReg()) {
777785
CallReg = CalleeOp.getReg();
@@ -781,7 +789,19 @@ void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP,
781789
CalleeDecl = dyn_cast<Function>(CalleeOp.getGlobal());
782790
if (!CalleeDecl || !CalleeDecl->getSubprogram())
783791
continue;
784-
CalleeSP = CalleeDecl->getSubprogram();
792+
const DISubprogram *CalleeSP = CalleeDecl->getSubprogram();
793+
794+
if (CalleeSP->isDefinition()) {
795+
// Ensure that a subprogram DIE for the callee is available in the
796+
// appropriate CU.
797+
CalleeDIE = &constructSubprogramDefinitionDIE(CalleeSP);
798+
} else {
799+
// Create the declaration DIE if it is missing. This is required to
800+
// support compilation of old bitcode with an incomplete list of
801+
// retained metadata.
802+
CalleeDIE = CU.getOrCreateSubprogramDIE(CalleeSP);
803+
}
804+
assert(CalleeDIE && "Must have a DIE for the callee");
785805
}
786806

787807
// TODO: Omit call site entries for runtime calls (objc_msgSend, etc).
@@ -812,7 +832,7 @@ void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP,
812832
->getName(CallReg)))
813833
<< (IsTail ? " [IsTail]" : "") << "\n");
814834

815-
DIE &CallSiteDIE = CU.constructCallSiteEntryDIE(ScopeDIE, CalleeSP,
835+
DIE &CallSiteDIE = CU.constructCallSiteEntryDIE(ScopeDIE, CalleeDIE,
816836
IsTail, PCAddr, CallReg);
817837

818838
// GDB and LLDB support call site parameter debug info.
@@ -929,11 +949,6 @@ DwarfDebug::getOrCreateDwarfCompileUnit(const DICompileUnit *DIUnit) {
929949
NewCU.setSection(Asm->getObjFileLowering().getDwarfInfoSection());
930950
}
931951

932-
// Create DIEs for function declarations used for call site debug info.
933-
for (auto Scope : DIUnit->getRetainedTypes())
934-
if (auto *SP = dyn_cast_or_null<DISubprogram>(Scope))
935-
NewCU.getOrCreateSubprogramDIE(SP);
936-
937952
CUMap.insert({DIUnit, &NewCU});
938953
CUDieMap.insert({&NewCU.getUnitDie(), &NewCU});
939954
return NewCU;

llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h

+3
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,9 @@ class DwarfDebug : public DebugHandlerBase {
442442
/// Construct a DIE for this abstract scope.
443443
void constructAbstractSubprogramScopeDIE(DwarfCompileUnit &SrcCU, LexicalScope *Scope);
444444

445+
/// Construct a DIE for the subprogram definition \p SP and return it.
446+
DIE &constructSubprogramDefinitionDIE(const DISubprogram *SP);
447+
445448
/// Construct DIEs for call site entries describing the calls in \p MF.
446449
void constructCallSiteEntryDIEs(const DISubprogram &SP, DwarfCompileUnit &CU,
447450
DIE &ScopeDIE, const MachineFunction &MF);

llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp

+4-5
Original file line numberDiff line numberDiff line change
@@ -188,18 +188,17 @@ int64_t DwarfUnit::getDefaultLowerBound() const {
188188

189189
/// Check whether the DIE for this MDNode can be shared across CUs.
190190
bool DwarfUnit::isShareableAcrossCUs(const DINode *D) const {
191-
// When the MDNode can be part of the type system, the DIE can be shared
192-
// across CUs.
191+
// When the MDNode can be part of the type system (this includes subprogram
192+
// declarations *and* subprogram definitions, even local definitions), the
193+
// DIE must be shared across CUs.
193194
// Combining type units and cross-CU DIE sharing is lower value (since
194195
// cross-CU DIE sharing is used in LTO and removes type redundancy at that
195196
// level already) but may be implementable for some value in projects
196197
// building multiple independent libraries with LTO and then linking those
197198
// together.
198199
if (isDwoUnit() && !DD->shareAcrossDWOCUs())
199200
return false;
200-
return (isa<DIType>(D) ||
201-
(isa<DISubprogram>(D) && !cast<DISubprogram>(D)->isDefinition())) &&
202-
!DD->generateTypeUnits();
201+
return (isa<DIType>(D) || isa<DISubprogram>(D)) && !DD->generateTypeUnits();
203202
}
204203

205204
DIE *DwarfUnit::getDIE(const DINode *D) const {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
; RUN: llc -mtriple=arm64-apple-ios -filetype=obj < %s -o %t.o
2+
; RUN: llvm-dwarfdump %t.o | FileCheck %s -implicit-check-not=DW_TAG_subprogram
3+
4+
; The declaration subprogram for "function" is not in the CU's list of
5+
; retained types. Test that a DWARF call site entry can still be constructed.
6+
7+
; CHECK: DW_TAG_subprogram
8+
; CHECK: DW_AT_name {{.*}}__hidden#3_
9+
; CHECK: DW_TAG_call_site
10+
; CHECK: DW_AT_call_origin (0x{{0+}}[[FUNCTION_DIE:.*]])
11+
12+
; CHECK: 0x{{0+}}[[FUNCTION_DIE]]: DW_TAG_subprogram
13+
; CHECK: DW_AT_name {{.*}}function
14+
15+
target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
16+
target triple = "arm64-apple-ios9.0.0"
17+
18+
define i32 @main() local_unnamed_addr !dbg !8 {
19+
%1 = tail call [2 x i64] @function([2 x i64] zeroinitializer), !dbg !11
20+
%2 = extractvalue [2 x i64] %1, 0, !dbg !11
21+
%3 = trunc i64 %2 to i32, !dbg !11
22+
ret i32 %3, !dbg !12
23+
}
24+
25+
declare !dbg !13 [2 x i64] @function([2 x i64]) local_unnamed_addr
26+
27+
!llvm.module.flags = !{!0, !1, !2, !3, !4}
28+
!llvm.dbg.cu = !{!5}
29+
!llvm.ident = !{!7}
30+
31+
!0 = !{i32 2, !"SDK Version", [2 x i32] [i32 13, i32 4]}
32+
!1 = !{i32 7, !"Dwarf Version", i32 4}
33+
!2 = !{i32 2, !"Debug Info Version", i32 3}
34+
!3 = !{i32 1, !"wchar_size", i32 4}
35+
!4 = !{i32 7, !"PIC Level", i32 2}
36+
!5 = distinct !DICompileUnit(language: DW_LANG_C99, file: !6, producer: "__hidden#0_", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, nameTableKind: None)
37+
!6 = !DIFile(filename: "__hidden#1_", directory: "__hidden#2_")
38+
!7 = !{!"Apple clang version 11.0.0 (llvm-project fa407d93fd5e618d76378c1ce4e4f517e0563278) (+internal-os)"}
39+
!8 = distinct !DISubprogram(name: "__hidden#3_", scope: !6, file: !6, line: 9, type: !9, scopeLine: 9, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !5)
40+
!9 = !DISubroutineType(types: !10)
41+
!10 = !{}
42+
!11 = !DILocation(line: 12, column: 10, scope: !8)
43+
!12 = !DILocation(line: 13, column: 3, scope: !8)
44+
!13 = !DISubprogram(name: "function", scope: !6, file: !6, line: 7, type: !9, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized)

llvm/test/DebugInfo/MIR/AArch64/dbgcall-site-orr-moves.mir

+3-3
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ body: |
159159
...
160160

161161
# CHECK: DW_TAG_GNU_call_site
162-
# CHECK-NEXT: DW_AT_abstract_origin (0x0000002a "call_int")
162+
# CHECK-NEXT: DW_AT_abstract_origin ({{.*}} "call_int")
163163
#
164164
# CHECK: DW_TAG_GNU_call_site_parameter
165165
# CHECK-NEXT: DW_AT_location (DW_OP_reg0 W0)
@@ -205,7 +205,7 @@ body: |
205205
...
206206

207207
# CHECK: DW_TAG_GNU_call_site
208-
# CHECK-NEXT: DW_AT_abstract_origin (0x0000003e "call_long")
208+
# CHECK-NEXT: DW_AT_abstract_origin ({{.*}} "call_long")
209209
#
210210
# CHECK: DW_TAG_GNU_call_site_parameter
211211
# CHECK-NEXT: DW_AT_location (DW_OP_reg0 W0)
@@ -265,7 +265,7 @@ body: |
265265
...
266266

267267
# CHECK: DW_TAG_GNU_call_site
268-
# CHECK-NEXT: DW_AT_abstract_origin (0x00000052 "call_int_int")
268+
# CHECK-NEXT: DW_AT_abstract_origin ({{.*}} "call_int_int")
269269
#
270270
# CHECK: DW_TAG_GNU_call_site_parameter
271271
# CHECK-NEXT: DW_AT_location (DW_OP_reg0 W0)

llvm/test/DebugInfo/MIR/X86/debug-call-site-param.mir

+9-9
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,11 @@
3939
# CHECK-GNU-NEXT: DW_AT_location (DW_OP_reg8 R8)
4040
# CHECK-GNU-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg14 R14+3)
4141

42-
# CHECK-DWARF5: [[getValue_SP:.*]]: DW_TAG_subprogram
43-
# CHECK-DWARF5-NEXT: DW_AT_name ("getVal")
44-
45-
# CHECK-DWARF5: [[foo_SP:.*]]: DW_TAG_subprogram
46-
# CHECK-DWARF5-NEXT: DW_AT_name ("foo")
47-
4842
# CHECK-DWARF5: DW_TAG_call_site
49-
# CHECK-DWARF5: DW_AT_call_origin ([[getValue_SP]])
50-
#
43+
# CHECK-DWARF5: DW_AT_call_origin ([[getValue_SP:.*]])
44+
5145
# CHECK-DWARF5: DW_TAG_call_site
52-
# CHECK-DWARF5: DW_AT_call_origin ([[foo_SP]])
46+
# CHECK-DWARF5: DW_AT_call_origin ([[foo_SP:.*]])
5347
# CHECK-DWARF5: DW_AT_call_return_pc {{.*}}
5448
# CHECK-DWARF5-EMPTY:
5549
# CHECK-DWARF5: DW_TAG_call_site_parameter
@@ -71,6 +65,12 @@
7165
# CHECK-DWARF5-NEXT: DW_AT_location (DW_OP_reg8 R8)
7266
# CHECK-DWARF5-NEXT: DW_AT_call_value (DW_OP_breg14 R14+3)
7367

68+
# CHECK-DWARF5: [[getValue_SP]]: DW_TAG_subprogram
69+
# CHECK-DWARF5-NEXT: DW_AT_name ("getVal")
70+
71+
# CHECK-DWARF5: [[foo_SP]]: DW_TAG_subprogram
72+
# CHECK-DWARF5-NEXT: DW_AT_name ("foo")
73+
7474
--- |
7575
; ModuleID = 'test.c'
7676
source_filename = "test.c"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
; Check that call site entries are emitted correctly when using split-dwarf
2+
; together with some form of LTO.
3+
4+
; Original C source:
5+
;
6+
; // cu1.c
7+
; extern void callee(void);
8+
; void caller(void) {
9+
; callee();
10+
; }
11+
;
12+
; // cu2.c
13+
; __attribute__((optnone)) void callee(void) {}
14+
;
15+
; Steps to reproduce:
16+
;
17+
; (The -O1 here is needed to trigger call site entry emission, as these tags are
18+
; generally not emitted at -O0.)
19+
;
20+
; clang -target x86_64-unknown-linux-gnu -gsplit-dwarf=split -O1 ~/tmp/cu1.c -S -emit-llvm -o ~/tmp/cu1.ll
21+
; clang -target x86_64-unknown-linux-gnu -gsplit-dwarf=split -O1 ~/tmp/cu2.c -S -emit-llvm -o ~/tmp/cu2.ll
22+
; llvm-link -o ~/tmp/cu-merged.bc ~/tmp/cu1.ll ~/tmp/cu2.ll
23+
; llc -split-dwarf-file=foo.dwo -O0 -mtriple=x86_64-unknown-linux-gnu -filetype=obj -o - ~/tmp/cu-merged.bc
24+
25+
; RUN: llc -split-dwarf-file=foo.dwo -O0 %s -mtriple=x86_64-unknown-linux-gnu -filetype=obj -o %t
26+
; RUN: llvm-dwarfdump %t | FileCheck %s
27+
28+
; CHECK: DW_TAG_GNU_call_site
29+
; CHECK-NEXT: DW_AT_abstract_origin {{.*}} "callee"
30+
31+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
32+
target triple = "x86_64-unknown-linux-gnu"
33+
34+
define dso_local void @caller() local_unnamed_addr !dbg !14 {
35+
entry:
36+
call void @callee(), !dbg !15
37+
ret void, !dbg !16
38+
}
39+
40+
define dso_local void @callee() local_unnamed_addr noinline optnone !dbg !17 {
41+
entry:
42+
ret void, !dbg !19
43+
}
44+
45+
!llvm.dbg.cu = !{!0, !8}
46+
!llvm.ident = !{!10, !10}
47+
!llvm.module.flags = !{!11, !12, !13}
48+
49+
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 11.0.0 ([email protected]:llvm/llvm-project.git 170f4b972e7bcf1f2af98bdd7145954efd16e038)", isOptimized: true, runtimeVersion: 0, splitDebugFilename: "cu1.dwo", emissionKind: FullDebug, enums: !2, retainedTypes: !3, splitDebugInlining: false, nameTableKind: GNU)
50+
!1 = !DIFile(filename: "/Users/vsk/tmp/cu1.c", directory: "/Users/vsk/src/builds/llvm-project-master-RA")
51+
!2 = !{}
52+
!3 = !{!4}
53+
!4 = !DISubprogram(name: "callee", scope: !5, file: !5, line: 1, type: !6, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2)
54+
!5 = !DIFile(filename: "tmp/cu1.c", directory: "/Users/vsk")
55+
!6 = !DISubroutineType(types: !7)
56+
!7 = !{null}
57+
!8 = distinct !DICompileUnit(language: DW_LANG_C99, file: !9, producer: "clang version 11.0.0 ([email protected]:llvm/llvm-project.git 170f4b972e7bcf1f2af98bdd7145954efd16e038)", isOptimized: true, runtimeVersion: 0, splitDebugFilename: "cu2.dwo", emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: GNU)
58+
!9 = !DIFile(filename: "/Users/vsk/tmp/cu2.c", directory: "/Users/vsk/src/builds/llvm-project-master-RA")
59+
!10 = !{!"clang version 11.0.0 ([email protected]:llvm/llvm-project.git 170f4b972e7bcf1f2af98bdd7145954efd16e038)"}
60+
!11 = !{i32 7, !"Dwarf Version", i32 4}
61+
!12 = !{i32 2, !"Debug Info Version", i32 3}
62+
!13 = !{i32 1, !"wchar_size", i32 4}
63+
!14 = distinct !DISubprogram(name: "caller", scope: !5, file: !5, line: 2, type: !6, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
64+
!15 = !DILocation(line: 3, column: 3, scope: !14)
65+
!16 = !DILocation(line: 4, column: 1, scope: !14)
66+
!17 = distinct !DISubprogram(name: "callee", scope: !18, file: !18, line: 1, type: !6, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !8, retainedNodes: !2)
67+
!18 = !DIFile(filename: "tmp/cu2.c", directory: "/Users/vsk")
68+
!19 = !DILocation(line: 1, column: 45, scope: !17)

0 commit comments

Comments
 (0)