Skip to content

Commit 905e4ec

Browse files
Poseydon42nikic
andauthored
[InstCombine] Implement folds of icmp of UCMP/SCMP call and a constant (#96118)
This patch handles various cases where an operation of the kind `icmp (ucmp/scmp x, y), constant` folds to `icmp x, y`. Another patch with cases where this operation folds to a constant (i.e. dumb cases like `icmp eq (cmp x, y), 4` should be published in a couple of days. I wasn't sure what negative tests should be added here, if any are necessary at all. I'd love to hear your suggestions. Proofs (ucmp): https://alive2.llvm.org/ce/z/qQ7ihz Proofs (scmp): https://alive2.llvm.org/ce/z/cipKEn --------- Co-authored-by: Nikita Popov <[email protected]>
1 parent c2735d2 commit 905e4ec

File tree

3 files changed

+363
-0
lines changed

3 files changed

+363
-0
lines changed

llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3933,6 +3933,52 @@ foldICmpUSubSatOrUAddSatWithConstant(ICmpInst::Predicate Pred,
39333933
ConstantInt::get(Op1->getType(), EquivInt));
39343934
}
39353935

3936+
static Instruction *
3937+
foldICmpOfCmpIntrinsicWithConstant(ICmpInst::Predicate Pred, IntrinsicInst *I,
3938+
const APInt &C,
3939+
InstCombiner::BuilderTy &Builder) {
3940+
std::optional<ICmpInst::Predicate> NewPredicate = std::nullopt;
3941+
switch (Pred) {
3942+
case ICmpInst::ICMP_EQ:
3943+
case ICmpInst::ICMP_NE:
3944+
if (C.isZero())
3945+
NewPredicate = Pred;
3946+
else if (C.isOne())
3947+
NewPredicate =
3948+
Pred == ICmpInst::ICMP_EQ ? ICmpInst::ICMP_UGT : ICmpInst::ICMP_ULE;
3949+
else if (C.isAllOnes())
3950+
NewPredicate =
3951+
Pred == ICmpInst::ICMP_EQ ? ICmpInst::ICMP_ULT : ICmpInst::ICMP_UGE;
3952+
break;
3953+
3954+
case ICmpInst::ICMP_SGT:
3955+
if (C.isAllOnes())
3956+
NewPredicate = ICmpInst::ICMP_UGE;
3957+
else if (C.isZero())
3958+
NewPredicate = ICmpInst::ICMP_UGT;
3959+
break;
3960+
3961+
case ICmpInst::ICMP_SLT:
3962+
if (C.isZero())
3963+
NewPredicate = ICmpInst::ICMP_ULT;
3964+
else if (C.isOne())
3965+
NewPredicate = ICmpInst::ICMP_ULE;
3966+
break;
3967+
3968+
default:
3969+
break;
3970+
}
3971+
3972+
if (!NewPredicate)
3973+
return nullptr;
3974+
3975+
if (I->getIntrinsicID() == Intrinsic::scmp)
3976+
NewPredicate = ICmpInst::getSignedPredicate(*NewPredicate);
3977+
Value *LHS = I->getOperand(0);
3978+
Value *RHS = I->getOperand(1);
3979+
return new ICmpInst(*NewPredicate, LHS, RHS);
3980+
}
3981+
39363982
/// Fold an icmp with LLVM intrinsic and constant operand: icmp Pred II, C.
39373983
Instruction *InstCombinerImpl::foldICmpIntrinsicWithConstant(ICmpInst &Cmp,
39383984
IntrinsicInst *II,
@@ -3954,6 +4000,11 @@ Instruction *InstCombinerImpl::foldICmpIntrinsicWithConstant(ICmpInst &Cmp,
39544000
if (Instruction *R = foldCtpopPow2Test(Cmp, II, C, Builder, Q))
39554001
return R;
39564002
} break;
4003+
case Intrinsic::scmp:
4004+
case Intrinsic::ucmp:
4005+
if (auto *Folded = foldICmpOfCmpIntrinsicWithConstant(Pred, II, C, Builder))
4006+
return Folded;
4007+
break;
39574008
}
39584009

39594010
if (Cmp.isEquality())
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
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+
define i1 @scmp_eq_0(i32 %x, i32 %y) {
5+
; CHECK-LABEL: define i1 @scmp_eq_0(
6+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
7+
; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[X]], [[Y]]
8+
; CHECK-NEXT: ret i1 [[TMP2]]
9+
;
10+
%1 = call i8 @llvm.scmp(i32 %x, i32 %y)
11+
%2 = icmp eq i8 %1, 0
12+
ret i1 %2
13+
}
14+
15+
define i1 @scmp_ne_0(i32 %x, i32 %y) {
16+
; CHECK-LABEL: define i1 @scmp_ne_0(
17+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
18+
; CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[X]], [[Y]]
19+
; CHECK-NEXT: ret i1 [[TMP2]]
20+
;
21+
%1 = call i8 @llvm.scmp(i32 %x, i32 %y)
22+
%2 = icmp ne i8 %1, 0
23+
ret i1 %2
24+
}
25+
26+
define i1 @scmp_eq_1(i32 %x, i32 %y) {
27+
; CHECK-LABEL: define i1 @scmp_eq_1(
28+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
29+
; CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[X]], [[Y]]
30+
; CHECK-NEXT: ret i1 [[TMP2]]
31+
;
32+
%1 = call i8 @llvm.scmp(i32 %x, i32 %y)
33+
%2 = icmp eq i8 %1, 1
34+
ret i1 %2
35+
}
36+
37+
define i1 @scmp_ne_1(i32 %x, i32 %y) {
38+
; CHECK-LABEL: define i1 @scmp_ne_1(
39+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
40+
; CHECK-NEXT: [[TMP2:%.*]] = icmp sle i32 [[X]], [[Y]]
41+
; CHECK-NEXT: ret i1 [[TMP2]]
42+
;
43+
%1 = call i8 @llvm.scmp(i32 %x, i32 %y)
44+
%2 = icmp ne i8 %1, 1
45+
ret i1 %2
46+
}
47+
48+
define i1 @scmp_eq_negative_1(i32 %x, i32 %y) {
49+
; CHECK-LABEL: define i1 @scmp_eq_negative_1(
50+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
51+
; CHECK-NEXT: [[TMP2:%.*]] = icmp slt i32 [[X]], [[Y]]
52+
; CHECK-NEXT: ret i1 [[TMP2]]
53+
;
54+
%1 = call i8 @llvm.scmp(i32 %x, i32 %y)
55+
%2 = icmp eq i8 %1, -1
56+
ret i1 %2
57+
}
58+
59+
define i1 @scmp_ne_negative_1(i32 %x, i32 %y) {
60+
; CHECK-LABEL: define i1 @scmp_ne_negative_1(
61+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
62+
; CHECK-NEXT: [[TMP2:%.*]] = icmp sge i32 [[X]], [[Y]]
63+
; CHECK-NEXT: ret i1 [[TMP2]]
64+
;
65+
%1 = call i8 @llvm.scmp(i32 %x, i32 %y)
66+
%2 = icmp ne i8 %1, -1
67+
ret i1 %2
68+
}
69+
70+
define i1 @scmp_sgt_0(i32 %x, i32 %y) {
71+
; CHECK-LABEL: define i1 @scmp_sgt_0(
72+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
73+
; CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[X]], [[Y]]
74+
; CHECK-NEXT: ret i1 [[TMP2]]
75+
;
76+
%1 = call i8 @llvm.scmp(i32 %x, i32 %y)
77+
%2 = icmp sgt i8 %1, 0
78+
ret i1 %2
79+
}
80+
81+
define i1 @scmp_sgt_neg_1(i32 %x, i32 %y) {
82+
; CHECK-LABEL: define i1 @scmp_sgt_neg_1(
83+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
84+
; CHECK-NEXT: [[TMP2:%.*]] = icmp sge i32 [[X]], [[Y]]
85+
; CHECK-NEXT: ret i1 [[TMP2]]
86+
;
87+
%1 = call i8 @llvm.scmp(i32 %x, i32 %y)
88+
%2 = icmp sgt i8 %1, -1
89+
ret i1 %2
90+
}
91+
92+
define i1 @scmp_sge_0(i32 %x, i32 %y) {
93+
; CHECK-LABEL: define i1 @scmp_sge_0(
94+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
95+
; CHECK-NEXT: [[TMP2:%.*]] = icmp sge i32 [[X]], [[Y]]
96+
; CHECK-NEXT: ret i1 [[TMP2]]
97+
;
98+
%1 = call i8 @llvm.scmp(i32 %x, i32 %y)
99+
%2 = icmp sge i8 %1, 0
100+
ret i1 %2
101+
}
102+
103+
define i1 @scmp_sge_1(i32 %x, i32 %y) {
104+
; CHECK-LABEL: define i1 @scmp_sge_1(
105+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
106+
; CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[X]], [[Y]]
107+
; CHECK-NEXT: ret i1 [[TMP2]]
108+
;
109+
%1 = call i8 @llvm.scmp(i32 %x, i32 %y)
110+
%2 = icmp sge i8 %1, 1
111+
ret i1 %2
112+
}
113+
114+
define i1 @scmp_slt_0(i32 %x, i32 %y) {
115+
; CHECK-LABEL: define i1 @scmp_slt_0(
116+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
117+
; CHECK-NEXT: [[TMP2:%.*]] = icmp slt i32 [[X]], [[Y]]
118+
; CHECK-NEXT: ret i1 [[TMP2]]
119+
;
120+
%1 = call i8 @llvm.scmp(i32 %x, i32 %y)
121+
%2 = icmp slt i8 %1, 0
122+
ret i1 %2
123+
}
124+
125+
define i1 @scmp_slt_1(i32 %x, i32 %y) {
126+
; CHECK-LABEL: define i1 @scmp_slt_1(
127+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
128+
; CHECK-NEXT: [[TMP2:%.*]] = icmp sle i32 [[X]], [[Y]]
129+
; CHECK-NEXT: ret i1 [[TMP2]]
130+
;
131+
%1 = call i8 @llvm.scmp(i32 %x, i32 %y)
132+
%2 = icmp slt i8 %1, 1
133+
ret i1 %2
134+
}
135+
136+
define i1 @scmp_sle_0(i32 %x, i32 %y) {
137+
; CHECK-LABEL: define i1 @scmp_sle_0(
138+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
139+
; CHECK-NEXT: [[TMP2:%.*]] = icmp sle i32 [[X]], [[Y]]
140+
; CHECK-NEXT: ret i1 [[TMP2]]
141+
;
142+
%1 = call i8 @llvm.scmp(i32 %x, i32 %y)
143+
%2 = icmp sle i8 %1, 0
144+
ret i1 %2
145+
}
146+
147+
define i1 @scmp_sle_neg_1(i32 %x, i32 %y) {
148+
; CHECK-LABEL: define i1 @scmp_sle_neg_1(
149+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
150+
; CHECK-NEXT: [[TMP2:%.*]] = icmp slt i32 [[X]], [[Y]]
151+
; CHECK-NEXT: ret i1 [[TMP2]]
152+
;
153+
%1 = call i8 @llvm.scmp(i32 %x, i32 %y)
154+
%2 = icmp sle i8 %1, -1
155+
ret i1 %2
156+
}
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
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+
define i1 @ucmp_eq_0(i32 %x, i32 %y) {
5+
; CHECK-LABEL: define i1 @ucmp_eq_0(
6+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
7+
; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[X]], [[Y]]
8+
; CHECK-NEXT: ret i1 [[TMP2]]
9+
;
10+
%1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
11+
%2 = icmp eq i8 %1, 0
12+
ret i1 %2
13+
}
14+
15+
define i1 @ucmp_ne_0(i32 %x, i32 %y) {
16+
; CHECK-LABEL: define i1 @ucmp_ne_0(
17+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
18+
; CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[X]], [[Y]]
19+
; CHECK-NEXT: ret i1 [[TMP2]]
20+
;
21+
%1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
22+
%2 = icmp ne i8 %1, 0
23+
ret i1 %2
24+
}
25+
26+
define i1 @ucmp_eq_1(i32 %x, i32 %y) {
27+
; CHECK-LABEL: define i1 @ucmp_eq_1(
28+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
29+
; CHECK-NEXT: [[TMP2:%.*]] = icmp ugt i32 [[X]], [[Y]]
30+
; CHECK-NEXT: ret i1 [[TMP2]]
31+
;
32+
%1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
33+
%2 = icmp eq i8 %1, 1
34+
ret i1 %2
35+
}
36+
37+
define i1 @ucmp_ne_1(i32 %x, i32 %y) {
38+
; CHECK-LABEL: define i1 @ucmp_ne_1(
39+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
40+
; CHECK-NEXT: [[TMP2:%.*]] = icmp ule i32 [[X]], [[Y]]
41+
; CHECK-NEXT: ret i1 [[TMP2]]
42+
;
43+
%1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
44+
%2 = icmp ne i8 %1, 1
45+
ret i1 %2
46+
}
47+
48+
define i1 @ucmp_eq_negative_1(i32 %x, i32 %y) {
49+
; CHECK-LABEL: define i1 @ucmp_eq_negative_1(
50+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
51+
; CHECK-NEXT: [[TMP2:%.*]] = icmp ult i32 [[X]], [[Y]]
52+
; CHECK-NEXT: ret i1 [[TMP2]]
53+
;
54+
%1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
55+
%2 = icmp eq i8 %1, -1
56+
ret i1 %2
57+
}
58+
59+
define i1 @ucmp_ne_negative_1(i32 %x, i32 %y) {
60+
; CHECK-LABEL: define i1 @ucmp_ne_negative_1(
61+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
62+
; CHECK-NEXT: [[TMP2:%.*]] = icmp uge i32 [[X]], [[Y]]
63+
; CHECK-NEXT: ret i1 [[TMP2]]
64+
;
65+
%1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
66+
%2 = icmp ne i8 %1, -1
67+
ret i1 %2
68+
}
69+
70+
define i1 @ucmp_sgt_0(i32 %x, i32 %y) {
71+
; CHECK-LABEL: define i1 @ucmp_sgt_0(
72+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
73+
; CHECK-NEXT: [[TMP2:%.*]] = icmp ugt i32 [[X]], [[Y]]
74+
; CHECK-NEXT: ret i1 [[TMP2]]
75+
;
76+
%1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
77+
%2 = icmp sgt i8 %1, 0
78+
ret i1 %2
79+
}
80+
81+
define i1 @ucmp_sgt_neg_1(i32 %x, i32 %y) {
82+
; CHECK-LABEL: define i1 @ucmp_sgt_neg_1(
83+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
84+
; CHECK-NEXT: [[TMP2:%.*]] = icmp uge i32 [[X]], [[Y]]
85+
; CHECK-NEXT: ret i1 [[TMP2]]
86+
;
87+
%1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
88+
%2 = icmp sgt i8 %1, -1
89+
ret i1 %2
90+
}
91+
92+
define i1 @ucmp_sge_0(i32 %x, i32 %y) {
93+
; CHECK-LABEL: define i1 @ucmp_sge_0(
94+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
95+
; CHECK-NEXT: [[TMP2:%.*]] = icmp uge i32 [[X]], [[Y]]
96+
; CHECK-NEXT: ret i1 [[TMP2]]
97+
;
98+
%1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
99+
%2 = icmp sge i8 %1, 0
100+
ret i1 %2
101+
}
102+
103+
define i1 @ucmp_sge_1(i32 %x, i32 %y) {
104+
; CHECK-LABEL: define i1 @ucmp_sge_1(
105+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
106+
; CHECK-NEXT: [[TMP2:%.*]] = icmp ugt i32 [[X]], [[Y]]
107+
; CHECK-NEXT: ret i1 [[TMP2]]
108+
;
109+
%1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
110+
%2 = icmp sge i8 %1, 1
111+
ret i1 %2
112+
}
113+
114+
define i1 @ucmp_slt_0(i32 %x, i32 %y) {
115+
; CHECK-LABEL: define i1 @ucmp_slt_0(
116+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
117+
; CHECK-NEXT: [[TMP2:%.*]] = icmp ult i32 [[X]], [[Y]]
118+
; CHECK-NEXT: ret i1 [[TMP2]]
119+
;
120+
%1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
121+
%2 = icmp slt i8 %1, 0
122+
ret i1 %2
123+
}
124+
125+
define i1 @ucmp_slt_1(i32 %x, i32 %y) {
126+
; CHECK-LABEL: define i1 @ucmp_slt_1(
127+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
128+
; CHECK-NEXT: [[TMP2:%.*]] = icmp ule i32 [[X]], [[Y]]
129+
; CHECK-NEXT: ret i1 [[TMP2]]
130+
;
131+
%1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
132+
%2 = icmp slt i8 %1, 1
133+
ret i1 %2
134+
}
135+
136+
define i1 @ucmp_sle_0(i32 %x, i32 %y) {
137+
; CHECK-LABEL: define i1 @ucmp_sle_0(
138+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
139+
; CHECK-NEXT: [[TMP2:%.*]] = icmp ule i32 [[X]], [[Y]]
140+
; CHECK-NEXT: ret i1 [[TMP2]]
141+
;
142+
%1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
143+
%2 = icmp sle i8 %1, 0
144+
ret i1 %2
145+
}
146+
147+
define i1 @ucmp_sle_neg_1(i32 %x, i32 %y) {
148+
; CHECK-LABEL: define i1 @ucmp_sle_neg_1(
149+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
150+
; CHECK-NEXT: [[TMP2:%.*]] = icmp ult i32 [[X]], [[Y]]
151+
; CHECK-NEXT: ret i1 [[TMP2]]
152+
;
153+
%1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
154+
%2 = icmp sle i8 %1, -1
155+
ret i1 %2
156+
}

0 commit comments

Comments
 (0)