[llvm] fad6375 - [InstCombine] Fold xor of bittests into bittest of xor'd value (#125676)

via llvm-commits llvm-commits at lists.llvm.org
Tue Feb 4 13:38:08 PST 2025


Author: Yingwei Zheng
Date: 2025-02-05T05:38:04+08:00
New Revision: fad6375428807fad466a176688e8f450ec4cab44

URL: https://github.com/llvm/llvm-project/commit/fad6375428807fad466a176688e8f450ec4cab44
DIFF: https://github.com/llvm/llvm-project/commit/fad6375428807fad466a176688e8f450ec4cab44.diff

LOG: [InstCombine] Fold xor of bittests into bittest of xor'd value (#125676)

Motivating case:
https://github.com/llvm/llvm-project/blob/64927af52a3bedf2b20d6cdd98bb47d9bba630f9/llvm/lib/Analysis/ValueTracking.cpp#L8600-L8602

It is translated into `xor (X & 2) != 0, (Y & 2) != 0`.
Alive2: https://alive2.llvm.org/ce/z/dJehZ8

Added: 
    

Modified: 
    llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
    llvm/test/Transforms/InstCombine/xor-icmps.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index ca8a20b4b7312d1..8701f7c28a39fc4 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -4150,9 +4150,6 @@ Value *InstCombinerImpl::foldXorOfICmps(ICmpInst *LHS, ICmpInst *RHS,
     }
   }
 
-  // TODO: This can be generalized to compares of non-signbits using
-  // decomposeBitTestICmp(). It could be enhanced more by using (something like)
-  // foldLogOpOfMaskedICmps().
   const APInt *LC, *RC;
   if (match(LHS1, m_APInt(LC)) && match(RHS1, m_APInt(RC)) &&
       LHS0->getType() == RHS0->getType() &&
@@ -4200,6 +4197,21 @@ Value *InstCombinerImpl::foldXorOfICmps(ICmpInst *LHS, ICmpInst *RHS,
           }
         }
     }
+
+    // Fold (icmp eq/ne (X & Pow2), 0) ^ (icmp eq/ne (Y & Pow2), 0) into
+    // (icmp eq/ne ((X ^ Y) & Pow2), 0)
+    Value *X, *Y;
+    const APInt *Mask;
+    if (ICmpInst::isEquality(PredL) && ICmpInst::isEquality(PredR) &&
+        LC->isZero() && RC->isZero() && LHS->hasOneUse() && RHS->hasOneUse() &&
+        match(LHS0, m_And(m_Value(X), m_Power2(Mask))) &&
+        match(RHS0, m_And(m_Value(Y), m_SpecificInt(*Mask)))) {
+      Value *Xor = Builder.CreateXor(X, Y);
+      Value *And = Builder.CreateAnd(Xor, *Mask);
+      return Builder.CreateICmp(PredL == PredR ? ICmpInst::ICMP_NE
+                                               : ICmpInst::ICMP_EQ,
+                                And, ConstantInt::getNullValue(Xor->getType()));
+    }
   }
 
   // Instead of trying to imitate the folds for and/or, decompose this 'xor'

diff  --git a/llvm/test/Transforms/InstCombine/xor-icmps.ll b/llvm/test/Transforms/InstCombine/xor-icmps.ll
index 0384c1aa184b824..382355cae7694d3 100644
--- a/llvm/test/Transforms/InstCombine/xor-icmps.ll
+++ b/llvm/test/Transforms/InstCombine/xor-icmps.ll
@@ -319,3 +319,158 @@ define i1 @xor_icmp_to_icmp_add_multiuse2(i32 %a) {
   %cmp3 = xor i1 %cmp, %cmp1
   ret i1 %cmp3
 }
+
+define i1 @test_xor_of_bittest_ne_ne(i8 %x, i8 %y) {
+; CHECK-LABEL: @test_xor_of_bittest_ne_ne(
+; CHECK-NEXT:    [[Y:%.*]] = xor i8 [[X:%.*]], [[Y1:%.*]]
+; CHECK-NEXT:    [[MASK2:%.*]] = and i8 [[Y]], 2
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp ne i8 [[MASK2]], 0
+; CHECK-NEXT:    ret i1 [[CMP2]]
+;
+  %mask1 = and i8 %x, 2
+  %cmp1 = icmp ne i8 %mask1, 0
+  %mask2 = and i8 %y, 2
+  %cmp2 = icmp ne i8 %mask2, 0
+  %xor = xor i1 %cmp1, %cmp2
+  ret i1 %xor
+}
+
+define i1 @test_xor_of_bittest_eq_eq(i8 %x, i8 %y) {
+; CHECK-LABEL: @test_xor_of_bittest_eq_eq(
+; CHECK-NEXT:    [[Y:%.*]] = xor i8 [[X:%.*]], [[Y1:%.*]]
+; CHECK-NEXT:    [[MASK2:%.*]] = and i8 [[Y]], 2
+; CHECK-NEXT:    [[XOR:%.*]] = icmp ne i8 [[MASK2]], 0
+; CHECK-NEXT:    ret i1 [[XOR]]
+;
+  %mask1 = and i8 %x, 2
+  %cmp1 = icmp eq i8 %mask1, 0
+  %mask2 = and i8 %y, 2
+  %cmp2 = icmp eq i8 %mask2, 0
+  %xor = xor i1 %cmp1, %cmp2
+  ret i1 %xor
+}
+
+define i1 @test_xor_of_bittest_ne_eq(i8 %x, i8 %y) {
+; CHECK-LABEL: @test_xor_of_bittest_ne_eq(
+; CHECK-NEXT:    [[Y:%.*]] = xor i8 [[X:%.*]], [[Y1:%.*]]
+; CHECK-NEXT:    [[MASK2:%.*]] = and i8 [[Y]], 2
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i8 [[MASK2]], 0
+; CHECK-NEXT:    ret i1 [[CMP2]]
+;
+  %mask1 = and i8 %x, 2
+  %cmp1 = icmp ne i8 %mask1, 0
+  %mask2 = and i8 %y, 2
+  %cmp2 = icmp eq i8 %mask2, 0
+  %xor = xor i1 %cmp1, %cmp2
+  ret i1 %xor
+}
+
+define i1 @test_xor_of_bittest_eq_ne(i8 %x, i8 %y) {
+; CHECK-LABEL: @test_xor_of_bittest_eq_ne(
+; CHECK-NEXT:    [[X:%.*]] = xor i8 [[X1:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[MASK1:%.*]] = and i8 [[X]], 2
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i8 [[MASK1]], 0
+; CHECK-NEXT:    ret i1 [[CMP1]]
+;
+  %mask1 = and i8 %x, 2
+  %cmp1 = icmp eq i8 %mask1, 0
+  %mask2 = and i8 %y, 2
+  %cmp2 = icmp ne i8 %mask2, 0
+  %xor = xor i1 %cmp1, %cmp2
+  ret i1 %xor
+}
+
+define i1 @test_xor_of_bittest_ne_ne_multiuse1(i8 %x, i8 %y) {
+; CHECK-LABEL: @test_xor_of_bittest_ne_ne_multiuse1(
+; CHECK-NEXT:    [[MASK1:%.*]] = and i8 [[X:%.*]], 2
+; CHECK-NEXT:    call void @usei8(i8 [[MASK1]])
+; CHECK-NEXT:    [[MASK2:%.*]] = and i8 [[Y:%.*]], 2
+; CHECK-NEXT:    call void @usei8(i8 [[MASK2]])
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i8 [[X]], [[Y]]
+; CHECK-NEXT:    [[TMP2:%.*]] = and i8 [[TMP1]], 2
+; CHECK-NEXT:    [[XOR:%.*]] = icmp ne i8 [[TMP2]], 0
+; CHECK-NEXT:    ret i1 [[XOR]]
+;
+  %mask1 = and i8 %x, 2
+  call void @usei8(i8 %mask1)
+  %cmp1 = icmp ne i8 %mask1, 0
+  %mask2 = and i8 %y, 2
+  call void @usei8(i8 %mask2)
+  %cmp2 = icmp ne i8 %mask2, 0
+  %xor = xor i1 %cmp1, %cmp2
+  ret i1 %xor
+}
+
+; Negative tests
+
+define i1 @test_xor_of_bittest_ne_ne_type_mismatch(i8 %x, i16 %y) {
+; CHECK-LABEL: @test_xor_of_bittest_ne_ne_type_mismatch(
+; CHECK-NEXT:    [[MASK1:%.*]] = and i8 [[X:%.*]], 2
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i8 [[MASK1]], 0
+; CHECK-NEXT:    [[MASK2:%.*]] = and i16 [[Y:%.*]], 2
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp ne i16 [[MASK2]], 0
+; CHECK-NEXT:    [[XOR:%.*]] = xor i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    ret i1 [[XOR]]
+;
+  %mask1 = and i8 %x, 2
+  %cmp1 = icmp ne i8 %mask1, 0
+  %mask2 = and i16 %y, 2
+  %cmp2 = icmp ne i16 %mask2, 0
+  %xor = xor i1 %cmp1, %cmp2
+  ret i1 %xor
+}
+
+define i1 @test_xor_of_bittest_ne_ne_mask_mismatch(i8 %x, i8 %y) {
+; CHECK-LABEL: @test_xor_of_bittest_ne_ne_mask_mismatch(
+; CHECK-NEXT:    [[MASK1:%.*]] = and i8 [[X:%.*]], 4
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i8 [[MASK1]], 0
+; CHECK-NEXT:    [[MASK2:%.*]] = and i8 [[Y:%.*]], 2
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp ne i8 [[MASK2]], 0
+; CHECK-NEXT:    [[XOR:%.*]] = xor i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    ret i1 [[XOR]]
+;
+  %mask1 = and i8 %x, 4
+  %cmp1 = icmp ne i8 %mask1, 0
+  %mask2 = and i8 %y, 2
+  %cmp2 = icmp ne i8 %mask2, 0
+  %xor = xor i1 %cmp1, %cmp2
+  ret i1 %xor
+}
+
+define i1 @test_xor_of_bittest_ne_ne_nonpower2(i8 %x, i8 %y) {
+; CHECK-LABEL: @test_xor_of_bittest_ne_ne_nonpower2(
+; CHECK-NEXT:    [[MASK1:%.*]] = and i8 [[X:%.*]], 3
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i8 [[MASK1]], 0
+; CHECK-NEXT:    [[MASK2:%.*]] = and i8 [[Y:%.*]], 3
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp ne i8 [[MASK2]], 0
+; CHECK-NEXT:    [[XOR:%.*]] = xor i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    ret i1 [[XOR]]
+;
+  %mask1 = and i8 %x, 3
+  %cmp1 = icmp ne i8 %mask1, 0
+  %mask2 = and i8 %y, 3
+  %cmp2 = icmp ne i8 %mask2, 0
+  %xor = xor i1 %cmp1, %cmp2
+  ret i1 %xor
+}
+
+define i1 @test_xor_of_bittest_ne_ne_multiuse2(i8 %x, i8 %y) {
+; CHECK-LABEL: @test_xor_of_bittest_ne_ne_multiuse2(
+; CHECK-NEXT:    [[MASK1:%.*]] = and i8 [[X:%.*]], 2
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i8 [[MASK1]], 0
+; CHECK-NEXT:    call void @use(i1 [[CMP1]])
+; CHECK-NEXT:    [[MASK2:%.*]] = and i8 [[Y:%.*]], 2
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp ne i8 [[MASK2]], 0
+; CHECK-NEXT:    [[XOR:%.*]] = xor i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    ret i1 [[XOR]]
+;
+  %mask1 = and i8 %x, 2
+  %cmp1 = icmp ne i8 %mask1, 0
+  call void @use(i1 %cmp1)
+  %mask2 = and i8 %y, 2
+  %cmp2 = icmp ne i8 %mask2, 0
+  %xor = xor i1 %cmp1, %cmp2
+  ret i1 %xor
+}
+
+declare void @usei8(i8)


        


More information about the llvm-commits mailing list