diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp index d7971e8e3caea..912ef5999e964 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp @@ -1338,16 +1338,51 @@ Value *InstCombinerImpl::foldAndOrOfICmpsUsingRanges(ICmpInst *ICmp1, V2 = X; } + // Look through and with a negative power of 2 mask on V1 or V2. This + // detects idioms of the form `(x == A) || ((x & Mask) == A + 1)` where A + 1 + // is aligned to the mask and A + 1 >= |Mask|. This pattern corresponds to a + // contiguous range check, which can be folded into an addition and compare. + // The same applies for `(x != A) && ((x & Mask) != A + 1)`. + auto AreContiguousRangePredicates = [](CmpPredicate Pred1, CmpPredicate Pred2, + bool IsAnd) { + if (IsAnd) + return Pred1 == ICmpInst::ICMP_NE && Pred2 == ICmpInst::ICMP_NE; + return Pred1 == ICmpInst::ICMP_EQ && Pred2 == ICmpInst::ICMP_EQ; + }; + const APInt *Mask1 = nullptr, *Mask2 = nullptr; + bool MatchedAnd1 = false, MatchedAnd2 = false; + if (V1 != V2 && AreContiguousRangePredicates(Pred1, Pred2, IsAnd)) { + Value *X; + if (match(V1, m_OneUse(m_And(m_Value(X), m_NegatedPower2(Mask1)))) && + C1->getBitWidth() == C2->getBitWidth() && *C1 == *C2 + 1 && + C1->uge(Mask1->abs()) && C1->isPowerOf2()) { + MatchedAnd1 = true; + V1 = X; + } + if (match(V2, m_OneUse(m_And(m_Value(X), m_NegatedPower2(Mask2)))) && + C1->getBitWidth() == C2->getBitWidth() && *C2 == *C1 + 1 && + C2->uge(Mask2->abs()) && C2->isPowerOf2()) { + MatchedAnd2 = true; + V2 = X; + } + } + if (V1 != V2) return nullptr; - ConstantRange CR1 = ConstantRange::makeExactICmpRegion( - IsAnd ? ICmpInst::getInverseCmpPredicate(Pred1) : Pred1, *C1); + ConstantRange CR1 = + MatchedAnd1 + ? ConstantRange(*C1, *C1 - *Mask1) + : ConstantRange::makeExactICmpRegion( + IsAnd ? ICmpInst::getInverseCmpPredicate(Pred1) : Pred1, *C1); if (Offset1) CR1 = CR1.subtract(*Offset1); - ConstantRange CR2 = ConstantRange::makeExactICmpRegion( - IsAnd ? ICmpInst::getInverseCmpPredicate(Pred2) : Pred2, *C2); + ConstantRange CR2 = + MatchedAnd2 + ? ConstantRange(*C2, *C2 - *Mask2) + : ConstantRange::makeExactICmpRegion( + IsAnd ? ICmpInst::getInverseCmpPredicate(Pred2) : Pred2, *C2); if (Offset2) CR2 = CR2.subtract(*Offset2); diff --git a/llvm/test/Transforms/InstCombine/and-or-icmps.ll b/llvm/test/Transforms/InstCombine/and-or-icmps.ll index 42e5020748129..553c7ac5af0e9 100644 --- a/llvm/test/Transforms/InstCombine/and-or-icmps.ll +++ b/llvm/test/Transforms/InstCombine/and-or-icmps.ll @@ -3491,3 +3491,198 @@ define i1 @and_icmp_eq_with_binary_range_operands(i8 range(i8 0, 2) %x, i8 range %ret = and i1 %icmp1, %icmp2 ret i1 %ret } + +define i1 @or_icmp_eq_and_pow2(i32 %x) { +; CHECK-LABEL: @or_icmp_eq_and_pow2( +; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -127 +; CHECK-NEXT: [[RET:%.*]] = icmp ult i32 [[TMP1]], 33 +; CHECK-NEXT: ret i1 [[RET]] +; + %icmp1 = icmp eq i32 %x, 127 + %and = and i32 %x, -32 + %icmp2 = icmp eq i32 %and, 128 + %ret = or i1 %icmp1, %icmp2 + ret i1 %ret +} + +define i1 @or_icmp_eq_and_pow2_mask_equal_to_icmp(i32 %x) { +; CHECK-LABEL: @or_icmp_eq_and_pow2_mask_equal_to_icmp( +; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -63 +; CHECK-NEXT: [[RET:%.*]] = icmp ult i32 [[TMP1]], 65 +; CHECK-NEXT: ret i1 [[RET]] +; + %icmp1 = icmp eq i32 %x, 63 + %and = and i32 %x, -64 + %icmp2 = icmp eq i32 %and, 64 + %ret = or i1 %icmp1, %icmp2 + ret i1 %ret +} + +define i1 @and_icmp_ne_and_pow2(i32 %x) { +; CHECK-LABEL: @and_icmp_ne_and_pow2( +; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -160 +; CHECK-NEXT: [[RET:%.*]] = icmp ult i32 [[TMP1]], -33 +; CHECK-NEXT: ret i1 [[RET]] +; + %icmp1 = icmp ne i32 %x, 127 + %and = and i32 %x, -32 + %icmp2 = icmp ne i32 %and, 128 + %ret = and i1 %icmp1, %icmp2 + ret i1 %ret +} + +define i1 @and_icmp_ne_and_pow2_mask_equal_to_icmp(i32 %x) { +; CHECK-LABEL: @and_icmp_ne_and_pow2_mask_equal_to_icmp( +; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -128 +; CHECK-NEXT: [[RET:%.*]] = icmp ult i32 [[TMP1]], -65 +; CHECK-NEXT: ret i1 [[RET]] +; + %icmp1 = icmp ne i32 %x, 63 + %and = and i32 %x, -64 + %icmp2 = icmp ne i32 %and, 64 + %ret = and i1 %icmp1, %icmp2 + ret i1 %ret +} + +define i1 @or_icmp_eq_and_pow2_commute(i32 %x) { +; CHECK-LABEL: @or_icmp_eq_and_pow2_commute( +; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -127 +; CHECK-NEXT: [[TMP4:%.*]] = icmp ult i32 [[TMP1]], 33 +; CHECK-NEXT: ret i1 [[TMP4]] +; + %and = and i32 %x, -32 + %icmp1 = icmp eq i32 %and, 128 + %icmp2 = icmp eq i32 %x, 127 + %ret = or i1 %icmp1, %icmp2 + ret i1 %ret +} + +define i1 @and_icmp_ne_and_pow2_commute(i32 %x) { +; CHECK-LABEL: @and_icmp_ne_and_pow2_commute( +; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -160 +; CHECK-NEXT: [[RET:%.*]] = icmp ult i32 [[TMP1]], -33 +; CHECK-NEXT: ret i1 [[RET]] +; + %and = and i32 %x, -32 + %icmp1 = icmp ne i32 %and, 128 + %icmp2 = icmp ne i32 %x, 127 + %ret = and i1 %icmp1, %icmp2 + ret i1 %ret +} + +define i1 @neg_or_icmp_eq_and_pow2_multi_use(i32 %x) { +; CHECK-LABEL: @neg_or_icmp_eq_and_pow2_multi_use( +; CHECK-NEXT: [[ICMP1:%.*]] = icmp eq i32 [[X:%.*]], 127 +; CHECK-NEXT: [[AND:%.*]] = and i32 [[X]], -32 +; CHECK-NEXT: call void @use32(i32 [[AND]]) +; CHECK-NEXT: [[ICMP2:%.*]] = icmp eq i32 [[AND]], 128 +; CHECK-NEXT: [[RET:%.*]] = or i1 [[ICMP1]], [[ICMP2]] +; CHECK-NEXT: ret i1 [[RET]] +; + %icmp1 = icmp eq i32 %x, 127 + %and = and i32 %x, -32 + call void @use32(i32 %and) + %icmp2 = icmp eq i32 %and, 128 + %ret = or i1 %icmp1, %icmp2 + ret i1 %ret +} + +define i1 @neg_and_icmp_eq_and_pow2(i32 %x) { +; CHECK-LABEL: @neg_and_icmp_eq_and_pow2( +; CHECK-NEXT: ret i1 false +; + %icmp1 = icmp eq i32 %x, 127 + %and = and i32 %x, -32 + %icmp2 = icmp eq i32 %and, 128 + %ret = and i1 %icmp1, %icmp2 + ret i1 %ret +} + +define i1 @neg_or_icmp_eq_and_non_pow2_mask(i32 %x) { +; CHECK-LABEL: @neg_or_icmp_eq_and_non_pow2_mask( +; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[X:%.*]], 127 +; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[X]], -33 +; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 128 +; CHECK-NEXT: [[TMP4:%.*]] = or i1 [[TMP1]], [[TMP3]] +; CHECK-NEXT: ret i1 [[TMP4]] +; + %icmp1 = icmp eq i32 %x, 127 + %and = and i32 %x, -33 + %icmp2 = icmp eq i32 %and, 128 + %ret = or i1 %icmp1, %icmp2 + ret i1 %ret +} + +define i1 @neg_and_icmp_ne_and_non_pow2_icmp(i32 %x) { +; CHECK-LABEL: @neg_and_icmp_ne_and_non_pow2_icmp( +; CHECK-NEXT: [[ICMP1:%.*]] = icmp ne i32 [[X:%.*]], 127 +; CHECK-NEXT: [[AND:%.*]] = and i32 [[X]], -33 +; CHECK-NEXT: [[ICMP2:%.*]] = icmp ne i32 [[AND]], 128 +; CHECK-NEXT: [[RET:%.*]] = and i1 [[ICMP1]], [[ICMP2]] +; CHECK-NEXT: ret i1 [[RET]] +; + %icmp1 = icmp ne i32 %x, 127 + %and = and i32 %x, -33 + %icmp2 = icmp ne i32 %and, 128 + %ret = and i1 %icmp1, %icmp2 + ret i1 %ret +} + +define i1 @neg_or_icmp_eq_and_const_less_than_mask(i32 %x) { +; CHECK-LABEL: @neg_or_icmp_eq_and_const_less_than_mask( +; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[X:%.*]], 15 +; CHECK-NEXT: ret i1 [[TMP1]] +; + %icmp1 = icmp eq i32 %x, 15 + %and = and i32 %x, -32 + %icmp2 = icmp eq i32 %and, 16 + %ret = or i1 %icmp1, %icmp2 + ret i1 %ret +} + +define i1 @neg_and_icmp_ne_and_pow2_disjoint(i32 %x) { +; CHECK-LABEL: @neg_and_icmp_ne_and_pow2_disjoint( +; CHECK-NEXT: [[ICMP1:%.*]] = icmp ne i32 [[X:%.*]], 126 +; CHECK-NEXT: [[AND:%.*]] = and i32 [[X]], -32 +; CHECK-NEXT: [[ICMP2:%.*]] = icmp ne i32 [[AND]], 128 +; CHECK-NEXT: [[RET:%.*]] = and i1 [[ICMP1]], [[ICMP2]] +; CHECK-NEXT: ret i1 [[RET]] +; + %icmp1 = icmp ne i32 %x, 126 + %and = and i32 %x, -32 + %icmp2 = icmp ne i32 %and, 128 + %ret = and i1 %icmp1, %icmp2 + ret i1 %ret +} + +define i1 @neg_or_icmp_eq_double_and_pow2(i32 %x) { +; CHECK-LABEL: @neg_or_icmp_eq_double_and_pow2( +; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[X:%.*]], -16 +; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[TMP1]], 64 +; CHECK-NEXT: [[AND2:%.*]] = and i32 [[X]], -32 +; CHECK-NEXT: [[TMP4:%.*]] = icmp eq i32 [[AND2]], 128 +; CHECK-NEXT: [[TMP5:%.*]] = or i1 [[TMP2]], [[TMP4]] +; CHECK-NEXT: ret i1 [[TMP5]] +; + %and1 = and i32 %x, -16 + %icmp1 = icmp eq i32 %and1, 64 + %and2 = and i32 %x, -32 + %icmp2 = icmp eq i32 %and2, 128 + %ret = or i1 %icmp1, %icmp2 + ret i1 %ret +} + +define i1 @neg_select_icmp_eq_and_pow2(i32 %x) { +; CHECK-LABEL: @neg_select_icmp_eq_and_pow2( +; CHECK-NEXT: [[ICMP1:%.*]] = icmp sgt i32 [[X:%.*]], 127 +; CHECK-NEXT: [[AND:%.*]] = and i32 [[X]], -32 +; CHECK-NEXT: [[ICMP2:%.*]] = icmp eq i32 [[AND]], 128 +; CHECK-NEXT: [[TMP1:%.*]] = and i1 [[ICMP1]], [[ICMP2]] +; CHECK-NEXT: ret i1 [[TMP1]] +; + %icmp1 = icmp sgt i32 %x, 127 + %and = and i32 %x, -32 + %icmp2 = icmp eq i32 %and, 128 + %1 = select i1 %icmp1, i1 %icmp2, i1 false + ret i1 %1 +}