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] Simplify select if it combinated and/or/xor #73362

Merged
merged 2 commits into from
Apr 3, 2024

Conversation

ParkHanbum
Copy link
Contributor

@ParkHanbum ParkHanbum commented Nov 24, 2023

and/or/xor operations can each be changed to sum of logical
operations including operators other than themselves.

x&y -> (x|y) ^ (x^y)
x|y -> (x&y) | (x^y)
x^y -> (x|y) ^ (x&y)

if left of condition of SelectInst is and/or/xor logical
operation and right is equal to 0, -1, or a constant, and
if TrueVal consist of and/or/xor logical operation then we
can optimize this case.

This patch implements this combination.

Proof: https://alive2.llvm.org/ce/z/WW8iRR

Fixed #71792.

@llvmbot
Copy link
Member

llvmbot commented Nov 24, 2023

@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-llvm-analysis

Author: hanbum (ParkHanbum)

Changes
  • (X & Y) == 0 ? X | Y : X ^ Y --> X ^ Y
  • (X | Y) == 0 ? X & Y : X ^ Y --> X ^ Y
  • (X ^ Y) == 0 ? X | Y : X & Y --> X & Y

certain combinations of bitwise operators can be simplified to returning the value of the True or False clause.

Proofs : https://alive2.llvm.org/ce/z/9jidcw

Fixed llvm#71792.

hello! It's an honor to send a PR to LLVM!
thanks for review first!

I'm sending this to get your opinion on whether it's okay to write a patch this way.


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

1 Files Affected:

  • (modified) llvm/lib/Analysis/InstructionSimplify.cpp (+67)
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 7ef2ffd23b95e76..a69de0da8b6d046 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -4425,6 +4425,48 @@ Value *llvm::simplifyWithOpReplaced(Value *V, Value *Op, Value *RepOp,
                                   RecursionLimit);
 }
 
+/// Try simplifying the selection command when condition, trueValue, and
+/// falseValue are combinations of special bitwise operators.
+static Value *simplifySelectBitTestSpec(Value *TrueVal, Value *FalseVal,
+                                        Value *X, Value *Y,
+                                        bool TrueWhenUnset) {
+  // (X & Y) == 0 ? X | Y : X ^ Y   --> X ^ Y
+  // (X & Y) == 0 ? X ^ Y : X | Y   --> X | Y
+  // (X & Y) == 1 ? X | Y : X ^ Y   --> X | Y
+  // (X & Y) == 1 ? X ^ Y : X | Y   --> X ^ Y
+  if (match(TrueVal, m_Or(m_Specific(X), m_Specific(Y))) &&
+      match(FalseVal, m_Xor(m_Specific(X), m_Specific(Y)))) {
+    return TrueWhenUnset ? TrueVal : FalseVal;
+  } else if (match(FalseVal, m_Or(m_Specific(X), m_Specific(Y))) &&
+             match(TrueVal, m_Xor(m_Specific(X), m_Specific(Y)))) {
+    return TrueWhenUnset ? TrueVal : FalseVal;
+  }
+  // (X | Y) == 0 ? X & Y : X ^ Y   --> X ^ Y
+  // (X | Y) == 0 ? X ^ Y : X & Y   --> X & Y
+  // (X | Y) == 1 ? X & Y : X ^ Y   --> X & Y
+  // (X | Y) == 1 ? X ^ Y : X & Y   --> X ^ Y
+  if (match(TrueVal, m_And(m_Specific(X), m_Specific(Y))) &&
+      match(FalseVal, m_Xor(m_Specific(X), m_Specific(Y)))) {
+    return TrueWhenUnset ? TrueVal : FalseVal;
+  } else if (match(FalseVal, m_And(m_Specific(X), m_Specific(Y))) &&
+             match(TrueVal, m_Xor(m_Specific(X), m_Specific(Y)))) {
+    return TrueWhenUnset ? TrueVal : FalseVal;
+  }
+  // (X ^ Y) == 0 ? X | Y : X & Y   --> X & Y
+  // (X ^ Y) == 0 ? X & Y : X | Y   --> X | Y
+  // (X ^ Y) == 1 ? X | Y : X & Y   --> X | Y
+  // (X ^ Y) == 1 ? X & Y : X | Y   --> X & Y
+  if (match(TrueVal, m_Or(m_Specific(X), m_Specific(Y))) &&
+      match(FalseVal, m_Xor(m_Specific(X), m_Specific(Y)))) {
+    return TrueWhenUnset ? TrueVal : FalseVal;
+  } else if (match(FalseVal, m_Or(m_Specific(X), m_Specific(Y))) &&
+             match(TrueVal, m_Xor(m_Specific(X), m_Specific(Y)))) {
+    return TrueWhenUnset ? TrueVal : FalseVal;
+  }
+
+  return nullptr;
+}
+
 /// Try to simplify a select instruction when its condition operand is an
 /// integer comparison where one operand of the compare is a constant.
 static Value *simplifySelectBitTest(Value *TrueVal, Value *FalseVal, Value *X,
@@ -4572,12 +4614,37 @@ static Value *simplifySelectWithICmpCond(Value *CondVal, Value *TrueVal,
                                          unsigned MaxRecurse) {
   ICmpInst::Predicate Pred;
   Value *CmpLHS, *CmpRHS;
+  const APInt *C;
   if (!match(CondVal, m_ICmp(Pred, m_Value(CmpLHS), m_Value(CmpRHS))))
     return nullptr;
 
   if (Value *V = simplifyCmpSelOfMaxMin(CmpLHS, CmpRHS, Pred, TrueVal, FalseVal))
     return V;
 
+  if (ICmpInst::isEquality(Pred) && match(CmpRHS, m_APInt(C)) &&
+      (C->isZero() | C->isOne())) {
+    bool which;
+    Value *X, *Y;
+
+    if (Pred == ICmpInst::ICMP_EQ)
+      which = C->isZero() ? false : true;
+    else
+      which = C->isZero() ? true : false;
+
+    if (match(CmpLHS, m_And(m_Value(X), m_Value(Y)))) {
+      if (Value *V = simplifySelectBitTestSpec(TrueVal, FalseVal, X, Y, which))
+        return V;
+    }
+    if (match(CmpLHS, m_Or(m_Value(X), m_Value(Y)))) {
+      if (Value *V = simplifySelectBitTestSpec(TrueVal, FalseVal, X, Y, which))
+        return V;
+    }
+    if (match(CmpLHS, m_Xor(m_Value(X), m_Value(Y)))) {
+      if (Value *V = simplifySelectBitTestSpec(TrueVal, FalseVal, X, Y, which))
+        return V;
+    }
+  }
+
   // Canonicalize ne to eq predicate.
   if (Pred == ICmpInst::ICMP_NE) {
     Pred = ICmpInst::ICMP_EQ;

@dtcxzyw dtcxzyw requested a review from goldsteinn November 26, 2023 07:55
@goldsteinn goldsteinn changed the title [InstCombine] Simplify select if it combinated and/or/xor [InstSimplify] Simplify select if it combinated and/or/xor Nov 26, 2023
@ParkHanbum ParkHanbum force-pushed the issue_71792_ branch 2 times, most recently from 44486d2 to 6539259 Compare November 28, 2023 12:22
@ParkHanbum
Copy link
Contributor Author

I have mistake while resolve conflict. sorry.

@ParkHanbum ParkHanbum requested a review from goldsteinn December 1, 2023 18:13
Copy link

github-actions bot commented Dec 6, 2023

✅ With the latest revision this PR passed the C/C++ code formatter.

goldsteinn added a commit to goldsteinn/llvm-project that referenced this pull request Mar 26, 2024
Just a missing matcher that came up in llvm#73362
goldsteinn added a commit to goldsteinn/llvm-project that referenced this pull request Mar 26, 2024
Just a missing matcher that came up in llvm#73362
goldsteinn added a commit that referenced this pull request Mar 26, 2024
Just a missing matcher that came up in #73362

Closes #86632
`and/or/xor` operations can each be changed to sum of logical
operations including operators other than themselves.

 `x&y -> (x|y) ^ (x^y)`
 `x|y -> (x&y) | (x^y)`
 `x^y -> (x|y) ^ (x&y)`

This commit contains to test these cases.

Proof: https://alive2.llvm.org/ce/z/WW8iRR
@goldsteinn
Copy link
Contributor

LGTM.

@ParkHanbum
Copy link
Contributor Author

@goldsteinn thanks for review. I wish I could take TODOs.

@goldsteinn
Copy link
Contributor

goldsteinn commented Mar 28, 2024

@goldsteinn thanks for review. I wish I could take TODOs.

You can, I think the way to do this is actually to handle this case in computeKnownBitsFromCmp.

We currently have:

 else if (match(LHS, m_And(m_V, m_APInt(Mask))) &&
               match(RHS, m_APInt(C))) {
      // For one bits in Mask, we can propagate bits from C to V.
      Known.Zero |= ~*C & *Mask;
      Known.One |= *C & *Mask;
      // assume(V | Mask = C)
    }

But we could (and should) also have:

 else if (match(LHS, m_c_And(m_V, m_Value()) &&
               match(RHS, m_APInt(C))) {
      Known.One |= *C;
   }

Likewise for the or case:

 else if (match(LHS, m_c_Or(m_V, m_Value()) &&
               match(RHS, m_APInt(C))) {
      Known.Zero |= ~*C;
   }

The only case that requires special logic is xor where we can't actually deduce any bits, but we
can deduce non-common bits.

That case I think you could handle here in a future patch.

Let me know if you are going to pursue a patch in computeKnownBitsFromCmp.

Edit: Maybe wait a bit, I looked at this a bit and it creates some bugs related to #87015,
so until that is resolved, not much use.

@goldsteinn
Copy link
Contributor

@dtcxzyw can you provide a second review of this?

@goldsteinn
Copy link
Contributor

goldsteinn commented Mar 29, 2024

@goldsteinn thanks for review. I wish I could take TODOs.

You can, I think the way to do this is actually to handle this case in computeKnownBitsFromCmp.

We currently have:

 else if (match(LHS, m_And(m_V, m_APInt(Mask))) &&
               match(RHS, m_APInt(C))) {
      // For one bits in Mask, we can propagate bits from C to V.
      Known.Zero |= ~*C & *Mask;
      Known.One |= *C & *Mask;
      // assume(V | Mask = C)
    }

But we could (and should) also have:

 else if (match(LHS, m_c_And(m_V, m_Value()) &&
               match(RHS, m_APInt(C))) {
      Known.One |= *C;
   }

Likewise for the or case:

 else if (match(LHS, m_c_Or(m_V, m_Value()) &&
               match(RHS, m_APInt(C))) {
      Known.Zero |= ~*C;
   }

The only case that requires special logic is xor where we can't actually deduce any bits, but we can deduce non-common bits.

That case I think you could handle here in a future patch.

Let me know if you are going to pursue a patch in computeKnownBitsFromCmp.

Edit: Maybe wait a bit, I looked at this a bit and it creates some bugs related to #87015, so until that is resolved, not much use.

Think its a non-issue actually. @ParkHanbum lmk if you want to make a patch for this.

`and/or/xor` operations can each be changed to sum of logical
operations including operators other than themselves.

 `x&y -> (x|y) ^ (x^y)`
 `x|y -> (x&y) | (x^y)`
 `x^y -> (x|y) ^ (x&y)`

if left of condition of `SelectInst` is `and/or/xor` logical
operation and right is equal to `0, -1`, or a `constant`, and
if `TrueVal` consist of `and/or/xor` logical operation then we
can optimize this case.

This patch implements this combination.

Proof: https://alive2.llvm.org/ce/z/WW8iRR
Copy link
Member

@dtcxzyw dtcxzyw left a comment

Choose a reason for hiding this comment

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

LGTM. BTW the current implementation is a bit larger than the first version I approved. I am not sure whether it is an over-generalization. So I will leave the final decision to @goldsteinn and @nikic.

@goldsteinn
Copy link
Contributor

LGTM

@goldsteinn
Copy link
Contributor

@ParkHanbum do you have commit access or do you need me to merge for you?

@ParkHanbum
Copy link
Contributor Author

@goldsteinn I don't remember receiving any commit access. can I ask you why you asking that to me because I'm newbie.

@dtcxzyw dtcxzyw merged commit 4ef22fc into llvm:main Apr 3, 2024
4 checks passed
goldsteinn added a commit to goldsteinn/llvm-project that referenced this pull request Jan 5, 2025
…d simple cases.

The original intent of the folds (from llvm#71792) where to handle selects
where the condition was a bitwise operator compared with zero.

During review of the original PR (llvm#73362), however, we ended up over
generalizing at the expense of code complexity, and ironically in a
way that didn't actually fix the issue as reported.

The goal of this PR is to simplify the code and only handle the
compares with zero cases that actually show up in the real world.

New code handles three cases:

1) `X & Y == 0` implies `X | Y == X ^ Y` thus:
    - `X & Y == 0 ? X |/^/+ Y : X |/^/+ Y` -> `X |/^/+ Y` (the false arm)
    - https://alive2.llvm.org/ce/z/jjcduh

2) `X | Y == 0` implies `X == Y == 0` thus for `Op0` and `Op1` s.t
   `0 Op0 0 == 0 Op1 0 == 0`:
    - `X & Y == 0 ? X Op0 Y : X Op1 Y` -> `X Op1 Y`
    - `X & Y == 0 ? 0 : X Op1 Y` -> `X Op1 Y`
    - https://alive2.llvm.org/ce/z/RBuFQE

3) `X ^ Y == 0` (`X == Y`) implies `X | Y == X & Y`:
    - `X ^ Y == 0 ? X | Y : X & Y` -> `X & Y`
    - `X ^ Y == 0 ? X & Y : X | Y` -> `X | Y`
    - https://alive2.llvm.org/ce/z/SJskbz
@ParkHanbum ParkHanbum deleted the issue_71792_ branch January 28, 2025 17:35
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.

Simplification Comparison for (a | b) ? (a ^ b) : (a & b) etc. (Clang13 vs Clang trunk)
5 participants