[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