xref: /llvm-project/llvm/test/Analysis/ValueTracking/known-nonnull-at.ll (revision f226cabbb1b9737676536bc4417336bef4808992)
1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2; RUN: opt -S -passes=instsimplify < %s | FileCheck %s
3
4declare void @bar(ptr %a, ptr nonnull noundef %b)
5declare void @bar_without_noundef(ptr %a, ptr nonnull %b)
6
7; 'y' must be nonnull.
8
9define i1 @caller1(ptr %x, ptr %y) {
10; CHECK-LABEL: @caller1(
11; CHECK-NEXT:    call void @bar(ptr [[X:%.*]], ptr [[Y:%.*]])
12; CHECK-NEXT:    ret i1 false
13;
14  call void @bar(ptr %x, ptr %y)
15  %null_check = icmp eq ptr %y, null
16  ret i1 %null_check
17}
18
19; Don't know anything about 'y'.
20
21define i1 @caller1_maybepoison(ptr %x, ptr %y) {
22; CHECK-LABEL: @caller1_maybepoison(
23; CHECK-NEXT:    call void @bar_without_noundef(ptr [[X:%.*]], ptr [[Y:%.*]])
24; CHECK-NEXT:    [[NULL_CHECK:%.*]] = icmp eq ptr [[Y]], null
25; CHECK-NEXT:    ret i1 [[NULL_CHECK]]
26;
27  call void @bar_without_noundef(ptr %x, ptr %y)
28  %null_check = icmp eq ptr %y, null
29  ret i1 %null_check
30}
31
32; Don't know anything about 'y'.
33
34define i1 @caller2(ptr %x, ptr %y) {
35; CHECK-LABEL: @caller2(
36; CHECK-NEXT:    call void @bar(ptr [[Y:%.*]], ptr [[X:%.*]])
37; CHECK-NEXT:    [[NULL_CHECK:%.*]] = icmp eq ptr [[Y]], null
38; CHECK-NEXT:    ret i1 [[NULL_CHECK]]
39;
40  call void @bar(ptr %y, ptr %x)
41  %null_check = icmp eq ptr %y, null
42  ret i1 %null_check
43}
44
45; 'y' must be nonnull.
46
47define i1 @caller3(ptr %x, ptr %y) {
48; CHECK-LABEL: @caller3(
49; CHECK-NEXT:    call void @bar(ptr [[X:%.*]], ptr [[Y:%.*]])
50; CHECK-NEXT:    ret i1 true
51;
52  call void @bar(ptr %x, ptr %y)
53  %null_check = icmp ne ptr %y, null
54  ret i1 %null_check
55}
56
57; FIXME: The call is guaranteed to execute, so 'y' must be nonnull throughout.
58
59define i1 @caller4(ptr %x, ptr %y) {
60; CHECK-LABEL: @caller4(
61; CHECK-NEXT:    [[NULL_CHECK:%.*]] = icmp ne ptr [[Y:%.*]], null
62; CHECK-NEXT:    call void @bar(ptr [[X:%.*]], ptr [[Y]])
63; CHECK-NEXT:    ret i1 [[NULL_CHECK]]
64;
65  %null_check = icmp ne ptr %y, null
66  call void @bar(ptr %x, ptr %y)
67  ret i1 %null_check
68}
69
70; The call to bar() does not dominate the null check, so no change.
71
72define i1 @caller5(ptr %x, ptr %y) {
73; CHECK-LABEL: @caller5(
74; CHECK-NEXT:    [[NULL_CHECK:%.*]] = icmp eq ptr [[Y:%.*]], null
75; CHECK-NEXT:    br i1 [[NULL_CHECK]], label [[T:%.*]], label [[F:%.*]]
76; CHECK:       t:
77; CHECK-NEXT:    ret i1 [[NULL_CHECK]]
78; CHECK:       f:
79; CHECK-NEXT:    call void @bar(ptr [[X:%.*]], ptr [[Y]])
80; CHECK-NEXT:    ret i1 [[NULL_CHECK]]
81;
82  %null_check = icmp eq ptr %y, null
83  br i1 %null_check, label %t, label %f
84t:
85  ret i1 %null_check
86f:
87  call void @bar(ptr %x, ptr %y)
88  ret i1 %null_check
89}
90
91; Make sure that an invoke works similarly to a call.
92
93declare i32 @esfp(...)
94
95define i1 @caller6(ptr %x, ptr %y) personality ptr @esfp{
96; CHECK-LABEL: @caller6(
97; CHECK-NEXT:    invoke void @bar(ptr [[X:%.*]], ptr nonnull [[Y:%.*]])
98; CHECK-NEXT:    to label [[CONT:%.*]] unwind label [[EXC:%.*]]
99; CHECK:       cont:
100; CHECK-NEXT:    ret i1 false
101; CHECK:       exc:
102; CHECK-NEXT:    [[LP:%.*]] = landingpad { ptr, i32 }
103; CHECK-NEXT:    filter [0 x ptr] zeroinitializer
104; CHECK-NEXT:    unreachable
105;
106  invoke void @bar(ptr %x, ptr nonnull %y)
107  to label %cont unwind label %exc
108
109cont:
110  %null_check = icmp eq ptr %y, null
111  ret i1 %null_check
112
113exc:
114  %lp = landingpad { ptr, i32 }
115  filter [0 x ptr] zeroinitializer
116  unreachable
117}
118
119declare ptr @returningPtr(ptr returned %p)
120
121define i1 @nonnullReturnTest(ptr nonnull %x) {
122; CHECK-LABEL: @nonnullReturnTest(
123; CHECK-NEXT:    [[X2:%.*]] = call ptr @returningPtr(ptr [[X:%.*]])
124; CHECK-NEXT:    ret i1 false
125;
126  %x2 = call ptr @returningPtr(ptr %x)
127  %null_check = icmp eq ptr %x2, null
128  ret i1 %null_check
129}
130
131define i1 @unknownReturnTest(ptr %x) {
132; CHECK-LABEL: @unknownReturnTest(
133; CHECK-NEXT:    [[X2:%.*]] = call ptr @returningPtr(ptr [[X:%.*]])
134; CHECK-NEXT:    [[NULL_CHECK:%.*]] = icmp eq ptr [[X2]], null
135; CHECK-NEXT:    ret i1 [[NULL_CHECK]]
136;
137  %x2 = call ptr @returningPtr(ptr %x)
138  %null_check = icmp eq ptr %x2, null
139  ret i1 %null_check
140}
141
142; Make sure that if load/store happened, the pointer is nonnull.
143
144define i32 @test_null_after_store(ptr %0) {
145; CHECK-LABEL: @test_null_after_store(
146; CHECK-NEXT:    store i32 123, ptr [[TMP0:%.*]], align 4
147; CHECK-NEXT:    ret i32 2
148;
149  store i32 123, ptr %0, align 4
150  %2 = icmp eq ptr %0, null
151  %3 = select i1 %2, i32 1, i32 2
152  ret i32 %3
153}
154
155define i32 @test_null_after_load(ptr %0) {
156; CHECK-LABEL: @test_null_after_load(
157; CHECK-NEXT:    ret i32 1
158;
159  %2 = load i32, ptr %0, align 4
160  %3 = icmp eq ptr %0, null
161  %4 = select i1 %3, i32 %2, i32 1
162  ret i32 %4
163}
164
165; Make sure that different address space does not affect null pointer check.
166
167define i32 @test_null_after_store_addrspace(ptr addrspace(1) %0) {
168; CHECK-LABEL: @test_null_after_store_addrspace(
169; CHECK-NEXT:    store i32 123, ptr addrspace(1) [[TMP0:%.*]], align 4
170; CHECK-NEXT:    [[TMP2:%.*]] = icmp eq ptr addrspace(1) [[TMP0]], null
171; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[TMP2]], i32 1, i32 2
172; CHECK-NEXT:    ret i32 [[TMP3]]
173;
174  store i32 123, ptr addrspace(1) %0, align 4
175  %2 = icmp eq ptr addrspace(1) %0, null
176  %3 = select i1 %2, i32 1, i32 2
177  ret i32 %3
178}
179
180define i32 @test_null_after_load_addrspace(ptr addrspace(1) %0) {
181; CHECK-LABEL: @test_null_after_load_addrspace(
182; CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr addrspace(1) [[TMP0:%.*]], align 4
183; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq ptr addrspace(1) [[TMP0]], null
184; CHECK-NEXT:    [[TMP4:%.*]] = select i1 [[TMP3]], i32 [[TMP2]], i32 1
185; CHECK-NEXT:    ret i32 [[TMP4]]
186;
187  %2 = load i32, ptr addrspace(1) %0, align 4
188  %3 = icmp eq ptr addrspace(1) %0, null
189  %4 = select i1 %3, i32 %2, i32 1
190  ret i32 %4
191}
192
193; Make sure if store happened after the check, nullptr check is not removed.
194
195declare ptr @func(i64)
196
197define ptr @test_load_store_after_check(ptr %0) {
198; CHECK-LABEL: @test_load_store_after_check(
199; CHECK-NEXT:  entry:
200; CHECK-NEXT:    [[TMP1:%.*]] = call ptr @func(i64 0)
201; CHECK-NEXT:    [[NULL_CHECK:%.*]] = icmp eq ptr [[TMP1]], null
202; CHECK-NEXT:    br i1 [[NULL_CHECK]], label [[RETURN:%.*]], label [[IF_END:%.*]]
203; CHECK:       if.end:
204; CHECK-NEXT:    store i8 7, ptr [[TMP1]], align 1
205; CHECK-NEXT:    br label [[RETURN]]
206; CHECK:       return:
207; CHECK-NEXT:    [[RETVAL_0:%.*]] = phi ptr [ [[TMP1]], [[IF_END]] ], [ null, [[ENTRY:%.*]] ]
208; CHECK-NEXT:    ret ptr [[RETVAL_0]]
209;
210entry:
211  %1 = call ptr @func(i64 0)
212  %null_check = icmp eq ptr %1, null
213  br i1 %null_check, label %return, label %if.end
214
215if.end:
216  store i8 7, ptr %1
217  br label %return
218
219return:
220  %retval.0 = phi ptr [ %1, %if.end ], [ null, %entry ]
221  ret ptr %retval.0
222}
223
224define i1 @test_known_nonnull_at_callsite(ptr %src) {
225; CHECK-LABEL: @test_known_nonnull_at_callsite(
226; CHECK-NEXT:  entry:
227; CHECK-NEXT:    call void @callee(ptr noundef nonnull [[SRC:%.*]])
228; CHECK-NEXT:    ret i1 false
229;
230entry:
231  call void @callee(ptr noundef nonnull %src)
232  %nonnull = icmp eq ptr %src, null
233  ret i1 %nonnull
234}
235
236define i1 @test_known_nonnull_mixed(ptr %src) {
237; CHECK-LABEL: @test_known_nonnull_mixed(
238; CHECK-NEXT:  entry:
239; CHECK-NEXT:    call void @callee2(ptr nonnull [[SRC:%.*]])
240; CHECK-NEXT:    ret i1 false
241;
242entry:
243  call void @callee2(ptr nonnull %src)
244  %nonnull = icmp eq ptr %src, null
245  ret i1 %nonnull
246}
247
248define i1 @test_known_nonnull_at_callsite_dereferenceable(ptr %src) {
249; CHECK-LABEL: @test_known_nonnull_at_callsite_dereferenceable(
250; CHECK-NEXT:  entry:
251; CHECK-NEXT:    call void @callee(ptr dereferenceable(1) [[SRC:%.*]])
252; CHECK-NEXT:    ret i1 false
253;
254entry:
255  call void @callee(ptr dereferenceable(1) %src)
256  %nonnull = icmp eq ptr %src, null
257  ret i1 %nonnull
258}
259
260; Negative tests
261
262define i1 @test_known_nonnull_at_callsite_without_noundef(ptr %src) {
263; CHECK-LABEL: @test_known_nonnull_at_callsite_without_noundef(
264; CHECK-NEXT:  entry:
265; CHECK-NEXT:    call void @callee(ptr nonnull [[SRC:%.*]])
266; CHECK-NEXT:    [[NONNULL:%.*]] = icmp eq ptr [[SRC]], null
267; CHECK-NEXT:    ret i1 [[NONNULL]]
268;
269entry:
270  call void @callee(ptr nonnull %src)
271  %nonnull = icmp eq ptr %src, null
272  ret i1 %nonnull
273}
274
275define i1 @test_known_nonnull_at_callsite_dereferenceable_null_is_defined(ptr %src) null_pointer_is_valid {
276; CHECK-LABEL: @test_known_nonnull_at_callsite_dereferenceable_null_is_defined(
277; CHECK-NEXT:  entry:
278; CHECK-NEXT:    call void @callee(ptr dereferenceable(1) [[SRC:%.*]])
279; CHECK-NEXT:    [[NONNULL:%.*]] = icmp eq ptr [[SRC]], null
280; CHECK-NEXT:    ret i1 [[NONNULL]]
281;
282entry:
283  call void @callee(ptr dereferenceable(1) %src)
284  %nonnull = icmp eq ptr %src, null
285  ret i1 %nonnull
286}
287
288declare void @callee(ptr)
289declare void @callee2(ptr noundef)
290