xref: /llvm-project/llvm/test/Transforms/GuardWidening/posion.ll (revision 2b9509627caa2b0ca27eb1b05099eb016491f027)
1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2; RUN: opt -S -passes=guard-widening,dce < %s | FileCheck %s
3
4declare void @llvm.experimental.guard(i1,...)
5declare i1 @dummy()
6
7; This tests shows the incorrect behavior of guard widening in terms of
8; interaction with poison values.
9
10; Let x incoming parameter is used for rane checks.
11; Test generates 5 checks. One of them (c2) is used to get the corretness
12; of nuw/nsw flags for x3 and x5. Others are used in guards and represent
13; the checks x + 10 u< L, x + 15 u< L, x + 20 u< L and x + 3 u< L.
14; The first two checks are in the first basic block and guard widening
15; considers them as profitable to combine.
16; When c4 and c3 are considered, number of check becomes more than two
17; and combineRangeCheck consider them as profitable even if they are in
18; different basic blocks.
19; Accoding to algorithm of combineRangeCheck it detects that c3 and c4
20; are enough to cover c1 and c5, so it ends up with guard of c3 && c4
21; while both of them are poison at entry. This is a bug.
22
23define void @combine_range_checks(i32 %x) {
24; CHECK-LABEL: @combine_range_checks(
25; CHECK-NEXT:  entry:
26; CHECK-NEXT:    [[X_GW_FR:%.*]] = freeze i32 [[X:%.*]]
27; CHECK-NEXT:    [[X2:%.*]] = add i32 [[X_GW_FR]], 0
28; CHECK-NEXT:    [[C2:%.*]] = icmp ult i32 [[X2]], 200
29; CHECK-NEXT:    [[X3:%.*]] = add i32 [[X_GW_FR]], 3
30; CHECK-NEXT:    [[C3:%.*]] = icmp ult i32 [[X3]], 100
31; CHECK-NEXT:    [[X4:%.*]] = add i32 [[X_GW_FR]], 20
32; CHECK-NEXT:    [[C4:%.*]] = icmp ult i32 [[X4]], 100
33; CHECK-NEXT:    [[WIDE_CHK2:%.*]] = and i1 [[C4]], [[C3]]
34; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK2]]) [ "deopt"(i64 1) ]
35; CHECK-NEXT:    br i1 [[C2]], label [[OK:%.*]], label [[OUT:%.*]]
36; CHECK:       ok:
37; CHECK-NEXT:    br label [[OUT]]
38; CHECK:       out:
39; CHECK-NEXT:    ret void
40;
41entry:
42  %x1 = add i32 %x, 10
43  %c1 = icmp ult i32 %x1, 100
44  %x2 = add i32 %x, 0
45  %c2 = icmp ult i32 %x2, 200
46  %x3 = add nuw nsw i32 %x, 3
47  %c3 = icmp ult i32 %x3, 100
48  %x4 = add nuw nsw i32 %x, 20
49  %c4 = icmp ult i32 %x4, 100
50  %x5 = add i32 %x, 15
51  %c5 = icmp ult i32 %x5, 100
52  call void(i1, ...) @llvm.experimental.guard(i1 %c1) [ "deopt"(i64 1) ]
53  call void(i1, ...) @llvm.experimental.guard(i1 %c5) [ "deopt"(i64 5) ]
54  br i1 %c2, label %ok, label %out
55ok:
56  call void(i1, ...) @llvm.experimental.guard(i1 %c4) [ "deopt"(i64 4) ]
57  call void(i1, ...) @llvm.experimental.guard(i1 %c3) [ "deopt"(i64 3) ]
58  br label %out
59out:
60  ret void
61}
62
63; This is similar to @combine_range_checks but shows that simple freeze
64; over c3 and c4 will not help due to with X = SMAX_INT, guard with c1 will
65; go to deoptimization. But after guard widening freeze of c3 and c4 may return
66; true due to c3 and c4 are poisons and we pass guard executing side effect store
67; which never been executed in original program.
68define void @combine_range_checks_with_side_effect(i32 %x, ptr %p) {
69; CHECK-LABEL: @combine_range_checks_with_side_effect(
70; CHECK-NEXT:  entry:
71; CHECK-NEXT:    [[X_GW_FR:%.*]] = freeze i32 [[X:%.*]]
72; CHECK-NEXT:    [[X2:%.*]] = add i32 [[X_GW_FR]], 0
73; CHECK-NEXT:    [[C2:%.*]] = icmp ult i32 [[X2]], 200
74; CHECK-NEXT:    [[X3:%.*]] = add i32 [[X_GW_FR]], 3
75; CHECK-NEXT:    [[C3:%.*]] = icmp ult i32 [[X3]], 100
76; CHECK-NEXT:    [[X4:%.*]] = add i32 [[X_GW_FR]], 20
77; CHECK-NEXT:    [[C4:%.*]] = icmp ult i32 [[X4]], 100
78; CHECK-NEXT:    [[WIDE_CHK2:%.*]] = and i1 [[C4]], [[C3]]
79; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK2]]) [ "deopt"(i64 1) ]
80; CHECK-NEXT:    store i32 0, ptr [[P:%.*]], align 4
81; CHECK-NEXT:    br i1 [[C2]], label [[OK:%.*]], label [[OUT:%.*]]
82; CHECK:       ok:
83; CHECK-NEXT:    br label [[OUT]]
84; CHECK:       out:
85; CHECK-NEXT:    ret void
86;
87entry:
88  %x1 = add i32 %x, 10
89  %c1 = icmp ult i32 %x1, 100
90  %x2 = add i32 %x, 0
91  %c2 = icmp ult i32 %x2, 200
92  %x3 = add nuw nsw i32 %x, 3
93  %c3 = icmp ult i32 %x3, 100
94  %x4 = add nuw nsw i32 %x, 20
95  %c4 = icmp ult i32 %x4, 100
96  %x5 = add i32 %x, 15
97  %c5 = icmp ult i32 %x5, 100
98  call void(i1, ...) @llvm.experimental.guard(i1 %c1) [ "deopt"(i64 1) ]
99  call void(i1, ...) @llvm.experimental.guard(i1 %c5) [ "deopt"(i64 5) ]
100  store i32 0, ptr %p
101  br i1 %c2, label %ok, label %out
102ok:
103  call void(i1, ...) @llvm.experimental.guard(i1 %c4) [ "deopt"(i64 4) ]
104  call void(i1, ...) @llvm.experimental.guard(i1 %c3) [ "deopt"(i64 3) ]
105  br label %out
106out:
107  ret void
108}
109
110
111; The test shows the bug in guard widening. Critical pieces.
112; There is a %cond_1 check which provides the correctness of nuw nsw in %b.shift.
113; %b.shift and %cond_2 are poisons and after guard widening it leads to UB
114; for both arithmetic and logcal and.
115define void @simple_case(i32 %a, i32 %b, i1 %cnd) {
116; CHECK-LABEL: @simple_case(
117; CHECK-NEXT:  entry:
118; CHECK-NEXT:    [[B_GW_FR:%.*]] = freeze i32 [[B:%.*]]
119; CHECK-NEXT:    [[COND_0:%.*]] = icmp ult i32 [[A:%.*]], 10
120; CHECK-NEXT:    [[B_SHIFT:%.*]] = add i32 [[B_GW_FR]], 5
121; CHECK-NEXT:    [[COND_2:%.*]] = icmp ult i32 [[B_SHIFT]], 10
122; CHECK-NEXT:    [[WIDE_CHK:%.*]] = and i1 [[COND_0]], [[COND_2]]
123; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ]
124; CHECK-NEXT:    br label [[LOOP:%.*]]
125; CHECK:       loop:
126; CHECK-NEXT:    [[COND_1:%.*]] = icmp ult i32 [[B_GW_FR]], 10
127; CHECK-NEXT:    br i1 [[COND_1]], label [[OK:%.*]], label [[LEAVE_LOOPEXIT:%.*]]
128; CHECK:       ok:
129; CHECK-NEXT:    br i1 [[CND:%.*]], label [[LOOP]], label [[LEAVE_LOOPEXIT]]
130; CHECK:       leave.loopexit:
131; CHECK-NEXT:    br label [[LEAVE:%.*]]
132; CHECK:       leave:
133; CHECK-NEXT:    ret void
134;
135entry:
136  %cond_0 = icmp ult i32 %a, 10
137  %b.shift = add nuw nsw i32 %b, 5
138  %cond_2 = icmp ult i32 %b.shift, 10
139  call void (i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
140  br label %loop
141
142loop:
143  %cond_1 = icmp ult i32 %b, 10
144  br i1 %cond_1, label %ok, label %leave.loopexit
145ok:
146  call void (i1, ...) @llvm.experimental.guard(i1 %cond_2) [ "deopt"() ]
147  br i1 %cnd, label %loop, label %leave.loopexit
148
149leave.loopexit:
150  br label %leave
151
152leave:
153  ret void
154}
155
156declare ptr @fake_personality_function()
157
158define void @case_with_invoke(i1 %c, i1 %gc) personality ptr @fake_personality_function {
159; CHECK-LABEL: @case_with_invoke(
160; CHECK-NEXT:  entry:
161; CHECK-NEXT:    br i1 [[C:%.*]], label [[NORMAL:%.*]], label [[INVOK:%.*]]
162; CHECK:       invok:
163; CHECK-NEXT:    [[INVOKE_RESULT:%.*]] = invoke i1 @dummy()
164; CHECK-NEXT:    to label [[NORMAL]] unwind label [[EXCEPTION:%.*]]
165; CHECK:       normal:
166; CHECK-NEXT:    [[PHI_C:%.*]] = phi i1 [ true, [[ENTRY:%.*]] ], [ [[INVOKE_RESULT]], [[INVOK]] ]
167; CHECK-NEXT:    [[PHI_C_GW_FR:%.*]] = freeze i1 [[PHI_C]]
168; CHECK-NEXT:    [[WIDE_CHK:%.*]] = and i1 [[GC:%.*]], [[PHI_C_GW_FR]]
169; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ]
170; CHECK-NEXT:    ret void
171; CHECK:       exception:
172; CHECK-NEXT:    [[LANDING_PAD:%.*]] = landingpad { ptr, i32 }
173; CHECK-NEXT:    cleanup
174; CHECK-NEXT:    ret void
175;
176entry:
177  br i1 %c, label %normal, label %invok
178
179invok:
180  %invoke.result = invoke i1 @dummy() to label %normal unwind label %exception
181
182normal:
183  %phi.c = phi i1 [true, %entry], [%invoke.result, %invok]
184  call void (i1, ...) @llvm.experimental.guard(i1 %gc) [ "deopt"() ]
185  call void (i1, ...) @llvm.experimental.guard(i1 %phi.c) [ "deopt"() ]
186  ret void
187
188exception:
189  %landing_pad = landingpad { ptr, i32 } cleanup
190  ret void
191}
192
193define void @case_with_invoke_in_latch(i1 %c, i1 %gc) personality ptr @fake_personality_function {
194; CHECK-LABEL: @case_with_invoke_in_latch(
195; CHECK-NEXT:  entry:
196; CHECK-NEXT:    br label [[HEADER:%.*]]
197; CHECK:       header:
198; CHECK-NEXT:    [[PHI_C:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[INVOKE_RESULT:%.*]], [[HEADER]] ]
199; CHECK-NEXT:    [[PHI_C_GW_FR:%.*]] = freeze i1 [[PHI_C]]
200; CHECK-NEXT:    [[WIDE_CHK:%.*]] = and i1 [[GC:%.*]], [[PHI_C_GW_FR]]
201; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ]
202; CHECK-NEXT:    [[INVOKE_RESULT]] = invoke i1 @dummy()
203; CHECK-NEXT:    to label [[HEADER]] unwind label [[EXCEPTION:%.*]]
204; CHECK:       exception:
205; CHECK-NEXT:    [[LANDING_PAD:%.*]] = landingpad { ptr, i32 }
206; CHECK-NEXT:    cleanup
207; CHECK-NEXT:    ret void
208;
209entry:
210  br label %header
211
212header:
213  %phi.c = phi i1 [false, %entry], [%invoke.result, %header]
214  call void (i1, ...) @llvm.experimental.guard(i1 %gc) [ "deopt"() ]
215  call void (i1, ...) @llvm.experimental.guard(i1 %phi.c) [ "deopt"() ]
216  %invoke.result = invoke i1 @dummy() to label %header unwind label %exception
217
218exception:
219  %landing_pad = landingpad { ptr, i32 } cleanup
220  ret void
221}
222
223declare void @dummy_vec(<4 x i1> %arg)
224
225define void @freeze_poison(i1 %c, i1 %g) {
226; CHECK-LABEL: @freeze_poison(
227; CHECK-NEXT:  entry:
228; CHECK-NEXT:    [[DOTGW_FR:%.*]] = freeze i1 poison
229; CHECK-NEXT:    br i1 [[C:%.*]], label [[LEFT:%.*]], label [[RIGHT:%.*]]
230; CHECK:       left:
231; CHECK-NEXT:    call void @dummy_vec(<4 x i1> <i1 false, i1 poison, i1 poison, i1 poison>)
232; CHECK-NEXT:    ret void
233; CHECK:       right:
234; CHECK-NEXT:    [[WIDE_CHK:%.*]] = and i1 [[G:%.*]], [[DOTGW_FR]]
235; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ]
236; CHECK-NEXT:    ret void
237;
238entry:
239  br i1 %c, label %left, label %right
240
241left:
242  call void @dummy_vec(<4 x i1> <i1 0, i1 poison, i1 poison, i1 poison>)
243  ret void
244
245
246right:
247  call void (i1, ...) @llvm.experimental.guard(i1 %g) [ "deopt"() ]
248  call void (i1, ...) @llvm.experimental.guard(i1 poison) [ "deopt"() ]
249  ret void
250
251}
252
253