diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp @@ -654,6 +654,42 @@ return Builder.CreateICmp(NewCC, NewAnd, NewOr2); } + if (Mask & BMask_NotMixed) { + // Check the intersection (B & D) for inequality. + // Assume that (B & D) == B || (B & D) == D, i.e B/D is a subset of D/B + // and (B & D) & (C ^ E) == 0, bits of C and E, which are shared by both the + // B and the D, don't contradict. + // Note that we can assume (~B & C) == 0 && (~D & E) == 0, previous + // operation should delete these icmps if it hadn't been met. + + // (icmp ne (A & B), C) & (icmp ne (A & D), E) + // -> (icmp ne (A & (B & D)), (C & E)) + + NewCC = CmpInst::getInversePredicate(NewCC); + + const APInt *OldConstC, *OldConstE; + if (!match(C, m_APInt(OldConstC)) || !match(E, m_APInt(OldConstE))) + return nullptr; + + const APInt ConstC = PredL != NewCC ? *ConstB ^ *OldConstC : *OldConstC; + const APInt ConstE = PredR != NewCC ? *ConstD ^ *OldConstE : *OldConstE; + + const APInt ConstT = *ConstB & *ConstD; + if (ConstT != *ConstB && ConstT != *ConstD) + return nullptr; + + assert((~*ConstB & ConstC) == 0); + assert((~*ConstD & ConstE) == 0); + + // If there is a conflict, we still have to check both sides. + if (((*ConstB & *ConstD) & (ConstC ^ ConstE)).getBoolValue()) + return nullptr; + + Value *NewAnd1 = Builder.CreateAnd(B, D); + Value *NewAnd2 = Builder.CreateAnd(A, NewAnd1); + Value *NewAnd3 = ConstantInt::get(A->getType(), ConstC & ConstE); + return Builder.CreateICmp(NewCC, NewAnd2, NewAnd3); + } return nullptr; } diff --git a/llvm/test/Transforms/InstCombine/icmp-logical.ll b/llvm/test/Transforms/InstCombine/icmp-logical.ll --- a/llvm/test/Transforms/InstCombine/icmp-logical.ll +++ b/llvm/test/Transforms/InstCombine/icmp-logical.ll @@ -280,11 +280,8 @@ define i1 @masked_or_eq(i32 %A) { ; CHECK-LABEL: @masked_or_eq( -; CHECK-NEXT: [[MASK1:%.*]] = and i32 [[A:%.*]], 15 -; CHECK-NEXT: [[TST1:%.*]] = icmp eq i32 [[MASK1]], 3 -; CHECK-NEXT: [[MASK2:%.*]] = and i32 [[A]], 255 -; CHECK-NEXT: [[TST2:%.*]] = icmp eq i32 [[MASK2]], 243 -; CHECK-NEXT: [[RES:%.*]] = or i1 [[TST1]], [[TST2]] +; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[A:%.*]], 15 +; CHECK-NEXT: [[RES:%.*]] = icmp eq i32 [[TMP1]], 3 ; CHECK-NEXT: ret i1 [[RES]] ; %mask1 = and i32 %A, 15 ; 0x0f @@ -314,11 +311,8 @@ define i1 @masked_and_ne(i32 %A) { ; CHECK-LABEL: @masked_and_ne( -; CHECK-NEXT: [[MASK1:%.*]] = and i32 [[A:%.*]], 15 -; CHECK-NEXT: [[TST1:%.*]] = icmp ne i32 [[MASK1]], 3 -; CHECK-NEXT: [[MASK2:%.*]] = and i32 [[A]], 255 -; CHECK-NEXT: [[TST2:%.*]] = icmp ne i32 [[MASK2]], 243 -; CHECK-NEXT: [[RES:%.*]] = and i1 [[TST1]], [[TST2]] +; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[A:%.*]], 15 +; CHECK-NEXT: [[RES:%.*]] = icmp ne i32 [[TMP1]], 3 ; CHECK-NEXT: ret i1 [[RES]] ; %mask1 = and i32 %A, 15 ; 0x0f @@ -346,19 +340,6 @@ ret i1 %res } -define i1 @masked_and_expected_false(i32 %A) { -; CHECK-LABEL: @masked_and_expected_false( -; CHECK-NEXT: [[MASK2:%.*]] = and i32 [[A:%.*]], 255 -; CHECK-NEXT: [[TST2:%.*]] = icmp ne i32 [[MASK2]], 242 -; CHECK-NEXT: ret i1 [[TST2]] -; - %mask1 = and i32 %A, 3 ; 0x0f - %tst1 = icmp ne i32 %mask1, 15 ; 0x03 - %mask2 = and i32 %A, 255 ; 0xff - %tst2 = icmp ne i32 %mask2, 242 ; 0xf2 - %res = and i1 %tst1, %tst2 - ret i1 %res -} define i1 @nomask_lhs(i32 %in) { ; CHECK-LABEL: @nomask_lhs(