Skip to content

Commit b94ac20

Browse files
nikicmahesh-attarde
authored andcommitted
[DropUnnecessaryAssumes] Make the ephemeral value check more precise (llvm#160700)
The initial implementation used a very crude check where a value was considered ephemeral if it has only one use. This is insufficient if there are multiple assumes acting on the same value, or in more complex cases like cyclic phis. Generalize this to a more typical ephemeral value check, i.e. make sure that all transitive users are in assumes, while stopping at side-effecting instructions.
1 parent c6ba515 commit b94ac20

File tree

2 files changed

+179
-7
lines changed

2 files changed

+179
-7
lines changed

llvm/lib/Transforms/Scalar/DropUnnecessaryAssumes.cpp

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "llvm/Transforms/Scalar/DropUnnecessaryAssumes.h"
10+
#include "llvm/ADT/SetVector.h"
1011
#include "llvm/Analysis/AssumptionCache.h"
1112
#include "llvm/Analysis/ValueTracking.h"
1213
#include "llvm/IR/IntrinsicInst.h"
@@ -17,13 +18,48 @@ using namespace llvm;
1718
using namespace llvm::PatternMatch;
1819

1920
static bool affectedValuesAreEphemeral(ArrayRef<Value *> Affected) {
20-
// If all the affected uses have only one use (part of the assume), then
21-
// the assume does not provide useful information. Note that additional
22-
// users may appear as a result of inlining and CSE, so we should only
23-
// make this assumption late in the optimization pipeline.
24-
// TODO: Handle dead cyclic usages.
25-
// TODO: Handle multiple dead assumes on the same value.
26-
return all_of(Affected, match_fn(m_OneUse(m_Value())));
21+
// Check whether all the uses are ephemeral, i.e. recursively only used
22+
// by assumes. In that case, the assume does not provide useful information.
23+
// Note that additional users may appear as a result of inlining and CSE,
24+
// so we should only make this assumption late in the optimization pipeline.
25+
SmallSetVector<Instruction *, 32> Worklist;
26+
auto AddUsers = [&](Value *V) {
27+
for (User *U : V->users()) {
28+
// Bail out if we need to inspect too many users.
29+
if (Worklist.size() >= 32)
30+
return false;
31+
Worklist.insert(cast<Instruction>(U));
32+
}
33+
return true;
34+
};
35+
36+
for (Value *V : Affected) {
37+
// Do not handle assumes on globals for now. The use list for them may
38+
// contain uses in other functions.
39+
if (!isa<Instruction, Argument>(V))
40+
return false;
41+
42+
if (!AddUsers(V))
43+
return false;
44+
}
45+
46+
for (unsigned Idx = 0; Idx < Worklist.size(); ++Idx) {
47+
Instruction *I = Worklist[Idx];
48+
49+
// Use in assume is ephemeral.
50+
if (isa<AssumeInst>(I))
51+
continue;
52+
53+
// Use in side-effecting instruction is non-ephemeral.
54+
if (I->mayHaveSideEffects() || I->isTerminator())
55+
return false;
56+
57+
// Otherwise, recursively look at the users.
58+
if (!AddUsers(I))
59+
return false;
60+
}
61+
62+
return true;
2763
}
2864

2965
PreservedAnalyses

llvm/test/Transforms/DropUnnecessaryAssumes/basic.ll

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
22
; RUN: opt -S -passes=drop-unnecessary-assumes < %s | FileCheck %s
33

4+
declare void @use(i32 %x)
5+
declare i32 @get()
6+
47
define void @basic_dead(i32 %x) {
58
; CHECK-LABEL: define void @basic_dead(
69
; CHECK-SAME: i32 [[X:%.*]]) {
@@ -180,3 +183,136 @@ define void @type_test(ptr %x) {
180183
call void @llvm.assume(i1 %test)
181184
ret void
182185
}
186+
187+
define void @multiple_dead_conds(i32 %x) {
188+
; CHECK-LABEL: define void @multiple_dead_conds(
189+
; CHECK-SAME: i32 [[X:%.*]]) {
190+
; CHECK-NEXT: ret void
191+
;
192+
%cond1 = icmp sge i32 %x, 0
193+
call void @llvm.assume(i1 %cond1)
194+
%cond2 = icmp ne i32 %x, 64
195+
call void @llvm.assume(i1 %cond2)
196+
ret void
197+
}
198+
199+
define void @multiple_dead_bundles(ptr %x) {
200+
; CHECK-LABEL: define void @multiple_dead_bundles(
201+
; CHECK-SAME: ptr [[X:%.*]]) {
202+
; CHECK-NEXT: ret void
203+
;
204+
call void @llvm.assume(i1 true) ["align"(ptr %x, i64 8), "nonnull"(ptr %x)]
205+
ret void
206+
}
207+
208+
; The assume is eliminated, but currently leaves behind a dead cycle.
209+
define void @dead_cycle(i1 %loop.cond) {
210+
; CHECK-LABEL: define void @dead_cycle(
211+
; CHECK-SAME: i1 [[LOOP_COND:%.*]]) {
212+
; CHECK-NEXT: [[ENTRY:.*]]:
213+
; CHECK-NEXT: br label %[[LOOP:.*]]
214+
; CHECK: [[LOOP]]:
215+
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
216+
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
217+
; CHECK-NEXT: br i1 [[LOOP_COND]], label %[[LOOP]], label %[[EXIT:.*]]
218+
; CHECK: [[EXIT]]:
219+
; CHECK-NEXT: ret void
220+
;
221+
entry:
222+
br label %loop
223+
224+
loop:
225+
%iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
226+
%cond = icmp ne i32 %iv, 64
227+
call void @llvm.assume(i1 %cond)
228+
%iv.next = add i32 %iv, 1
229+
br i1 %loop.cond, label %loop, label %exit
230+
231+
exit:
232+
ret void
233+
}
234+
235+
define void @use_in_side_effect(i32 %x) {
236+
; CHECK-LABEL: define void @use_in_side_effect(
237+
; CHECK-SAME: i32 [[X:%.*]]) {
238+
; CHECK-NEXT: [[COND:%.*]] = icmp sge i32 [[X]], 0
239+
; CHECK-NEXT: call void @llvm.assume(i1 [[COND]])
240+
; CHECK-NEXT: call void @use(i32 [[X]])
241+
; CHECK-NEXT: ret void
242+
;
243+
%cond = icmp sge i32 %x, 0
244+
call void @llvm.assume(i1 %cond)
245+
call void @use(i32 %x)
246+
ret void
247+
}
248+
249+
define void @indirect_use_in_side_effect(i32 %x) {
250+
; CHECK-LABEL: define void @indirect_use_in_side_effect(
251+
; CHECK-SAME: i32 [[X:%.*]]) {
252+
; CHECK-NEXT: [[COND:%.*]] = icmp sge i32 [[X]], 0
253+
; CHECK-NEXT: call void @llvm.assume(i1 [[COND]])
254+
; CHECK-NEXT: [[ADD:%.*]] = add i32 [[X]], 1
255+
; CHECK-NEXT: call void @use(i32 [[ADD]])
256+
; CHECK-NEXT: ret void
257+
;
258+
%cond = icmp sge i32 %x, 0
259+
call void @llvm.assume(i1 %cond)
260+
%add = add i32 %x, 1
261+
call void @use(i32 %add)
262+
ret void
263+
}
264+
265+
; The affected value itself has a side effect, but we can still drop the
266+
; assume.
267+
define void @affected_value_has_side_effect() {
268+
; CHECK-LABEL: define void @affected_value_has_side_effect() {
269+
; CHECK-NEXT: [[X:%.*]] = call i32 @get()
270+
; CHECK-NEXT: ret void
271+
;
272+
%x = call i32 @get()
273+
%cond = icmp sge i32 %x, 0
274+
call void @llvm.assume(i1 %cond)
275+
ret void
276+
}
277+
278+
define i32 @affected_value_has_side_effect_and_is_used() {
279+
; CHECK-LABEL: define i32 @affected_value_has_side_effect_and_is_used() {
280+
; CHECK-NEXT: [[X:%.*]] = call i32 @get()
281+
; CHECK-NEXT: [[COND:%.*]] = icmp sge i32 [[X]], 0
282+
; CHECK-NEXT: call void @llvm.assume(i1 [[COND]])
283+
; CHECK-NEXT: ret i32 [[X]]
284+
;
285+
%x = call i32 @get()
286+
%cond = icmp sge i32 %x, 0
287+
call void @llvm.assume(i1 %cond)
288+
ret i32 %x
289+
}
290+
291+
@g = external global i8
292+
@g2 = external global i8
293+
294+
; Assumes on globals are currently not supported.
295+
define void @assume_on_global() {
296+
; CHECK-LABEL: define void @assume_on_global() {
297+
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr @g, i64 8) ]
298+
; CHECK-NEXT: ret void
299+
;
300+
call void @llvm.assume(i1 true) ["align"(ptr @g, i64 8)]
301+
ret void
302+
}
303+
304+
define void @assume_on_global_used_in_other_func() {
305+
; CHECK-LABEL: define void @assume_on_global_used_in_other_func() {
306+
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr @g2, i64 8) ]
307+
; CHECK-NEXT: ret void
308+
;
309+
call void @llvm.assume(i1 true) ["align"(ptr @g2, i64 8)]
310+
ret void
311+
}
312+
313+
define ptr @other_func() {
314+
; CHECK-LABEL: define ptr @other_func() {
315+
; CHECK-NEXT: ret ptr @g2
316+
;
317+
ret ptr @g2
318+
}

0 commit comments

Comments
 (0)