Skip to content

Commit 453361d

Browse files
fhahntstellar
authored andcommitted
[DSE] Fall back to CFG scan for unreachable terminators.
Blocks with UnreachableInst terminators are considered as root nodes in the PDT. This pessimize DSE, if there are no aliasing reads from the potentially dead store and the block with the unreachable terminator. If any of the root nodes of the PDF has UnreachableInst as terminator, fall back to the CFG scan, even the common dominator of all killing blocks does not post-dominate the block with potentially dead store. It looks like the compile-time impact for the extra scans is negligible. https://llvm-compile-time-tracker.com/compare.php?from=779bbbf27fe631154bdfaac7a443f198d4654688&to=ac59945f1bec1c6a7d7f5590c8c69fd9c5369c53&stat=instructions Fixes #53800. Reviewed By: nikic Differential Revision: https://reviews.llvm.org/D119760
1 parent 03cf88f commit 453361d

File tree

2 files changed

+156
-5
lines changed

2 files changed

+156
-5
lines changed

llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,10 @@ struct DSEState {
770770
/// Keep track of instructions (partly) overlapping with killing MemoryDefs per
771771
/// basic block.
772772
MapVector<BasicBlock *, InstOverlapIntervalsTy> IOLs;
773+
// Check if there are root nodes that are terminated by UnreachableInst.
774+
// Those roots pessimize post-dominance queries. If there are such roots,
775+
// fall back to CFG scan starting from all non-unreachable roots.
776+
bool AnyUnreachableExit;
773777

774778
// Class contains self-reference, make sure it's not copied/moved.
775779
DSEState(const DSEState &) = delete;
@@ -805,6 +809,10 @@ struct DSEState {
805809

806810
// Collect whether there is any irreducible control flow in the function.
807811
ContainsIrreducibleLoops = mayContainIrreducibleControl(F, &LI);
812+
813+
AnyUnreachableExit = any_of(PDT.roots(), [](const BasicBlock *E) {
814+
return isa<UnreachableInst>(E->getTerminator());
815+
});
808816
}
809817

810818
/// Return 'OW_Complete' if a store to the 'KillingLoc' location (by \p
@@ -1511,22 +1519,29 @@ struct DSEState {
15111519
// If the common post-dominator does not post-dominate MaybeDeadAccess,
15121520
// there is a path from MaybeDeadAccess to an exit not going through a
15131521
// killing block.
1514-
if (!PDT.dominates(CommonPred, MaybeDeadAccess->getBlock()))
1515-
return None;
1522+
if (!PDT.dominates(CommonPred, MaybeDeadAccess->getBlock())) {
1523+
if (!AnyUnreachableExit)
1524+
return None;
1525+
1526+
// Fall back to CFG scan starting at all non-unreachable roots if not
1527+
// all paths to the exit go through CommonPred.
1528+
CommonPred = nullptr;
1529+
}
15161530

15171531
// If CommonPred itself is in the set of killing blocks, we're done.
15181532
if (KillingBlocks.count(CommonPred))
15191533
return {MaybeDeadAccess};
15201534

15211535
SetVector<BasicBlock *> WorkList;
1522-
15231536
// If CommonPred is null, there are multiple exits from the function.
15241537
// They all have to be added to the worklist.
15251538
if (CommonPred)
15261539
WorkList.insert(CommonPred);
15271540
else
1528-
for (BasicBlock *R : PDT.roots())
1529-
WorkList.insert(R);
1541+
for (BasicBlock *R : PDT.roots()) {
1542+
if (!isa<UnreachableInst>(R->getTerminator()))
1543+
WorkList.insert(R);
1544+
}
15301545

15311546
NumCFGTries++;
15321547
// Check if all paths starting from an exit node go through one of the

llvm/test/Transforms/DeadStoreElimination/multiblock-unreachable.ll

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,139 @@ bb50: ; preds = %bb43
5757
bb53: ; preds = %bb53, %bb50, %bb22, %bb
5858
br label %bb53
5959
}
60+
61+
declare void @exit()
62+
63+
define void @unreachable_exit_with_no_call(i64* noalias %ptr, i1 %c.1) {
64+
; CHECK-LABEL: @unreachable_exit_with_no_call(
65+
; CHECK-NEXT: entry:
66+
; CHECK-NEXT: br i1 [[C_1:%.*]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
67+
; CHECK: if.then:
68+
; CHECK-NEXT: unreachable
69+
; CHECK: if.end:
70+
; CHECK-NEXT: store i64 0, i64* [[PTR:%.*]], align 8
71+
; CHECK-NEXT: ret void
72+
;
73+
entry:
74+
store i64 1, i64* %ptr, align 8
75+
br i1 %c.1, label %if.then, label %if.end
76+
77+
if.then:
78+
unreachable
79+
80+
if.end:
81+
store i64 0, i64* %ptr, align 8
82+
ret void
83+
}
84+
85+
; Test for PR53800.
86+
define void @unreachable_exit_with_nounwind_call_pr53800(i64* noalias %ptr, i1 %c.1) {
87+
; CHECK-LABEL: @unreachable_exit_with_nounwind_call_pr53800(
88+
; CHECK-NEXT: entry:
89+
; CHECK-NEXT: br i1 [[C_1:%.*]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
90+
; CHECK: if.then:
91+
; CHECK-NEXT: tail call void @exit() #[[ATTR0:[0-9]+]]
92+
; CHECK-NEXT: unreachable
93+
; CHECK: if.end:
94+
; CHECK-NEXT: store i64 0, i64* [[PTR:%.*]], align 8
95+
; CHECK-NEXT: ret void
96+
;
97+
entry:
98+
store i64 1, i64* %ptr, align 8
99+
br i1 %c.1, label %if.then, label %if.end
100+
101+
if.then:
102+
tail call void @exit() nounwind
103+
unreachable
104+
105+
if.end:
106+
store i64 0, i64* %ptr, align 8
107+
ret void
108+
}
109+
110+
; The call @exit may read %ptr as it is not marked as noalias
111+
define void @unreachable_exit_and_call_may_read(i64* %ptr, i1 %c.1) {
112+
; CHECK-LABEL: @unreachable_exit_and_call_may_read(
113+
; CHECK-NEXT: entry:
114+
; CHECK-NEXT: store i64 1, i64* [[PTR:%.*]], align 8
115+
; CHECK-NEXT: br i1 [[C_1:%.*]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
116+
; CHECK: if.then:
117+
; CHECK-NEXT: tail call void @exit() #[[ATTR0]]
118+
; CHECK-NEXT: unreachable
119+
; CHECK: if.end:
120+
; CHECK-NEXT: store i64 0, i64* [[PTR]], align 8
121+
; CHECK-NEXT: ret void
122+
;
123+
entry:
124+
store i64 1, i64* %ptr, align 8
125+
br i1 %c.1, label %if.then, label %if.end
126+
127+
if.then:
128+
tail call void @exit() nounwind
129+
unreachable
130+
131+
if.end:
132+
store i64 0, i64* %ptr, align 8
133+
ret void
134+
}
135+
136+
define void @unreachable_exit_with_may_unwind_call(i64* noalias %ptr, i1 %c.1) {
137+
; CHECK-LABEL: @unreachable_exit_with_may_unwind_call(
138+
; CHECK-NEXT: entry:
139+
; CHECK-NEXT: store i64 1, i64* [[PTR:%.*]], align 8
140+
; CHECK-NEXT: br i1 [[C_1:%.*]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
141+
; CHECK: if.then:
142+
; CHECK-NEXT: tail call void @exit()
143+
; CHECK-NEXT: unreachable
144+
; CHECK: if.end:
145+
; CHECK-NEXT: store i64 0, i64* [[PTR]], align 8
146+
; CHECK-NEXT: ret void
147+
;
148+
entry:
149+
store i64 1, i64* %ptr, align 8
150+
br i1 %c.1, label %if.then, label %if.end
151+
152+
if.then:
153+
tail call void @exit()
154+
unreachable
155+
156+
if.end:
157+
store i64 0, i64* %ptr, align 8
158+
ret void
159+
}
160+
161+
; Cannot remove the store in entry, because it is not dead on the path to e.1
162+
define void @unreachable_exit_but_another_exit(i64* noalias %ptr, i1 %c.1, i32 %s, i1 %c.2) {
163+
; CHECK-LABEL: @unreachable_exit_but_another_exit(
164+
; CHECK-NEXT: entry:
165+
; CHECK-NEXT: store i64 1, i64* [[PTR:%.*]], align 8
166+
; CHECK-NEXT: br i1 [[C_1:%.*]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
167+
; CHECK: if.then:
168+
; CHECK-NEXT: br i1 [[C_2:%.*]], label [[E_0:%.*]], label [[E_1:%.*]]
169+
; CHECK: e.0:
170+
; CHECK-NEXT: tail call void @exit() #[[ATTR0]]
171+
; CHECK-NEXT: unreachable
172+
; CHECK: e.1:
173+
; CHECK-NEXT: ret void
174+
; CHECK: if.end:
175+
; CHECK-NEXT: store i64 0, i64* [[PTR]], align 8
176+
; CHECK-NEXT: ret void
177+
;
178+
entry:
179+
store i64 1, i64* %ptr, align 8
180+
br i1 %c.1, label %if.then, label %if.end
181+
182+
if.then:
183+
br i1 %c.2, label %e.0, label %e.1
184+
185+
e.0:
186+
tail call void @exit() nounwind
187+
unreachable
188+
189+
e.1:
190+
ret void
191+
192+
if.end:
193+
store i64 0, i64* %ptr, align 8
194+
ret void
195+
}

0 commit comments

Comments
 (0)