Skip to content

Commit a7dcf39

Browse files
committed
[Test] Add two tests showing unprofitable case of Guard Widening
Guard Widening is ignorant about blocks frequency. As result, it may end up widening conditions from cold/effectively dead code into some much hotter place, harming average performance.
1 parent 417fe52 commit a7dcf39

File tree

2 files changed

+560
-0
lines changed

2 files changed

+560
-0
lines changed
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2
2+
; RUN: opt -S -passes=guard-widening < %s | FileCheck %s
3+
4+
; Function Attrs: nocallback nofree nosync willreturn
5+
declare void @llvm.experimental.guard(i1, ...) #0
6+
7+
; Hot loop, frequently entered, should widen.
8+
define i32 @test_intrinsic_very_profitable(i32 %n, i1 %cond.1, i1 %cond.2) {
9+
; CHECK-LABEL: define i32 @test_intrinsic_very_profitable
10+
; CHECK-SAME: (i32 [[N:%.*]], i1 [[COND_1:%.*]], i1 [[COND_2:%.*]]) {
11+
; CHECK-NEXT: entry:
12+
; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_1]], [[COND_2]]
13+
; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ]
14+
; CHECK-NEXT: [[LOOP_PRECONDITION:%.*]] = icmp uge i32 [[N]], 100
15+
; CHECK-NEXT: br i1 [[LOOP_PRECONDITION]], label [[LOOP:%.*]], label [[FAILED:%.*]], !prof [[PROF0:![0-9]+]]
16+
; CHECK: loop:
17+
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
18+
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1
19+
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 100
20+
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]], !prof [[PROF1:![0-9]+]]
21+
; CHECK: exit:
22+
; CHECK-NEXT: ret i32 0
23+
; CHECK: failed:
24+
; CHECK-NEXT: ret i32 -1
25+
;
26+
entry:
27+
call void (i1, ...) @llvm.experimental.guard(i1 %cond.1) [ "deopt"() ]
28+
%loop.precondition = icmp uge i32 %n, 100
29+
br i1 %loop.precondition, label %loop, label %failed, !prof !0
30+
31+
loop: ; preds = %loop, %entry
32+
%iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
33+
call void (i1, ...) @llvm.experimental.guard(i1 %cond.2) [ "deopt"() ]
34+
%iv.next = add nuw nsw i32 %iv, 1
35+
%loop.cond = icmp ult i32 %iv.next, 100
36+
br i1 %loop.cond, label %loop, label %exit, !prof !1
37+
38+
exit: ; preds = %loop
39+
ret i32 0
40+
41+
failed: ; preds = %entry
42+
ret i32 -1
43+
}
44+
45+
; Even though the loop is rarely entered, it has so many iterations that the widening
46+
; is still profitable.
47+
define i32 @test_intrinsic_profitable(i32 %n, i1 %cond.1, i1 %cond.2) {
48+
; CHECK-LABEL: define i32 @test_intrinsic_profitable
49+
; CHECK-SAME: (i32 [[N:%.*]], i1 [[COND_1:%.*]], i1 [[COND_2:%.*]]) {
50+
; CHECK-NEXT: entry:
51+
; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_1]], [[COND_2]]
52+
; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ]
53+
; CHECK-NEXT: [[LOOP_PRECONDITION:%.*]] = icmp uge i32 [[N]], 100
54+
; CHECK-NEXT: br i1 [[LOOP_PRECONDITION]], label [[LOOP:%.*]], label [[FAILED:%.*]], !prof [[PROF2:![0-9]+]]
55+
; CHECK: loop:
56+
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
57+
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1
58+
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 100
59+
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]], !prof [[PROF1]]
60+
; CHECK: exit:
61+
; CHECK-NEXT: ret i32 0
62+
; CHECK: failed:
63+
; CHECK-NEXT: ret i32 -1
64+
;
65+
entry:
66+
call void (i1, ...) @llvm.experimental.guard(i1 %cond.1) [ "deopt"() ]
67+
%loop.precondition = icmp uge i32 %n, 100
68+
br i1 %loop.precondition, label %loop, label %failed, !prof !2
69+
70+
loop: ; preds = %loop, %entry
71+
%iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
72+
call void (i1, ...) @llvm.experimental.guard(i1 %cond.2) [ "deopt"() ]
73+
%iv.next = add nuw nsw i32 %iv, 1
74+
%loop.cond = icmp ult i32 %iv.next, 100
75+
br i1 %loop.cond, label %loop, label %exit, !prof !1
76+
77+
exit: ; preds = %loop
78+
ret i32 0
79+
80+
failed: ; preds = %entry
81+
ret i32 -1
82+
}
83+
84+
; Loop's hotness compensates rareness of its entrance. We still want to widen, because
85+
; it may open up some optimization opportunities.
86+
define i32 @test_intrinsic_neutral(i32 %n, i1 %cond.1, i1 %cond.2) {
87+
; CHECK-LABEL: define i32 @test_intrinsic_neutral
88+
; CHECK-SAME: (i32 [[N:%.*]], i1 [[COND_1:%.*]], i1 [[COND_2:%.*]]) {
89+
; CHECK-NEXT: entry:
90+
; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_1]], [[COND_2]]
91+
; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ]
92+
; CHECK-NEXT: [[LOOP_PRECONDITION:%.*]] = icmp uge i32 [[N]], 100
93+
; CHECK-NEXT: br i1 [[LOOP_PRECONDITION]], label [[LOOP:%.*]], label [[FAILED:%.*]], !prof [[PROF3:![0-9]+]]
94+
; CHECK: loop:
95+
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
96+
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1
97+
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 100
98+
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]], !prof [[PROF1]]
99+
; CHECK: exit:
100+
; CHECK-NEXT: ret i32 0
101+
; CHECK: failed:
102+
; CHECK-NEXT: ret i32 -1
103+
;
104+
entry:
105+
call void (i1, ...) @llvm.experimental.guard(i1 %cond.1) [ "deopt"() ]
106+
%loop.precondition = icmp uge i32 %n, 100
107+
br i1 %loop.precondition, label %loop, label %failed, !prof !3
108+
109+
loop: ; preds = %loop, %entry
110+
%iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
111+
call void (i1, ...) @llvm.experimental.guard(i1 %cond.2) [ "deopt"() ]
112+
%iv.next = add nuw nsw i32 %iv, 1
113+
%loop.cond = icmp ult i32 %iv.next, 100
114+
br i1 %loop.cond, label %loop, label %exit, !prof !1
115+
116+
exit: ; preds = %loop
117+
ret i32 0
118+
119+
failed: ; preds = %entry
120+
ret i32 -1
121+
}
122+
123+
; FIXME: This loop is so rarely entered, that we don't want to widen here.
124+
define i32 @test_intrinsic_very_unprofitable(i32 %n, i1 %cond.1, i1 %cond.2) {
125+
; CHECK-LABEL: define i32 @test_intrinsic_very_unprofitable
126+
; CHECK-SAME: (i32 [[N:%.*]], i1 [[COND_1:%.*]], i1 [[COND_2:%.*]]) {
127+
; CHECK-NEXT: entry:
128+
; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_1]], [[COND_2]]
129+
; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ]
130+
; CHECK-NEXT: [[LOOP_PRECONDITION:%.*]] = icmp uge i32 [[N]], 100
131+
; CHECK-NEXT: br i1 [[LOOP_PRECONDITION]], label [[LOOP:%.*]], label [[FAILED:%.*]], !prof [[PROF4:![0-9]+]]
132+
; CHECK: loop:
133+
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
134+
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1
135+
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 100
136+
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]], !prof [[PROF1]]
137+
; CHECK: exit:
138+
; CHECK-NEXT: ret i32 0
139+
; CHECK: failed:
140+
; CHECK-NEXT: ret i32 -1
141+
;
142+
entry:
143+
call void (i1, ...) @llvm.experimental.guard(i1 %cond.1) [ "deopt"() ]
144+
%loop.precondition = icmp uge i32 %n, 100
145+
br i1 %loop.precondition, label %loop, label %failed, !prof !4
146+
147+
loop: ; preds = %loop, %entry
148+
%iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
149+
call void (i1, ...) @llvm.experimental.guard(i1 %cond.2) [ "deopt"() ]
150+
%iv.next = add nuw nsw i32 %iv, 1
151+
%loop.cond = icmp ult i32 %iv.next, 100
152+
br i1 %loop.cond, label %loop, label %exit, !prof !1
153+
154+
exit: ; preds = %loop
155+
ret i32 0
156+
157+
failed: ; preds = %entry
158+
ret i32 -1
159+
}
160+
161+
; FIXME: This loop is so rarely entered, that we don't want to widen here.
162+
define i32 @test_intrinsic_unprofitable(i32 %n, i1 %cond.1, i1 %cond.2) {
163+
; CHECK-LABEL: define i32 @test_intrinsic_unprofitable
164+
; CHECK-SAME: (i32 [[N:%.*]], i1 [[COND_1:%.*]], i1 [[COND_2:%.*]]) {
165+
; CHECK-NEXT: entry:
166+
; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_1]], [[COND_2]]
167+
; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ]
168+
; CHECK-NEXT: [[LOOP_PRECONDITION:%.*]] = icmp uge i32 [[N]], 100
169+
; CHECK-NEXT: br i1 [[LOOP_PRECONDITION]], label [[LOOP:%.*]], label [[FAILED:%.*]], !prof [[PROF5:![0-9]+]]
170+
; CHECK: loop:
171+
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
172+
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1
173+
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 100
174+
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]], !prof [[PROF1]]
175+
; CHECK: exit:
176+
; CHECK-NEXT: ret i32 0
177+
; CHECK: failed:
178+
; CHECK-NEXT: ret i32 -1
179+
;
180+
entry:
181+
call void (i1, ...) @llvm.experimental.guard(i1 %cond.1) [ "deopt"() ]
182+
%loop.precondition = icmp uge i32 %n, 100
183+
br i1 %loop.precondition, label %loop, label %failed, !prof !5
184+
185+
loop: ; preds = %loop, %entry
186+
%iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
187+
call void (i1, ...) @llvm.experimental.guard(i1 %cond.2) [ "deopt"() ]
188+
%iv.next = add nuw nsw i32 %iv, 1
189+
%loop.cond = icmp ult i32 %iv.next, 100
190+
br i1 %loop.cond, label %loop, label %exit, !prof !1
191+
192+
exit: ; preds = %loop
193+
ret i32 0
194+
195+
failed: ; preds = %entry
196+
ret i32 -1
197+
}
198+
199+
attributes #0 = { nocallback nofree nosync willreturn }
200+
201+
!0 = !{!"branch_weights", i32 1048576, i32 1}
202+
!1 = !{!"branch_weights", i32 99, i32 1}
203+
!2 = !{!"branch_weights", i32 1, i32 10}
204+
!3 = !{!"branch_weights", i32 1, i32 99}
205+
!4 = !{!"branch_weights", i32 1, i32 1048576}
206+
!5 = !{!"branch_weights", i32 1, i32 1000}

0 commit comments

Comments
 (0)