Skip to content

Commit a7b0417

Browse files
author
Zachary Turner
committedAug 28, 2017
[CodeView] Don't output S_UDT symbols for forward decls.
S_UDT symbols are the debugger's "index" for all the structs, typedefs, classes, and enums in a program. If any of those structs/classes don't have a complete declaration, or if there is a typedef to something that doesn't have a complete definition, then emitting the S_UDT is unhelpful because it doesn't give the debugger enough information to do anything useful. On the other hand, it results in a huge size blow-up in the resulting PDB, which is exacerbated by an order of magnitude when linking with /DEBUG:FASTLINK. With this patch, we drop S_UDT records for types that refer either directly or indirectly (e.g. through a typedef, pointer, etc) to a class/struct/union/enum without a complete definition. This brings us about 50% of the way towards parity with /DEBUG:FASTLINK PDBs generated from cl-compiled object files. Differential Revision: https://reviews.llvm.org/D37162 llvm-svn: 311904
1 parent c35e4de commit a7b0417

File tree

4 files changed

+159
-16
lines changed

4 files changed

+159
-16
lines changed
 

Diff for: ‎llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h

+4
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,10 @@ class TagRecord : public TypeRecord {
416416
return (Options & ClassOptions::Nested) != ClassOptions::None;
417417
}
418418

419+
bool isForwardRef() const {
420+
return (Options & ClassOptions::ForwardReference) != ClassOptions::None;
421+
}
422+
419423
uint16_t getMemberCount() const { return MemberCount; }
420424
ClassOptions getOptions() const { return Options; }
421425
TypeIndex getFieldList() const { return FieldList; }

Diff for: ‎llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp

+30-11
Original file line numberDiff line numberDiff line change
@@ -1099,10 +1099,25 @@ void CodeViewDebug::beginFunctionImpl(const MachineFunction *MF) {
10991099
}
11001100
}
11011101

1102-
void CodeViewDebug::addToUDTs(const DIType *Ty, TypeIndex TI) {
1102+
static bool shouldEmitUdt(const DIType *T) {
1103+
while (true) {
1104+
if (!T || T->isForwardDecl())
1105+
return false;
1106+
1107+
const DIDerivedType *DT = dyn_cast<DIDerivedType>(T);
1108+
if (!DT)
1109+
return true;
1110+
T = DT->getBaseType().resolve();
1111+
}
1112+
return true;
1113+
}
1114+
1115+
void CodeViewDebug::addToUDTs(const DIType *Ty) {
11031116
// Don't record empty UDTs.
11041117
if (Ty->getName().empty())
11051118
return;
1119+
if (!shouldEmitUdt(Ty))
1120+
return;
11061121

11071122
SmallVector<StringRef, 5> QualifiedNameComponents;
11081123
const DISubprogram *ClosestSubprogram = getQualifiedNameComponents(
@@ -1111,10 +1126,11 @@ void CodeViewDebug::addToUDTs(const DIType *Ty, TypeIndex TI) {
11111126
std::string FullyQualifiedName =
11121127
getQualifiedName(QualifiedNameComponents, getPrettyScopeName(Ty));
11131128

1114-
if (ClosestSubprogram == nullptr)
1115-
GlobalUDTs.emplace_back(std::move(FullyQualifiedName), TI);
1116-
else if (ClosestSubprogram == CurrentSubprogram)
1117-
LocalUDTs.emplace_back(std::move(FullyQualifiedName), TI);
1129+
if (ClosestSubprogram == nullptr) {
1130+
GlobalUDTs.emplace_back(std::move(FullyQualifiedName), Ty);
1131+
} else if (ClosestSubprogram == CurrentSubprogram) {
1132+
LocalUDTs.emplace_back(std::move(FullyQualifiedName), Ty);
1133+
}
11181134

11191135
// TODO: What if the ClosestSubprogram is neither null or the current
11201136
// subprogram? Currently, the UDT just gets dropped on the floor.
@@ -1172,7 +1188,7 @@ TypeIndex CodeViewDebug::lowerTypeAlias(const DIDerivedType *Ty) {
11721188
TypeIndex UnderlyingTypeIndex = getTypeIndex(UnderlyingTypeRef);
11731189
StringRef TypeName = Ty->getName();
11741190

1175-
addToUDTs(Ty, UnderlyingTypeIndex);
1191+
addToUDTs(Ty);
11761192

11771193
if (UnderlyingTypeIndex == TypeIndex(SimpleTypeKind::Int32Long) &&
11781194
TypeName == "HRESULT")
@@ -1764,7 +1780,7 @@ TypeIndex CodeViewDebug::lowerCompleteTypeClass(const DICompositeType *Ty) {
17641780
TypeTable.writeKnownType(USLR);
17651781
}
17661782

1767-
addToUDTs(Ty, ClassTI);
1783+
addToUDTs(Ty);
17681784

17691785
return ClassTI;
17701786
}
@@ -1803,7 +1819,7 @@ TypeIndex CodeViewDebug::lowerCompleteTypeUnion(const DICompositeType *Ty) {
18031819
UdtSourceLineRecord USLR(UnionTI, SIRI, Ty->getLine());
18041820
TypeTable.writeKnownType(USLR);
18051821

1806-
addToUDTs(Ty, UnionTI);
1822+
addToUDTs(Ty);
18071823

18081824
return UnionTI;
18091825
}
@@ -2203,8 +2219,11 @@ void CodeViewDebug::endCVSubsection(MCSymbol *EndLabel) {
22032219
}
22042220

22052221
void CodeViewDebug::emitDebugInfoForUDTs(
2206-
ArrayRef<std::pair<std::string, TypeIndex>> UDTs) {
2207-
for (const std::pair<std::string, codeview::TypeIndex> &UDT : UDTs) {
2222+
ArrayRef<std::pair<std::string, const DIType *>> UDTs) {
2223+
for (const auto &UDT : UDTs) {
2224+
const DIType *T = UDT.second;
2225+
assert(shouldEmitUdt(T));
2226+
22082227
MCSymbol *UDTRecordBegin = MMI->getContext().createTempSymbol(),
22092228
*UDTRecordEnd = MMI->getContext().createTempSymbol();
22102229
OS.AddComment("Record length");
@@ -2215,7 +2234,7 @@ void CodeViewDebug::emitDebugInfoForUDTs(
22152234
OS.EmitIntValue(unsigned(SymbolKind::S_UDT), 2);
22162235

22172236
OS.AddComment("Type");
2218-
OS.EmitIntValue(UDT.second.getIndex(), 4);
2237+
OS.EmitIntValue(getCompleteTypeIndex(T).getIndex(), 4);
22192238

22202239
emitNullTerminatedSymbolName(OS, UDT.first);
22212240
OS.EmitLabel(UDTRecordEnd);

Diff for: ‎llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h

+5-5
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,8 @@ class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase {
187187

188188
// The UDTs we have seen while processing types; each entry is a pair of type
189189
// index and type name.
190-
std::vector<std::pair<std::string, codeview::TypeIndex>> LocalUDTs,
191-
GlobalUDTs;
190+
std::vector<std::pair<std::string, const DIType *>> LocalUDTs;
191+
std::vector<std::pair<std::string, const DIType *>> GlobalUDTs;
192192

193193
using FileToFilepathMapTy = std::map<const DIFile *, std::string>;
194194
FileToFilepathMapTy FileToFilepathMap;
@@ -222,8 +222,8 @@ class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase {
222222

223223
void emitDebugInfoForRetainedTypes();
224224

225-
void emitDebugInfoForUDTs(
226-
ArrayRef<std::pair<std::string, codeview::TypeIndex>> UDTs);
225+
void
226+
emitDebugInfoForUDTs(ArrayRef<std::pair<std::string, const DIType *>> UDTs);
227227

228228
void emitDebugInfoForGlobal(const DIGlobalVariable *DIGV,
229229
const GlobalVariable *GV, MCSymbol *GVSym);
@@ -266,7 +266,7 @@ class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase {
266266

267267
codeview::TypeIndex getVBPTypeIndex();
268268

269-
void addToUDTs(const DIType *Ty, codeview::TypeIndex TI);
269+
void addToUDTs(const DIType *Ty);
270270

271271
codeview::TypeIndex lowerType(const DIType *Ty, const DIType *ClassTy);
272272
codeview::TypeIndex lowerTypeAlias(const DIDerivedType *Ty);

Diff for: ‎llvm/test/DebugInfo/COFF/purge-typedef-udts.ll

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
; RUN: llc < %s -filetype=obj | llvm-readobj - -codeview | FileCheck %s
2+
source_filename = "test/DebugInfo/COFF/purge-typedef-udts.ll"
3+
target datalayout = "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32"
4+
target triple = "i686-pc-windows-msvc19.11.25506"
5+
6+
; C++ source to regenerate:
7+
; $ cat t.cpp
8+
; struct Foo;
9+
; struct Bar {
10+
; Bar() {}
11+
; int X;
12+
; };
13+
;
14+
; typedef Foo FooTypedef;
15+
; typedef Bar BarTypedef;
16+
;
17+
; int func(void *F) { return 7; }
18+
; int func(const FooTypedef *F) { return func((void*)F); }
19+
; int func(const BarTypedef *B) { return func((void*)B->X); }
20+
21+
; CHECK-NOT: UDTName: FooTypedef
22+
; CHECK: UDTName: BarTypedef
23+
24+
%struct.Foo = type opaque
25+
%struct.Bar = type { i32 }
26+
27+
; Function Attrs: noinline nounwind optnone
28+
define i32 @"\01?func@@YAHPAX@Z"(i8* %F) #0 !dbg !10 {
29+
entry:
30+
%F.addr = alloca i8*, align 4
31+
store i8* %F, i8** %F.addr, align 4
32+
call void @llvm.dbg.declare(metadata i8** %F.addr, metadata !14, metadata !DIExpression()), !dbg !15
33+
ret i32 7, !dbg !16
34+
}
35+
36+
; Function Attrs: nounwind readnone speculatable
37+
declare void @llvm.dbg.declare(metadata, metadata, metadata) #1
38+
39+
; Function Attrs: noinline nounwind optnone
40+
define i32 @"\01?func@@YAHPBUFoo@@@Z"(%struct.Foo* %F) #0 !dbg !17 {
41+
entry:
42+
%F.addr = alloca %struct.Foo*, align 4
43+
store %struct.Foo* %F, %struct.Foo** %F.addr, align 4
44+
call void @llvm.dbg.declare(metadata %struct.Foo** %F.addr, metadata !24, metadata !DIExpression()), !dbg !25
45+
%0 = load %struct.Foo*, %struct.Foo** %F.addr, align 4, !dbg !26
46+
%1 = bitcast %struct.Foo* %0 to i8*, !dbg !26
47+
%call = call i32 @"\01?func@@YAHPAX@Z"(i8* %1), !dbg !27
48+
ret i32 %call, !dbg !28
49+
}
50+
51+
; Function Attrs: noinline nounwind optnone
52+
define i32 @"\01?func@@YAHPBUBar@@@Z"(%struct.Bar* %B) #0 !dbg !29 {
53+
entry:
54+
%B.addr = alloca %struct.Bar*, align 4
55+
store %struct.Bar* %B, %struct.Bar** %B.addr, align 4
56+
call void @llvm.dbg.declare(metadata %struct.Bar** %B.addr, metadata !42, metadata !DIExpression()), !dbg !43
57+
%0 = load %struct.Bar*, %struct.Bar** %B.addr, align 4, !dbg !44
58+
%X = getelementptr inbounds %struct.Bar, %struct.Bar* %0, i32 0, i32 0, !dbg !45
59+
%1 = load i32, i32* %X, align 4, !dbg !45
60+
%2 = inttoptr i32 %1 to i8*, !dbg !46
61+
%call = call i32 @"\01?func@@YAHPAX@Z"(i8* %2), !dbg !47
62+
ret i32 %call, !dbg !48
63+
}
64+
65+
attributes #0 = { noinline nounwind optnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="pentium4" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
66+
attributes #1 = { nounwind readnone speculatable }
67+
68+
!llvm.dbg.cu = !{!0}
69+
!llvm.module.flags = !{!5, !6, !7, !8}
70+
!llvm.ident = !{!9}
71+
72+
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 6.0.0 ", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3)
73+
!1 = !DIFile(filename: "t.cpp", directory: "D:\5Csrc\5Cllvmbuild\5Cninja", checksumkind: CSK_MD5, checksum: "27c44c8a5531845f61f582a24ef5c151")
74+
!2 = !{}
75+
!3 = !{!4}
76+
!4 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 32)
77+
!5 = !{i32 1, !"NumRegisterParameters", i32 0}
78+
!6 = !{i32 2, !"CodeView", i32 1}
79+
!7 = !{i32 2, !"Debug Info Version", i32 3}
80+
!8 = !{i32 1, !"wchar_size", i32 2}
81+
!9 = !{!"clang version 6.0.0 "}
82+
!10 = distinct !DISubprogram(name: "func", linkageName: "\01?func@@YAHPAX@Z", scope: !1, file: !1, line: 10, type: !11, isLocal: false, isDefinition: true, scopeLine: 10, flags: DIFlagPrototyped, isOptimized: false, unit: !0, variables: !2)
83+
!11 = !DISubroutineType(types: !12)
84+
!12 = !{!13, !4}
85+
!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
86+
!14 = !DILocalVariable(name: "F", arg: 1, scope: !10, file: !1, line: 10, type: !4)
87+
!15 = !DILocation(line: 10, column: 16, scope: !10)
88+
!16 = !DILocation(line: 10, column: 21, scope: !10)
89+
!17 = distinct !DISubprogram(name: "func", linkageName: "\01?func@@YAHPBUFoo@@@Z", scope: !1, file: !1, line: 11, type: !18, isLocal: false, isDefinition: true, scopeLine: 11, flags: DIFlagPrototyped, isOptimized: false, unit: !0, variables: !2)
90+
!18 = !DISubroutineType(types: !19)
91+
!19 = !{!13, !20}
92+
!20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 32)
93+
!21 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !22)
94+
!22 = !DIDerivedType(tag: DW_TAG_typedef, name: "FooTypedef", file: !1, line: 7, baseType: !23)
95+
!23 = !DICompositeType(tag: DW_TAG_structure_type, name: "Foo", file: !1, line: 1, flags: DIFlagFwdDecl, identifier: ".?AUFoo@@")
96+
!24 = !DILocalVariable(name: "F", arg: 1, scope: !17, file: !1, line: 11, type: !20)
97+
!25 = !DILocation(line: 11, column: 28, scope: !17)
98+
!26 = !DILocation(line: 11, column: 52, scope: !17)
99+
!27 = !DILocation(line: 11, column: 40, scope: !17)
100+
!28 = !DILocation(line: 11, column: 33, scope: !17)
101+
!29 = distinct !DISubprogram(name: "func", linkageName: "\01?func@@YAHPBUBar@@@Z", scope: !1, file: !1, line: 12, type: !30, isLocal: false, isDefinition: true, scopeLine: 12, flags: DIFlagPrototyped, isOptimized: false, unit: !0, variables: !2)
102+
!30 = !DISubroutineType(types: !31)
103+
!31 = !{!13, !32}
104+
!32 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !33, size: 32)
105+
!33 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !34)
106+
!34 = !DIDerivedType(tag: DW_TAG_typedef, name: "BarTypedef", file: !1, line: 8, baseType: !35)
107+
!35 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Bar", file: !1, line: 2, size: 32, elements: !36, identifier: ".?AUBar@@")
108+
!36 = !{!37, !38}
109+
!37 = !DIDerivedType(tag: DW_TAG_member, name: "X", scope: !35, file: !1, line: 4, baseType: !13, size: 32)
110+
!38 = !DISubprogram(name: "Bar", scope: !35, file: !1, line: 3, type: !39, isLocal: false, isDefinition: false, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: false)
111+
!39 = !DISubroutineType(cc: DW_CC_BORLAND_thiscall, types: !40)
112+
!40 = !{null, !41}
113+
!41 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !35, size: 32, flags: DIFlagArtificial | DIFlagObjectPointer)
114+
!42 = !DILocalVariable(name: "B", arg: 1, scope: !29, file: !1, line: 12, type: !32)
115+
!43 = !DILocation(line: 12, column: 28, scope: !29)
116+
!44 = !DILocation(line: 12, column: 52, scope: !29)
117+
!45 = !DILocation(line: 12, column: 55, scope: !29)
118+
!46 = !DILocation(line: 12, column: 45, scope: !29)
119+
!47 = !DILocation(line: 12, column: 40, scope: !29)
120+
!48 = !DILocation(line: 12, column: 33, scope: !29)

0 commit comments

Comments
 (0)
Please sign in to comment.