diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp index 010b77548c152..6f0ec66420a51 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp @@ -3115,6 +3115,39 @@ static Instruction *foldNestedSelects(SelectInst &OuterSelVal, !IsAndVariant ? SelInner : InnerSel.FalseVal); } +/// Return true if V is poison or \p Expected given that ValAssumedPoison is +/// already poison. For example, if ValAssumedPoison is `icmp samesign X, 10` +/// and V is `icmp ne X, 5`, impliesPoisonOrCond returns true. +static bool impliesPoisonOrCond(const Value *ValAssumedPoison, const Value *V, + bool Expected) { + if (impliesPoison(ValAssumedPoison, V)) + return true; + + // Handle the case that ValAssumedPoison is `icmp samesign pred X, C1` and V + // is `icmp pred X, C2`, where C1 is well-defined. + if (auto *ICmp = dyn_cast(ValAssumedPoison)) { + Value *LHS = ICmp->getOperand(0); + const APInt *RHSC1; + const APInt *RHSC2; + ICmpInst::Predicate Pred; + if (ICmp->hasSameSign() && + match(ICmp->getOperand(1), m_APIntForbidPoison(RHSC1)) && + match(V, m_ICmp(Pred, m_Specific(LHS), m_APIntAllowPoison(RHSC2)))) { + unsigned BitWidth = RHSC1->getBitWidth(); + ConstantRange CRX = + RHSC1->isNonNegative() + ? ConstantRange(APInt::getSignedMinValue(BitWidth), + APInt::getZero(BitWidth)) + : ConstantRange(APInt::getZero(BitWidth), + APInt::getSignedMinValue(BitWidth)); + return CRX.icmp(Expected ? Pred : ICmpInst::getInversePredicate(Pred), + *RHSC2); + } + } + + return false; +} + Instruction *InstCombinerImpl::foldSelectOfBools(SelectInst &SI) { Value *CondVal = SI.getCondition(); Value *TrueVal = SI.getTrueValue(); @@ -3136,13 +3169,13 @@ Instruction *InstCombinerImpl::foldSelectOfBools(SelectInst &SI) { // checks whether folding it does not convert a well-defined value into // poison. if (match(TrueVal, m_One())) { - if (impliesPoison(FalseVal, CondVal)) { + if (impliesPoisonOrCond(FalseVal, CondVal, /*Expected=*/false)) { // Change: A = select B, true, C --> A = or B, C return BinaryOperator::CreateOr(CondVal, FalseVal); } if (match(CondVal, m_OneUse(m_Select(m_Value(A), m_One(), m_Value(B)))) && - impliesPoison(FalseVal, B)) { + impliesPoisonOrCond(FalseVal, B, /*Expected=*/false)) { // (A || B) || C --> A || (B | C) return replaceInstUsesWith( SI, Builder.CreateLogicalOr(A, Builder.CreateOr(B, FalseVal))); @@ -3178,13 +3211,13 @@ Instruction *InstCombinerImpl::foldSelectOfBools(SelectInst &SI) { } if (match(FalseVal, m_Zero())) { - if (impliesPoison(TrueVal, CondVal)) { + if (impliesPoisonOrCond(TrueVal, CondVal, /*Expected=*/true)) { // Change: A = select B, C, false --> A = and B, C return BinaryOperator::CreateAnd(CondVal, TrueVal); } if (match(CondVal, m_OneUse(m_Select(m_Value(A), m_Value(B), m_Zero()))) && - impliesPoison(TrueVal, B)) { + impliesPoisonOrCond(TrueVal, B, /*Expected=*/true)) { // (A && B) && C --> A && (B & C) return replaceInstUsesWith( SI, Builder.CreateLogicalAnd(A, Builder.CreateAnd(B, TrueVal))); diff --git a/llvm/test/Transforms/InstCombine/logical-select.ll b/llvm/test/Transforms/InstCombine/logical-select.ll index 1b6e816d2e624..050a53406a9c5 100644 --- a/llvm/test/Transforms/InstCombine/logical-select.ll +++ b/llvm/test/Transforms/InstCombine/logical-select.ll @@ -1521,3 +1521,121 @@ bb: %and2 = or i1 %and1, %cmp ret i1 %and2 } + +define i1 @test_logical_and_icmp_samesign(i8 %x) { +; CHECK-LABEL: @test_logical_and_icmp_samesign( +; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i8 [[X:%.*]], 9 +; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign ult i8 [[X]], 11 +; CHECK-NEXT: [[AND:%.*]] = and i1 [[CMP1]], [[CMP2]] +; CHECK-NEXT: ret i1 [[AND]] +; + %cmp1 = icmp ne i8 %x, 9 + %cmp2 = icmp samesign ult i8 %x, 11 + %and = select i1 %cmp1, i1 %cmp2, i1 false + ret i1 %and +} + +define i1 @test_logical_or_icmp_samesign(i8 %x) { +; CHECK-LABEL: @test_logical_or_icmp_samesign( +; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i8 [[X:%.*]], -9 +; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign ult i8 [[X]], -11 +; CHECK-NEXT: [[OR:%.*]] = or i1 [[CMP1]], [[CMP2]] +; CHECK-NEXT: ret i1 [[OR]] +; + %cmp1 = icmp eq i8 %x, -9 + %cmp2 = icmp samesign ult i8 %x, -11 + %or = select i1 %cmp1, i1 true, i1 %cmp2 + ret i1 %or +} + +define i1 @test_double_logical_and_icmp_samesign1(i1 %cond, i32 %y) { +; CHECK-LABEL: @test_double_logical_and_icmp_samesign1( +; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign ult i32 [[Y:%.*]], 4 +; CHECK-NEXT: [[SEL2:%.*]] = select i1 [[SEL1:%.*]], i1 [[CMP2]], i1 false +; CHECK-NEXT: ret i1 [[SEL2]] +; + %cmp1 = icmp ne i32 %y, 5 + %sel1 = select i1 %cond, i1 %cmp1, i1 false + %cmp2 = icmp samesign ult i32 %y, 4 + %sel2 = select i1 %sel1, i1 %cmp2, i1 false + ret i1 %sel2 +} + +define i1 @test_double_logical_and_icmp_samesign2(i1 %cond, i32 %y) { +; CHECK-LABEL: @test_double_logical_and_icmp_samesign2( +; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[Y:%.*]], -65536 +; CHECK-NEXT: [[CMP2:%.*]] = icmp ult i32 [[TMP1]], 1048576 +; CHECK-NEXT: [[SEL2:%.*]] = select i1 [[SEL1:%.*]], i1 [[CMP2]], i1 false +; CHECK-NEXT: ret i1 [[SEL2]] +; + %cmp1 = icmp samesign ugt i32 %y, 65535 + %sel1 = select i1 %cond, i1 %cmp1, i1 false + %cmp2 = icmp samesign ult i32 %y, 1114112 + %sel2 = select i1 %sel1, i1 %cmp2, i1 false + ret i1 %sel2 +} + +define <2 x i1> @test_logical_and_icmp_samesign_vec(<2 x i8> %x) { +; CHECK-LABEL: @test_logical_and_icmp_samesign_vec( +; CHECK-NEXT: [[CMP1:%.*]] = icmp ne <2 x i8> [[X:%.*]], splat (i8 9) +; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign ult <2 x i8> [[X]], splat (i8 11) +; CHECK-NEXT: [[AND:%.*]] = and <2 x i1> [[CMP1]], [[CMP2]] +; CHECK-NEXT: ret <2 x i1> [[AND]] +; + %cmp1 = icmp ne <2 x i8> %x, splat(i8 9) + %cmp2 = icmp samesign ult <2 x i8> %x, splat(i8 11) + %and = select <2 x i1> %cmp1, <2 x i1> %cmp2, <2 x i1> zeroinitializer + ret <2 x i1> %and +} + +define <2 x i1> @test_logical_and_icmp_samesign_vec_with_poison_cond(<2 x i8> %x) { +; CHECK-LABEL: @test_logical_and_icmp_samesign_vec_with_poison_cond( +; CHECK-NEXT: [[CMP1:%.*]] = icmp ne <2 x i8> [[X:%.*]], +; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign ult <2 x i8> [[X]], splat (i8 11) +; CHECK-NEXT: [[AND:%.*]] = and <2 x i1> [[CMP1]], [[CMP2]] +; CHECK-NEXT: ret <2 x i1> [[AND]] +; + %cmp1 = icmp ne <2 x i8> %x, + %cmp2 = icmp samesign ult <2 x i8> %x, splat(i8 11) + %and = select <2 x i1> %cmp1, <2 x i1> %cmp2, <2 x i1> zeroinitializer + ret <2 x i1> %and +} + +define i1 @test_logical_and_icmp_samesign_do_not_imply(i8 %x) { +; CHECK-LABEL: @test_logical_and_icmp_samesign_do_not_imply( +; CHECK-NEXT: [[AND:%.*]] = icmp ult i8 [[X:%.*]], 11 +; CHECK-NEXT: ret i1 [[AND]] +; + %cmp1 = icmp ne i8 %x, -9 + %cmp2 = icmp samesign ult i8 %x, 11 + %and = select i1 %cmp1, i1 %cmp2, i1 false + ret i1 %and +} + +define i1 @test_logical_and_icmp_no_samesign(i8 %x) { +; CHECK-LABEL: @test_logical_and_icmp_no_samesign( +; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i8 [[X:%.*]], 9 +; CHECK-NEXT: [[CMP2:%.*]] = icmp ult i8 [[X]], 11 +; CHECK-NEXT: [[AND:%.*]] = and i1 [[CMP1]], [[CMP2]] +; CHECK-NEXT: ret i1 [[AND]] +; + %cmp1 = icmp ne i8 %x, 9 + %cmp2 = icmp ult i8 %x, 11 + %and = select i1 %cmp1, i1 %cmp2, i1 false + ret i1 %and +} + +; Negative tests + +define <2 x i1> @test_logical_and_icmp_samesign_vec_with_poison_tv(<2 x i8> %x) { +; CHECK-LABEL: @test_logical_and_icmp_samesign_vec_with_poison_tv( +; CHECK-NEXT: [[CMP1:%.*]] = icmp ne <2 x i8> [[X:%.*]], splat (i8 9) +; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign ult <2 x i8> [[X]], +; CHECK-NEXT: [[AND:%.*]] = select <2 x i1> [[CMP1]], <2 x i1> [[CMP2]], <2 x i1> zeroinitializer +; CHECK-NEXT: ret <2 x i1> [[AND]] +; + %cmp1 = icmp ne <2 x i8> %x, splat(i8 9) + %cmp2 = icmp samesign ult <2 x i8> %x, + %and = select <2 x i1> %cmp1, <2 x i1> %cmp2, <2 x i1> zeroinitializer + ret <2 x i1> %and +}