Skip to content

Commit c09eb96

Browse files
Poseydon42cjdb
authored andcommitted
[InstCombine] Fold sext(A < B) + zext(A > B) into ucmp/scmp(A, B) (llvm#103833)
This change also covers the fold of `zext(A > B) - zext(A < B)` since it is already being canonicalized into the aforementioned pattern. Proof: https://alive2.llvm.org/ce/z/AgnfMn
1 parent e7be09e commit c09eb96

File tree

3 files changed

+206
-2
lines changed

3 files changed

+206
-2
lines changed

llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1626,6 +1626,26 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
16261626
A->getType()->isIntOrIntVectorTy(1))
16271627
return replaceInstUsesWith(I, Constant::getNullValue(I.getType()));
16281628

1629+
// sext(A < B) + zext(A > B) => ucmp/scmp(A, B)
1630+
ICmpInst::Predicate LTPred, GTPred;
1631+
if (match(&I,
1632+
m_c_Add(m_SExt(m_c_ICmp(LTPred, m_Value(A), m_Value(B))),
1633+
m_ZExt(m_c_ICmp(GTPred, m_Deferred(A), m_Deferred(B))))) &&
1634+
A->getType()->isIntOrIntVectorTy()) {
1635+
if (ICmpInst::isGT(LTPred)) {
1636+
std::swap(LTPred, GTPred);
1637+
std::swap(A, B);
1638+
}
1639+
1640+
if (ICmpInst::isLT(LTPred) && ICmpInst::isGT(GTPred) &&
1641+
ICmpInst::isSigned(LTPred) == ICmpInst::isSigned(GTPred))
1642+
return replaceInstUsesWith(
1643+
I, Builder.CreateIntrinsic(
1644+
Ty,
1645+
ICmpInst::isSigned(LTPred) ? Intrinsic::scmp : Intrinsic::ucmp,
1646+
{A, B}));
1647+
}
1648+
16291649
// A+B --> A|B iff A and B have no bits set in common.
16301650
WithCache<const Value *> LHSCache(LHS), RHSCache(RHS);
16311651
if (haveNoCommonBitsSet(LHSCache, RHSCache, SQ.getWithInstruction(&I)))

llvm/test/Transforms/InstCombine/add.ll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1315,8 +1315,8 @@ define <2 x i8> @ashr_add_commute(<2 x i1> %x, <2 x i1> %y) {
13151315

13161316
define i32 @cmp_math(i32 %x, i32 %y) {
13171317
; CHECK-LABEL: @cmp_math(
1318-
; CHECK-NEXT: [[LT:%.*]] = icmp ult i32 [[X:%.*]], [[Y:%.*]]
1319-
; CHECK-NEXT: [[R:%.*]] = zext i1 [[LT]] to i32
1318+
; CHECK-NEXT: [[TMP1:%.*]] = icmp ult i32 [[X:%.*]], [[Y:%.*]]
1319+
; CHECK-NEXT: [[R:%.*]] = zext i1 [[TMP1]] to i32
13201320
; CHECK-NEXT: ret i32 [[R]]
13211321
;
13221322
%gt = icmp ugt i32 %x, %y
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
2+
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
3+
4+
; sext(A s< B) + zext(A s> B) => scmp(A, B)
5+
define i8 @signed_add(i32 %a, i32 %b) {
6+
; CHECK-LABEL: define i8 @signed_add(
7+
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
8+
; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.scmp.i8.i32(i32 [[A]], i32 [[B]])
9+
; CHECK-NEXT: ret i8 [[R]]
10+
;
11+
%lt = icmp slt i32 %a, %b
12+
%lt8 = sext i1 %lt to i8
13+
%gt = icmp sgt i32 %a, %b
14+
%gt8 = zext i1 %gt to i8
15+
%r = add i8 %lt8, %gt8
16+
ret i8 %r
17+
}
18+
19+
; Unsigned version
20+
define i8 @unsigned_add(i32 %a, i32 %b) {
21+
; CHECK-LABEL: define i8 @unsigned_add(
22+
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
23+
; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.ucmp.i8.i32(i32 [[A]], i32 [[B]])
24+
; CHECK-NEXT: ret i8 [[R]]
25+
;
26+
%lt = icmp ult i32 %a, %b
27+
%lt8 = sext i1 %lt to i8
28+
%gt = icmp ugt i32 %a, %b
29+
%gt8 = zext i1 %gt to i8
30+
%r = add i8 %lt8, %gt8
31+
ret i8 %r
32+
}
33+
34+
; Commuted operands
35+
define i8 @signed_add_commuted1(i32 %a, i32 %b) {
36+
; CHECK-LABEL: define i8 @signed_add_commuted1(
37+
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
38+
; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.scmp.i8.i32(i32 [[B]], i32 [[A]])
39+
; CHECK-NEXT: ret i8 [[R]]
40+
;
41+
%lt = icmp slt i32 %a, %b
42+
%lt8 = zext i1 %lt to i8
43+
%gt = icmp sgt i32 %a, %b
44+
%gt8 = sext i1 %gt to i8
45+
%r = add i8 %lt8, %gt8
46+
ret i8 %r
47+
}
48+
49+
define i8 @signed_add_commuted2(i32 %a, i32 %b) {
50+
; CHECK-LABEL: define i8 @signed_add_commuted2(
51+
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
52+
; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.scmp.i8.i32(i32 [[A]], i32 [[B]])
53+
; CHECK-NEXT: ret i8 [[R]]
54+
;
55+
%lt = icmp sgt i32 %b, %a
56+
%lt8 = sext i1 %lt to i8
57+
%gt = icmp sgt i32 %a, %b
58+
%gt8 = zext i1 %gt to i8
59+
%r = add i8 %lt8, %gt8
60+
ret i8 %r
61+
}
62+
63+
; zext(A s> B) - zext(A s< B) => scmp(A, B)
64+
define i8 @signed_sub(i32 %a, i32 %b) {
65+
; CHECK-LABEL: define i8 @signed_sub(
66+
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
67+
; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.scmp.i8.i32(i32 [[A]], i32 [[B]])
68+
; CHECK-NEXT: ret i8 [[R]]
69+
;
70+
%lt = icmp slt i32 %a, %b
71+
%lt8 = zext i1 %lt to i8
72+
%gt = icmp sgt i32 %a, %b
73+
%gt8 = zext i1 %gt to i8
74+
%r = sub i8 %gt8, %lt8
75+
ret i8 %r
76+
}
77+
78+
; Unsigned version
79+
define i8 @unsigned_sub(i32 %a, i32 %b) {
80+
; CHECK-LABEL: define i8 @unsigned_sub(
81+
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
82+
; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.ucmp.i8.i32(i32 [[A]], i32 [[B]])
83+
; CHECK-NEXT: ret i8 [[R]]
84+
;
85+
%lt = icmp ult i32 %a, %b
86+
%lt8 = zext i1 %lt to i8
87+
%gt = icmp ugt i32 %a, %b
88+
%gt8 = zext i1 %gt to i8
89+
%r = sub i8 %gt8, %lt8
90+
ret i8 %r
91+
}
92+
93+
; Negative test: incorrect predicates
94+
define i8 @signed_add_neg1(i32 %a, i32 %b) {
95+
; CHECK-LABEL: define i8 @signed_add_neg1(
96+
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
97+
; CHECK-NEXT: [[LT:%.*]] = icmp sgt i32 [[A]], [[B]]
98+
; CHECK-NEXT: [[LT8:%.*]] = sext i1 [[LT]] to i8
99+
; CHECK-NEXT: [[GT:%.*]] = icmp sgt i32 [[A]], [[B]]
100+
; CHECK-NEXT: [[GT8:%.*]] = zext i1 [[GT]] to i8
101+
; CHECK-NEXT: [[R:%.*]] = add nsw i8 [[LT8]], [[GT8]]
102+
; CHECK-NEXT: ret i8 [[R]]
103+
;
104+
%lt = icmp sgt i32 %a, %b
105+
%lt8 = sext i1 %lt to i8
106+
%gt = icmp sgt i32 %a, %b
107+
%gt8 = zext i1 %gt to i8
108+
%r = add i8 %lt8, %gt8
109+
ret i8 %r
110+
}
111+
112+
define i8 @signed_add_neg2(i32 %a, i32 %b) {
113+
; CHECK-LABEL: define i8 @signed_add_neg2(
114+
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
115+
; CHECK-NEXT: [[LT:%.*]] = icmp slt i32 [[A]], [[B]]
116+
; CHECK-NEXT: [[LT8:%.*]] = sext i1 [[LT]] to i8
117+
; CHECK-NEXT: [[GT:%.*]] = icmp ne i32 [[A]], [[B]]
118+
; CHECK-NEXT: [[GT8:%.*]] = zext i1 [[GT]] to i8
119+
; CHECK-NEXT: [[R:%.*]] = add nsw i8 [[LT8]], [[GT8]]
120+
; CHECK-NEXT: ret i8 [[R]]
121+
;
122+
%lt = icmp slt i32 %a, %b
123+
%lt8 = sext i1 %lt to i8
124+
%gt = icmp ne i32 %a, %b
125+
%gt8 = zext i1 %gt to i8
126+
%r = add i8 %lt8, %gt8
127+
ret i8 %r
128+
}
129+
130+
; Negative test: mismatched signedness of predicates
131+
define i8 @signed_add_neg3(i32 %a, i32 %b) {
132+
; CHECK-LABEL: define i8 @signed_add_neg3(
133+
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
134+
; CHECK-NEXT: [[LT:%.*]] = icmp slt i32 [[A]], [[B]]
135+
; CHECK-NEXT: [[LT8:%.*]] = sext i1 [[LT]] to i8
136+
; CHECK-NEXT: [[GT:%.*]] = icmp ugt i32 [[A]], [[B]]
137+
; CHECK-NEXT: [[GT8:%.*]] = zext i1 [[GT]] to i8
138+
; CHECK-NEXT: [[R:%.*]] = add nsw i8 [[LT8]], [[GT8]]
139+
; CHECK-NEXT: ret i8 [[R]]
140+
;
141+
%lt = icmp slt i32 %a, %b
142+
%lt8 = sext i1 %lt to i8
143+
%gt = icmp ugt i32 %a, %b
144+
%gt8 = zext i1 %gt to i8
145+
%r = add i8 %lt8, %gt8
146+
ret i8 %r
147+
}
148+
149+
; Negative test: zext instead of sext or vice-versa (NOT commuted operands)
150+
define i8 @signed_add_neg4(i32 %a, i32 %b) {
151+
; CHECK-LABEL: define i8 @signed_add_neg4(
152+
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
153+
; CHECK-NEXT: [[LT:%.*]] = icmp slt i32 [[A]], [[B]]
154+
; CHECK-NEXT: [[LT8:%.*]] = sext i1 [[LT]] to i8
155+
; CHECK-NEXT: [[GT:%.*]] = icmp sgt i32 [[A]], [[B]]
156+
; CHECK-NEXT: [[GT8:%.*]] = sext i1 [[GT]] to i8
157+
; CHECK-NEXT: [[R:%.*]] = add nsw i8 [[LT8]], [[GT8]]
158+
; CHECK-NEXT: ret i8 [[R]]
159+
;
160+
%lt = icmp slt i32 %a, %b
161+
%lt8 = sext i1 %lt to i8
162+
%gt = icmp sgt i32 %a, %b
163+
%gt8 = sext i1 %gt to i8
164+
%r = add i8 %lt8, %gt8
165+
ret i8 %r
166+
}
167+
168+
define i8 @signed_add_neg5(i32 %a, i32 %b) {
169+
; CHECK-LABEL: define i8 @signed_add_neg5(
170+
; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
171+
; CHECK-NEXT: [[LT:%.*]] = icmp slt i32 [[A]], [[B]]
172+
; CHECK-NEXT: [[LT8:%.*]] = zext i1 [[LT]] to i8
173+
; CHECK-NEXT: [[GT:%.*]] = icmp sgt i32 [[A]], [[B]]
174+
; CHECK-NEXT: [[GT8:%.*]] = zext i1 [[GT]] to i8
175+
; CHECK-NEXT: [[R:%.*]] = add nuw nsw i8 [[LT8]], [[GT8]]
176+
; CHECK-NEXT: ret i8 [[R]]
177+
;
178+
%lt = icmp slt i32 %a, %b
179+
%lt8 = zext i1 %lt to i8
180+
%gt = icmp sgt i32 %a, %b
181+
%gt8 = zext i1 %gt to i8
182+
%r = add i8 %lt8, %gt8
183+
ret i8 %r
184+
}

0 commit comments

Comments
 (0)