-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[InstSimplify] Fold X * C >= X to true
#129352
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
@llvm/pr-subscribers-llvm-analysis @llvm/pr-subscribers-llvm-transforms Author: Veera (veera-sivarajan) ChangesProof: https://alive2.llvm.org/ce/z/T_ocLy Discovered in: rust-lang/rust#114386 This PR folds Folds for other math operators exist already: https://llvm-ir.godbolt.org/z/GKcYEf5Kb Full diff: https://github.com/llvm/llvm-project/pull/129352.diff 2 Files Affected:
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 59002cd934ab1..2db1572f55a27 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -3031,7 +3031,9 @@ enum class MonotonicType { GreaterEq, LowerEq };
/// Get values V_i such that V uge V_i (GreaterEq) or V ule V_i (LowerEq).
static void getUnsignedMonotonicValues(SmallPtrSetImpl<Value *> &Res, Value *V,
- MonotonicType Type, unsigned Depth = 0) {
+ MonotonicType Type,
+ const SimplifyQuery &Q,
+ unsigned Depth = 0) {
if (!Res.insert(V).second)
return;
@@ -3047,24 +3049,31 @@ static void getUnsignedMonotonicValues(SmallPtrSetImpl<Value *> &Res, Value *V,
if (Type == MonotonicType::GreaterEq) {
if (match(I, m_Or(m_Value(X), m_Value(Y))) ||
match(I, m_Intrinsic<Intrinsic::uadd_sat>(m_Value(X), m_Value(Y)))) {
- getUnsignedMonotonicValues(Res, X, Type, Depth);
- getUnsignedMonotonicValues(Res, Y, Type, Depth);
+ getUnsignedMonotonicValues(Res, X, Type, Q, Depth);
+ getUnsignedMonotonicValues(Res, Y, Type, Q, Depth);
+ }
+ // X * Y >= X --> true
+ if (match(I, m_NUWMul(m_Value(X), m_Value(Y)))) {
+ if (isKnownNonZero(X, Q))
+ getUnsignedMonotonicValues(Res, Y, Type, Q, Depth);
+ if (isKnownNonZero(Y, Q))
+ getUnsignedMonotonicValues(Res, X, Type, Q, Depth);
}
} else {
assert(Type == MonotonicType::LowerEq);
switch (I->getOpcode()) {
case Instruction::And:
- getUnsignedMonotonicValues(Res, I->getOperand(0), Type, Depth);
- getUnsignedMonotonicValues(Res, I->getOperand(1), Type, Depth);
+ getUnsignedMonotonicValues(Res, I->getOperand(0), Type, Q, Depth);
+ getUnsignedMonotonicValues(Res, I->getOperand(1), Type, Q, Depth);
break;
case Instruction::URem:
case Instruction::UDiv:
case Instruction::LShr:
- getUnsignedMonotonicValues(Res, I->getOperand(0), Type, Depth);
+ getUnsignedMonotonicValues(Res, I->getOperand(0), Type, Q, Depth);
break;
case Instruction::Call:
if (match(I, m_Intrinsic<Intrinsic::usub_sat>(m_Value(X))))
- getUnsignedMonotonicValues(Res, X, Type, Depth);
+ getUnsignedMonotonicValues(Res, X, Type, Q, Depth);
break;
default:
break;
@@ -3073,16 +3082,16 @@ static void getUnsignedMonotonicValues(SmallPtrSetImpl<Value *> &Res, Value *V,
}
static Value *simplifyICmpUsingMonotonicValues(CmpPredicate Pred, Value *LHS,
- Value *RHS) {
+ Value *RHS,
+ const SimplifyQuery &Q) {
if (Pred != ICmpInst::ICMP_UGE && Pred != ICmpInst::ICMP_ULT)
return nullptr;
-
// We have LHS uge GreaterValues and LowerValues uge RHS. If any of the
// GreaterValues and LowerValues are the same, it follows that LHS uge RHS.
SmallPtrSet<Value *, 4> GreaterValues;
SmallPtrSet<Value *, 4> LowerValues;
- getUnsignedMonotonicValues(GreaterValues, LHS, MonotonicType::GreaterEq);
- getUnsignedMonotonicValues(LowerValues, RHS, MonotonicType::LowerEq);
+ getUnsignedMonotonicValues(GreaterValues, LHS, MonotonicType::GreaterEq, Q);
+ getUnsignedMonotonicValues(LowerValues, RHS, MonotonicType::LowerEq, Q);
for (Value *GV : GreaterValues)
if (LowerValues.contains(GV))
return ConstantInt::getBool(getCompareTy(LHS),
@@ -3994,14 +4003,16 @@ static Value *simplifyICmpInst(CmpPredicate Pred, Value *LHS, Value *RHS,
if (Value *V = simplifyICmpWithIntrinsicOnLHS(Pred, LHS, RHS))
return V;
+
if (Value *V = simplifyICmpWithIntrinsicOnLHS(
ICmpInst::getSwappedPredicate(Pred), RHS, LHS))
return V;
- if (Value *V = simplifyICmpUsingMonotonicValues(Pred, LHS, RHS))
+ if (Value *V = simplifyICmpUsingMonotonicValues(Pred, LHS, RHS, Q))
return V;
+
if (Value *V = simplifyICmpUsingMonotonicValues(
- ICmpInst::getSwappedPredicate(Pred), RHS, LHS))
+ ICmpInst::getSwappedPredicate(Pred), RHS, LHS, Q))
return V;
if (Value *V = simplifyICmpWithDominatingAssume(Pred, LHS, RHS, Q))
diff --git a/llvm/test/Transforms/InstSimplify/icmp-monotonic.ll b/llvm/test/Transforms/InstSimplify/icmp-monotonic.ll
index e1a4ee91bd15c..1063136ab54c4 100644
--- a/llvm/test/Transforms/InstSimplify/icmp-monotonic.ll
+++ b/llvm/test/Transforms/InstSimplify/icmp-monotonic.ll
@@ -216,3 +216,132 @@ define i1 @lshr_add_ule_non_monotonic(i32 %x, i32 %y, i32 %z) {
%cmp = icmp ule i32 %op1, %op2
ret i1 %cmp
}
+
+define i1 @mul_nuw_nonzero_rhs_monotonic(i8 %x, i8 %c) {
+; CHECK-LABEL: define i1 @mul_nuw_nonzero_rhs_monotonic(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[C:%.*]]) {
+; CHECK-NEXT: [[C_NONZERO:%.*]] = icmp ne i8 [[C]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[C_NONZERO]])
+; CHECK-NEXT: ret i1 true
+;
+ %c_nonzero = icmp ne i8 %c, 0
+ call void @llvm.assume(i1 %c_nonzero)
+
+ %prod = mul nuw i8 %x, %c
+ %cmp = icmp uge i8 %prod, %x
+ ret i1 %cmp
+}
+
+define i1 @mul_nuw_nonzero_rhs_monotonic_inverse_predicate(i8 %x, i8 %c) {
+; CHECK-LABEL: define i1 @mul_nuw_nonzero_rhs_monotonic_inverse_predicate(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[C:%.*]]) {
+; CHECK-NEXT: [[C_NONZERO:%.*]] = icmp ne i8 [[C]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[C_NONZERO]])
+; CHECK-NEXT: ret i1 false
+;
+ %c_nonzero = icmp ne i8 %c, 0
+ call void @llvm.assume(i1 %c_nonzero)
+
+ %prod = mul nuw i8 %x, %c
+ %cmp = icmp ult i8 %prod, %x
+ ret i1 %cmp
+}
+
+define i1 @mul_nuw_nonzero_lhs_monotonic(i8 %x, i8 %c) {
+; CHECK-LABEL: define i1 @mul_nuw_nonzero_lhs_monotonic(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[C:%.*]]) {
+; CHECK-NEXT: [[C_NONZERO:%.*]] = icmp ne i8 [[X]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[C_NONZERO]])
+; CHECK-NEXT: ret i1 true
+;
+ %c_nonzero = icmp ne i8 %x, 0
+ call void @llvm.assume(i1 %c_nonzero)
+
+ %prod = mul nuw i8 %x, %c
+ %cmp = icmp uge i8 %prod, %c
+ ret i1 %cmp
+}
+
+define i1 @mul_nuw_nonzero_lhs_rhs_monotonic(i8 %x, i8 %c) {
+; CHECK-LABEL: define i1 @mul_nuw_nonzero_lhs_rhs_monotonic(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[C:%.*]]) {
+; CHECK-NEXT: [[C_NONZERO:%.*]] = icmp ne i8 [[C]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[C_NONZERO]])
+; CHECK-NEXT: [[X_NONZERO:%.*]] = icmp ne i8 [[X]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[X_NONZERO]])
+; CHECK-NEXT: ret i1 true
+;
+ %c_nonzero = icmp ne i8 %c, 0
+ call void @llvm.assume(i1 %c_nonzero)
+
+ %x_nonzero = icmp ne i8 %x, 0
+ call void @llvm.assume(i1 %x_nonzero)
+
+ %prod = mul nuw i8 %x, %c
+ %cmp = icmp uge i8 %prod, %x
+ ret i1 %cmp
+}
+
+
+define i1 @negative_mul_non_nsw(i8 %x, i8 %c) {
+; CHECK-LABEL: define i1 @negative_mul_non_nsw(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[C:%.*]]) {
+; CHECK-NEXT: [[C_NONZERO:%.*]] = icmp ne i8 [[C]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[C_NONZERO]])
+; CHECK-NEXT: [[X3:%.*]] = mul i8 [[X]], [[C]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp uge i8 [[X3]], [[X]]
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %c_nonzero = icmp ne i8 %c, 0
+ call void @llvm.assume(i1 %c_nonzero)
+
+ %prod = mul i8 %x, %c
+ %cmp = icmp uge i8 %prod, %x
+ ret i1 %cmp
+}
+
+define i1 @negative_mul_no_nonzero(i8 %x, i8 %c) {
+; CHECK-LABEL: define i1 @negative_mul_no_nonzero(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[C:%.*]]) {
+; CHECK-NEXT: [[X3:%.*]] = mul nsw i8 [[X]], [[C]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp uge i8 [[X3]], [[X]]
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %prod = mul nsw i8 %x, %c
+ %cmp = icmp uge i8 %prod, %x
+ ret i1 %cmp
+}
+
+define i1 @negative_mul_lhs_maybe_zero(i8 %x, i8 %c) {
+; CHECK-LABEL: define i1 @negative_mul_lhs_maybe_zero(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[C:%.*]]) {
+; CHECK-NEXT: [[C_NONZERO:%.*]] = icmp ne i8 [[C]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[C_NONZERO]])
+; CHECK-NEXT: [[X3:%.*]] = mul nuw i8 [[X]], [[C]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp uge i8 [[X3]], [[C]]
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %c_nonzero = icmp ne i8 %c, 0
+ call void @llvm.assume(i1 %c_nonzero)
+
+ %prod = mul nuw i8 %x, %c
+ %cmp = icmp uge i8 %prod, %c
+ ret i1 %cmp
+}
+
+define i1 @negative_mul_rhs_maybe_zero(i8 %x, i8 %c) {
+; CHECK-LABEL: define i1 @negative_mul_rhs_maybe_zero(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[C:%.*]]) {
+; CHECK-NEXT: [[C_NONZERO:%.*]] = icmp ne i8 [[X]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[C_NONZERO]])
+; CHECK-NEXT: [[X3:%.*]] = mul nuw i8 [[X]], [[C]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp uge i8 [[X3]], [[X]]
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %c_nonzero = icmp ne i8 %x, 0
+ call void @llvm.assume(i1 %c_nonzero)
+
+ %prod = mul nuw i8 %x, %c
+ %cmp = icmp uge i8 %prod, %x
+ ret i1 %cmp
+}
|
dtcxzyw
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM w/ some nits
| } | ||
| // X * Y >= X --> true | ||
| if (match(I, m_NUWMul(m_Value(X), m_Value(Y)))) { | ||
| if (isKnownNonZero(X, Q)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need the non-constant support?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is needed by assimp:
https://github.com/assimp/assimp/blob/2bce77f909b9f56c8c67cff33cc73a244505aa46/code/AssetLib/glTF2/glTF2Asset.inl#L897-L903
%switch.gep = getelementptr inbounds nuw [7 x i64], ptr @switch.table._ZN5glTF28Accessor6Sparse9PatchDataEj, i64 0, i64 %307
%switch.load = load i64, ptr %switch.gep, align 8
%308 = mul nuw nsw i64 %switch.load, %202
store i64 %308, ptr %20, align 8
%309 = icmp samesign ult i64 %308, %202
Proof: https://alive2.llvm.org/ce/z/T_ocLy This patch folds `X * C >= X` to `true` when `C` is known to be non-zero and `mul` is `nuw`. Folds for other math operators exist already: https://llvm-ir.godbolt.org/z/GKcYEf5Kb
b785b55 to
9007445
Compare
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/88/builds/8627 Here is the relevant piece of the build log for the reference |
Proof: https://alive2.llvm.org/ce/z/T_ocLy
Discovered in: rust-lang/rust#114386
This PR folds
X * C >= XtotruewhenCis known to be non-zero andmulisnuw.Folds for other math operators exist already: https://llvm-ir.godbolt.org/z/GKcYEf5Kb