diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp index ca8a20b4b7312..8701f7c28a39f 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 0384c1aa184b8..382355cae7694 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)