Skip to content

Commit c59ea32

Browse files
authored
[InstCombine] Canonicalize icmp pred (X +/- C1), C2 into icmp pred X, C2 -/+ C1 with nowrap flag implied by with.overflow intrinsic (#75511)
This patch tries to canonicalize the pattern `Overflow | icmp pred Res, C2` into `Overflow | icmp pred X, C2 +/- C1`, where `Overflow` and `Res` are return values of `xxx.with.overflow X, C1`. Alive2: https://alive2.llvm.org/ce/z/PhR_3S Fixes #75360.
1 parent 4faeb7d commit c59ea32

File tree

2 files changed

+304
-0
lines changed

2 files changed

+304
-0
lines changed

llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3756,6 +3756,35 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
37563756
}
37573757
}
37583758

3759+
/// Res, Overflow = xxx_with_overflow X, C1
3760+
/// Try to canonicalize the pattern "Overflow | icmp pred Res, C2" into
3761+
/// "Overflow | icmp pred X, C2 +/- C1".
3762+
const WithOverflowInst *WO;
3763+
const Value *WOV;
3764+
const APInt *C1, *C2;
3765+
if (match(&I, m_c_Or(m_CombineAnd(m_ExtractValue<1>(m_CombineAnd(
3766+
m_WithOverflowInst(WO), m_Value(WOV))),
3767+
m_Value(Ov)),
3768+
m_OneUse(m_ICmp(Pred, m_ExtractValue<0>(m_Deferred(WOV)),
3769+
m_APInt(C2))))) &&
3770+
(WO->getBinaryOp() == Instruction::Add ||
3771+
WO->getBinaryOp() == Instruction::Sub) &&
3772+
(ICmpInst::isEquality(Pred) ||
3773+
WO->isSigned() == ICmpInst::isSigned(Pred)) &&
3774+
match(WO->getRHS(), m_APInt(C1))) {
3775+
bool Overflow;
3776+
APInt NewC = WO->getBinaryOp() == Instruction::Add
3777+
? (ICmpInst::isSigned(Pred) ? C2->ssub_ov(*C1, Overflow)
3778+
: C2->usub_ov(*C1, Overflow))
3779+
: (ICmpInst::isSigned(Pred) ? C2->sadd_ov(*C1, Overflow)
3780+
: C2->uadd_ov(*C1, Overflow));
3781+
if (!Overflow || ICmpInst::isEquality(Pred)) {
3782+
Value *NewCmp = Builder.CreateICmp(
3783+
Pred, WO->getLHS(), ConstantInt::get(WO->getLHS()->getType(), NewC));
3784+
return BinaryOperator::CreateOr(Ov, NewCmp);
3785+
}
3786+
}
3787+
37593788
// (~x) | y --> ~(x & (~y)) iff that gets rid of inversions
37603789
if (sinkNotIntoOtherHandOfLogicalOp(I))
37613790
return &I;
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
2+
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
3+
4+
declare { i32, i1 } @llvm.sadd.with.overflow.i32(i32, i32)
5+
declare { i32, i1 } @llvm.ssub.with.overflow.i32(i32, i32)
6+
declare { i32, i1 } @llvm.smul.with.overflow.i32(i32, i32)
7+
declare { i32, i1 } @llvm.uadd.with.overflow.i32(i32, i32)
8+
9+
declare void @use(i1)
10+
11+
; Tests from PR75360
12+
define i1 @ckd_add_unsigned(i31 %num) {
13+
; CHECK-LABEL: define i1 @ckd_add_unsigned(
14+
; CHECK-SAME: i31 [[NUM:%.*]]) {
15+
; CHECK-NEXT: [[A2:%.*]] = icmp eq i31 [[NUM]], -1
16+
; CHECK-NEXT: ret i1 [[A2]]
17+
;
18+
%a0 = zext i31 %num to i32
19+
%a1 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a0, i32 1)
20+
%a2 = extractvalue { i32, i1 } %a1, 1
21+
%a3 = extractvalue { i32, i1 } %a1, 0
22+
%a4 = icmp slt i32 %a3, 0
23+
%a5 = or i1 %a2, %a4
24+
ret i1 %a5
25+
}
26+
27+
define i1 @ckd_add_unsigned_commuted(i31 %num) {
28+
; CHECK-LABEL: define i1 @ckd_add_unsigned_commuted(
29+
; CHECK-SAME: i31 [[NUM:%.*]]) {
30+
; CHECK-NEXT: [[A2:%.*]] = icmp eq i31 [[NUM]], -1
31+
; CHECK-NEXT: ret i1 [[A2]]
32+
;
33+
%a0 = zext i31 %num to i32
34+
%a1 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a0, i32 1)
35+
%a2 = extractvalue { i32, i1 } %a1, 1
36+
%a3 = extractvalue { i32, i1 } %a1, 0
37+
%a4 = icmp slt i32 %a3, 0
38+
%a5 = or i1 %a4, %a2
39+
ret i1 %a5
40+
}
41+
42+
define i1 @ckd_add_unsigned_imply_true(i31 %num) {
43+
; CHECK-LABEL: define i1 @ckd_add_unsigned_imply_true(
44+
; CHECK-SAME: i31 [[NUM:%.*]]) {
45+
; CHECK-NEXT: ret i1 true
46+
;
47+
%a0 = zext i31 %num to i32
48+
%a1 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a0, i32 1)
49+
%a2 = extractvalue { i32, i1 } %a1, 1
50+
%a3 = extractvalue { i32, i1 } %a1, 0
51+
%a4 = icmp sgt i32 %a3, -1
52+
%a5 = or i1 %a2, %a4
53+
ret i1 %a5
54+
}
55+
56+
define i1 @canonicalize_or_sadd_with_overflow_icmp(i32 %a0) {
57+
; CHECK-LABEL: define i1 @canonicalize_or_sadd_with_overflow_icmp(
58+
; CHECK-SAME: i32 [[A0:%.*]]) {
59+
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[A0]], -2147483647
60+
; CHECK-NEXT: [[A5:%.*]] = icmp sgt i32 [[TMP1]], -1
61+
; CHECK-NEXT: ret i1 [[A5]]
62+
;
63+
%a1 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a0, i32 1)
64+
%a2 = extractvalue { i32, i1 } %a1, 1
65+
%a3 = extractvalue { i32, i1 } %a1, 0
66+
%a4 = icmp slt i32 %a3, 0
67+
%a5 = or i1 %a2, %a4
68+
ret i1 %a5
69+
}
70+
71+
define i1 @canonicalize_or_ssub_with_overflow_icmp(i32 %a0) {
72+
; CHECK-LABEL: define i1 @canonicalize_or_ssub_with_overflow_icmp(
73+
; CHECK-SAME: i32 [[A0:%.*]]) {
74+
; CHECK-NEXT: [[TMP1:%.*]] = icmp slt i32 [[A0]], 1
75+
; CHECK-NEXT: ret i1 [[TMP1]]
76+
;
77+
%a1 = tail call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 %a0, i32 1)
78+
%a2 = extractvalue { i32, i1 } %a1, 1
79+
%a3 = extractvalue { i32, i1 } %a1, 0
80+
%a4 = icmp slt i32 %a3, 0
81+
%a5 = or i1 %a2, %a4
82+
ret i1 %a5
83+
}
84+
85+
define i1 @canonicalize_or_uadd_with_overflow_icmp(i32 %a0) {
86+
; CHECK-LABEL: define i1 @canonicalize_or_uadd_with_overflow_icmp(
87+
; CHECK-SAME: i32 [[A0:%.*]]) {
88+
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[A0]], 1
89+
; CHECK-NEXT: [[A5:%.*]] = icmp ult i32 [[TMP1]], 10
90+
; CHECK-NEXT: ret i1 [[A5]]
91+
;
92+
%a1 = tail call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 %a0, i32 1)
93+
%a2 = extractvalue { i32, i1 } %a1, 1
94+
%a3 = extractvalue { i32, i1 } %a1, 0
95+
%a4 = icmp ult i32 %a3, 10
96+
%a5 = or i1 %a2, %a4
97+
ret i1 %a5
98+
}
99+
100+
define i1 @canonicalize_or_sadd_with_overflow_icmp_eq(i32 %a0) {
101+
; CHECK-LABEL: define i1 @canonicalize_or_sadd_with_overflow_icmp_eq(
102+
; CHECK-SAME: i32 [[A0:%.*]]) {
103+
; CHECK-NEXT: [[A2:%.*]] = icmp eq i32 [[A0]], 2147483647
104+
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[A0]], 9
105+
; CHECK-NEXT: [[A5:%.*]] = or i1 [[A2]], [[TMP1]]
106+
; CHECK-NEXT: ret i1 [[A5]]
107+
;
108+
%a1 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a0, i32 1)
109+
%a2 = extractvalue { i32, i1 } %a1, 1
110+
%a3 = extractvalue { i32, i1 } %a1, 0
111+
%a4 = icmp eq i32 %a3, 10
112+
%a5 = or i1 %a2, %a4
113+
ret i1 %a5
114+
}
115+
116+
define i1 @canonicalize_or_uadd_with_overflow_icmp_ne(i32 %a0) {
117+
; CHECK-LABEL: define i1 @canonicalize_or_uadd_with_overflow_icmp_ne(
118+
; CHECK-SAME: i32 [[A0:%.*]]) {
119+
; CHECK-NEXT: [[TMP1:%.*]] = icmp ne i32 [[A0]], 9
120+
; CHECK-NEXT: ret i1 [[TMP1]]
121+
;
122+
%a1 = tail call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 %a0, i32 1)
123+
%a2 = extractvalue { i32, i1 } %a1, 1
124+
%a3 = extractvalue { i32, i1 } %a1, 0
125+
%a4 = icmp ne i32 %a3, 10
126+
%a5 = or i1 %a2, %a4
127+
ret i1 %a5
128+
}
129+
130+
; Negative tests
131+
define i1 @canonicalize_or_sadd_with_overflow_icmp_mismatched_pred(i32 %a0) {
132+
; CHECK-LABEL: define i1 @canonicalize_or_sadd_with_overflow_icmp_mismatched_pred(
133+
; CHECK-SAME: i32 [[A0:%.*]]) {
134+
; CHECK-NEXT: [[A1:%.*]] = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[A0]], i32 1)
135+
; CHECK-NEXT: [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
136+
; CHECK-NEXT: [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
137+
; CHECK-NEXT: [[A4:%.*]] = icmp ult i32 [[A3]], 2
138+
; CHECK-NEXT: [[A5:%.*]] = or i1 [[A2]], [[A4]]
139+
; CHECK-NEXT: ret i1 [[A5]]
140+
;
141+
%a1 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a0, i32 1)
142+
%a2 = extractvalue { i32, i1 } %a1, 1
143+
%a3 = extractvalue { i32, i1 } %a1, 0
144+
%a4 = icmp ult i32 %a3, 2
145+
%a5 = or i1 %a2, %a4
146+
ret i1 %a5
147+
}
148+
149+
define i1 @canonicalize_or_sadd_with_overflow_icmp_non_constant1(i32 %a0, i32 %c) {
150+
; CHECK-LABEL: define i1 @canonicalize_or_sadd_with_overflow_icmp_non_constant1(
151+
; CHECK-SAME: i32 [[A0:%.*]], i32 [[C:%.*]]) {
152+
; CHECK-NEXT: [[A1:%.*]] = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[A0]], i32 [[C]])
153+
; CHECK-NEXT: [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
154+
; CHECK-NEXT: [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
155+
; CHECK-NEXT: [[A4:%.*]] = icmp slt i32 [[A3]], 0
156+
; CHECK-NEXT: [[A5:%.*]] = or i1 [[A2]], [[A4]]
157+
; CHECK-NEXT: ret i1 [[A5]]
158+
;
159+
%a1 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a0, i32 %c)
160+
%a2 = extractvalue { i32, i1 } %a1, 1
161+
%a3 = extractvalue { i32, i1 } %a1, 0
162+
%a4 = icmp slt i32 %a3, 0
163+
%a5 = or i1 %a2, %a4
164+
ret i1 %a5
165+
}
166+
167+
define i1 @canonicalize_or_sadd_with_overflow_icmp_non_constant2(i32 %a0, i32 %c) {
168+
; CHECK-LABEL: define i1 @canonicalize_or_sadd_with_overflow_icmp_non_constant2(
169+
; CHECK-SAME: i32 [[A0:%.*]], i32 [[C:%.*]]) {
170+
; CHECK-NEXT: [[A1:%.*]] = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[A0]], i32 1)
171+
; CHECK-NEXT: [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
172+
; CHECK-NEXT: [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
173+
; CHECK-NEXT: [[A4:%.*]] = icmp slt i32 [[A3]], [[C]]
174+
; CHECK-NEXT: [[A5:%.*]] = or i1 [[A2]], [[A4]]
175+
; CHECK-NEXT: ret i1 [[A5]]
176+
;
177+
%a1 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a0, i32 1)
178+
%a2 = extractvalue { i32, i1 } %a1, 1
179+
%a3 = extractvalue { i32, i1 } %a1, 0
180+
%a4 = icmp slt i32 %a3, %c
181+
%a5 = or i1 %a2, %a4
182+
ret i1 %a5
183+
}
184+
185+
define i1 @canonicalize_or_sadd_with_overflow_icmp_multiuse(i32 %a0) {
186+
; CHECK-LABEL: define i1 @canonicalize_or_sadd_with_overflow_icmp_multiuse(
187+
; CHECK-SAME: i32 [[A0:%.*]]) {
188+
; CHECK-NEXT: [[A1:%.*]] = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[A0]], i32 1)
189+
; CHECK-NEXT: [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
190+
; CHECK-NEXT: [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
191+
; CHECK-NEXT: [[A4:%.*]] = icmp slt i32 [[A3]], 0
192+
; CHECK-NEXT: call void @use(i1 [[A4]])
193+
; CHECK-NEXT: [[A5:%.*]] = or i1 [[A2]], [[A4]]
194+
; CHECK-NEXT: ret i1 [[A5]]
195+
;
196+
%a1 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a0, i32 1)
197+
%a2 = extractvalue { i32, i1 } %a1, 1
198+
%a3 = extractvalue { i32, i1 } %a1, 0
199+
%a4 = icmp slt i32 %a3, 0
200+
call void @use(i1 %a4)
201+
%a5 = or i1 %a2, %a4
202+
ret i1 %a5
203+
}
204+
205+
define i1 @canonicalize_or_sadd_with_overflow_icmp_overflow(i32 %a0) {
206+
; CHECK-LABEL: define i1 @canonicalize_or_sadd_with_overflow_icmp_overflow(
207+
; CHECK-SAME: i32 [[A0:%.*]]) {
208+
; CHECK-NEXT: [[A1:%.*]] = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[A0]], i32 -2147483647)
209+
; CHECK-NEXT: [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
210+
; CHECK-NEXT: [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
211+
; CHECK-NEXT: [[A4:%.*]] = icmp slt i32 [[A3]], 2
212+
; CHECK-NEXT: [[A5:%.*]] = or i1 [[A2]], [[A4]]
213+
; CHECK-NEXT: ret i1 [[A5]]
214+
;
215+
%a1 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a0, i32 -2147483647)
216+
%a2 = extractvalue { i32, i1 } %a1, 1
217+
%a3 = extractvalue { i32, i1 } %a1, 0
218+
%a4 = icmp slt i32 %a3, 2
219+
%a5 = or i1 %a2, %a4
220+
ret i1 %a5
221+
}
222+
223+
define i1 @canonicalize_or_uadd_with_overflow_icmp_overflow(i32 %a0) {
224+
; CHECK-LABEL: define i1 @canonicalize_or_uadd_with_overflow_icmp_overflow(
225+
; CHECK-SAME: i32 [[A0:%.*]]) {
226+
; CHECK-NEXT: [[A1:%.*]] = tail call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 [[A0]], i32 3)
227+
; CHECK-NEXT: [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
228+
; CHECK-NEXT: [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
229+
; CHECK-NEXT: [[A4:%.*]] = icmp ult i32 [[A3]], 2
230+
; CHECK-NEXT: [[A5:%.*]] = or i1 [[A2]], [[A4]]
231+
; CHECK-NEXT: ret i1 [[A5]]
232+
;
233+
%a1 = tail call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 %a0, i32 3)
234+
%a2 = extractvalue { i32, i1 } %a1, 1
235+
%a3 = extractvalue { i32, i1 } %a1, 0
236+
%a4 = icmp ult i32 %a3, 2
237+
%a5 = or i1 %a2, %a4
238+
ret i1 %a5
239+
}
240+
241+
define i1 @canonicalize_or_ssub_with_overflow_icmp_overflow(i32 %a0) {
242+
; CHECK-LABEL: define i1 @canonicalize_or_ssub_with_overflow_icmp_overflow(
243+
; CHECK-SAME: i32 [[A0:%.*]]) {
244+
; CHECK-NEXT: [[A1:%.*]] = tail call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 [[A0]], i32 -2147483648)
245+
; CHECK-NEXT: [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
246+
; CHECK-NEXT: [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
247+
; CHECK-NEXT: [[A4:%.*]] = icmp slt i32 [[A3]], -1
248+
; CHECK-NEXT: [[A5:%.*]] = or i1 [[A2]], [[A4]]
249+
; CHECK-NEXT: ret i1 [[A5]]
250+
;
251+
%a1 = tail call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 %a0, i32 -2147483648)
252+
%a2 = extractvalue { i32, i1 } %a1, 1
253+
%a3 = extractvalue { i32, i1 } %a1, 0
254+
%a4 = icmp slt i32 %a3, -1
255+
%a5 = or i1 %a2, %a4
256+
ret i1 %a5
257+
}
258+
259+
define i1 @canonicalize_or_smul_with_overflow_icmp(i32 %a0) {
260+
; CHECK-LABEL: define i1 @canonicalize_or_smul_with_overflow_icmp(
261+
; CHECK-SAME: i32 [[A0:%.*]]) {
262+
; CHECK-NEXT: [[A1:%.*]] = tail call { i32, i1 } @llvm.smul.with.overflow.i32(i32 [[A0]], i32 3)
263+
; CHECK-NEXT: [[A2:%.*]] = extractvalue { i32, i1 } [[A1]], 1
264+
; CHECK-NEXT: [[A3:%.*]] = extractvalue { i32, i1 } [[A1]], 0
265+
; CHECK-NEXT: [[A4:%.*]] = icmp slt i32 [[A3]], 10
266+
; CHECK-NEXT: [[A5:%.*]] = or i1 [[A2]], [[A4]]
267+
; CHECK-NEXT: ret i1 [[A5]]
268+
;
269+
%a1 = tail call { i32, i1 } @llvm.smul.with.overflow.i32(i32 %a0, i32 3)
270+
%a2 = extractvalue { i32, i1 } %a1, 1
271+
%a3 = extractvalue { i32, i1 } %a1, 0
272+
%a4 = icmp slt i32 %a3, 10
273+
%a5 = or i1 %a2, %a4
274+
ret i1 %a5
275+
}

0 commit comments

Comments
 (0)