Skip to content

Commit e5df0a5

Browse files
committed
[NFC][PhaseOrdering] Add additional loop deletion tests
Test thanks to Michael Kuklinski from #llvm, originally inspired by Daniel Lemire's https://lemire.me/blog/2021/10/26/in-c-is-empty-faster-than-comparing-the-size-with-zero/
1 parent f70343d commit e5df0a5

File tree

1 file changed

+229
-0
lines changed

1 file changed

+229
-0
lines changed
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2+
; RUN: opt -passes='default<O3>' -S < %s | FileCheck %s --check-prefixes=ALL,O3
3+
; RUN: opt -passes='default<O2>' -S < %s | FileCheck %s --check-prefixes=ALL,O2
4+
; RUN: opt -passes='default<O1>' -S < %s | FileCheck %s --check-prefixes=ALL,O1
5+
6+
; All these tests should optimize to a single comparison
7+
; of the original argument with null. There should be no loops.
8+
9+
%struct.node = type { %struct.node*, i32 }
10+
11+
define dso_local zeroext i1 @is_not_empty_variant1(%struct.node* %p) {
12+
; ALL-LABEL: @is_not_empty_variant1(
13+
; ALL-NEXT: entry:
14+
; ALL-NEXT: [[TOBOOL_NOT3_I:%.*]] = icmp eq %struct.node* [[P:%.*]], null
15+
; ALL-NEXT: br i1 [[TOBOOL_NOT3_I]], label [[COUNT_NODES_VARIANT1_EXIT:%.*]], label [[WHILE_BODY_I:%.*]]
16+
; ALL: while.body.i:
17+
; ALL-NEXT: [[P_ADDR_04_I:%.*]] = phi %struct.node* [ [[TMP0:%.*]], [[WHILE_BODY_I]] ], [ [[P]], [[ENTRY:%.*]] ]
18+
; ALL-NEXT: [[NEXT_I:%.*]] = getelementptr inbounds [[STRUCT_NODE:%.*]], %struct.node* [[P_ADDR_04_I]], i64 0, i32 0
19+
; ALL-NEXT: [[TMP0]] = load %struct.node*, %struct.node** [[NEXT_I]], align 8
20+
; ALL-NEXT: [[TOBOOL_NOT_I:%.*]] = icmp eq %struct.node* [[TMP0]], null
21+
; ALL-NEXT: br i1 [[TOBOOL_NOT_I]], label [[COUNT_NODES_VARIANT1_EXIT]], label [[WHILE_BODY_I]], !llvm.loop [[LOOP0:![0-9]+]]
22+
; ALL: count_nodes_variant1.exit:
23+
; ALL-NEXT: [[TMP1:%.*]] = xor i1 [[TOBOOL_NOT3_I]], true
24+
; ALL-NEXT: ret i1 [[TMP1]]
25+
;
26+
entry:
27+
%p.addr = alloca %struct.node*, align 8
28+
store %struct.node* %p, %struct.node** %p.addr, align 8
29+
%0 = load %struct.node*, %struct.node** %p.addr, align 8
30+
%call = call i32 @count_nodes_variant1(%struct.node* %0)
31+
%cmp = icmp sgt i32 %call, 0
32+
ret i1 %cmp
33+
}
34+
35+
define internal i32 @count_nodes_variant1(%struct.node* %p) {
36+
entry:
37+
%p.addr = alloca %struct.node*, align 8
38+
%size = alloca i32, align 4
39+
store %struct.node* %p, %struct.node** %p.addr, align 8
40+
%0 = bitcast i32* %size to i8*
41+
store i32 0, i32* %size, align 4
42+
br label %while.cond
43+
44+
while.cond:
45+
%1 = load %struct.node*, %struct.node** %p.addr, align 8
46+
%tobool = icmp ne %struct.node* %1, null
47+
br i1 %tobool, label %while.body, label %while.end
48+
49+
while.body:
50+
%2 = load %struct.node*, %struct.node** %p.addr, align 8
51+
%next = getelementptr inbounds %struct.node, %struct.node* %2, i32 0, i32 0
52+
%3 = load %struct.node*, %struct.node** %next, align 8
53+
store %struct.node* %3, %struct.node** %p.addr, align 8
54+
%4 = load i32, i32* %size, align 4
55+
%inc = add nsw i32 %4, 1
56+
store i32 %inc, i32* %size, align 4
57+
br label %while.cond, !llvm.loop !0
58+
59+
while.end:
60+
%5 = load i32, i32* %size, align 4
61+
%6 = bitcast i32* %size to i8*
62+
ret i32 %5
63+
}
64+
65+
define dso_local zeroext i1 @is_not_empty_variant2(%struct.node* %p) {
66+
; ALL-LABEL: @is_not_empty_variant2(
67+
; ALL-NEXT: entry:
68+
; ALL-NEXT: [[TOBOOL_NOT4_I:%.*]] = icmp ne %struct.node* [[P:%.*]], null
69+
; ALL-NEXT: ret i1 [[TOBOOL_NOT4_I]]
70+
;
71+
entry:
72+
%p.addr = alloca %struct.node*, align 8
73+
store %struct.node* %p, %struct.node** %p.addr, align 8
74+
%0 = load %struct.node*, %struct.node** %p.addr, align 8
75+
%call = call i64 @count_nodes_variant2(%struct.node* %0)
76+
%cmp = icmp ugt i64 %call, 0
77+
ret i1 %cmp
78+
}
79+
80+
define internal i64 @count_nodes_variant2(%struct.node* %p) {
81+
entry:
82+
%p.addr = alloca %struct.node*, align 8
83+
%size = alloca i64, align 8
84+
store %struct.node* %p, %struct.node** %p.addr, align 8
85+
%0 = bitcast i64* %size to i8*
86+
store i64 0, i64* %size, align 8
87+
br label %while.cond
88+
89+
while.cond:
90+
%1 = load %struct.node*, %struct.node** %p.addr, align 8
91+
%tobool = icmp ne %struct.node* %1, null
92+
br i1 %tobool, label %while.body, label %while.end
93+
94+
while.body:
95+
%2 = load %struct.node*, %struct.node** %p.addr, align 8
96+
%next = getelementptr inbounds %struct.node, %struct.node* %2, i32 0, i32 0
97+
%3 = load %struct.node*, %struct.node** %next, align 8
98+
store %struct.node* %3, %struct.node** %p.addr, align 8
99+
%4 = load i64, i64* %size, align 8
100+
%inc = add i64 %4, 1
101+
store i64 %inc, i64* %size, align 8
102+
%5 = load i64, i64* %size, align 8
103+
%cmp = icmp ne i64 %5, 0
104+
call void @_ZL6assumeb(i1 zeroext %cmp)
105+
br label %while.cond, !llvm.loop !2
106+
107+
while.end:
108+
%6 = load i64, i64* %size, align 8
109+
%7 = bitcast i64* %size to i8*
110+
ret i64 %6
111+
}
112+
113+
define dso_local zeroext i1 @is_not_empty_variant3(%struct.node* %p) {
114+
; O3-LABEL: @is_not_empty_variant3(
115+
; O3-NEXT: entry:
116+
; O3-NEXT: [[TOBOOL_NOT4_I:%.*]] = icmp eq %struct.node* [[P:%.*]], null
117+
; O3-NEXT: br i1 [[TOBOOL_NOT4_I]], label [[COUNT_NODES_VARIANT3_EXIT:%.*]], label [[WHILE_BODY_I:%.*]]
118+
; O3: while.body.i:
119+
; O3-NEXT: [[SIZE_06_I:%.*]] = phi i64 [ [[INC_I:%.*]], [[WHILE_BODY_I]] ], [ 0, [[ENTRY:%.*]] ]
120+
; O3-NEXT: [[P_ADDR_05_I:%.*]] = phi %struct.node* [ [[TMP0:%.*]], [[WHILE_BODY_I]] ], [ [[P]], [[ENTRY]] ]
121+
; O3-NEXT: [[CMP_I:%.*]] = icmp ne i64 [[SIZE_06_I]], -1
122+
; O3-NEXT: tail call void @llvm.assume(i1 [[CMP_I]]) #[[ATTR3:[0-9]+]]
123+
; O3-NEXT: [[NEXT_I:%.*]] = getelementptr inbounds [[STRUCT_NODE:%.*]], %struct.node* [[P_ADDR_05_I]], i64 0, i32 0
124+
; O3-NEXT: [[TMP0]] = load %struct.node*, %struct.node** [[NEXT_I]], align 8
125+
; O3-NEXT: [[INC_I]] = add nuw i64 [[SIZE_06_I]], 1
126+
; O3-NEXT: [[TOBOOL_NOT_I:%.*]] = icmp eq %struct.node* [[TMP0]], null
127+
; O3-NEXT: br i1 [[TOBOOL_NOT_I]], label [[COUNT_NODES_VARIANT3_EXIT]], label [[WHILE_BODY_I]], !llvm.loop [[LOOP2:![0-9]+]]
128+
; O3: count_nodes_variant3.exit:
129+
; O3-NEXT: [[TMP1:%.*]] = xor i1 [[TOBOOL_NOT4_I]], true
130+
; O3-NEXT: ret i1 [[TMP1]]
131+
;
132+
; O2-LABEL: @is_not_empty_variant3(
133+
; O2-NEXT: entry:
134+
; O2-NEXT: [[TOBOOL_NOT4_I:%.*]] = icmp eq %struct.node* [[P:%.*]], null
135+
; O2-NEXT: br i1 [[TOBOOL_NOT4_I]], label [[COUNT_NODES_VARIANT3_EXIT:%.*]], label [[WHILE_BODY_I:%.*]]
136+
; O2: while.body.i:
137+
; O2-NEXT: [[SIZE_06_I:%.*]] = phi i64 [ [[INC_I:%.*]], [[WHILE_BODY_I]] ], [ 0, [[ENTRY:%.*]] ]
138+
; O2-NEXT: [[P_ADDR_05_I:%.*]] = phi %struct.node* [ [[TMP0:%.*]], [[WHILE_BODY_I]] ], [ [[P]], [[ENTRY]] ]
139+
; O2-NEXT: [[CMP_I:%.*]] = icmp ne i64 [[SIZE_06_I]], -1
140+
; O2-NEXT: tail call void @llvm.assume(i1 [[CMP_I]]) #[[ATTR3:[0-9]+]]
141+
; O2-NEXT: [[NEXT_I:%.*]] = getelementptr inbounds [[STRUCT_NODE:%.*]], %struct.node* [[P_ADDR_05_I]], i64 0, i32 0
142+
; O2-NEXT: [[TMP0]] = load %struct.node*, %struct.node** [[NEXT_I]], align 8
143+
; O2-NEXT: [[INC_I]] = add nuw i64 [[SIZE_06_I]], 1
144+
; O2-NEXT: [[TOBOOL_NOT_I:%.*]] = icmp eq %struct.node* [[TMP0]], null
145+
; O2-NEXT: br i1 [[TOBOOL_NOT_I]], label [[COUNT_NODES_VARIANT3_EXIT]], label [[WHILE_BODY_I]], !llvm.loop [[LOOP2:![0-9]+]]
146+
; O2: count_nodes_variant3.exit:
147+
; O2-NEXT: [[TMP1:%.*]] = xor i1 [[TOBOOL_NOT4_I]], true
148+
; O2-NEXT: ret i1 [[TMP1]]
149+
;
150+
; O1-LABEL: @is_not_empty_variant3(
151+
; O1-NEXT: entry:
152+
; O1-NEXT: [[TOBOOL_NOT4_I:%.*]] = icmp eq %struct.node* [[P:%.*]], null
153+
; O1-NEXT: br i1 [[TOBOOL_NOT4_I]], label [[COUNT_NODES_VARIANT3_EXIT:%.*]], label [[WHILE_BODY_I:%.*]]
154+
; O1: while.body.i:
155+
; O1-NEXT: [[SIZE_06_I:%.*]] = phi i64 [ [[INC_I:%.*]], [[WHILE_BODY_I]] ], [ 0, [[ENTRY:%.*]] ]
156+
; O1-NEXT: [[P_ADDR_05_I:%.*]] = phi %struct.node* [ [[TMP0:%.*]], [[WHILE_BODY_I]] ], [ [[P]], [[ENTRY]] ]
157+
; O1-NEXT: [[CMP_I:%.*]] = icmp ne i64 [[SIZE_06_I]], -1
158+
; O1-NEXT: call void @llvm.assume(i1 [[CMP_I]]) #[[ATTR3:[0-9]+]]
159+
; O1-NEXT: [[NEXT_I:%.*]] = getelementptr inbounds [[STRUCT_NODE:%.*]], %struct.node* [[P_ADDR_05_I]], i64 0, i32 0
160+
; O1-NEXT: [[TMP0]] = load %struct.node*, %struct.node** [[NEXT_I]], align 8
161+
; O1-NEXT: [[INC_I]] = add i64 [[SIZE_06_I]], 1
162+
; O1-NEXT: [[TOBOOL_NOT_I:%.*]] = icmp eq %struct.node* [[TMP0]], null
163+
; O1-NEXT: br i1 [[TOBOOL_NOT_I]], label [[COUNT_NODES_VARIANT3_EXIT_LOOPEXIT:%.*]], label [[WHILE_BODY_I]], !llvm.loop [[LOOP2:![0-9]+]]
164+
; O1: count_nodes_variant3.exit.loopexit:
165+
; O1-NEXT: [[PHI_CMP:%.*]] = icmp ne i64 [[INC_I]], 0
166+
; O1-NEXT: br label [[COUNT_NODES_VARIANT3_EXIT]]
167+
; O1: count_nodes_variant3.exit:
168+
; O1-NEXT: [[SIZE_0_LCSSA_I:%.*]] = phi i1 [ false, [[ENTRY]] ], [ [[PHI_CMP]], [[COUNT_NODES_VARIANT3_EXIT_LOOPEXIT]] ]
169+
; O1-NEXT: ret i1 [[SIZE_0_LCSSA_I]]
170+
;
171+
entry:
172+
%p.addr = alloca %struct.node*, align 8
173+
store %struct.node* %p, %struct.node** %p.addr, align 8
174+
%0 = load %struct.node*, %struct.node** %p.addr, align 8
175+
%call = call i64 @count_nodes_variant3(%struct.node* %0)
176+
%cmp = icmp ugt i64 %call, 0
177+
ret i1 %cmp
178+
}
179+
180+
define internal i64 @count_nodes_variant3(%struct.node* %p) {
181+
entry:
182+
%p.addr = alloca %struct.node*, align 8
183+
%size = alloca i64, align 8
184+
store %struct.node* %p, %struct.node** %p.addr, align 8
185+
%0 = bitcast i64* %size to i8*
186+
store i64 0, i64* %size, align 8
187+
br label %while.cond
188+
189+
while.cond:
190+
%1 = load %struct.node*, %struct.node** %p.addr, align 8
191+
%tobool = icmp ne %struct.node* %1, null
192+
br i1 %tobool, label %while.body, label %while.end
193+
194+
while.body:
195+
%2 = load i64, i64* %size, align 8
196+
%cmp = icmp ne i64 %2, -1
197+
call void @_ZL6assumeb(i1 zeroext %cmp)
198+
%3 = load %struct.node*, %struct.node** %p.addr, align 8
199+
%next = getelementptr inbounds %struct.node, %struct.node* %3, i32 0, i32 0
200+
%4 = load %struct.node*, %struct.node** %next, align 8
201+
store %struct.node* %4, %struct.node** %p.addr, align 8
202+
%5 = load i64, i64* %size, align 8
203+
%inc = add i64 %5, 1
204+
store i64 %inc, i64* %size, align 8
205+
br label %while.cond, !llvm.loop !3
206+
207+
while.end:
208+
%6 = load i64, i64* %size, align 8
209+
%7 = bitcast i64* %size to i8*
210+
ret i64 %6
211+
}
212+
213+
define internal void @_ZL6assumeb(i1 zeroext %expression) {
214+
entry:
215+
%expression.addr = alloca i8, align 1
216+
%frombool = zext i1 %expression to i8
217+
store i8 %frombool, i8* %expression.addr, align 1
218+
%0 = load i8, i8* %expression.addr, align 1
219+
%tobool = trunc i8 %0 to i1
220+
call void @llvm.assume(i1 %tobool)
221+
ret void
222+
}
223+
224+
declare void @llvm.assume(i1 noundef)
225+
226+
!0 = distinct !{!0, !1}
227+
!1 = !{!"llvm.loop.mustprogress"}
228+
!2 = distinct !{!2, !1}
229+
!3 = distinct !{!3, !1}

0 commit comments

Comments
 (0)