Skip to content

Commit 8201926

Browse files
authored
[InstSimplify] Generalize simplification of icmps with monotonic operands (#69471)
InstSimplify currently folds patterns like `(x | y) uge x` and `(x & y) ule x` to true. However, it cannot handle combinations of such situations, such as `(x | y) uge (x & z)` etc. To support this, recursively collect operands of monotonic instructions (that preserve either a greater-or-equal or less-or-equal relationship) and then check whether any of them match. Fixes #69333.
1 parent a545cf5 commit 8201926

File tree

3 files changed

+105
-84
lines changed

3 files changed

+105
-84
lines changed

llvm/lib/Analysis/InstructionSimplify.cpp

Lines changed: 71 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3070,6 +3070,69 @@ static Value *simplifyICmpWithConstant(CmpInst::Predicate Pred, Value *LHS,
30703070
return nullptr;
30713071
}
30723072

3073+
enum class MonotonicType { GreaterEq, LowerEq };
3074+
3075+
/// Get values V_i such that V uge V_i (GreaterEq) or V ule V_i (LowerEq).
3076+
static void getUnsignedMonotonicValues(SmallPtrSetImpl<Value *> &Res, Value *V,
3077+
MonotonicType Type, unsigned Depth = 0) {
3078+
if (!Res.insert(V).second)
3079+
return;
3080+
3081+
// Can be increased if useful.
3082+
if (++Depth > 1)
3083+
return;
3084+
3085+
auto *I = dyn_cast<Instruction>(V);
3086+
if (!I)
3087+
return;
3088+
3089+
Value *X, *Y;
3090+
if (Type == MonotonicType::GreaterEq) {
3091+
if (match(I, m_Or(m_Value(X), m_Value(Y))) ||
3092+
match(I, m_Intrinsic<Intrinsic::uadd_sat>(m_Value(X), m_Value(Y)))) {
3093+
getUnsignedMonotonicValues(Res, X, Type, Depth);
3094+
getUnsignedMonotonicValues(Res, Y, Type, Depth);
3095+
}
3096+
} else {
3097+
assert(Type == MonotonicType::LowerEq);
3098+
switch (I->getOpcode()) {
3099+
case Instruction::And:
3100+
getUnsignedMonotonicValues(Res, I->getOperand(0), Type, Depth);
3101+
getUnsignedMonotonicValues(Res, I->getOperand(1), Type, Depth);
3102+
break;
3103+
case Instruction::URem:
3104+
case Instruction::UDiv:
3105+
case Instruction::LShr:
3106+
getUnsignedMonotonicValues(Res, I->getOperand(0), Type, Depth);
3107+
break;
3108+
case Instruction::Call:
3109+
if (match(I, m_Intrinsic<Intrinsic::usub_sat>(m_Value(X))))
3110+
getUnsignedMonotonicValues(Res, X, Type, Depth);
3111+
break;
3112+
default:
3113+
break;
3114+
}
3115+
}
3116+
}
3117+
3118+
static Value *simplifyICmpUsingMonotonicValues(ICmpInst::Predicate Pred,
3119+
Value *LHS, Value *RHS) {
3120+
if (Pred != ICmpInst::ICMP_UGE && Pred != ICmpInst::ICMP_ULT)
3121+
return nullptr;
3122+
3123+
// We have LHS uge GreaterValues and LowerValues uge RHS. If any of the
3124+
// GreaterValues and LowerValues are the same, it follows that LHS uge RHS.
3125+
SmallPtrSet<Value *, 4> GreaterValues;
3126+
SmallPtrSet<Value *, 4> LowerValues;
3127+
getUnsignedMonotonicValues(GreaterValues, LHS, MonotonicType::GreaterEq);
3128+
getUnsignedMonotonicValues(LowerValues, RHS, MonotonicType::LowerEq);
3129+
for (Value *GV : GreaterValues)
3130+
if (LowerValues.contains(GV))
3131+
return ConstantInt::getBool(getCompareTy(LHS),
3132+
Pred == ICmpInst::ICMP_UGE);
3133+
return nullptr;
3134+
}
3135+
30733136
static Value *simplifyICmpWithBinOpOnLHS(CmpInst::Predicate Pred,
30743137
BinaryOperator *LBO, Value *RHS,
30753138
const SimplifyQuery &Q,
@@ -3079,11 +3142,6 @@ static Value *simplifyICmpWithBinOpOnLHS(CmpInst::Predicate Pred,
30793142
Value *Y = nullptr;
30803143
// icmp pred (or X, Y), X
30813144
if (match(LBO, m_c_Or(m_Value(Y), m_Specific(RHS)))) {
3082-
if (Pred == ICmpInst::ICMP_ULT)
3083-
return getFalse(ITy);
3084-
if (Pred == ICmpInst::ICMP_UGE)
3085-
return getTrue(ITy);
3086-
30873145
if (Pred == ICmpInst::ICMP_SLT || Pred == ICmpInst::ICMP_SGE) {
30883146
KnownBits RHSKnown = computeKnownBits(RHS, /* Depth */ 0, Q);
30893147
KnownBits YKnown = computeKnownBits(Y, /* Depth */ 0, Q);
@@ -3094,14 +3152,6 @@ static Value *simplifyICmpWithBinOpOnLHS(CmpInst::Predicate Pred,
30943152
}
30953153
}
30963154

3097-
// icmp pred (and X, Y), X
3098-
if (match(LBO, m_c_And(m_Value(), m_Specific(RHS)))) {
3099-
if (Pred == ICmpInst::ICMP_UGT)
3100-
return getFalse(ITy);
3101-
if (Pred == ICmpInst::ICMP_ULE)
3102-
return getTrue(ITy);
3103-
}
3104-
31053155
// icmp pred (urem X, Y), Y
31063156
if (match(LBO, m_URem(m_Value(), m_Specific(RHS)))) {
31073157
switch (Pred) {
@@ -3132,27 +3182,6 @@ static Value *simplifyICmpWithBinOpOnLHS(CmpInst::Predicate Pred,
31323182
}
31333183
}
31343184

3135-
// icmp pred (urem X, Y), X
3136-
if (match(LBO, m_URem(m_Specific(RHS), m_Value()))) {
3137-
if (Pred == ICmpInst::ICMP_ULE)
3138-
return getTrue(ITy);
3139-
if (Pred == ICmpInst::ICMP_UGT)
3140-
return getFalse(ITy);
3141-
}
3142-
3143-
// x >>u y <=u x --> true.
3144-
// x >>u y >u x --> false.
3145-
// x udiv y <=u x --> true.
3146-
// x udiv y >u x --> false.
3147-
if (match(LBO, m_LShr(m_Specific(RHS), m_Value())) ||
3148-
match(LBO, m_UDiv(m_Specific(RHS), m_Value()))) {
3149-
// icmp pred (X op Y), X
3150-
if (Pred == ICmpInst::ICMP_UGT)
3151-
return getFalse(ITy);
3152-
if (Pred == ICmpInst::ICMP_ULE)
3153-
return getTrue(ITy);
3154-
}
3155-
31563185
// If x is nonzero:
31573186
// x >>u C <u x --> true for C != 0.
31583187
// x >>u C != x --> true for C != 0.
@@ -3172,14 +3201,12 @@ static Value *simplifyICmpWithBinOpOnLHS(CmpInst::Predicate Pred,
31723201
break;
31733202
case ICmpInst::ICMP_EQ:
31743203
case ICmpInst::ICMP_UGE:
3204+
case ICmpInst::ICMP_UGT:
31753205
return getFalse(ITy);
31763206
case ICmpInst::ICMP_NE:
31773207
case ICmpInst::ICMP_ULT:
3178-
return getTrue(ITy);
3179-
case ICmpInst::ICMP_UGT:
31803208
case ICmpInst::ICMP_ULE:
3181-
// UGT/ULE are handled by the more general case just above
3182-
llvm_unreachable("Unexpected UGT/ULE, should have been handled");
3209+
return getTrue(ITy);
31833210
}
31843211
}
31853212
}
@@ -3702,13 +3729,6 @@ static Value *simplifyICmpWithIntrinsicOnLHS(CmpInst::Predicate Pred,
37023729

37033730
switch (II->getIntrinsicID()) {
37043731
case Intrinsic::uadd_sat:
3705-
// uadd.sat(X, Y) uge X, uadd.sat(X, Y) uge Y
3706-
if (II->getArgOperand(0) == RHS || II->getArgOperand(1) == RHS) {
3707-
if (Pred == ICmpInst::ICMP_UGE)
3708-
return ConstantInt::getTrue(getCompareTy(II));
3709-
if (Pred == ICmpInst::ICMP_ULT)
3710-
return ConstantInt::getFalse(getCompareTy(II));
3711-
}
37123732
// uadd.sat(X, Y) uge X + Y
37133733
if (match(RHS, m_c_Add(m_Specific(II->getArgOperand(0)),
37143734
m_Specific(II->getArgOperand(1))))) {
@@ -3719,13 +3739,6 @@ static Value *simplifyICmpWithIntrinsicOnLHS(CmpInst::Predicate Pred,
37193739
}
37203740
return nullptr;
37213741
case Intrinsic::usub_sat:
3722-
// usub.sat(X, Y) ule X
3723-
if (II->getArgOperand(0) == RHS) {
3724-
if (Pred == ICmpInst::ICMP_ULE)
3725-
return ConstantInt::getTrue(getCompareTy(II));
3726-
if (Pred == ICmpInst::ICMP_UGT)
3727-
return ConstantInt::getFalse(getCompareTy(II));
3728-
}
37293742
// usub.sat(X, Y) ule X - Y
37303743
if (match(RHS, m_Sub(m_Specific(II->getArgOperand(0)),
37313744
m_Specific(II->getArgOperand(1))))) {
@@ -4030,6 +4043,12 @@ static Value *simplifyICmpInst(unsigned Predicate, Value *LHS, Value *RHS,
40304043
ICmpInst::getSwappedPredicate(Pred), RHS, LHS))
40314044
return V;
40324045

4046+
if (Value *V = simplifyICmpUsingMonotonicValues(Pred, LHS, RHS))
4047+
return V;
4048+
if (Value *V = simplifyICmpUsingMonotonicValues(
4049+
ICmpInst::getSwappedPredicate(Pred), RHS, LHS))
4050+
return V;
4051+
40334052
if (Value *V = simplifyICmpWithDominatingAssume(Pred, LHS, RHS, Q))
40344053
return V;
40354054

llvm/test/Transforms/InstSimplify/compare.ll

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,32 @@ define i1 @lshr_nonzero_ult(i32 %x) {
780780
ret i1 %cmp
781781
}
782782

783+
define i1 @lshr_nonzero_ugt(i32 %x) {
784+
; CHECK-LABEL: @lshr_nonzero_ugt(
785+
; CHECK-NEXT: [[X_NE_0:%.*]] = icmp ne i32 [[X:%.*]], 0
786+
; CHECK-NEXT: call void @llvm.assume(i1 [[X_NE_0]])
787+
; CHECK-NEXT: ret i1 false
788+
;
789+
%x_ne_0 = icmp ne i32 %x, 0
790+
call void @llvm.assume(i1 %x_ne_0)
791+
%lhs = lshr i32 %x, 1
792+
%cmp = icmp ugt i32 %lhs, %x
793+
ret i1 %cmp
794+
}
795+
796+
define i1 @lshr_nonzero_ule(i32 %x) {
797+
; CHECK-LABEL: @lshr_nonzero_ule(
798+
; CHECK-NEXT: [[X_NE_0:%.*]] = icmp ne i32 [[X:%.*]], 0
799+
; CHECK-NEXT: call void @llvm.assume(i1 [[X_NE_0]])
800+
; CHECK-NEXT: ret i1 true
801+
;
802+
%x_ne_0 = icmp ne i32 %x, 0
803+
call void @llvm.assume(i1 %x_ne_0)
804+
%lhs = lshr i32 %x, 1
805+
%cmp = icmp ule i32 %lhs, %x
806+
ret i1 %cmp
807+
}
808+
783809
; Negative test - unknown shift amount
784810
define i1 @lshr_nonzero_neg_unknown(i32 %x, i32 %c) {
785811
; CHECK-LABEL: @lshr_nonzero_neg_unknown(

llvm/test/Transforms/InstSimplify/icmp-monotonic.ll

Lines changed: 8 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@
44
define i1 @lshr_or_ule(i32 %x, i32 %y, i32 %z) {
55
; CHECK-LABEL: define i1 @lshr_or_ule(
66
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]], i32 [[Z:%.*]]) {
7-
; CHECK-NEXT: [[OP1:%.*]] = lshr i32 [[X]], [[Y]]
8-
; CHECK-NEXT: [[OP2:%.*]] = or i32 [[X]], [[Z]]
9-
; CHECK-NEXT: [[CMP:%.*]] = icmp ule i32 [[OP1]], [[OP2]]
10-
; CHECK-NEXT: ret i1 [[CMP]]
7+
; CHECK-NEXT: ret i1 true
118
;
129
%op1 = lshr i32 %x, %y
1310
%op2 = or i32 %x, %z
@@ -18,10 +15,7 @@ define i1 @lshr_or_ule(i32 %x, i32 %y, i32 %z) {
1815
define i1 @lshr_or_uge_swapped(i32 %x, i32 %y, i32 %z) {
1916
; CHECK-LABEL: define i1 @lshr_or_uge_swapped(
2017
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]], i32 [[Z:%.*]]) {
21-
; CHECK-NEXT: [[OP1:%.*]] = lshr i32 [[X]], [[Y]]
22-
; CHECK-NEXT: [[OP2:%.*]] = or i32 [[X]], [[Z]]
23-
; CHECK-NEXT: [[CMP:%.*]] = icmp uge i32 [[OP2]], [[OP1]]
24-
; CHECK-NEXT: ret i1 [[CMP]]
18+
; CHECK-NEXT: ret i1 true
2519
;
2620
%op1 = lshr i32 %x, %y
2721
%op2 = or i32 %x, %z
@@ -32,10 +26,7 @@ define i1 @lshr_or_uge_swapped(i32 %x, i32 %y, i32 %z) {
3226
define i1 @lshr_or_ugt(i32 %x, i32 %y, i32 %z) {
3327
; CHECK-LABEL: define i1 @lshr_or_ugt(
3428
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]], i32 [[Z:%.*]]) {
35-
; CHECK-NEXT: [[OP1:%.*]] = lshr i32 [[X]], [[Y]]
36-
; CHECK-NEXT: [[OP2:%.*]] = or i32 [[X]], [[Z]]
37-
; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[OP1]], [[OP2]]
38-
; CHECK-NEXT: ret i1 [[CMP]]
29+
; CHECK-NEXT: ret i1 false
3930
;
4031
%op1 = lshr i32 %x, %y
4132
%op2 = or i32 %x, %z
@@ -74,10 +65,7 @@ define i1 @lshr_or_sle_wrong_pred(i32 %x, i32 %y, i32 %z) {
7465
define i1 @lshr_or_swapped_ule(i32 %x, i32 %y, i32 %z) {
7566
; CHECK-LABEL: define i1 @lshr_or_swapped_ule(
7667
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]], i32 [[Z:%.*]]) {
77-
; CHECK-NEXT: [[OP1:%.*]] = lshr i32 [[X]], [[Y]]
78-
; CHECK-NEXT: [[OP2:%.*]] = or i32 [[Z]], [[X]]
79-
; CHECK-NEXT: [[CMP:%.*]] = icmp ule i32 [[OP1]], [[OP2]]
80-
; CHECK-NEXT: ret i1 [[CMP]]
68+
; CHECK-NEXT: ret i1 true
8169
;
8270
%op1 = lshr i32 %x, %y
8371
%op2 = or i32 %z, %x
@@ -102,10 +90,7 @@ define i1 @lshr_or_ule_invalid_swapped(i32 %x, i32 %y, i32 %z) {
10290
define i1 @and_uadd_sat_ule(i32 %x, i32 %y, i32 %z) {
10391
; CHECK-LABEL: define i1 @and_uadd_sat_ule(
10492
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]], i32 [[Z:%.*]]) {
105-
; CHECK-NEXT: [[OP1:%.*]] = and i32 [[X]], [[Y]]
106-
; CHECK-NEXT: [[OP2:%.*]] = call i32 @llvm.uadd.sat.i32(i32 [[X]], i32 [[Z]])
107-
; CHECK-NEXT: [[CMP:%.*]] = icmp ule i32 [[OP1]], [[OP2]]
108-
; CHECK-NEXT: ret i1 [[CMP]]
93+
; CHECK-NEXT: ret i1 true
10994
;
11095
%op1 = and i32 %x, %y
11196
%op2 = call i32 @llvm.uadd.sat(i32 %x, i32 %z)
@@ -116,10 +101,7 @@ define i1 @and_uadd_sat_ule(i32 %x, i32 %y, i32 %z) {
116101
define i1 @urem_or_ule(i32 %x, i32 %y, i32 %z) {
117102
; CHECK-LABEL: define i1 @urem_or_ule(
118103
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]], i32 [[Z:%.*]]) {
119-
; CHECK-NEXT: [[OP1:%.*]] = urem i32 [[X]], [[Y]]
120-
; CHECK-NEXT: [[OP2:%.*]] = or i32 [[X]], [[Z]]
121-
; CHECK-NEXT: [[CMP:%.*]] = icmp ule i32 [[OP1]], [[OP2]]
122-
; CHECK-NEXT: ret i1 [[CMP]]
104+
; CHECK-NEXT: ret i1 true
123105
;
124106
%op1 = urem i32 %x, %y
125107
%op2 = or i32 %x, %z
@@ -144,10 +126,7 @@ define i1 @urem_or_ule_invalid_swapped(i32 %x, i32 %y, i32 %z) {
144126
define i1 @udiv_or_ule(i32 %x, i32 %y, i32 %z) {
145127
; CHECK-LABEL: define i1 @udiv_or_ule(
146128
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]], i32 [[Z:%.*]]) {
147-
; CHECK-NEXT: [[OP1:%.*]] = udiv i32 [[X]], [[Y]]
148-
; CHECK-NEXT: [[OP2:%.*]] = or i32 [[X]], [[Z]]
149-
; CHECK-NEXT: [[CMP:%.*]] = icmp ule i32 [[OP1]], [[OP2]]
150-
; CHECK-NEXT: ret i1 [[CMP]]
129+
; CHECK-NEXT: ret i1 true
151130
;
152131
%op1 = udiv i32 %x, %y
153132
%op2 = or i32 %x, %z
@@ -172,10 +151,7 @@ define i1 @udiv_or_ule_invalid_swapped(i32 %x, i32 %y, i32 %z) {
172151
define i1 @usub_sat_uadd_sat_ule(i32 %x, i32 %y, i32 %z) {
173152
; CHECK-LABEL: define i1 @usub_sat_uadd_sat_ule(
174153
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]], i32 [[Z:%.*]]) {
175-
; CHECK-NEXT: [[OP1:%.*]] = call i32 @llvm.usub.sat.i32(i32 [[X]], i32 [[Y]])
176-
; CHECK-NEXT: [[OP2:%.*]] = call i32 @llvm.uadd.sat.i32(i32 [[X]], i32 [[Z]])
177-
; CHECK-NEXT: [[CMP:%.*]] = icmp ule i32 [[OP1]], [[OP2]]
178-
; CHECK-NEXT: ret i1 [[CMP]]
154+
; CHECK-NEXT: ret i1 true
179155
;
180156
%op1 = call i32 @llvm.usub.sat(i32 %x, i32 %y)
181157
%op2 = call i32 @llvm.uadd.sat(i32 %x, i32 %z)

0 commit comments

Comments
 (0)