xref: /llvm-project/llvm/test/Transforms/InstCombine/compare-alloca.ll (revision 29441e4f5fa5f5c7709f7cf180815ba97f611297)
1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2; RUN: opt -passes=instcombine -S %s | FileCheck %s
3target datalayout = "p:32:32"
4
5
6define i1 @alloca_argument_compare(ptr %arg) {
7; CHECK-LABEL: @alloca_argument_compare(
8; CHECK-NEXT:    ret i1 false
9;
10  %alloc = alloca i64
11  %cmp = icmp eq ptr %arg, %alloc
12  ret i1 %cmp
13}
14
15define i1 @alloca_argument_compare_swapped(ptr %arg) {
16; CHECK-LABEL: @alloca_argument_compare_swapped(
17; CHECK-NEXT:    ret i1 false
18;
19  %alloc = alloca i64
20  %cmp = icmp eq ptr %alloc, %arg
21  ret i1 %cmp
22}
23
24define i1 @alloca_argument_compare_ne(ptr %arg) {
25; CHECK-LABEL: @alloca_argument_compare_ne(
26; CHECK-NEXT:    ret i1 true
27;
28  %alloc = alloca i64
29  %cmp = icmp ne ptr %arg, %alloc
30  ret i1 %cmp
31}
32
33define i1 @alloca_argument_compare_derived_ptrs(ptr %arg, i64 %x) {
34; CHECK-LABEL: @alloca_argument_compare_derived_ptrs(
35; CHECK-NEXT:    ret i1 false
36;
37  %alloc = alloca i64, i64 8
38  %p = getelementptr i64, ptr %arg, i64 %x
39  %q = getelementptr i64, ptr %alloc, i64 3
40  %cmp = icmp eq ptr %p, %q
41  ret i1 %cmp
42}
43
44declare void @escape(ptr)
45define i1 @alloca_argument_compare_escaped_alloca(ptr %arg) {
46; CHECK-LABEL: @alloca_argument_compare_escaped_alloca(
47; CHECK-NEXT:    [[ALLOC:%.*]] = alloca i64, align 8
48; CHECK-NEXT:    call void @escape(ptr nonnull [[ALLOC]])
49; CHECK-NEXT:    [[CMP:%.*]] = icmp eq ptr [[ALLOC]], [[ARG:%.*]]
50; CHECK-NEXT:    ret i1 [[CMP]]
51;
52  %alloc = alloca i64
53  call void @escape(ptr %alloc)
54  %cmp = icmp eq ptr %alloc, %arg
55  ret i1 %cmp
56}
57
58declare void @check_compares(i1, i1)
59define void @alloca_argument_compare_two_compares(ptr %p) {
60; CHECK-LABEL: @alloca_argument_compare_two_compares(
61; CHECK-NEXT:    call void @check_compares(i1 false, i1 false)
62; CHECK-NEXT:    ret void
63;
64  %q = alloca i64, i64 8
65  %r = getelementptr i64, ptr %p, i64 1
66  %s = getelementptr i64, ptr %q, i64 2
67  %cmp1 = icmp eq ptr %p, %q
68  %cmp2 = icmp eq ptr %r, %s
69  call void @check_compares(i1 %cmp1, i1 %cmp2)
70  ret void
71  ; We will only fold if there is a single cmp.
72}
73
74define i1 @alloca_argument_compare_escaped_through_store(ptr %arg, ptr %ptr) {
75; CHECK-LABEL: @alloca_argument_compare_escaped_through_store(
76; CHECK-NEXT:    [[ALLOC:%.*]] = alloca i64, align 8
77; CHECK-NEXT:    [[CMP:%.*]] = icmp eq ptr [[ALLOC]], [[ARG:%.*]]
78; CHECK-NEXT:    [[P:%.*]] = getelementptr inbounds nuw i8, ptr [[ALLOC]], i32 8
79; CHECK-NEXT:    store ptr [[P]], ptr [[PTR:%.*]], align 4
80; CHECK-NEXT:    ret i1 [[CMP]]
81;
82  %alloc = alloca i64
83  %cmp = icmp eq ptr %alloc, %arg
84  %p = getelementptr i64, ptr %alloc, i64 1
85  store ptr %p, ptr %ptr
86  ret i1 %cmp
87}
88
89declare void @llvm.lifetime.start.p0(i64, ptr nocapture)
90declare void @llvm.lifetime.end.p0(i64, ptr nocapture)
91define i1 @alloca_argument_compare_benign_instrs(ptr %arg) {
92; CHECK-LABEL: @alloca_argument_compare_benign_instrs(
93; CHECK-NEXT:    ret i1 false
94;
95  %alloc = alloca i8
96  call void @llvm.lifetime.start.p0(i64 1, ptr %alloc)
97  %cmp = icmp eq ptr %arg, %alloc
98  %x = load i8, ptr %arg
99  store i8 %x, ptr %alloc
100  call void @llvm.lifetime.end.p0(i64 1, ptr %alloc)
101  ret i1 %cmp
102}
103
104declare ptr @allocator()
105define i1 @alloca_call_compare() {
106; CHECK-LABEL: @alloca_call_compare(
107; CHECK-NEXT:    [[Q:%.*]] = call ptr @allocator()
108; CHECK-NEXT:    ret i1 false
109;
110  %p = alloca i64
111  %q = call ptr @allocator()
112  %cmp = icmp eq ptr %p, %q
113  ret i1 %cmp
114}
115
116
117; The next block of tests demonstrate a very subtle correctness requirement.
118; We can generally assume any *single* stack layout we chose for the result of
119; an alloca, but we can't simultanious assume two different ones.  As a
120; result, we must make sure that we only fold conditions if we can ensure that
121; we fold *all* potentially address capturing compares the same.
122
123; These two functions represents either a) forging a pointer via inttoptr or
124; b) indexing off an adjacent allocation.  In either case, the operation is
125; obscured by an uninlined helper and not visible to instcombine.
126declare ptr @hidden_inttoptr()
127declare ptr @hidden_offset(ptr %other)
128
129define i1 @ptrtoint_single_cmp() {
130; CHECK-LABEL: @ptrtoint_single_cmp(
131; CHECK-NEXT:    ret i1 false
132;
133  %m = alloca i8, i32 4
134  %rhs = inttoptr i64 2048 to ptr
135  %cmp = icmp eq ptr %m, %rhs
136  ret i1 %cmp
137}
138
139define i1 @offset_single_cmp() {
140; CHECK-LABEL: @offset_single_cmp(
141; CHECK-NEXT:    ret i1 false
142;
143  %m = alloca i8, i32 4
144  %n = alloca i8, i32 4
145  %rhs = getelementptr i8, ptr %n, i32 4
146  %cmp = icmp eq ptr %m, %rhs
147  ret i1 %cmp
148}
149
150declare void @witness(i1, i1)
151
152define void @consistent_fold1() {
153; CHECK-LABEL: @consistent_fold1(
154; CHECK-NEXT:    [[RHS2:%.*]] = call ptr @hidden_inttoptr()
155; CHECK-NEXT:    call void @witness(i1 false, i1 false)
156; CHECK-NEXT:    ret void
157;
158  %m = alloca i8, i32 4
159  %rhs = inttoptr i64 2048 to ptr
160  %rhs2 = call ptr @hidden_inttoptr()
161  %cmp1 = icmp eq ptr %m, %rhs
162  %cmp2 = icmp eq ptr %m, %rhs2
163  call void @witness(i1 %cmp1, i1 %cmp2)
164  ret void
165}
166
167define void @consistent_fold2() {
168; CHECK-LABEL: @consistent_fold2(
169; CHECK-NEXT:    [[N2:%.*]] = alloca [4 x i8], align 1
170; CHECK-NEXT:    [[RHS2:%.*]] = call ptr @hidden_offset(ptr nonnull [[N2]])
171; CHECK-NEXT:    call void @witness(i1 false, i1 false)
172; CHECK-NEXT:    ret void
173;
174  %m = alloca i8, i32 4
175  %n = alloca i8, i32 4
176  %rhs = getelementptr i8, ptr %n, i32 4
177  %rhs2 = call ptr @hidden_offset(ptr %n)
178  %cmp1 = icmp eq ptr %m, %rhs
179  %cmp2 = icmp eq ptr %m, %rhs2
180  call void @witness(i1 %cmp1, i1 %cmp2)
181  ret void
182}
183
184define void @consistent_fold3() {
185; CHECK-LABEL: @consistent_fold3(
186; CHECK-NEXT:    [[RHS2:%.*]] = call ptr @hidden_inttoptr()
187; CHECK-NEXT:    call void @witness(i1 false, i1 false)
188; CHECK-NEXT:    ret void
189;
190  %m = alloca i8, i32 4
191  %lgp = load ptr, ptr @gp, align 8
192  %rhs2 = call ptr @hidden_inttoptr()
193  %cmp1 = icmp eq ptr %m, %lgp
194  %cmp2 = icmp eq ptr %m, %rhs2
195  call void @witness(i1 %cmp1, i1 %cmp2)
196  ret void
197}
198
199define void @neg_consistent_fold4() {
200; CHECK-LABEL: @neg_consistent_fold4(
201; CHECK-NEXT:    call void @witness(i1 false, i1 false)
202; CHECK-NEXT:    ret void
203;
204  %m = alloca i8, i32 4
205  %lgp = load ptr, ptr @gp, align 8
206  %cmp1 = icmp eq ptr %m, %lgp
207  %cmp2 = icmp eq ptr %m, %lgp
208  call void @witness(i1 %cmp1, i1 %cmp2)
209  ret void
210}
211
212; A nocapture call can't cause a consistent result issue as it is (by
213; assumption) not able to contain a comparison which might capture the
214; address.
215
216declare void @unknown(ptr)
217
218define i1 @consistent_nocapture_inttoptr() {
219; CHECK-LABEL: @consistent_nocapture_inttoptr(
220; CHECK-NEXT:    [[M1:%.*]] = alloca [4 x i8], align 1
221; CHECK-NEXT:    call void @unknown(ptr nonnull captures(none) [[M1]])
222; CHECK-NEXT:    ret i1 false
223;
224  %m = alloca i8, i32 4
225  call void @unknown(ptr nocapture %m)
226  %rhs = inttoptr i64 2048 to ptr
227  %cmp = icmp eq ptr %m, %rhs
228  ret i1 %cmp
229}
230
231define i1 @consistent_nocapture_offset() {
232; CHECK-LABEL: @consistent_nocapture_offset(
233; CHECK-NEXT:    [[M1:%.*]] = alloca [4 x i8], align 1
234; CHECK-NEXT:    call void @unknown(ptr nonnull captures(none) [[M1]])
235; CHECK-NEXT:    ret i1 false
236;
237  %m = alloca i8, i32 4
238  call void @unknown(ptr nocapture %m)
239  %n = alloca i8, i32 4
240  %rhs = getelementptr i8, ptr %n, i32 4
241  %cmp = icmp eq ptr %m, %rhs
242  ret i1 %cmp
243}
244
245@gp = global ptr null, align 8
246
247define i1 @consistent_nocapture_through_global() {
248; CHECK-LABEL: @consistent_nocapture_through_global(
249; CHECK-NEXT:    [[M1:%.*]] = alloca [4 x i8], align 1
250; CHECK-NEXT:    call void @unknown(ptr nonnull captures(none) [[M1]])
251; CHECK-NEXT:    ret i1 false
252;
253  %m = alloca i8, i32 4
254  call void @unknown(ptr nocapture %m)
255  %lgp = load ptr, ptr @gp, align 8, !nonnull !{}
256  %cmp = icmp eq ptr %m, %lgp
257  ret i1 %cmp
258}
259
260define void @select_alloca_unrelated_ptr(i1 %c, ptr %p, ptr %p2) {
261; CHECK-LABEL: @select_alloca_unrelated_ptr(
262; CHECK-NEXT:    [[M:%.*]] = alloca i8, align 1
263; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq ptr [[M]], [[P:%.*]]
264; CHECK-NEXT:    [[S:%.*]] = select i1 [[C:%.*]], ptr [[M]], ptr [[P2:%.*]]
265; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq ptr [[S]], [[P]]
266; CHECK-NEXT:    call void @witness(i1 [[CMP1]], i1 [[CMP2]])
267; CHECK-NEXT:    ret void
268;
269  %m = alloca i8
270  %cmp1 = icmp eq ptr %m, %p
271  %s = select i1 %c, ptr %m, ptr %p2
272  %cmp2 = icmp eq ptr %s, %p
273  call void @witness(i1 %cmp1, i1 %cmp2)
274  ret void
275}
276
277define void @alloca_offset_icmp(ptr %p, i32 %offset) {
278; CHECK-LABEL: @alloca_offset_icmp(
279; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i32 [[OFFSET:%.*]], 0
280; CHECK-NEXT:    call void @witness(i1 false, i1 [[CMP2]])
281; CHECK-NEXT:    ret void
282;
283  %m = alloca [4 x i8]
284  %g = getelementptr i8, ptr %m, i32 %offset
285  %cmp1 = icmp eq ptr %m, %p
286  %cmp2 = icmp eq ptr %m, %g
287  call void @witness(i1 %cmp1, i1 %cmp2)
288  ret void
289}
290