Skip to content

Commit 24f2d67

Browse files
committed
[DebugInfo][DwarfDebug][CodeView] Allow DISubprogram to be attached to multiple Functions
Depends on: * #152680 * #162852 In #75385 (and the following tries), an attempt was made, to support attaching local types to DILocalScopes, and to store function local types in DISubprogram's `retainedNodes:` field. That patch failed to land due to issues arising during LTO process. If two definition DISubprograms from different compile units represent, essentially, the same source code function, and have common local DICompositeType, and if this DICompositeType is uniqued (due to ODRUniquingDebugTypes feature), the subprograms end up having wrong retainedNodes list/scoping relationship. To tackle this issue, in #142166, it was proposed to force-unique all DISubporgrams even if they don't contain odr-uniqued types (#142166 (comment)). It should establish one-to-one-to-many relationship between DISubprograms, abstract DIEs and function clones (from different CUs, in case of LTO). To implement that, AsmPrinter should support correct emission of debug info for DISubprograms attached to multiple functions. This is the goal of this commit. Here, LexicalScope's function map is changed to multimap between DISubprogram and (possible multiple) functions attached to it. LexicalScope is modified to create an abstract scope for a DISubprogram having multiple lllvm::Function attachments. `DwarfCompileUnit::getOrCreateSubprogramDIE` can recognize the case of DISubprogram attached to multiple Functions, and return abstract DIE when needed. CodeViewDebug is adopted as well. UDTs are ensured to be emmited properly in the cases that are addressed here. Please let me know if more changes to CodeView needed, as I'm not very familiar with the format.
1 parent 17c20a4 commit 24f2d67

File tree

15 files changed

+454
-48
lines changed

15 files changed

+454
-48
lines changed

llvm/include/llvm/CodeGen/LexicalScopes.h

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -199,12 +199,18 @@ class LexicalScopes {
199199
return I != LexicalScopeMap.end() ? &I->second : nullptr;
200200
}
201201

202+
bool currentFunctionHasInlinedScopes() {
203+
return !InlinedLexicalScopeMap.empty();
204+
}
205+
202206
/// Find or create an abstract lexical scope.
203207
LLVM_ABI LexicalScope *getOrCreateAbstractScope(const DILocalScope *Scope);
204208

205-
/// Get function to which the given subprogram is attached, if exists.
206-
const Function *getFunction(const DISubprogram *SP) const {
207-
return FunctionMap.lookup(SP);
209+
/// Get functions to which the given subprogram is attached.
210+
const SmallPtrSet<const Function *, 1> *
211+
getFunctions(const DISubprogram *SP) const {
212+
auto I = FunctionMap.find(SP);
213+
return I == FunctionMap.end() ? nullptr : &I->second;
208214
}
209215

210216
private:
@@ -218,6 +224,15 @@ class LexicalScopes {
218224
: nullptr;
219225
}
220226

227+
/// Create abstract lexical scope for local scopes used by multiple
228+
/// functions, if needed.
229+
void ensureAbstractLexicalScopeIsCreated(const DILocalScope *Scope) {
230+
const DISubprogram *SP = Scope->getSubprogram();
231+
const auto *Fns = getFunctions(SP);
232+
if (!Fns || Fns->size() != 1)
233+
getOrCreateAbstractScope(Scope);
234+
}
235+
221236
/// Find or create a regular lexical scope.
222237
LexicalScope *getOrCreateRegularScope(const DILocalScope *Scope);
223238

@@ -237,7 +252,7 @@ class LexicalScopes {
237252
const MachineFunction *MF = nullptr;
238253

239254
/// Mapping between DISubprograms and IR functions.
240-
DenseMap<const DISubprogram *, const Function *> FunctionMap;
255+
DenseMap<const DISubprogram *, SmallPtrSet<const Function *, 1>> FunctionMap;
241256

242257
/// Tracks the scopes in the current function.
243258
// Use an unordered_map to ensure value pointer validity over insertion.

llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,9 +1658,9 @@ void CodeViewDebug::addToUDTs(const DIType *Ty) {
16581658
formatNestedName(ParentScopeNames, getPrettyScopeName(Ty));
16591659

16601660
if (ClosestSubprogram == nullptr) {
1661-
GlobalUDTs.emplace_back(std::move(FullyQualifiedName), Ty);
1661+
GlobalUDTs[Ty] = std::move(FullyQualifiedName);
16621662
} else if (ClosestSubprogram == CurrentSubprogram) {
1663-
LocalUDTs.emplace_back(std::move(FullyQualifiedName), Ty);
1663+
LocalUDTs[Ty] = std::move(FullyQualifiedName);
16641664
}
16651665

16661666
// TODO: What if the ClosestSubprogram is neither null or the current
@@ -2698,6 +2698,12 @@ TypeIndex CodeViewDebug::getTypeIndex(const DIType *Ty, const DIType *ClassTy) {
26982698
if (!Ty)
26992699
return TypeIndex::Void();
27002700

2701+
if (Ty->getTag() == dwarf::DW_TAG_typedef) {
2702+
// Ensure that UDT is added even if the (local) type has been translated
2703+
// during processing of the previous function.
2704+
addToUDTs(Ty);
2705+
}
2706+
27012707
// Check if we've already translated this type. Don't try to do a
27022708
// get-or-create style insertion that caches the hash lookup across the
27032709
// lowerType call. It will update the TypeIndices map.
@@ -2787,6 +2793,10 @@ TypeIndex CodeViewDebug::getCompleteTypeIndex(const DIType *Ty) {
27872793
return FwdDeclTI;
27882794
}
27892795

2796+
// Ensure that UDT is added even if the (local) type has been translated
2797+
// during processing of the previous function.
2798+
addToUDTs(CTy);
2799+
27902800
// Check if we've already translated the complete record type.
27912801
// Insert the type with a null TypeIndex to signify that the type is currently
27922802
// being lowered.
@@ -3217,20 +3227,19 @@ void CodeViewDebug::emitEndSymbolRecord(SymbolKind EndKind) {
32173227
OS.emitInt16(uint16_t(EndKind)); // Record Kind
32183228
}
32193229

3220-
void CodeViewDebug::emitDebugInfoForUDTs(
3221-
const std::vector<std::pair<std::string, const DIType *>> &UDTs) {
3230+
template <typename Range>
3231+
void CodeViewDebug::emitDebugInfoForUDTs(Range &&UDTs) {
32223232
#ifndef NDEBUG
32233233
size_t OriginalSize = UDTs.size();
32243234
#endif
3225-
for (const auto &UDT : UDTs) {
3226-
const DIType *T = UDT.second;
3235+
for (const auto &[T, Name] : UDTs) {
32273236
assert(shouldEmitUdt(T));
32283237
MCSymbol *UDTRecordEnd = beginSymbolRecord(SymbolKind::S_UDT);
32293238
OS.AddComment("Type");
32303239
OS.emitInt32(getCompleteTypeIndex(T).getIndex());
32313240
assert(OriginalSize == UDTs.size() &&
32323241
"getCompleteTypeIndex found new UDTs!");
3233-
emitNullTerminatedSymbolName(OS, UDT.first);
3242+
emitNullTerminatedSymbolName(OS, Name);
32343243
endSymbolRecord(UDTRecordEnd);
32353244
}
32363245
}

llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -306,8 +306,8 @@ class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase {
306306

307307
// The UDTs we have seen while processing types; each entry is a pair of type
308308
// index and type name.
309-
std::vector<std::pair<std::string, const DIType *>> LocalUDTs;
310-
std::vector<std::pair<std::string, const DIType *>> GlobalUDTs;
309+
MapVector<const DIType *, std::string> LocalUDTs;
310+
MapVector<const DIType *, std::string> GlobalUDTs;
311311

312312
using FileToFilepathMapTy = std::map<const DIFile *, std::string>;
313313
FileToFilepathMapTy FileToFilepathMap;
@@ -352,8 +352,7 @@ class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase {
352352

353353
void emitDebugInfoForRetainedTypes();
354354

355-
void emitDebugInfoForUDTs(
356-
const std::vector<std::pair<std::string, const DIType *>> &UDTs);
355+
template <typename Range> void emitDebugInfoForUDTs(Range &&UDTs);
357356

358357
void collectDebugInfoForGlobals();
359358
void emitDebugInfoForGlobals();

llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1839,14 +1839,16 @@ DIE *DwarfCompileUnit::getOrCreateSubprogramDIE(const DISubprogram *SP,
18391839
const Function *F,
18401840
bool Minimal) {
18411841
if (!F && SP->isDefinition()) {
1842-
F = DD->getLexicalScopes().getFunction(SP);
1842+
const auto *Fs = DD->getLexicalScopes().getFunctions(SP);
18431843

1844-
if (!F) {
1844+
if (!Fs || Fs->size() != 1) {
18451845
// SP may belong to another CU. Determine the CU similarly
18461846
// to DwarfDebug::constructAbstractSubprogramScopeDIE.
18471847
return &DD->getOrCreateAbstractSubprogramCU(SP, *this)
18481848
.getOrCreateAbstractSubprogramDIE(SP);
18491849
}
1850+
1851+
F = *Fs->begin();
18501852
}
18511853

18521854
return DwarfUnit::getOrCreateSubprogramDIE(SP, F, Minimal);

llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2750,7 +2750,7 @@ void DwarfDebug::endFunctionImpl(const MachineFunction *MF) {
27502750
// is still needed as we need its source location.
27512751
if (!TheCU.getCUNode()->getDebugInfoForProfiling() &&
27522752
TheCU.getCUNode()->getEmissionKind() == DICompileUnit::LineTablesOnly &&
2753-
LScopes.getAbstractScopesList().empty() && !IsDarwin) {
2753+
!LScopes.currentFunctionHasInlinedScopes() && !IsDarwin) {
27542754
for (const auto &R : Asm->MBBSectionRanges)
27552755
addArangeLabel(SymbolCU(&TheCU, R.second.BeginLabel));
27562756

@@ -2791,7 +2791,7 @@ void DwarfDebug::endFunctionImpl(const MachineFunction *MF) {
27912791
DIE &ScopeDIE =
27922792
TheCU.constructSubprogramScopeDIE(SP, F, FnScope, FunctionLineTableLabel);
27932793
if (auto *SkelCU = TheCU.getSkeleton())
2794-
if (!LScopes.getAbstractScopesList().empty() &&
2794+
if (LScopes.currentFunctionHasInlinedScopes() &&
27952795
TheCU.getCUNode()->getSplitDebugInlining())
27962796
SkelCU->constructSubprogramScopeDIE(SP, F, FnScope,
27972797
FunctionLineTableLabel);

llvm/lib/CodeGen/LexicalScopes.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,19 +61,28 @@ void LexicalScopes::initialize(const Module &M) {
6161
for (const Function &F : M) {
6262
DISubprogram *SP = F.getSubprogram();
6363
if (SP && (!SP->getUnit() || !skipUnit(SP->getUnit())))
64-
FunctionMap[SP] = &F;
64+
FunctionMap[SP].insert(&F);
6565
}
6666
}
6767

6868
void LexicalScopes::scanFunction(const MachineFunction &Fn) {
6969
resetFunction();
7070
// Don't attempt any lexical scope creation for a NoDebug compile unit.
71-
if (skipUnit(Fn.getFunction().getSubprogram()->getUnit()))
71+
const Function &IRFunc = Fn.getFunction();
72+
const DISubprogram *SP = IRFunc.getSubprogram();
73+
if (skipUnit(SP->getUnit()))
7274
return;
75+
76+
// A new subprogram may be created during Codegen after module scan.
77+
FunctionMap[SP].insert(&IRFunc);
78+
7379
MF = &Fn;
7480
SmallVector<InsnRange, 4> MIRanges;
7581
DenseMap<const MachineInstr *, LexicalScope *> MI2ScopeMap;
7682
extractLexicalScopes(MIRanges, MI2ScopeMap);
83+
// If no scopes were extracted, abstract scope is still needed
84+
// for a subprogram attached to multiple functions.
85+
ensureAbstractLexicalScopeIsCreated(SP);
7786
if (CurrentFnLexicalScope) {
7887
constructScopeNest(CurrentFnLexicalScope);
7988
assignInstructionRanges(MIRanges, MI2ScopeMap);
@@ -167,6 +176,8 @@ LexicalScope *LexicalScopes::getOrCreateLexicalScope(const DILocalScope *Scope,
167176
return getOrCreateInlinedScope(Scope, IA);
168177
}
169178

179+
// Parent DISubprogram may be attached by multiple functions.
180+
ensureAbstractLexicalScopeIsCreated(Scope);
170181
return getOrCreateRegularScope(Scope);
171182
}
172183

llvm/lib/IR/Verifier.cpp

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -336,9 +336,6 @@ class Verifier : public InstVisitor<Verifier>, VerifierSupport {
336336
/// Keep track of the metadata nodes that have been checked already.
337337
SmallPtrSet<const Metadata *, 32> MDNodes;
338338

339-
/// Keep track which DISubprogram is attached to which function.
340-
DenseMap<const DISubprogram *, const Function *> DISubprogramAttachments;
341-
342339
/// Track all DICompileUnits visited.
343340
SmallPtrSet<const Metadata *, 2> CUVisited;
344341

@@ -485,7 +482,6 @@ class Verifier : public InstVisitor<Verifier>, VerifierSupport {
485482
verifyCompileUnits();
486483

487484
verifyDeoptimizeCallingConvs();
488-
DISubprogramAttachments.clear();
489485
return !Broken;
490486
}
491487

@@ -3145,11 +3141,6 @@ void Verifier::visitFunction(const Function &F) {
31453141
"function definition may only have a distinct !dbg attachment",
31463142
&F);
31473143

3148-
auto *SP = cast<DISubprogram>(I.second);
3149-
const Function *&AttachedTo = DISubprogramAttachments[SP];
3150-
CheckDI(!AttachedTo || AttachedTo == &F,
3151-
"DISubprogram attached to more than one function", SP, &F);
3152-
AttachedTo = &F;
31533144
AllowLocs = AreDebugLocsAllowed::Yes;
31543145
break;
31553146
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
; RUN: llc -filetype=obj < %s -o - 2>&1 | llvm-readobj - --codeview | FileCheck %s
2+
3+
; Check that when DISubprogram is attached to two functions, CodeView is
4+
; produced correctly.
5+
6+
; CHECK: Subsection [
7+
; CHECK: GlobalProcIdSym {
8+
; CHECK: Kind: S_GPROC32_ID (0x1147)
9+
; CHECK: FunctionType: foo (0x1002)
10+
; CHECK: CodeOffset: foo+0x0
11+
; CHECK: Flags [ (0x80)
12+
; CHECK: HasOptimizedDebugInfo (0x80)
13+
; CHECK: ]
14+
; CHECK: DisplayName: foo
15+
; CHECK: LinkageName: foo
16+
; CHECK: }
17+
; CHECK: LocalSym {
18+
; CHECK: Kind: S_LOCAL (0x113E)
19+
; CHECK: Type: int (0x74)
20+
; CHECK: Flags [ (0x0)
21+
; CHECK: ]
22+
; CHECK: VarName: a
23+
; CHECK: }
24+
; CHECK: LocalSym {
25+
; CHECK: Kind: S_LOCAL (0x113E)
26+
; CHECK: Type: foo::bar (0x1005)
27+
; CHECK: Flags [ (0x0)
28+
; CHECK: ]
29+
; CHECK: VarName: c
30+
; CHECK: }
31+
; CHECK: UDTSym {
32+
; CHECK: Kind: S_UDT (0x1108)
33+
; CHECK: Type: foo::bar (0x1005)
34+
; CHECK: UDTName: foo::bar
35+
; CHECK: }
36+
; CHECK: ]
37+
; CHECK: Subsection [
38+
; CHECK: GlobalProcIdSym {
39+
; CHECK: Kind: S_GPROC32_ID (0x1147)
40+
; CHECK: FunctionType: foo (0x1002)
41+
; CHECK: CodeOffset: foo_clone+0x0
42+
; CHECK: Flags [ (0x80)
43+
; CHECK: HasOptimizedDebugInfo (0x80)
44+
; CHECK: ]
45+
; CHECK: DisplayName: foo
46+
; CHECK: LinkageName: foo_clone
47+
; CHECK: }
48+
; CHECK: LocalSym {
49+
; CHECK: Kind: S_LOCAL (0x113E)
50+
; CHECK: Type: int (0x74)
51+
; CHECK: Flags [ (0x0)
52+
; CHECK: ]
53+
; CHECK: VarName: a
54+
; CHECK: }
55+
; CHECK: LocalSym {
56+
; CHECK: Kind: S_LOCAL (0x113E)
57+
; CHECK: Type: foo::bar (0x1005)
58+
; CHECK: Flags [ (0x0)
59+
; CHECK: ]
60+
; CHECK: VarName: c
61+
; CHECK: }
62+
; CHECK: UDTSym {
63+
; CHECK: Kind: S_UDT (0x1108)
64+
; CHECK: Type: foo::bar (0x1005)
65+
; CHECK: UDTName: foo::bar
66+
; CHECK: }
67+
; CHECK: ]
68+
69+
; ModuleID = 'shared-sp.ll'
70+
target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
71+
target triple = "x86_64-pc-windows-msvc19.29.30133"
72+
73+
!0 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 1, type: !2, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !4, retainedNodes: !{})
74+
!1 = !DIFile(filename: "example.c", directory: "/")
75+
!2 = !DISubroutineType(types: !3)
76+
!3 = !{!5}
77+
!4 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, emissionKind: FullDebug)
78+
!5 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
79+
80+
; Local variable.
81+
!10 = !DILocalVariable(name: "a", scope: !0, file: !1, line: 2, type: !5)
82+
83+
; DICompositeType local to foo.
84+
!11 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "bar", scope: !0, file: !1, line: 2, size: 32, elements: !12)
85+
!12 = !{!13}
86+
!13 = !DIDerivedType(tag: DW_TAG_member, name: "m", scope: !11, file: !1, line: 2, baseType: !5, size: 32)
87+
88+
; Local variable of type struct bar, local to foo.
89+
!14 = !DILocalVariable(name: "c", scope: !0, file: !1, line: 2, type: !11)
90+
91+
!101 = !DILocation(line: 2, column: 5, scope: !0)
92+
!102 = !DILocation(line: 3, column: 1, scope: !0)
93+
!103 = !DILocation(line: 2, column: 12, scope: !0)
94+
95+
!llvm.dbg.cu = !{!4}
96+
!llvm.module.flags = !{!29, !30}
97+
98+
!29 = !{i32 2, !"CodeView", i32 1}
99+
!30 = !{i32 2, !"Debug Info Version", i32 3}
100+
101+
define i32 @foo() !dbg !0 {
102+
entry:
103+
; Local variable 'a' debug info.
104+
%a.addr = alloca i32, align 4, !dbg !101
105+
#dbg_declare(ptr %a.addr, !10, !DIExpression(), !101)
106+
store i32 42, ptr %a.addr, align 4, !dbg !101
107+
108+
; Local variable 'c' (struct bar) debug info.
109+
%c.addr = alloca %struct.bar, align 4, !dbg !103
110+
#dbg_declare(ptr %c.addr, !14, !DIExpression(), !103)
111+
112+
ret i32 42, !dbg !102
113+
}
114+
115+
define i32 @foo_clone() !dbg !0 {
116+
entry:
117+
; Local variable 'a' debug info.
118+
%a.addr = alloca i32, align 4, !dbg !101
119+
#dbg_declare(ptr %a.addr, !10, !DIExpression(), !101)
120+
store i32 42, ptr %a.addr, align 4, !dbg !101
121+
122+
; Local variable 'c' (struct bar) debug info.
123+
%c.addr = alloca %struct.bar, align 4, !dbg !103
124+
#dbg_declare(ptr %c.addr, !14, !DIExpression(), !103)
125+
126+
ret i32 42, !dbg !102
127+
}
128+
129+
%struct.bar = type { i32 }

llvm/test/DebugInfo/COFF/udts-complete.ll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@
3333
; CHECK: Mod 0000 | `.debug$S`:
3434
; CHECK: 0 | S_GDATA32 [size = 20] `gv`
3535
; CHECK: type = 0x1002 (Foo), addr = 0000:0000
36-
; CHECK: 0 | S_UDT [size = 12] `Bar`
37-
; CHECK: original type = 0x1002
3836
; CHECK: 0 | S_UDT [size = 12] `Baz`
3937
; CHECK: original type = 0x1002
38+
; CHECK: 0 | S_UDT [size = 12] `Bar`
39+
; CHECK: original type = 0x1002
4040
; CHECK: 0 | S_UDT [size = 12] `Foo`
4141
; CHECK: original type = 0x1002
4242

llvm/test/DebugInfo/COFF/udts-fixpoint.ll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@
1616
; Check that there are only two typedefs, a and c.
1717
; CHECK: .short 4360 # Record kind: S_UDT
1818
; CHECK: .long {{.*}} # Type
19-
; CHECK: .asciz "a"
19+
; CHECK: .asciz "c"
2020
; CHECK: .p2align 2
2121
; CHECK: .short 4360 # Record kind: S_UDT
2222
; CHECK: .long {{.*}} # Type
23-
; CHECK: .asciz "c"
23+
; CHECK: .asciz "a"
2424
; CHECK: .p2align 2
2525
; No other S_UDTs.
2626
; CHECK-NOT: S_UDT

0 commit comments

Comments
 (0)