Skip to content

[InstCombine] Fold (X & C1) | C2 into X & (C1 | C2) iff (X & C2) == C2 #76470

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

Merged
merged 2 commits into from
Dec 28, 2023

Conversation

dtcxzyw
Copy link
Member

@dtcxzyw dtcxzyw commented Dec 27, 2023

@llvmbot
Copy link
Member

llvmbot commented Dec 27, 2023

@llvm/pr-subscribers-llvm-transforms

Author: Yingwei Zheng (dtcxzyw)

Changes

Alive2: https://alive2.llvm.org/ce/z/VKJYaS


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

2 Files Affected:

  • (modified) llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp (+8)
  • (modified) llvm/test/Transforms/InstCombine/or.ll (+95)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 63b1e0f64a8824..6958418ba7f3fa 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -3872,6 +3872,14 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
     }
   }
 
+  // (X & C1) | C2 -> X & (C1 | C2) iff (X & C2) == C2
+  if (match(Op0, m_OneUse(m_And(m_Value(X), m_APInt(C1)))) &&
+      match(Op1, m_APInt(C2))) {
+    KnownBits KnownX = computeKnownBits(X, /*Depth*/ 0, &I);
+    if ((KnownX.One & *C2) == *C2)
+      return BinaryOperator::CreateAnd(X, ConstantInt::get(Ty, *C1 | *C2));
+  }
+
   return nullptr;
 }
 
diff --git a/llvm/test/Transforms/InstCombine/or.ll b/llvm/test/Transforms/InstCombine/or.ll
index 805546099398b4..010ef239744188 100644
--- a/llvm/test/Transforms/InstCombine/or.ll
+++ b/llvm/test/Transforms/InstCombine/or.ll
@@ -1636,3 +1636,98 @@ define i32 @assoc_cast_assoc_disjoint(i16 %x) {
   %c = or disjoint i32 %b, 65536
   ret i32 %c
 }
+
+; (X & C1) | C2 -> X & (C1 | C2) iff (X & C2) == C2
+define i32 @test_or_and_disjoint(i32 %a) {
+; CHECK-LABEL: @test_or_and_disjoint(
+; CHECK-NEXT:    [[A0:%.*]] = and i32 [[A:%.*]], 24
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[A0]], 8
+; CHECK-NEXT:    br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
+; CHECK:       if.then:
+; CHECK-NEXT:    [[A2:%.*]] = and i32 [[A]], 15
+; CHECK-NEXT:    ret i32 [[A2]]
+; CHECK:       if.else:
+; CHECK-NEXT:    ret i32 0
+;
+  %a0 = and i32 %a, 24
+  %cmp = icmp eq i32 %a0, 8
+  br i1 %cmp, label %if.then, label %if.else
+if.then:
+  %a1 = and i32 %a, 7
+  %a2 = or i32 %a1, 8
+  ret i32 %a2
+if.else:
+  ret i32 0
+}
+
+define i32 @test_or_and_mixed(i32 %a) {
+; CHECK-LABEL: @test_or_and_mixed(
+; CHECK-NEXT:    [[A0:%.*]] = and i32 [[A:%.*]], 27
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[A0]], 11
+; CHECK-NEXT:    br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
+; CHECK:       if.then:
+; CHECK-NEXT:    [[A2:%.*]] = and i32 [[A]], 15
+; CHECK-NEXT:    ret i32 [[A2]]
+; CHECK:       if.else:
+; CHECK-NEXT:    ret i32 0
+;
+  %a0 = and i32 %a, 27
+  %cmp = icmp eq i32 %a0, 11
+  br i1 %cmp, label %if.then, label %if.else
+if.then:
+  %a1 = and i32 %a, 7
+  %a2 = or i32 %a1, 11
+  ret i32 %a2
+if.else:
+  ret i32 0
+}
+
+; Negative tests
+
+define i32 @test_or_and_disjoint_fail(i32 %a) {
+; CHECK-LABEL: @test_or_and_disjoint_fail(
+; CHECK-NEXT:    [[A0:%.*]] = and i32 [[A:%.*]], 24
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[A0]], 16
+; CHECK-NEXT:    br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
+; CHECK:       if.then:
+; CHECK-NEXT:    [[A1:%.*]] = and i32 [[A]], 7
+; CHECK-NEXT:    [[A2:%.*]] = or disjoint i32 [[A1]], 8
+; CHECK-NEXT:    ret i32 [[A2]]
+; CHECK:       if.else:
+; CHECK-NEXT:    ret i32 0
+;
+  %a0 = and i32 %a, 24
+  %cmp = icmp eq i32 %a0, 16
+  br i1 %cmp, label %if.then, label %if.else
+if.then:
+  %a1 = and i32 %a, 7
+  %a2 = or i32 %a1, 8
+  ret i32 %a2
+if.else:
+  ret i32 0
+}
+
+define i32 @test_or_and_disjoint_multiuse(i32 %a) {
+; CHECK-LABEL: @test_or_and_disjoint_multiuse(
+; CHECK-NEXT:    [[A0:%.*]] = and i32 [[A:%.*]], 24
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[A0]], 8
+; CHECK-NEXT:    br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
+; CHECK:       if.then:
+; CHECK-NEXT:    [[A1:%.*]] = and i32 [[A]], 7
+; CHECK-NEXT:    call void @use(i32 [[A1]])
+; CHECK-NEXT:    [[A2:%.*]] = or disjoint i32 [[A1]], 8
+; CHECK-NEXT:    ret i32 [[A2]]
+; CHECK:       if.else:
+; CHECK-NEXT:    ret i32 0
+;
+  %a0 = and i32 %a, 24
+  %cmp = icmp eq i32 %a0, 8
+  br i1 %cmp, label %if.then, label %if.else
+if.then:
+  %a1 = and i32 %a, 7
+  call void @use(i32 %a1)
+  %a2 = or i32 %a1, 8
+  ret i32 %a2
+if.else:
+  ret i32 0
+}

dtcxzyw added a commit to dtcxzyw/llvm-opt-benchmark that referenced this pull request Dec 27, 2023
@nikic
Copy link
Contributor

nikic commented Dec 28, 2023

Does this have compile-time impact?

@dtcxzyw
Copy link
Member Author

dtcxzyw commented Dec 28, 2023

Does this have compile-time impact?

Compile-time impact: http://llvm-compile-time-tracker.com/compare.php?from=38c9390b59c4d2b9181614d6a909887497d3692f&to=b96af27a97818b41a127113f4d4313aa704b2469&stat=instructions:u

stage1-O3 stage1-ReleaseThinLTO stage1-ReleaseLTO-g stage1-O0-g stage2-O3 stage2-O0-g stage2-clang
+0.01% -0.00% -0.01% +0.03% -0.02% +0.01% -0.00%

Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

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

LGTM

@dtcxzyw dtcxzyw merged commit 7a1a476 into llvm:main Dec 28, 2023
@dtcxzyw dtcxzyw deleted the fold-or-and-with-knownbits branch December 28, 2023 12:47
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.

3 participants