Skip to content

Commit 883750c

Browse files
committed
[DebugInfo][OPT] Fixing a couple of DI duplication bugs of CloneModule
As demonstrated by the regression tests added in this patch, the following cases are valid cases: 1. A Function with no DISubprogram attached, but various debug info related to its instructions, coming, for instance, from an inlined function, also defined somewhere else in the same module; 2. ... or coming exclusively from the functions inlined and eliminated from the module entirely. The ValueMap shared between CloneFunctionInto calls within CloneModule needs to contain identity mappings for all of the DISubprogram's to prevent them from being duplicated by MapMetadata / RemapInstruction calls, this is achieved via DebugInfoFinder collecting all the DISubprogram's. However, CloneFunctionInto was missing calls into DebugInfoFinder for functions w/o DISubprogram's attached, but still referring DISubprogram's from within (case 1). This patch fixes that. The fix above, however, exposes another issue: if a module contains a DISubprogram referenced only indirectly from other debug info metadata, but not attached to any Function defined within the module (case 2), cloning such a module causes a DICompileUnit duplication: it will be moved in indirecty via a DISubprogram by DebugInfoFinder first (because of the first bug fix described above), without being self-mapped within the shared ValueMap, and then will be copied during named metadata cloning. So this patch makes sure DebugInfoFinder visits DICompileUnit's referenced from DISubprogram's as it goes w/o re-processing llvm.dbg.cu list over and over again for every function cloned, and makes sure that CloneFunctionInto self-maps DICompileUnit's referenced from the entire function, not just its own DISubprogram attached that may also be missing. The most convenient way of tesing CloneModule I found is to rely on CloneModule call from `opt -run-twice`, instead of writing tedious unit tests. That feature has a couple of properties that makes it hard to use for this purpose though: 1. CloneModule doesn't copy source filename, making `opt -run-twice` report it as a difference. 2. `opt -run-twice` does the second run on the original module, not its clone, making the result of cloning completely invisible in opt's actual output with and without `-run-twice` both, which directly contradicts `opt -run-twice`s own error message. This patch fixes this as well. Reviewed By: aprantl Reviewers: loladiro, GorNishanov, espindola, echristo, dexonsmith Subscribers: vsk, debug-info, JDevlieghere, llvm-commits Differential Revision: https://reviews.llvm.org/D45593 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@330069 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 129fc8c commit 883750c

File tree

7 files changed

+199
-13
lines changed

7 files changed

+199
-13
lines changed

include/llvm/IR/DebugInfo.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ class DebugInfoFinder {
8282

8383
void processType(DIType *DT);
8484
void processSubprogram(DISubprogram *SP);
85+
void processCompileUnit(DICompileUnit *CU);
8586
void processScope(DIScope *Scope);
8687
bool addCompileUnit(DICompileUnit *CU);
8788
bool addGlobalVariable(DIGlobalVariableExpression *DIG);

lib/IR/DebugInfo.cpp

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ void DebugInfoFinder::processModule(const Module &M) {
8787
processScope(NS->getScope());
8888
else if (auto *M = dyn_cast<DIModule>(Entity))
8989
processScope(M->getScope());
90+
else
91+
llvm_unreachable("unexpected imported entity type");
9092
}
9193
}
9294
for (auto &F : M.functions()) {
@@ -96,14 +98,50 @@ void DebugInfoFinder::processModule(const Module &M) {
9698
// instructions only. Walk the function to find them.
9799
for (const BasicBlock &BB : F) {
98100
for (const Instruction &I : BB) {
99-
if (!I.getDebugLoc())
100-
continue;
101-
processLocation(M, I.getDebugLoc().get());
101+
if (auto *DDI = dyn_cast<DbgDeclareInst>(&I))
102+
processDeclare(M, DDI);
103+
else if (auto *DVI = dyn_cast<DbgValueInst>(&I))
104+
processValue(M, DVI);
105+
106+
if (auto DbgLoc = I.getDebugLoc())
107+
processLocation(M, DbgLoc.get());
102108
}
103109
}
104110
}
105111
}
106112

113+
void DebugInfoFinder::processCompileUnit(DICompileUnit *CU) {
114+
if (!addCompileUnit(CU))
115+
return;
116+
for (auto DIG : CU->getGlobalVariables()) {
117+
if (!addGlobalVariable(DIG))
118+
continue;
119+
auto *GV = DIG->getVariable();
120+
processScope(GV->getScope());
121+
processType(GV->getType().resolve());
122+
}
123+
for (auto *ET : CU->getEnumTypes())
124+
processType(ET);
125+
for (auto *RT : CU->getRetainedTypes())
126+
if (auto *T = dyn_cast<DIType>(RT))
127+
processType(T);
128+
else
129+
processSubprogram(cast<DISubprogram>(RT));
130+
for (auto *Import : CU->getImportedEntities()) {
131+
auto *Entity = Import->getEntity().resolve();
132+
if (auto *T = dyn_cast<DIType>(Entity))
133+
processType(T);
134+
else if (auto *SP = dyn_cast<DISubprogram>(Entity))
135+
processSubprogram(SP);
136+
else if (auto *NS = dyn_cast<DINamespace>(Entity))
137+
processScope(NS->getScope());
138+
else if (auto *M = dyn_cast<DIModule>(Entity))
139+
processScope(M->getScope());
140+
else
141+
llvm_unreachable("unexpected imported entity type");
142+
}
143+
}
144+
107145
void DebugInfoFinder::processLocation(const Module &M, const DILocation *Loc) {
108146
if (!Loc)
109147
return;
@@ -165,6 +203,15 @@ void DebugInfoFinder::processSubprogram(DISubprogram *SP) {
165203
if (!addSubprogram(SP))
166204
return;
167205
processScope(SP->getScope().resolve());
206+
// Some of the users, e.g. CloneFunctionInto / CloneModule, need to set up a
207+
// ValueMap containing identity mappings for all of the DICompileUnit's, not
208+
// just DISubprogram's, referenced from anywhere within the Function being
209+
// cloned prior to calling MapMetadata / RemapInstruction to avoid their
210+
// duplication later as DICompileUnit's are also directly referenced by
211+
// llvm.dbg.cu list. Thefore we need to collect DICompileUnit's here as well.
212+
// Also, DICompileUnit's may reference DISubprogram's too and therefore need
213+
// to be at least looked through.
214+
processCompileUnit(SP->getUnit());
168215
processType(SP->getType());
169216
for (auto *Element : SP->getTemplateParams()) {
170217
if (auto *TType = dyn_cast<DITemplateTypeParameter>(Element)) {

lib/Transforms/Utils/CloneFunction.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ void llvm::CloneFunctionInto(Function *NewFunc, const Function *OldFunc,
175175

176176
// Create a new basic block and copy instructions into it!
177177
BasicBlock *CBB = CloneBasicBlock(&BB, VMap, NameSuffix, NewFunc, CodeInfo,
178-
SP ? &DIFinder : nullptr);
178+
ModuleLevelChanges ? &DIFinder : nullptr);
179179

180180
// Add basic block mapping.
181181
VMap[&BB] = CBB;
@@ -203,6 +203,9 @@ void llvm::CloneFunctionInto(Function *NewFunc, const Function *OldFunc,
203203
}
204204
}
205205

206+
for (DICompileUnit *CU : DIFinder.compile_units())
207+
VMap.MD()[CU].reset(CU);
208+
206209
for (auto *Type : DIFinder.types()) {
207210
VMap.MD()[Type].reset(Type);
208211
}

lib/Transforms/Utils/CloneModule.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ std::unique_ptr<Module> llvm::CloneModule(
5050
// First off, we need to create the new module.
5151
std::unique_ptr<Module> New =
5252
llvm::make_unique<Module>(M.getModuleIdentifier(), M.getContext());
53+
New->setSourceFileName(M.getSourceFileName());
5354
New->setDataLayout(M.getDataLayout());
5455
New->setTargetTriple(M.getTargetTriple());
5556
New->setModuleInlineAsm(M.getModuleInlineAsm());
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
; RUN: opt -run-twice -verify -S -o - %s | FileCheck %s
2+
3+
; If a module contains a DISubprogram referenced only indirectly from
4+
; instruction-level debug info metadata, but not attached to any Function
5+
; defined within the module, cloning such a module with CloneModule was
6+
; causing a DICompileUnit duplication: it would be moved in indirecty via a
7+
; DISubprogram by DebugInfoFinder (making sure DISubprogram's don't get
8+
; duplicated) first without being explicitly self-mapped within the ValueMap
9+
; shared among CloneFunctionInto calls, and then it would get copied during
10+
; named metadata cloning.
11+
;
12+
; This is to make sure we don't regress on that.
13+
14+
; Derived from the following C-snippet
15+
;
16+
; static int eliminated(int j);
17+
; __attribute__((nodebug)) int nodebug(int k) { return eliminated(k); }
18+
; __attribute__((always_inline)) static int eliminated(int j) { return j * 2; }
19+
;
20+
; compiled with `clang -O1 -g1 -emit-llvm -S`
21+
22+
; CHECK: DICompileUnit
23+
; CHECK-NOT: DICompileUnit
24+
25+
source_filename = "clone-module.c"
26+
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
27+
target triple = "x86_64-apple-macosx"
28+
29+
; Function Attrs: norecurse nounwind readnone ssp uwtable
30+
define i32 @nodebug(i32 %k) local_unnamed_addr #0 {
31+
entry:
32+
%mul.i = shl nsw i32 %k, 1, !dbg !8
33+
ret i32 %mul.i
34+
}
35+
36+
attributes #0 = { norecurse nounwind readnone ssp uwtable }
37+
38+
!llvm.dbg.cu = !{!0}
39+
!llvm.module.flags = !{!3, !4, !5, !6}
40+
!llvm.ident = !{!7}
41+
42+
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 7.0.0 (https://git.llvm.org/git/clang.git/ 195459d046e795f5952f7d2121e505eeb59c5574) (https://git.llvm.org/git/llvm.git/ e9dc5b5ade57869d1a443c568c6cf556ccf3b7af)", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, enums: !2)
43+
!1 = !DIFile(filename: "test.c", directory: "/Volumes/Data/llvm/build/obj")
44+
!2 = !{}
45+
!3 = !{i32 2, !"Dwarf Version", i32 4}
46+
!4 = !{i32 2, !"Debug Info Version", i32 3}
47+
!5 = !{i32 1, !"wchar_size", i32 4}
48+
!6 = !{i32 7, !"PIC Level", i32 2}
49+
!7 = !{!"clang version 7.0.0 (https://git.llvm.org/git/clang.git/ 195459d046e795f5952f7d2121e505eeb59c5574) (https://git.llvm.org/git/llvm.git/ e9dc5b5ade57869d1a443c568c6cf556ccf3b7af)"}
50+
!8 = !DILocation(line: 3, column: 72, scope: !9)
51+
!9 = distinct !DISubprogram(name: "eliminated", scope: !1, file: !1, line: 3, type: !10, isLocal: true, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !2)
52+
!10 = !DISubroutineType(types: !2)

test/DebugInfo/X86/clone-module.ll

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
; RUN: opt -run-twice -verify -S -o - %s | FileCheck %s
2+
3+
; The ValueMap shared between CloneFunctionInto calls within CloneModule needs
4+
; to contain identity mappings for all of the DISubprogram's to prevent them
5+
; from being duplicated by MapMetadata / RemapInstruction calls, this is
6+
; achieved via DebugInfoFinder collecting all the DISubprogram's. However,
7+
; CloneFunctionInto was missing calls into DebugInfoFinder for functions w/o
8+
; DISubprogram's attached, but still referring DISubprogram's from within.
9+
;
10+
; This is to make sure we don't regress on that.
11+
12+
; Derived from the following C-snippet
13+
;
14+
; int inlined(int j);
15+
; __attribute__((nodebug)) int nodebug(int k) { return inlined(k); }
16+
; __attribute__((always_inline)) int inlined(int j) { return j * 2; }
17+
;
18+
; compiled with `clang -O1 -g3 -emit-llvm -S` by removing
19+
;
20+
; call void @llvm.dbg.value(metadata i32 %k, metadata !8, metadata !DIExpression()), !dbg !14
21+
;
22+
; line from @nodebug function.
23+
24+
; The @llvm.dbg.value call is manually removed from @nodebug as not having
25+
; it there also may cause an incorrect remapping of the call in a case of a
26+
; regression, not just a duplication of a DISubprogram. Namely, the call's
27+
; metadata !8 2nd argument and the !dbg !14 debug location may get remapped
28+
; to reference different copies of the DISubprogram, which is verified by IR
29+
; Verifier, while having DISubprogram duplicates is not.
30+
31+
; CHECK: DISubprogram
32+
; CHECK-NOT: DISubprogram
33+
34+
source_filename = "clone-module.c"
35+
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
36+
target triple = "x86_64-apple-macosx"
37+
38+
; Function Attrs: nounwind readnone ssp uwtable
39+
define i32 @nodebug(i32 %k) local_unnamed_addr #0 {
40+
entry:
41+
%mul.i = shl nsw i32 %k, 1, !dbg !15
42+
ret i32 %mul.i
43+
}
44+
45+
; Function Attrs: alwaysinline nounwind readnone ssp uwtable
46+
define i32 @inlined(i32 %j) local_unnamed_addr #1 !dbg !9 {
47+
entry:
48+
call void @llvm.dbg.value(metadata i32 %j, metadata !8, metadata !DIExpression()), !dbg !14
49+
%mul = shl nsw i32 %j, 1, !dbg !15
50+
ret i32 %mul, !dbg !16
51+
}
52+
53+
; Function Attrs: nounwind readnone speculatable
54+
declare void @llvm.dbg.value(metadata, metadata, metadata) #2
55+
56+
attributes #0 = { nounwind readnone ssp uwtable }
57+
attributes #1 = { alwaysinline nounwind readnone ssp uwtable }
58+
attributes #2 = { nounwind readnone speculatable }
59+
60+
!llvm.dbg.cu = !{!0}
61+
!llvm.module.flags = !{!3, !4, !5, !6}
62+
!llvm.ident = !{!7}
63+
64+
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 7.0.0 (https://git.llvm.org/git/clang.git/ 195459d046e795f5952f7d2121e505eeb59c5574) (https://git.llvm.org/git/llvm.git/ 69ec7d5667e9928db8435bfbee0da151c85a91c9)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
65+
!1 = !DIFile(filename: "clone-module.c", directory: "/somewhere")
66+
!2 = !{}
67+
!3 = !{i32 2, !"Dwarf Version", i32 4}
68+
!4 = !{i32 2, !"Debug Info Version", i32 3}
69+
!5 = !{i32 1, !"wchar_size", i32 4}
70+
!6 = !{i32 7, !"PIC Level", i32 2}
71+
!7 = !{!"clang version 7.0.0 (https://git.llvm.org/git/clang.git/ 195459d046e795f5952f7d2121e505eeb59c5574) (https://git.llvm.org/git/llvm.git/ 69ec7d5667e9928db8435bfbee0da151c85a91c9)"}
72+
!8 = !DILocalVariable(name: "j", arg: 1, scope: !9, file: !1, line: 3, type: !12)
73+
!9 = distinct !DISubprogram(name: "inlined", scope: !1, file: !1, line: 3, type: !10, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !13)
74+
!10 = !DISubroutineType(types: !11)
75+
!11 = !{!12, !12}
76+
!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
77+
!13 = !{!8}
78+
!14 = !DILocation(line: 3, column: 48, scope: !9)
79+
!15 = !DILocation(line: 3, column: 62, scope: !9)
80+
!16 = !DILocation(line: 3, column: 53, scope: !9)

tools/opt/opt.cpp

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -755,20 +755,22 @@ int main(int argc, char **argv) {
755755
// Before executing passes, print the final values of the LLVM options.
756756
cl::PrintOptionValues();
757757

758-
// If requested, run all passes again with the same pass manager to catch
759-
// bugs caused by persistent state in the passes
760-
if (RunTwice) {
758+
if (!RunTwice) {
759+
// Now that we have all of the passes ready, run them.
760+
Passes.run(*M);
761+
} else {
762+
// If requested, run all passes twice with the same pass manager to catch
763+
// bugs caused by persistent state in the passes.
761764
std::unique_ptr<Module> M2(CloneModule(*M));
762-
Passes.run(*M2);
765+
// Run all passes on the original module first, so the second run processes
766+
// the clone to catch CloneModule bugs.
767+
Passes.run(*M);
763768
CompileTwiceBuffer = Buffer;
764769
Buffer.clear();
765-
}
766770

767-
// Now that we have all of the passes ready, run them.
768-
Passes.run(*M);
771+
Passes.run(*M2);
769772

770-
// Compare the two outputs and make sure they're the same
771-
if (RunTwice) {
773+
// Compare the two outputs and make sure they're the same
772774
assert(Out);
773775
if (Buffer.size() != CompileTwiceBuffer.size() ||
774776
(memcmp(Buffer.data(), CompileTwiceBuffer.data(), Buffer.size()) !=

0 commit comments

Comments
 (0)