1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s
3
4; Tests for using inbounds information from GEPs where the GEP only causes UB in the use blocks.
5
6declare void @noundef(ptr noundef) willreturn nounwind
7declare void @noundef2(ptr noundef)
8
9declare void @use(i1)
10
11; %start + %n.ext is guaranteed to not overflow (due to inbounds).
12; %start + %idx.ext does not overflow if %idx.ext <= %n.ext.
13define i1 @inbounds_poison_is_ub_in_use_block_1(ptr %src, i32 %n, i32 %idx) {
14; CHECK-LABEL: @inbounds_poison_is_ub_in_use_block_1(
15; CHECK-NEXT:  entry:
16; CHECK-NEXT:    [[N_EXT:%.*]] = zext i32 [[N:%.*]] to i64
17; CHECK-NEXT:    [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[SRC:%.*]], i64 [[N_EXT]]
18; CHECK-NEXT:    [[CMP_IDX:%.*]] = icmp ult i32 [[IDX:%.*]], [[N]]
19; CHECK-NEXT:    [[IDX_EXT:%.*]] = zext i32 [[IDX]] to i64
20; CHECK-NEXT:    [[SRC_IDX:%.*]] = getelementptr i32, ptr [[SRC]], i64 [[IDX_EXT]]
21; CHECK-NEXT:    br i1 [[CMP_IDX]], label [[THEN:%.*]], label [[ELSE:%.*]]
22; CHECK:       then:
23; CHECK-NEXT:    call void @noundef(ptr [[UPPER]])
24; CHECK-NEXT:    [[CMP_UPPER_1:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]]
25; CHECK-NEXT:    ret i1 [[CMP_UPPER_1]]
26; CHECK:       else:
27; CHECK-NEXT:    [[CMP_UPPER_2:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]]
28; CHECK-NEXT:    ret i1 [[CMP_UPPER_2]]
29;
30entry:
31  %n.ext = zext i32 %n to i64
32  %upper = getelementptr inbounds i32, ptr %src, i64 %n.ext
33  %cmp.idx = icmp ult i32 %idx, %n
34  %idx.ext = zext i32 %idx to i64
35  %src.idx = getelementptr i32, ptr %src, i64 %idx.ext
36  br i1 %cmp.idx, label %then, label %else
37
38then:
39  call void @noundef(ptr %upper)
40  %cmp.upper.1 = icmp ule ptr %src.idx, %upper
41  ret i1 %cmp.upper.1
42
43else:
44  %cmp.upper.2 = icmp ule ptr %src.idx, %upper
45  ret i1 %cmp.upper.2
46}
47
48define i1 @inbounds_poison_is_ub_in_use_block_2(ptr %src, i32 %n, i32 %idx, i1 %c) {
49; CHECK-LABEL: @inbounds_poison_is_ub_in_use_block_2(
50; CHECK-NEXT:  entry:
51; CHECK-NEXT:    [[N_EXT:%.*]] = zext i32 [[N:%.*]] to i64
52; CHECK-NEXT:    [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[SRC:%.*]], i64 [[N_EXT]]
53; CHECK-NEXT:    [[CMP_IDX:%.*]] = icmp ult i32 [[IDX:%.*]], [[N]]
54; CHECK-NEXT:    [[IDX_EXT:%.*]] = zext i32 [[IDX]] to i64
55; CHECK-NEXT:    [[SRC_IDX:%.*]] = getelementptr i32, ptr [[SRC]], i64 [[IDX_EXT]]
56; CHECK-NEXT:    br i1 [[CMP_IDX]], label [[THEN:%.*]], label [[ELSE:%.*]]
57; CHECK:       then:
58; CHECK-NEXT:    [[CMP_UPPER_1:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]]
59; CHECK-NEXT:    call void @noundef(ptr [[UPPER]])
60; CHECK-NEXT:    ret i1 [[CMP_UPPER_1]]
61; CHECK:       else:
62; CHECK-NEXT:    [[CMP_UPPER_2:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]]
63; CHECK-NEXT:    ret i1 [[CMP_UPPER_2]]
64;
65entry:
66  %n.ext = zext i32 %n to i64
67  %upper = getelementptr inbounds i32, ptr %src, i64 %n.ext
68  %cmp.idx = icmp ult i32 %idx, %n
69  %idx.ext = zext i32 %idx to i64
70  %src.idx = getelementptr i32, ptr %src, i64 %idx.ext
71  br i1 %cmp.idx, label %then, label %else
72
73then:
74  %cmp.upper.1 = icmp ule ptr %src.idx, %upper
75  call void @noundef(ptr %upper)
76  ret i1 %cmp.upper.1
77
78else:
79  %cmp.upper.2 = icmp ule ptr %src.idx, %upper
80  ret i1 %cmp.upper.2
81}
82
83declare void @llvm.assume(i1)
84
85; %start + %n.ext is guaranteed to not overflow (due to inbounds).
86; %start + %idx.ext does not overflow if %idx.ext <= %n.ext.
87define i1 @inbounds_poison_is_ub_in_use_block_by_assume(ptr %src, i32 %n, i32 %idx) {
88; CHECK-LABEL: @inbounds_poison_is_ub_in_use_block_by_assume(
89; CHECK-NEXT:  entry:
90; CHECK-NEXT:    [[N_EXT:%.*]] = zext i32 [[N:%.*]] to i64
91; CHECK-NEXT:    [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[SRC:%.*]], i64 [[N_EXT]]
92; CHECK-NEXT:    [[CMP_IDX:%.*]] = icmp ult i32 [[IDX:%.*]], [[N]]
93; CHECK-NEXT:    [[IDX_EXT:%.*]] = zext i32 [[IDX]] to i64
94; CHECK-NEXT:    [[SRC_IDX:%.*]] = getelementptr i32, ptr [[SRC]], i64 [[IDX_EXT]]
95; CHECK-NEXT:    br i1 [[CMP_IDX]], label [[THEN:%.*]], label [[ELSE:%.*]]
96; CHECK:       then:
97; CHECK-NEXT:    [[CMP_NE:%.*]] = icmp ule ptr null, [[UPPER]]
98; CHECK-NEXT:    call void @llvm.assume(i1 [[CMP_NE]])
99; CHECK-NEXT:    [[CMP_UPPER_1:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]]
100; CHECK-NEXT:    ret i1 [[CMP_UPPER_1]]
101; CHECK:       else:
102; CHECK-NEXT:    [[CMP_UPPER_2:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]]
103; CHECK-NEXT:    ret i1 [[CMP_UPPER_2]]
104;
105entry:
106  %n.ext = zext i32 %n to i64
107  %upper = getelementptr inbounds i32, ptr %src, i64 %n.ext
108  %cmp.idx = icmp ult i32 %idx, %n
109  %idx.ext = zext i32 %idx to i64
110  %src.idx = getelementptr i32, ptr %src, i64 %idx.ext
111  br i1 %cmp.idx, label %then, label %else
112
113then:
114  %cmp.ne = icmp ule ptr null, %upper
115  call void @llvm.assume(i1 %cmp.ne)
116  %cmp.upper.1 = icmp ule ptr %src.idx, %upper
117  ret i1 %cmp.upper.1
118
119else:
120  %cmp.upper.2 = icmp ule ptr %src.idx, %upper
121  ret i1 %cmp.upper.2
122}
123
124
125; %start + %n.ext is guaranteed to not overflow (due to inbounds).
126; %start + %idx.ext does not overflow if %idx.ext <= %n.ext.
127define i1 @inbounds_poison_is_ub_in_in_multiple_use_blocks_1(ptr %src, i32 %n, i32 %idx, i1 %c) {
128; CHECK-LABEL: @inbounds_poison_is_ub_in_in_multiple_use_blocks_1(
129; CHECK-NEXT:  entry:
130; CHECK-NEXT:    [[N_EXT:%.*]] = zext i32 [[N:%.*]] to i64
131; CHECK-NEXT:    [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[SRC:%.*]], i64 [[N_EXT]]
132; CHECK-NEXT:    br i1 [[C:%.*]], label [[CHECK_BB:%.*]], label [[EXIT:%.*]]
133; CHECK:       check.bb:
134; CHECK-NEXT:    call void @noundef(ptr [[UPPER]])
135; CHECK-NEXT:    [[CMP_IDX:%.*]] = icmp ult i32 [[IDX:%.*]], [[N]]
136; CHECK-NEXT:    [[IDX_EXT:%.*]] = zext i32 [[IDX]] to i64
137; CHECK-NEXT:    [[SRC_IDX:%.*]] = getelementptr i32, ptr [[SRC]], i64 [[IDX_EXT]]
138; CHECK-NEXT:    br i1 [[CMP_IDX]], label [[THEN:%.*]], label [[ELSE:%.*]]
139; CHECK:       then:
140; CHECK-NEXT:    call void @noundef(ptr [[UPPER]])
141; CHECK-NEXT:    [[CMP_UPPER_1:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]]
142; CHECK-NEXT:    ret i1 [[CMP_UPPER_1]]
143; CHECK:       else:
144; CHECK-NEXT:    [[CMP_UPPER_2:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]]
145; CHECK-NEXT:    ret i1 [[CMP_UPPER_2]]
146; CHECK:       exit:
147; CHECK-NEXT:    ret i1 false
148;
149entry:
150  %n.ext = zext i32 %n to i64
151  %upper = getelementptr inbounds i32, ptr %src, i64 %n.ext
152  br i1 %c, label %check.bb, label %exit
153
154check.bb:
155  call void @noundef(ptr %upper)
156  %cmp.idx = icmp ult i32 %idx, %n
157  %idx.ext = zext i32 %idx to i64
158  %src.idx = getelementptr i32, ptr %src, i64 %idx.ext
159  br i1 %cmp.idx, label %then, label %else
160
161then:
162  call void @noundef(ptr %upper)
163  %cmp.upper.1 = icmp ule ptr %src.idx, %upper
164  ret i1 %cmp.upper.1
165
166else:
167  %cmp.upper.2 = icmp ule ptr %src.idx, %upper
168  ret i1 %cmp.upper.2
169
170exit:
171  ret i1 false
172}
173
174define i1 @may_exit_before_ub_is_caused(ptr %src, i32 %n, i32 %idx) {
175; CHECK-LABEL: @may_exit_before_ub_is_caused(
176; CHECK-NEXT:  entry:
177; CHECK-NEXT:    [[N_EXT:%.*]] = zext i32 [[N:%.*]] to i64
178; CHECK-NEXT:    [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[SRC:%.*]], i64 [[N_EXT]]
179; CHECK-NEXT:    [[CMP_IDX:%.*]] = icmp ult i32 [[IDX:%.*]], [[N]]
180; CHECK-NEXT:    [[IDX_EXT:%.*]] = zext i32 [[IDX]] to i64
181; CHECK-NEXT:    [[SRC_IDX:%.*]] = getelementptr i32, ptr [[SRC]], i64 [[IDX_EXT]]
182; CHECK-NEXT:    br i1 [[CMP_IDX]], label [[THEN:%.*]], label [[ELSE:%.*]]
183; CHECK:       then:
184; CHECK-NEXT:    [[CMP_UPPER_1:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]]
185; CHECK-NEXT:    call void @use(i1 [[CMP_UPPER_1]])
186; CHECK-NEXT:    call void @noundef(ptr [[UPPER]])
187; CHECK-NEXT:    ret i1 [[CMP_UPPER_1]]
188; CHECK:       else:
189; CHECK-NEXT:    [[CMP_UPPER_2:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]]
190; CHECK-NEXT:    ret i1 [[CMP_UPPER_2]]
191;
192entry:
193  %n.ext = zext i32 %n to i64
194  %upper = getelementptr inbounds i32, ptr %src, i64 %n.ext
195  %cmp.idx = icmp ult i32 %idx, %n
196  %idx.ext = zext i32 %idx to i64
197  %src.idx = getelementptr i32, ptr %src, i64 %idx.ext
198  br i1 %cmp.idx, label %then, label %else
199
200then:
201  %cmp.upper.1 = icmp ule ptr %src.idx, %upper
202  call void @use(i1 %cmp.upper.1);
203  call void @noundef(ptr %upper)
204  ret i1 %cmp.upper.1
205
206else:
207  %cmp.upper.2 = icmp ule ptr %src.idx, %upper
208  ret i1 %cmp.upper.2
209}
210
211define i1 @only_UB_in_false_block(ptr %src, i32 %n, i32 %idx) {
212; CHECK-LABEL: @only_UB_in_false_block(
213; CHECK-NEXT:  entry:
214; CHECK-NEXT:    [[N_EXT:%.*]] = zext i32 [[N:%.*]] to i64
215; CHECK-NEXT:    [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[SRC:%.*]], i64 [[N_EXT]]
216; CHECK-NEXT:    [[CMP_IDX:%.*]] = icmp ult i32 [[IDX:%.*]], [[N]]
217; CHECK-NEXT:    [[IDX_EXT:%.*]] = zext i32 [[IDX]] to i64
218; CHECK-NEXT:    [[SRC_IDX:%.*]] = getelementptr i32, ptr [[SRC]], i64 [[IDX_EXT]]
219; CHECK-NEXT:    br i1 [[CMP_IDX]], label [[THEN:%.*]], label [[ELSE:%.*]]
220; CHECK:       then:
221; CHECK-NEXT:    [[CMP_UPPER_1:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]]
222; CHECK-NEXT:    ret i1 [[CMP_UPPER_1]]
223; CHECK:       else:
224; CHECK-NEXT:    call void @noundef(ptr [[UPPER]])
225; CHECK-NEXT:    [[CMP_UPPER_2:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]]
226; CHECK-NEXT:    ret i1 [[CMP_UPPER_2]]
227;
228entry:
229  %n.ext = zext i32 %n to i64
230  %upper = getelementptr inbounds i32, ptr %src, i64 %n.ext
231  %cmp.idx = icmp ult i32 %idx, %n
232  %idx.ext = zext i32 %idx to i64
233  %src.idx = getelementptr i32, ptr %src, i64 %idx.ext
234  br i1 %cmp.idx, label %then, label %else
235
236then:
237  %cmp.upper.1 = icmp ule ptr %src.idx, %upper
238  ret i1 %cmp.upper.1
239
240else:
241  call void @noundef(ptr %upper)
242  %cmp.upper.2 = icmp ule ptr %src.idx, %upper
243  ret i1 %cmp.upper.2
244}
245
246define i1 @only_ub_by_assume_in_false_block(ptr %src, i32 %n, i32 %idx) {
247; CHECK-LABEL: @only_ub_by_assume_in_false_block(
248; CHECK-NEXT:  entry:
249; CHECK-NEXT:    [[N_EXT:%.*]] = zext i32 [[N:%.*]] to i64
250; CHECK-NEXT:    [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[SRC:%.*]], i64 [[N_EXT]]
251; CHECK-NEXT:    [[CMP_IDX:%.*]] = icmp ult i32 [[IDX:%.*]], [[N]]
252; CHECK-NEXT:    [[IDX_EXT:%.*]] = zext i32 [[IDX]] to i64
253; CHECK-NEXT:    [[SRC_IDX:%.*]] = getelementptr i32, ptr [[SRC]], i64 [[IDX_EXT]]
254; CHECK-NEXT:    br i1 [[CMP_IDX]], label [[THEN:%.*]], label [[ELSE:%.*]]
255; CHECK:       then:
256; CHECK-NEXT:    [[CMP_UPPER_1:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]]
257; CHECK-NEXT:    ret i1 [[CMP_UPPER_1]]
258; CHECK:       else:
259; CHECK-NEXT:    [[CMP_NE:%.*]] = icmp ule ptr null, [[UPPER]]
260; CHECK-NEXT:    call void @llvm.assume(i1 [[CMP_NE]])
261; CHECK-NEXT:    [[CMP_UPPER_2:%.*]] = icmp ule ptr [[SRC_IDX]], [[UPPER]]
262; CHECK-NEXT:    ret i1 [[CMP_UPPER_2]]
263;
264entry:
265  %n.ext = zext i32 %n to i64
266  %upper = getelementptr inbounds i32, ptr %src, i64 %n.ext
267  %cmp.idx = icmp ult i32 %idx, %n
268  %idx.ext = zext i32 %idx to i64
269  %src.idx = getelementptr i32, ptr %src, i64 %idx.ext
270  br i1 %cmp.idx, label %then, label %else
271
272then:
273  %cmp.upper.1 = icmp ule ptr %src.idx, %upper
274  ret i1 %cmp.upper.1
275
276else:
277  %cmp.ne = icmp ule ptr null, %upper
278  call void @llvm.assume(i1 %cmp.ne)
279  %cmp.upper.2 = icmp ule ptr %src.idx, %upper
280  ret i1 %cmp.upper.2
281}
282