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] Remove AllOnes fallbacks in getMaskedTypeForICmpPair() #104941

Merged
merged 1 commit into from
Aug 26, 2024

Conversation

nikic
Copy link
Contributor

@nikic nikic commented Aug 20, 2024

getMaskedTypeForICmpPair() tries to model non-and operands as x & -1. However, this can end up confusing the matching logic, by picking the -1 operand as the "common" operand, resulting in a successful, but useless, match. This is what causes commutation failures for some of the optimizations driven by this function.

Fix this by treating a match against -1 as a non-match.

@llvmbot
Copy link
Member

llvmbot commented Aug 20, 2024

@llvm/pr-subscribers-llvm-transforms

Author: Nikita Popov (nikic)

Changes

getMaskedTypeForICmpPair() tries to model non-and operands as x & -1. However, this can end up confusing the matching logic, by picking the -1 operand as the "common" operand, resulting in a successful, but useless, match. This is what causes commutation failures for some of the optimizations driven by this function.

Fix this by removing this -1 fallback entirely. We don't seem to have any test coverage that demonstrates why it would be needed.


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

2 Files Affected:

  • (modified) llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp (+33-50)
  • (modified) llvm/test/Transforms/InstCombine/bit-checks.ll (+8-12)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 2bba83b5cde3c7..e7f21a42105add 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -211,23 +211,18 @@ static std::optional<std::pair<unsigned, unsigned>> getMaskedTypeForICmpPair(
   // above.
   Value *L1 = LHS->getOperand(0);
   Value *L2 = LHS->getOperand(1);
-  Value *L11, *L12, *L21, *L22;
+  Value *L11 = nullptr, *L12 = nullptr, *L21 = nullptr, *L22 = nullptr;
   // Check whether the icmp can be decomposed into a bit test.
   if (decomposeBitTestICmp(L1, L2, PredL, L11, L12, L2)) {
     L21 = L22 = L1 = nullptr;
   } else {
     // Look for ANDs in the LHS icmp.
-    if (!match(L1, m_And(m_Value(L11), m_Value(L12)))) {
-      // Any icmp can be viewed as being trivially masked; if it allows us to
-      // remove one, it's worth it.
-      L11 = L1;
-      L12 = Constant::getAllOnesValue(L1->getType());
-    }
+    match(L1, m_And(m_Value(L11), m_Value(L12)));
+    match(L2, m_And(m_Value(L21), m_Value(L22)));
 
-    if (!match(L2, m_And(m_Value(L21), m_Value(L22)))) {
-      L21 = L2;
-      L22 = Constant::getAllOnesValue(L2->getType());
-    }
+    // Check that at least one and was found.
+    if (!L11 && !L21)
+      return std::nullopt;
   }
 
   // Bail if LHS was a icmp that can't be decomposed into an equality.
@@ -252,54 +247,42 @@ static std::optional<std::pair<unsigned, unsigned>> getMaskedTypeForICmpPair(
     R1 = nullptr;
     Ok = true;
   } else {
-    if (!match(R1, m_And(m_Value(R11), m_Value(R12)))) {
-      // As before, model no mask as a trivial mask if it'll let us do an
-      // optimization.
-      R11 = R1;
-      R12 = Constant::getAllOnesValue(R1->getType());
+    if (match(R1, m_And(m_Value(R11), m_Value(R12)))) {
+      if (R11 == L11 || R11 == L12 || R11 == L21 || R11 == L22) {
+        A = R11;
+        D = R12;
+        E = R2;
+        Ok = true;
+      } else if (R12 == L11 || R12 == L12 || R12 == L21 || R12 == L22) {
+        A = R12;
+        D = R11;
+        E = R2;
+        Ok = true;
+      }
     }
 
-    if (R11 == L11 || R11 == L12 || R11 == L21 || R11 == L22) {
-      A = R11;
-      D = R12;
-      E = R2;
-      Ok = true;
-    } else if (R12 == L11 || R12 == L12 || R12 == L21 || R12 == L22) {
-      A = R12;
-      D = R11;
-      E = R2;
-      Ok = true;
+    if (match(R2, m_And(m_Value(R11), m_Value(R12)))) {
+      if (R11 == L11 || R11 == L12 || R11 == L21 || R11 == L22) {
+        A = R11;
+        D = R12;
+        E = R1;
+        Ok = true;
+      } else if (R12 == L11 || R12 == L12 || R12 == L21 || R12 == L22) {
+        A = R12;
+        D = R11;
+        E = R1;
+        Ok = true;
+      }
     }
+
+    if (!Ok)
+      return std::nullopt;
   }
 
   // Bail if RHS was a icmp that can't be decomposed into an equality.
   if (!ICmpInst::isEquality(PredR))
     return std::nullopt;
 
-  // Look for ANDs on the right side of the RHS icmp.
-  if (!Ok) {
-    if (!match(R2, m_And(m_Value(R11), m_Value(R12)))) {
-      R11 = R2;
-      R12 = Constant::getAllOnesValue(R2->getType());
-    }
-
-    if (R11 == L11 || R11 == L12 || R11 == L21 || R11 == L22) {
-      A = R11;
-      D = R12;
-      E = R1;
-      Ok = true;
-    } else if (R12 == L11 || R12 == L12 || R12 == L21 || R12 == L22) {
-      A = R12;
-      D = R11;
-      E = R1;
-      Ok = true;
-    } else {
-      return std::nullopt;
-    }
-
-    assert(Ok && "Failed to find AND on the right side of the RHS icmp.");
-  }
-
   if (L11 == A) {
     B = L12;
     C = L2;
diff --git a/llvm/test/Transforms/InstCombine/bit-checks.ll b/llvm/test/Transforms/InstCombine/bit-checks.ll
index c7e1fbb8945493..906e57e0979635 100644
--- a/llvm/test/Transforms/InstCombine/bit-checks.ll
+++ b/llvm/test/Transforms/InstCombine/bit-checks.ll
@@ -809,12 +809,10 @@ define i32 @main7a_logical(i32 %argc, i32 %argc2, i32 %argc3) {
 define i32 @main7b(i32 %argc, i32 %argc2, i32 %argc3x) {
 ; CHECK-LABEL: @main7b(
 ; CHECK-NEXT:    [[ARGC3:%.*]] = mul i32 [[ARGC3X:%.*]], 42
-; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[ARGC:%.*]], [[ARGC2:%.*]]
-; CHECK-NEXT:    [[TOBOOL:%.*]] = icmp ne i32 [[AND1]], [[ARGC2]]
-; CHECK-NEXT:    [[AND2:%.*]] = and i32 [[ARGC3]], [[ARGC]]
-; CHECK-NEXT:    [[TOBOOL3:%.*]] = icmp ne i32 [[ARGC3]], [[AND2]]
-; CHECK-NEXT:    [[AND_COND_NOT:%.*]] = or i1 [[TOBOOL]], [[TOBOOL3]]
-; CHECK-NEXT:    [[STOREMERGE:%.*]] = zext i1 [[AND_COND_NOT]] to i32
+; CHECK-NEXT:    [[TMP1:%.*]] = or i32 [[ARGC3]], [[ARGC2:%.*]]
+; CHECK-NEXT:    [[TMP2:%.*]] = and i32 [[TMP1]], [[ARGC:%.*]]
+; CHECK-NEXT:    [[AND_COND:%.*]] = icmp ne i32 [[TMP2]], [[TMP1]]
+; CHECK-NEXT:    [[STOREMERGE:%.*]] = zext i1 [[AND_COND]] to i32
 ; CHECK-NEXT:    ret i32 [[STOREMERGE]]
 ;
   %argc3 = mul i32 %argc3x, 42 ; thwart complexity-based canonicalization
@@ -850,12 +848,10 @@ define i32 @main7b_logical(i32 %argc, i32 %argc2, i32 %argc3) {
 define i32 @main7c(i32 %argc, i32 %argc2, i32 %argc3x) {
 ; CHECK-LABEL: @main7c(
 ; CHECK-NEXT:    [[ARGC3:%.*]] = mul i32 [[ARGC3X:%.*]], 42
-; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[ARGC2:%.*]], [[ARGC:%.*]]
-; CHECK-NEXT:    [[TOBOOL:%.*]] = icmp ne i32 [[AND1]], [[ARGC2]]
-; CHECK-NEXT:    [[AND2:%.*]] = and i32 [[ARGC3]], [[ARGC]]
-; CHECK-NEXT:    [[TOBOOL3:%.*]] = icmp ne i32 [[ARGC3]], [[AND2]]
-; CHECK-NEXT:    [[AND_COND_NOT:%.*]] = or i1 [[TOBOOL]], [[TOBOOL3]]
-; CHECK-NEXT:    [[STOREMERGE:%.*]] = zext i1 [[AND_COND_NOT]] to i32
+; CHECK-NEXT:    [[TMP1:%.*]] = or i32 [[ARGC3]], [[ARGC2:%.*]]
+; CHECK-NEXT:    [[TMP2:%.*]] = and i32 [[TMP1]], [[ARGC:%.*]]
+; CHECK-NEXT:    [[AND_COND:%.*]] = icmp ne i32 [[TMP2]], [[TMP1]]
+; CHECK-NEXT:    [[STOREMERGE:%.*]] = zext i1 [[AND_COND]] to i32
 ; CHECK-NEXT:    ret i32 [[STOREMERGE]]
 ;
   %argc3 = mul i32 %argc3x, 42 ; thwart complexity-based canonicalization

dtcxzyw added a commit to dtcxzyw/llvm-opt-benchmark that referenced this pull request Aug 20, 2024
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.

Regression:

define i1 @test(i32 %104, i32 %106, i32 noundef %.fr.i) {
  %206 = icmp ne i32 %104, 0
  %207 = icmp ne i32 %106, 63
  %or.cond19.i = select i1 %206, i1 true, i1 %207
  %208 = icmp ne i32 %.fr.i, 0
  %or.cond21.i = or i1 %or.cond19.i, %208
  ret i1 %or.cond21.i
}

should be folded into

define i1 @test(i32 %0, i32 %1, i32 noundef %.fr.i) {
  %3 = icmp ne i32 %1, 63
  %4 = or i32 %0, %.fr.i
  %5 = icmp ne i32 %4, 0
  %or.cond21.i = select i1 %5, i1 true, i1 %3
  ret i1 %or.cond21.i
}


assert(Ok && "Failed to find AND on the right side of the RHS icmp.");
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Would a more direct fix for the specific issue just be Ok &= !match(A, m_AllOnes())?

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 went with this approach.

Annoyingly, @dtcxzyw's case does actually need to match on -1 here. I ended up handling that case separately via 32679e1.

@nikic
Copy link
Contributor Author

nikic commented Aug 22, 2024

Another test case:

define i1 @test2(i64 %a) {
  %cmp1 = icmp ne i64 %a, 0
  %a.mask = and i64 %a, 9223372036854775807
  %cmp2 = icmp eq i64 %a.mask, 0
  %and = and i1 %cmp1, %cmp2 
  ret i1 %and 
}

define i1 @test2(i64 %a) {
  %and = icmp eq i64 %a, -9223372036854775808
  ret i1 %and
}

nikic added a commit that referenced this pull request Aug 22, 2024
Tests for cases that would have been regressed by
#104941.
getMaskedTypeForICmpPair() tries to model non-and operands as
x & -1. However, this can end up confusing the matching logic,
by picking the -1 operand as the "common" operand, resulting in
a successful, but useless, match. This is what causes commutation
failures for some of the optimizations driven by this function.

Fix this by removing this -1 fallback entirely. We don't seem to
have any test coverage that demonstrates why it would be needed.
Copy link
Contributor

@goldsteinn goldsteinn left a comment

Choose a reason for hiding this comment

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

LGTM

Edit: Assuming @dtcxzyw's benchmarks don't show regression anymore.

dtcxzyw added a commit to dtcxzyw/llvm-opt-benchmark that referenced this pull request Aug 22, 2024
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.

LG

cjdb pushed a commit to cjdb/llvm-project that referenced this pull request Aug 23, 2024
@nikic nikic merged commit 28fe6dd into llvm:main Aug 26, 2024
6 of 8 checks passed
@nikic nikic deleted the log-op-fix branch August 26, 2024 07:55
dmpolukhin pushed a commit to dmpolukhin/llvm-project that referenced this pull request Sep 2, 2024
dmpolukhin pushed a commit to dmpolukhin/llvm-project that referenced this pull request Sep 2, 2024
…lvm#104941)

getMaskedTypeForICmpPair() tries to model non-and operands as x & -1.
However, this can end up confusing the matching logic, by picking the -1
operand as the "common" operand, resulting in a successful, but useless,
match. This is what causes commutation failures for some of the
optimizations driven by this function.

Fix this by treating a match against -1 as a non-match.
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.

4 participants