Skip to content

Commit fea067b

Browse files
committed
[mem2reg] Remove dbg.values describing contents of dead allocas
This patch copies @vSK's fix to instcombine from D85555 over to mem2reg. The motivation and rationale are exactly the same: When mem2reg removes an alloca, it erases the dbg.{addr,declare} instructions which refer to the alloca. It would be better to instead remove all debug intrinsics which describe the contents of the dead alloca, namely all dbg.value(<dead alloca>, ..., DW_OP_deref)'s. As far as I can tell, prior to D80264 these `dbg.value+deref`s would have been silently dropped instead of being made `undef`, so we're just returning to previous behaviour with these patches. Testing: `llvm-lit llvm/test` and `ninja check-clang` gave no unexpected failures. Added 3 tests, each of which covers a dbg.value deletion path in mem2reg: mem2reg-promote-alloca-1.ll mem2reg-promote-alloca-2.ll mem2reg-promote-alloca-3.ll The first is based on the dexter test inlining.c from D89543. This patch also improves the debugging experience for loop.c from D89543, which suffers similarly after arg promotion instead of inlining.
1 parent 342040b commit fea067b

File tree

4 files changed

+316
-23
lines changed

4 files changed

+316
-23
lines changed

llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -101,22 +101,24 @@ bool llvm::isAllocaPromotable(const AllocaInst *AI) {
101101
namespace {
102102

103103
struct AllocaInfo {
104+
using DbgUserVec = SmallVector<DbgVariableIntrinsic *, 1>;
105+
104106
SmallVector<BasicBlock *, 32> DefiningBlocks;
105107
SmallVector<BasicBlock *, 32> UsingBlocks;
106108

107109
StoreInst *OnlyStore;
108110
BasicBlock *OnlyBlock;
109111
bool OnlyUsedInOneBlock;
110112

111-
TinyPtrVector<DbgVariableIntrinsic *> DbgDeclares;
113+
DbgUserVec DbgUsers;
112114

113115
void clear() {
114116
DefiningBlocks.clear();
115117
UsingBlocks.clear();
116118
OnlyStore = nullptr;
117119
OnlyBlock = nullptr;
118120
OnlyUsedInOneBlock = true;
119-
DbgDeclares.clear();
121+
DbgUsers.clear();
120122
}
121123

122124
/// Scan the uses of the specified alloca, filling in the AllocaInfo used
@@ -149,7 +151,7 @@ struct AllocaInfo {
149151
}
150152
}
151153

152-
DbgDeclares = FindDbgAddrUses(AI);
154+
findDbgUsers(DbgUsers, AI);
153155
}
154156
};
155157

@@ -247,7 +249,7 @@ struct PromoteMem2Reg {
247249
/// For each alloca, we keep track of the dbg.declare intrinsic that
248250
/// describes it, if any, so that we can convert it to a dbg.value
249251
/// intrinsic if the alloca gets promoted.
250-
SmallVector<TinyPtrVector<DbgVariableIntrinsic *>, 8> AllocaDbgDeclares;
252+
SmallVector<AllocaInfo::DbgUserVec, 8> AllocaDbgUsers;
251253

252254
/// The set of basic blocks the renamer has already visited.
253255
SmallPtrSet<BasicBlock *, 16> Visited;
@@ -421,10 +423,14 @@ static bool rewriteSingleStoreAlloca(AllocaInst *AI, AllocaInfo &Info,
421423

422424
// Record debuginfo for the store and remove the declaration's
423425
// debuginfo.
424-
for (DbgVariableIntrinsic *DII : Info.DbgDeclares) {
425-
DIBuilder DIB(*AI->getModule(), /*AllowUnresolved*/ false);
426-
ConvertDebugDeclareToDebugValue(DII, Info.OnlyStore, DIB);
427-
DII->eraseFromParent();
426+
for (DbgVariableIntrinsic *DII : Info.DbgUsers) {
427+
if (DII->isAddressOfVariable()) {
428+
DIBuilder DIB(*AI->getModule(), /*AllowUnresolved*/ false);
429+
ConvertDebugDeclareToDebugValue(DII, Info.OnlyStore, DIB);
430+
DII->eraseFromParent();
431+
} else if (DII->getExpression()->startsWithDeref()) {
432+
DII->eraseFromParent();
433+
}
428434
}
429435
// Remove the (now dead) store and alloca.
430436
Info.OnlyStore->eraseFromParent();
@@ -519,9 +525,11 @@ static bool promoteSingleBlockAlloca(AllocaInst *AI, const AllocaInfo &Info,
519525
while (!AI->use_empty()) {
520526
StoreInst *SI = cast<StoreInst>(AI->user_back());
521527
// Record debuginfo for the store before removing it.
522-
for (DbgVariableIntrinsic *DII : Info.DbgDeclares) {
523-
DIBuilder DIB(*AI->getModule(), /*AllowUnresolved*/ false);
524-
ConvertDebugDeclareToDebugValue(DII, SI, DIB);
528+
for (DbgVariableIntrinsic *DII : Info.DbgUsers) {
529+
if (DII->isAddressOfVariable()) {
530+
DIBuilder DIB(*AI->getModule(), /*AllowUnresolved*/ false);
531+
ConvertDebugDeclareToDebugValue(DII, SI, DIB);
532+
}
525533
}
526534
SI->eraseFromParent();
527535
LBI.deleteValue(SI);
@@ -530,8 +538,9 @@ static bool promoteSingleBlockAlloca(AllocaInst *AI, const AllocaInfo &Info,
530538
AI->eraseFromParent();
531539

532540
// The alloca's debuginfo can be removed as well.
533-
for (DbgVariableIntrinsic *DII : Info.DbgDeclares)
534-
DII->eraseFromParent();
541+
for (DbgVariableIntrinsic *DII : Info.DbgUsers)
542+
if (DII->isAddressOfVariable() || DII->getExpression()->startsWithDeref())
543+
DII->eraseFromParent();
535544

536545
++NumLocalPromoted;
537546
return true;
@@ -540,7 +549,7 @@ static bool promoteSingleBlockAlloca(AllocaInst *AI, const AllocaInfo &Info,
540549
void PromoteMem2Reg::run() {
541550
Function &F = *DT.getRoot()->getParent();
542551

543-
AllocaDbgDeclares.resize(Allocas.size());
552+
AllocaDbgUsers.resize(Allocas.size());
544553

545554
AllocaInfo Info;
546555
LargeBlockInfo LBI;
@@ -598,8 +607,8 @@ void PromoteMem2Reg::run() {
598607
}
599608

600609
// Remember the dbg.declare intrinsic describing this alloca, if any.
601-
if (!Info.DbgDeclares.empty())
602-
AllocaDbgDeclares[AllocaNum] = Info.DbgDeclares;
610+
if (!Info.DbgUsers.empty())
611+
AllocaDbgUsers[AllocaNum] = Info.DbgUsers;
603612

604613
// Keep the reverse mapping of the 'Allocas' array for the rename pass.
605614
AllocaLookup[Allocas[AllocaNum]] = AllocaNum;
@@ -672,9 +681,11 @@ void PromoteMem2Reg::run() {
672681
}
673682

674683
// Remove alloca's dbg.declare instrinsics from the function.
675-
for (auto &Declares : AllocaDbgDeclares)
676-
for (auto *DII : Declares)
677-
DII->eraseFromParent();
684+
for (auto &DbgUsers : AllocaDbgUsers) {
685+
for (auto *DII : DbgUsers)
686+
if (DII->isAddressOfVariable() || DII->getExpression()->startsWithDeref())
687+
DII->eraseFromParent();
688+
}
678689

679690
// Loop over all of the PHI nodes and see if there are any that we can get
680691
// rid of because they merge all of the same incoming values. This can
@@ -914,8 +925,9 @@ void PromoteMem2Reg::RenamePass(BasicBlock *BB, BasicBlock *Pred,
914925

915926
// The currently active variable for this block is now the PHI.
916927
IncomingVals[AllocaNo] = APN;
917-
for (DbgVariableIntrinsic *DII : AllocaDbgDeclares[AllocaNo])
918-
ConvertDebugDeclareToDebugValue(DII, APN, DIB);
928+
for (DbgVariableIntrinsic *DII : AllocaDbgUsers[AllocaNo])
929+
if (DII->isAddressOfVariable())
930+
ConvertDebugDeclareToDebugValue(DII, APN, DIB);
919931

920932
// Get the next phi node.
921933
++PNI;
@@ -974,8 +986,9 @@ void PromoteMem2Reg::RenamePass(BasicBlock *BB, BasicBlock *Pred,
974986

975987
// Record debuginfo for the store before removing it.
976988
IncomingLocs[AllocaNo] = SI->getDebugLoc();
977-
for (DbgVariableIntrinsic *DII : AllocaDbgDeclares[ai->second])
978-
ConvertDebugDeclareToDebugValue(DII, SI, DIB);
989+
for (DbgVariableIntrinsic *DII : AllocaDbgUsers[ai->second])
990+
if (DII->isAddressOfVariable())
991+
ConvertDebugDeclareToDebugValue(DII, SI, DIB);
979992
BB->getInstList().erase(SI);
980993
}
981994
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
; RUN: opt -mem2reg %s -S -o - | FileCheck %s
2+
3+
;; Check that mem2reg removes dbg.value(%param.addr, DIExpression(DW_OP_deref...))
4+
;; when promoting the alloca %param.addr.
5+
;;
6+
;; $ clang inlining.c -O2 -g -emit-llvm -S -o tmp.ll -Xclang -disable-llvm-passes
7+
;; $ opt tmp.ll -o - -instcombine -inline -S
8+
;; $ cat inlining.c
9+
;; int g;
10+
;; __attribute__((__always_inline__))
11+
;; static void use(int* p) {
12+
;; g = *p;
13+
;; }
14+
;;
15+
;; __attribute__((__noinline__))
16+
;; void fun(int param) {
17+
;; use(&param);
18+
;; }
19+
20+
; CHECK: define dso_local void @fun(i32 %param)
21+
; CHECK-NEXT: entry:
22+
; CHECK-NEXT: call void @llvm.dbg.value(metadata i32 %param, metadata ![[PARAM:[0-9]+]], metadata !DIExpression())
23+
; CHECK-NOT: call void @llvm.dbg.value({{.*}}, metadata ![[PARAM]]
24+
; CHECK: ![[PARAM]] = !DILocalVariable(name: "param",
25+
26+
@g = dso_local global i32 0, align 4, !dbg !0
27+
28+
define dso_local void @fun(i32 %param) !dbg !12 {
29+
entry:
30+
%param.addr = alloca i32, align 4
31+
call void @llvm.dbg.value(metadata i32 %param, metadata !16, metadata !DIExpression()), !dbg !17
32+
store i32 %param, i32* %param.addr, align 4
33+
call void @llvm.dbg.value(metadata i32* %param.addr, metadata !16, metadata !DIExpression(DW_OP_deref)), !dbg !17
34+
call void @llvm.dbg.value(metadata i32* %param.addr, metadata !22, metadata !DIExpression()), !dbg !28
35+
call void @llvm.dbg.value(metadata i32* %param.addr, metadata !22, metadata !DIExpression()), !dbg !28
36+
%0 = load i32, i32* %param.addr, align 4, !dbg !30
37+
store i32 %0, i32* @g, align 4, !dbg !31
38+
ret void, !dbg !32
39+
}
40+
41+
declare void @llvm.dbg.value(metadata, metadata, metadata)
42+
43+
!llvm.dbg.cu = !{!2}
44+
!llvm.module.flags = !{!8, !9, !10}
45+
!llvm.ident = !{!11}
46+
47+
!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
48+
!1 = distinct !DIGlobalVariable(name: "g", scope: !2, file: !6, line: 8, type: !7, isLocal: false, isDefinition: true)
49+
!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, splitDebugInlining: false, nameTableKind: None)
50+
!3 = !DIFile(filename: "inlining.c", directory: "/")
51+
!4 = !{}
52+
!5 = !{!0}
53+
!6 = !DIFile(filename: "inlining.c", directory: "/")
54+
!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
55+
!8 = !{i32 7, !"Dwarf Version", i32 4}
56+
!9 = !{i32 2, !"Debug Info Version", i32 3}
57+
!10 = !{i32 1, !"wchar_size", i32 4}
58+
!11 = !{!"clang version 12.0.0"}
59+
!12 = distinct !DISubprogram(name: "fun", scope: !6, file: !6, line: 15, type: !13, scopeLine: 15, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !15)
60+
!13 = !DISubroutineType(types: !14)
61+
!14 = !{null, !7}
62+
!15 = !{!16}
63+
!16 = !DILocalVariable(name: "param", arg: 1, scope: !12, file: !6, line: 15, type: !7)
64+
!17 = !DILocation(line: 0, scope: !12)
65+
!22 = !DILocalVariable(name: "p", arg: 1, scope: !23, file: !6, line: 10, type: !26)
66+
!23 = distinct !DISubprogram(name: "use", scope: !6, file: !6, line: 10, type: !24, scopeLine: 10, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !27)
67+
!24 = !DISubroutineType(types: !25)
68+
!25 = !{null, !26}
69+
!26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64)
70+
!27 = !{!22}
71+
!28 = !DILocation(line: 0, scope: !23, inlinedAt: !29)
72+
!29 = distinct !DILocation(line: 16, column: 3, scope: !12)
73+
!30 = !DILocation(line: 11, column: 7, scope: !23, inlinedAt: !29)
74+
!31 = !DILocation(line: 11, column: 5, scope: !23, inlinedAt: !29)
75+
!32 = !DILocation(line: 17, column: 1, scope: !12)
76+
!34 = !DISubroutineType(types: !35)
77+
!35 = !{!7}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
; RUN: opt -mem2reg %s -S -o - | FileCheck %s
2+
3+
;; Check that mem2reg removes dbg.value(%local, DIExpression(DW_OP_deref...))
4+
;; that instcombine LowerDbgDeclare inserted before the call to 'esc' when
5+
;; promoting the alloca %local after 'esc' has been inlined. Without this we
6+
;; provide no location for 'local', even though it is provably constant
7+
;; throughout after inlining.
8+
;;
9+
;; $ clang reduce.c -O2 -g -emit-llvm -S -o tmp.ll -Xclang -disable-llvm-passes
10+
;; $ opt tmp.ll -o - -instcombine -inline -S
11+
;; $ cat reduce.c
12+
;; long a;
13+
;; int b;
14+
;; void c();
15+
;; __attribute__((__always_inline__))
16+
;; static void esc(long *e) {
17+
;; *e = a;
18+
;; c();
19+
;; if (b)
20+
;; *e = 0;
21+
;; }
22+
;;
23+
;; void fun() {
24+
;; long local = 0;
25+
;; esc(&local);
26+
;; }
27+
28+
; CHECK: define dso_local void @fun()
29+
; CHECK-NEXT: entry:
30+
; CHECK-NEXT: call void @llvm.dbg.value(metadata i64 0, metadata ![[LOCAL:[0-9]+]], metadata !DIExpression())
31+
; CHECK-NOT: call void @llvm.dbg.value({{.*}}, metadata ![[LOCAL]]
32+
; CHECK: ![[LOCAL]] = !DILocalVariable(name: "local",
33+
34+
@a = dso_local global i64 0, align 8, !dbg !0
35+
@b = dso_local global i32 0, align 4, !dbg !6
36+
37+
define dso_local void @fun() !dbg !14 {
38+
entry:
39+
%e.addr.i = alloca i64*, align 8
40+
%local = alloca i64, align 8
41+
%0 = bitcast i64* %local to i8*, !dbg !19
42+
call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %0), !dbg !19
43+
call void @llvm.dbg.value(metadata i64 0, metadata !18, metadata !DIExpression()), !dbg !20
44+
store i64 0, i64* %local, align 8, !dbg !21
45+
call void @llvm.dbg.value(metadata i64* %local, metadata !18, metadata !DIExpression(DW_OP_deref)), !dbg !20
46+
%1 = bitcast i64** %e.addr.i to i8*, !dbg !26
47+
call void @llvm.lifetime.start.p0i8(i64 8, i8* %1), !dbg !26
48+
call void @llvm.dbg.value(metadata i64* %local, metadata !32, metadata !DIExpression()), !dbg !26
49+
store i64* %local, i64** %e.addr.i, align 8
50+
%2 = load i64, i64* @a, align 8, !dbg !36
51+
call void @llvm.dbg.value(metadata i64* %local, metadata !32, metadata !DIExpression()), !dbg !26
52+
store i64 %2, i64* %local, align 8, !dbg !37
53+
call void (...) @c(), !dbg !38
54+
%3 = load i32, i32* @b, align 4, !dbg !39
55+
%tobool.not.i = icmp eq i32 %3, 0, !dbg !39
56+
br i1 %tobool.not.i, label %esc.exit, label %if.then.i, !dbg !43
57+
58+
if.then.i: ; preds = %entry
59+
%4 = load i64*, i64** %e.addr.i, align 8, !dbg !44
60+
call void @llvm.dbg.value(metadata i64* %4, metadata !32, metadata !DIExpression()), !dbg !26
61+
store i64 0, i64* %4, align 8, !dbg !45
62+
br label %esc.exit, !dbg !46
63+
64+
esc.exit: ; preds = %entry, %if.then.i
65+
%5 = bitcast i64** %e.addr.i to i8*, !dbg !47
66+
call void @llvm.lifetime.end.p0i8(i64 8, i8* %5), !dbg !47
67+
%6 = bitcast i64* %local to i8*, !dbg !48
68+
call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %6), !dbg !48
69+
ret void, !dbg !48
70+
}
71+
72+
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture)
73+
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture)
74+
declare void @llvm.dbg.value(metadata, metadata, metadata)
75+
declare !dbg !49 dso_local void @c(...)
76+
77+
!llvm.dbg.cu = !{!2}
78+
!llvm.module.flags = !{!10, !11, !12}
79+
!llvm.ident = !{!13}
80+
81+
!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
82+
!1 = distinct !DIGlobalVariable(name: "a", scope: !2, file: !3, line: 1, type: !9, isLocal: false, isDefinition: true)
83+
!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 12.0.0)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, splitDebugInlining: false, nameTableKind: None)
84+
!3 = !DIFile(filename: "reduce.c", directory: "/")
85+
!4 = !{}
86+
!5 = !{!0, !6}
87+
!6 = !DIGlobalVariableExpression(var: !7, expr: !DIExpression())
88+
!7 = distinct !DIGlobalVariable(name: "b", scope: !2, file: !3, line: 2, type: !8, isLocal: false, isDefinition: true)
89+
!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
90+
!9 = !DIBasicType(name: "long int", size: 64, encoding: DW_ATE_signed)
91+
!10 = !{i32 7, !"Dwarf Version", i32 4}
92+
!11 = !{i32 2, !"Debug Info Version", i32 3}
93+
!12 = !{i32 1, !"wchar_size", i32 4}
94+
!13 = !{!"clang version 12.0.0"}
95+
!14 = distinct !DISubprogram(name: "fun", scope: !3, file: !3, line: 12, type: !15, scopeLine: 12, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !17)
96+
!15 = !DISubroutineType(types: !16)
97+
!16 = !{null}
98+
!17 = !{!18}
99+
!18 = !DILocalVariable(name: "local", scope: !14, file: !3, line: 13, type: !9)
100+
!19 = !DILocation(line: 13, column: 3, scope: !14)
101+
!20 = !DILocation(line: 0, scope: !14)
102+
!21 = !DILocation(line: 13, column: 8, scope: !14)
103+
!26 = !DILocation(line: 0, scope: !27, inlinedAt: !33)
104+
!27 = distinct !DISubprogram(name: "esc", scope: !3, file: !3, line: 5, type: !28, scopeLine: 5, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !31)
105+
!28 = !DISubroutineType(types: !29)
106+
!29 = !{null, !30}
107+
!30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !9, size: 64)
108+
!31 = !{!32}
109+
!32 = !DILocalVariable(name: "e", arg: 1, scope: !27, file: !3, line: 5, type: !30)
110+
!33 = distinct !DILocation(line: 14, column: 3, scope: !14)
111+
!36 = !DILocation(line: 6, column: 8, scope: !27, inlinedAt: !33)
112+
!37 = !DILocation(line: 6, column: 6, scope: !27, inlinedAt: !33)
113+
!38 = !DILocation(line: 7, column: 3, scope: !27, inlinedAt: !33)
114+
!39 = !DILocation(line: 8, column: 7, scope: !40, inlinedAt: !33)
115+
!40 = distinct !DILexicalBlock(scope: !27, file: !3, line: 8, column: 7)
116+
!43 = !DILocation(line: 8, column: 7, scope: !27, inlinedAt: !33)
117+
!44 = !DILocation(line: 9, column: 6, scope: !40, inlinedAt: !33)
118+
!45 = !DILocation(line: 9, column: 8, scope: !40, inlinedAt: !33)
119+
!46 = !DILocation(line: 9, column: 5, scope: !40, inlinedAt: !33)
120+
!47 = !DILocation(line: 10, column: 1, scope: !27, inlinedAt: !33)
121+
!48 = !DILocation(line: 15, column: 1, scope: !14)
122+
!49 = !DISubprogram(name: "c", scope: !3, file: !3, line: 3, type: !50, spFlags: DISPFlagOptimized, retainedNodes: !4)
123+
!50 = !DISubroutineType(types: !51)
124+
!51 = !{null, null}

0 commit comments

Comments
 (0)