Skip to content
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

[InstCombine] Fold icmp in select to smin #87157

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

Krishna-13-cyber
Copy link
Contributor

@Krishna-13-cyber Krishna-13-cyber commented Mar 30, 2024

Intends to solve #85844
Alive2 Proofs: https://alive2.llvm.org/ce/z/FRNgbc

@llvmbot
Copy link
Collaborator

llvmbot commented Mar 30, 2024

@llvm/pr-subscribers-llvm-transforms

Author: Krishna Narayanan (Krishna-13-cyber)

Changes

Intends to solve #85844


Full diff: https://github.com/llvm/llvm-project/pull/87157.diff

2 Files Affected:

  • (modified) llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp (+22)
  • (modified) llvm/test/Transforms/InstCombine/icmp-select.ll (+13)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 9ab2bd8f70aa15..4e58ba5f01df1c 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -1687,6 +1687,25 @@ static Value *foldSelectInstWithICmpConst(SelectInst &SI, ICmpInst *ICI,
   return nullptr;
 }
 
+static Value *foldSelectInstWithICmpOr(SelectInst &SI, ICmpInst *ICI,
+                                       InstCombiner::BuilderTy &Builder) {
+  /* a > -1 ? 1 : (a | 3) --> smin(1, a | 3) */
+  Type *Ty = SI.getType();
+  Value *TVal = SI.getTrueValue();
+  Value *FVal = SI.getFalseValue();
+  Constant *One = ConstantInt::get(Ty, 1);
+  CmpInst::Predicate Pred = ICI->getPredicate();
+  Value *A = ICI->getOperand(0);
+  const APInt *Cmp;
+
+  if (!match(TVal, m_One()) || !match(FVal, m_Or(m_Value(A), m_SpecificInt(3))))
+    return nullptr;
+
+  if (Pred == ICmpInst::ICMP_SGT && *Cmp == -1 && match(TVal, m_One()) &&
+      match(FVal, m_Or(m_Value(A), m_SpecificInt(3))))
+    return Builder.CreateBinaryIntrinsic(Intrinsic::smin, One, FVal);
+}
+
 /// Visit a SelectInst that has an ICmpInst as its first operand.
 Instruction *InstCombinerImpl::foldSelectInstWithICmp(SelectInst &SI,
                                                       ICmpInst *ICI) {
@@ -1700,6 +1719,9 @@ Instruction *InstCombinerImpl::foldSelectInstWithICmp(SelectInst &SI,
   if (Value *V = foldSelectInstWithICmpConst(SI, ICI, Builder))
     return replaceInstUsesWith(SI, V);
 
+  if (Value *V = foldSelectInstWithICmpOr(SI, ICI, Builder))
+    return replaceInstUsesWith(SI, V);
+
   if (Value *V = canonicalizeClampLike(SI, *ICI, Builder))
     return replaceInstUsesWith(SI, V);
 
diff --git a/llvm/test/Transforms/InstCombine/icmp-select.ll b/llvm/test/Transforms/InstCombine/icmp-select.ll
index 59d2a1b165c0f8..80341278b58f3a 100644
--- a/llvm/test/Transforms/InstCombine/icmp-select.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-select.ll
@@ -628,3 +628,16 @@ define i1 @icmp_slt_select(i1 %cond, i32 %a, i32 %b) {
   %res = icmp slt i32 %lhs, %rhs
   ret i1 %res
 }
+
+define i8 @icmp_slt_select_or(i8 %inl) {
+; CHECK-LABEL: @icmp_slt_select_or(
+; CHECK-NEXT:    [[OR:%.*]] = or i8 [[INL:%.*]], 3
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[INL]], -1
+; CHECK-NEXT:    [[S:%.*]] = select i1 [[CMP]], i8 1, i8 [[OR]]
+; CHECK-NEXT:    ret i8 [[S]]
+;
+  %or = or i8 %inl, 3
+  %cmp = icmp sgt i8 %inl, -1
+  %s = select i1 %cmp, i8 1, i8 %or
+  ret i8 %s
+}

Copy link
Member

@XChy XChy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue is a specifc case, you need to provide a generalized pattern at least. See also https://llvm.org/docs/InstCombineContributorGuide.html#generalization

@Krishna-13-cyber
Copy link
Contributor Author

The issue is a specifc case, you need to provide a generalized pattern at least. See also https://llvm.org/docs/InstCombineContributorGuide.html#generalization

Got it, I will make the required changes.

@goldsteinn goldsteinn requested a review from dtcxzyw April 3, 2024 18:33
@goldsteinn
Copy link
Contributor

Your proofs are incomplete, you only prove the specific constants you chose (and in fact your code does not hold for value <= 0).
For more info on how to write robust proofs see: https://llvm.org/docs/InstCombineContributorGuide.html#proofs
Here is an example: https://alive2.llvm.org/ce/z/sm9_gy

InstCombiner::BuilderTy &Builder) {

// a > -1 ? 1 : (a | value) --> smin(1, a | value)
// a >= 0 ? 1 : (a | value) --> smin(1, a | value)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to handle a >= 0, it is canonicalized to a > -1.

Constant *Zero = Constant::getNullValue(Ty);
CmpInst::Predicate Pred = ICI->getPredicate();

if (!match(B, m_APIntAllowUndef(Cmp)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are going to allow undef, please add a vector test with some undef elements.

Copy link
Contributor

@goldsteinn goldsteinn Apr 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But either way, this should just be if(!match(B, m_AllOnes())


// Swap TVal, FVal for Inverse
if (Pred == ICmpInst::ICMP_SLT || Pred == ICmpInst::ICMP_SLE)
std::swap(TVal, FVal);
Copy link
Contributor

@goldsteinn goldsteinn Apr 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think you are missing a Pred = ICmpInst::getInversePred(Pred) here too.

return nullptr;

if (((Pred == ICmpInst::ICMP_SGT && *Cmp == -1) ||
(Pred == ICmpInst::ICMP_SGE && *Cmp == 0)))
Copy link
Contributor

@goldsteinn goldsteinn Apr 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned above, no need for the SGE / *Cmp==0 case.
You need it for the swapped slt case.

static Value *foldSelectInstWithICmpOr(SelectInst &SI, ICmpInst *ICI,
InstCombiner::BuilderTy &Builder) {

// a > -1 ? 1 : (a | value) --> smin(1, a | value)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment should indicate value must be strictly positive.

if (match(TVal, m_Zero()))
return Builder.CreateBinaryIntrinsic(Intrinsic::smin, Zero, FVal);
if (match(TVal, m_AllOnes()))
return Builder.CreateBinaryIntrinsic(Intrinsic::smin, NegOne, FVal);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be simplified to:

if(match(TVal, m_One()) || match(TVal, m_AllOnes()) || match(TVal, m_Zero())) {
  return Builder.Create...(smin, TVal, FVal);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also your comment / proofs only seem to cover the TVal == 1 case, can you please update them to cover the other two cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the same link has tests with TVal for -1, 1 and 0. I will check again and update that accordingly.

!match(FVal, m_Or(m_Value(A), m_StrictlyPositive())))
return nullptr;

if (((Pred == ICmpInst::ICMP_SGT && *Cmp == -1) ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cmp->isAllOnes() and Cmp->isZero()

@dtcxzyw dtcxzyw marked this pull request as ready for review April 4, 2024 07:14
@Krishna-13-cyber
Copy link
Contributor Author

Your proofs are incomplete, you only prove the specific constants you chose (and in fact your code does not hold for value <= 0). For more info on how to write robust proofs see: https://llvm.org/docs/InstCombineContributorGuide.html#proofs Here is an example: https://alive2.llvm.org/ce/z/sm9_gy

Thanks a lot for the review, yes I did refer the guide, incorporated these proofs as well.
Actually this fold does'nt go well for negative values https://alive2.llvm.org/ce/z/geeSb6, due to which I constrained it to strictly positive. I am trying to this generalise for neg values as well by some means.
We have something like this https://alive2.llvm.org/ce/z/NrmVs8 which goes on similar basis for neg values(logic wise).

!(match(FVal, m_Or(m_Value(A), m_Value(C)))))
return nullptr;

if (!match(C, m_StrictlyPositive()))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whats the point if using C as an intermediate here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, there was no specific reason/point to do this.

if (....!(match(FVal, m_Or(m_Value(A), m_Value(C)))))
    return nullptr;
if (!match(C, m_StrictlyPositive()))
    return nullptr;
if (...... !(match(FVal, m_Or(m_Value(A), m_StrictlyPositive()))))

Either of them failed to pass the llvm.assume test cases as the expression don't fold, so tried using the earlier approach.
As this fold is only applicable for positive values, we cannot match any other values other than this as it would lead to invalid transform.
Not sure what constraint or needful I am missing out to handle these generic testcases, would need some assistance in this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

m_StrictlyPositive matches an constant that is positive. To check certain bits in a non-constant value use the ValueTracking interface. In this case you would use isKnownStrictlyPositive.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants