Index: llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp @@ -3279,6 +3279,35 @@ NotLHS, NotRHS); } } + + // Pull 'not' into operands of select if at least 1 operand is a one-use + // compare because that operand is free to invert by changing the predicate. + // Example: + // not (select Cond, (cmp TPred, ?, ?), FVal --> + // select Cond, (cmp TPred', ?, ?), (not FVal) + Value *Cond, *TVal, *FVal; + CmpInst::Predicate TPred = CmpInst::BAD_ICMP_PREDICATE; + CmpInst::Predicate FPred = CmpInst::BAD_ICMP_PREDICATE; + if (match(Op0, m_Select(m_Value(Cond), m_Value(TVal), m_Value(FVal))) && + (match(TVal, m_OneUse(m_Cmp(TPred, m_Value(), m_Value()))) || + match(FVal, m_OneUse(m_Cmp(FPred, m_Value(), m_Value()))))) { + assert((TPred != CmpInst::BAD_ICMP_PREDICATE || + FPred != CmpInst::BAD_ICMP_PREDICATE) && "Expected a good pred"); + + // If either select value was not a 1-use cmp, invert it with 'not'. + // Otherwise, invert the compare predicate. + if (TPred == CmpInst::BAD_ICMP_PREDICATE) + TVal = Builder.CreateNot(TVal); + else + cast(TVal)->setPredicate(CmpInst::getInversePredicate(TPred)); + + if (FPred == CmpInst::BAD_ICMP_PREDICATE) + FVal = Builder.CreateNot(FVal); + else + cast(FVal)->setPredicate(CmpInst::getInversePredicate(FPred)); + + return SelectInst::Create(Cond, TVal, FVal); + } } if (Instruction *NewXor = sinkNotIntoXor(I, Builder)) Index: llvm/test/Transforms/InstCombine/not.ll =================================================================== --- llvm/test/Transforms/InstCombine/not.ll +++ llvm/test/Transforms/InstCombine/not.ll @@ -253,10 +253,9 @@ define i1 @not_select_cmp_cmp(i32 %x, i32 %y, float %z, float %w, i1 %cond) { ; CHECK-LABEL: @not_select_cmp_cmp( -; CHECK-NEXT: [[CMPT:%.*]] = icmp sle i32 [[X:%.*]], [[Y:%.*]] -; CHECK-NEXT: [[CMPF:%.*]] = fcmp ugt float [[Z:%.*]], [[W:%.*]] -; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND:%.*]], i1 [[CMPT]], i1 [[CMPF]] -; CHECK-NEXT: [[NOT:%.*]] = xor i1 [[SEL]], true +; CHECK-NEXT: [[CMPT:%.*]] = icmp sgt i32 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[CMPF:%.*]] = fcmp ole float [[Z:%.*]], [[W:%.*]] +; CHECK-NEXT: [[NOT:%.*]] = select i1 [[COND:%.*]], i1 [[CMPT]], i1 [[CMPF]] ; CHECK-NEXT: ret i1 [[NOT]] ; %cmpt = icmp sle i32 %x, %y @@ -272,9 +271,9 @@ ; CHECK-LABEL: @not_select_cmp_cmp_extra_use1( ; CHECK-NEXT: [[CMPT:%.*]] = icmp sle i32 [[X:%.*]], [[Y:%.*]] ; CHECK-NEXT: call void @use1(i1 [[CMPT]]) -; CHECK-NEXT: [[CMPF:%.*]] = fcmp ugt float [[Z:%.*]], [[W:%.*]] -; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND:%.*]], i1 [[CMPT]], i1 [[CMPF]] -; CHECK-NEXT: [[NOT:%.*]] = xor i1 [[SEL]], true +; CHECK-NEXT: [[CMPF:%.*]] = fcmp ole float [[Z:%.*]], [[W:%.*]] +; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[CMPT]], true +; CHECK-NEXT: [[NOT:%.*]] = select i1 [[COND:%.*]], i1 [[TMP1]], i1 [[CMPF]] ; CHECK-NEXT: ret i1 [[NOT]] ; %cmpt = icmp sle i32 %x, %y @@ -287,11 +286,11 @@ define i1 @not_select_cmp_cmp_extra_use2(i32 %x, i32 %y, float %z, float %w, i1 %cond) { ; CHECK-LABEL: @not_select_cmp_cmp_extra_use2( -; CHECK-NEXT: [[CMPT:%.*]] = icmp sle i32 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[CMPT:%.*]] = icmp sgt i32 [[X:%.*]], [[Y:%.*]] ; CHECK-NEXT: [[CMPF:%.*]] = fcmp ugt float [[Z:%.*]], [[W:%.*]] ; CHECK-NEXT: call void @use1(i1 [[CMPF]]) -; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND:%.*]], i1 [[CMPT]], i1 [[CMPF]] -; CHECK-NEXT: [[NOT:%.*]] = xor i1 [[SEL]], true +; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[CMPF]], true +; CHECK-NEXT: [[NOT:%.*]] = select i1 [[COND:%.*]], i1 [[CMPT]], i1 [[TMP1]] ; CHECK-NEXT: ret i1 [[NOT]] ; %cmpt = icmp sle i32 %x, %y @@ -302,6 +301,8 @@ ret i1 %not } +; Negative test - extra uses would require more instructions. + define i1 @not_select_cmp_cmp_extra_use3(i32 %x, i32 %y, float %z, float %w, i1 %cond) { ; CHECK-LABEL: @not_select_cmp_cmp_extra_use3( ; CHECK-NEXT: [[CMPT:%.*]] = icmp sle i32 [[X:%.*]], [[Y:%.*]] @@ -321,6 +322,8 @@ ret i1 %not } +; Negative test - extra uses would require more instructions. + define i1 @not_select_cmp_cmp_extra_use4(i32 %x, i32 %y, float %z, float %w, i1 %cond) { ; CHECK-LABEL: @not_select_cmp_cmp_extra_use4( ; CHECK-NEXT: [[CMPT:%.*]] = icmp sle i32 [[X:%.*]], [[Y:%.*]] @@ -340,9 +343,9 @@ define i1 @not_select_cmpt(double %x, double %y, i1 %z, i1 %cond) { ; CHECK-LABEL: @not_select_cmpt( -; CHECK-NEXT: [[CMPT:%.*]] = fcmp oeq double [[X:%.*]], [[Y:%.*]] -; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND:%.*]], i1 [[CMPT]], i1 [[Z:%.*]] -; CHECK-NEXT: [[NOT:%.*]] = xor i1 [[SEL]], true +; CHECK-NEXT: [[CMPT:%.*]] = fcmp une double [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[Z:%.*]], true +; CHECK-NEXT: [[NOT:%.*]] = select i1 [[COND:%.*]], i1 [[CMPT]], i1 [[TMP1]] ; CHECK-NEXT: ret i1 [[NOT]] ; %cmpt = fcmp oeq double %x, %y @@ -353,9 +356,9 @@ define i1 @not_select_cmpf(i1 %x, i32 %z, i32 %w, i1 %cond) { ; CHECK-LABEL: @not_select_cmpf( -; CHECK-NEXT: [[CMPF:%.*]] = icmp ugt i32 [[Z:%.*]], [[W:%.*]] -; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND:%.*]], i1 [[X:%.*]], i1 [[CMPF]] -; CHECK-NEXT: [[NOT:%.*]] = xor i1 [[SEL]], true +; CHECK-NEXT: [[CMPF:%.*]] = icmp ule i32 [[Z:%.*]], [[W:%.*]] +; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[X:%.*]], true +; CHECK-NEXT: [[NOT:%.*]] = select i1 [[COND:%.*]], i1 [[TMP1]], i1 [[CMPF]] ; CHECK-NEXT: ret i1 [[NOT]] ; %cmpf = icmp ugt i32 %z, %w