diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp index 90ed6bc368c61a..f7fda4b9aa4152 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp @@ -3609,6 +3609,55 @@ static Instruction *canonicalizeAbs(BinaryOperator &Xor, return nullptr; } +// Transform +// z = ~(x &/| y) +// into: +// z = ((~x) |/& (~y)) +// iff both x and y are free to invert and all uses of z can be freely updated. +bool InstCombinerImpl::sinkNotIntoLogicalOp(Instruction &I) { + Value *Op0, *Op1; + if (!match(&I, m_LogicalOp(m_Value(Op0), m_Value(Op1)))) + return false; + Instruction::BinaryOps NewOpc = + match(&I, m_LogicalAnd()) ? Instruction::Or : Instruction::And; + bool IsBinaryOp = isa(I); + + // Can our users be adapted? + if (!InstCombiner::canFreelyInvertAllUsersOf(&I, /*IgnoredUser=*/nullptr)) + return false; + + // And can the operands be adapted? + for (Value *Op : {Op0, Op1}) + if (!InstCombiner::isFreeToInvert(Op, /*WillInvertAllUses=*/true) || + !InstCombiner::canFreelyInvertAllUsersOf(Op, /*IgnoredUser=*/&I)) + return false; + + for (Value **Op : {&Op0, &Op1}) { + Builder.SetInsertPoint( + &*cast(*Op)->getInsertionPointAfterDef()); + Value *NotOp = Builder.CreateNot(*Op, (*Op)->getName() + ".not"); + (*Op)->replaceUsesWithIf(NotOp, + [NotOp](Use &U) { return U.getUser() != NotOp; }); + freelyInvertAllUsersOf(NotOp, /*IgnoredUser=*/&I); + *Op = NotOp; + } + + Builder.SetInsertPoint(I.getInsertionPointAfterDef()); + Value *NewLogicOp; + if (IsBinaryOp) + NewLogicOp = Builder.CreateBinOp(NewOpc, Op0, Op1, I.getName() + ".not"); + else + NewLogicOp = + Builder.CreateLogicalOp(NewOpc, Op0, Op1, I.getName() + ".not"); + + replaceInstUsesWith(I, NewLogicOp); + // We can not just create an outer `not`, it will most likely be immediately + // folded back, reconstructing our initial pattern, and causing an + // infinite combine loop, so immediately manually fold it away. + freelyInvertAllUsersOf(NewLogicOp); + return true; +} + // Transform // z = (~x) &/| y // into: @@ -3694,23 +3743,6 @@ Instruction *InstCombinerImpl::foldNot(BinaryOperator &I) { // Is this a 'not' (~) fed by a binary operator? BinaryOperator *NotVal; if (match(NotOp, m_BinOp(NotVal))) { - if (NotVal->getOpcode() == Instruction::And || - NotVal->getOpcode() == Instruction::Or) { - // Apply DeMorgan's Law when inverts are free: - // ~(X & Y) --> (~X | ~Y) - // ~(X | Y) --> (~X & ~Y) - if (isFreeToInvert(NotVal->getOperand(0), - NotVal->getOperand(0)->hasOneUse()) && - isFreeToInvert(NotVal->getOperand(1), - NotVal->getOperand(1)->hasOneUse())) { - Value *NotX = Builder.CreateNot(NotVal->getOperand(0), "notlhs"); - Value *NotY = Builder.CreateNot(NotVal->getOperand(1), "notrhs"); - if (NotVal->getOpcode() == Instruction::And) - return BinaryOperator::CreateOr(NotX, NotY); - return BinaryOperator::CreateAnd(NotX, NotY); - } - } - // ~((-X) | Y) --> (X - 1) & (~Y) if (match(NotVal, m_OneUse(m_c_Or(m_OneUse(m_Neg(m_Value(X))), m_Value(Y))))) { @@ -3775,6 +3807,10 @@ Instruction *InstCombinerImpl::foldNot(BinaryOperator &I) { return &I; } + if (auto *NotOpI = dyn_cast(NotOp)) + if (sinkNotIntoLogicalOp(*NotOpI)) + return &I; + // Eliminate a bitwise 'not' op of 'not' min/max by inverting the min/max: // ~min(~X, ~Y) --> max(X, Y) // ~max(~X, Y) --> min(X, ~Y) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h index 83623aacc5e429..f700cdb84d573e 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h +++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h @@ -106,6 +106,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final Value *simplifyRangeCheck(ICmpInst *Cmp0, ICmpInst *Cmp1, bool Inverted); Instruction *visitAnd(BinaryOperator &I); Instruction *visitOr(BinaryOperator &I); + bool sinkNotIntoLogicalOp(Instruction &I); bool sinkNotIntoOtherHandOfLogicalOp(Instruction &I); Instruction *visitXor(BinaryOperator &I); Instruction *visitShl(BinaryOperator &I); @@ -330,7 +331,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final Instruction *matchSAddSubSat(IntrinsicInst &MinMax1); Instruction *foldNot(BinaryOperator &I); - void freelyInvertAllUsersOf(Value *V); + void freelyInvertAllUsersOf(Value *V, Value *IgnoredUser = nullptr); /// Determine if a pair of casts can be replaced by a single cast. /// diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp index 736b9aa8ec244d..c6e49110113b94 100644 --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -947,8 +947,10 @@ Value *InstCombinerImpl::SimplifySelectsFeedingBinaryOp(BinaryOperator &I, /// Freely adapt every user of V as-if V was changed to !V. /// WARNING: only if canFreelyInvertAllUsersOf() said this can be done. -void InstCombinerImpl::freelyInvertAllUsersOf(Value *I) { - for (User *U : I->users()) { +void InstCombinerImpl::freelyInvertAllUsersOf(Value *I, Value *IgnoredUser) { + for (User *U : make_early_inc_range(I->users())) { + if (U == IgnoredUser) + continue; // Don't consider this user. switch (cast(U)->getOpcode()) { case Instruction::Select: { auto *SI = cast(U); diff --git a/llvm/test/Transforms/InstCombine/sink-not-into-and.ll b/llvm/test/Transforms/InstCombine/sink-not-into-and.ll index b77e09ca3a6665..9db6440a49ee71 100644 --- a/llvm/test/Transforms/InstCombine/sink-not-into-and.ll +++ b/llvm/test/Transforms/InstCombine/sink-not-into-and.ll @@ -14,8 +14,8 @@ define i1 @t0(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { ; CHECK-LABEL: @t0( ; CHECK-NEXT: [[I1:%.*]] = icmp ne i32 [[V0:%.*]], [[V1:%.*]] ; CHECK-NEXT: [[I2:%.*]] = icmp ne i32 [[V2:%.*]], [[V3:%.*]] -; CHECK-NEXT: [[I4:%.*]] = or i1 [[I2]], [[I1]] -; CHECK-NEXT: ret i1 [[I4]] +; CHECK-NEXT: [[I3_NOT:%.*]] = or i1 [[I2]], [[I1]] +; CHECK-NEXT: ret i1 [[I3_NOT]] ; %i1 = icmp eq i32 %v0, %v1 %i2 = icmp eq i32 %v2, %v3 @@ -115,13 +115,11 @@ define i1 @n6(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { ; Hands have invertible uses define i1 @t7(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { ; CHECK-LABEL: @t7( -; CHECK-NEXT: [[I1:%.*]] = icmp eq i32 [[V0:%.*]], [[V1:%.*]] -; CHECK-NEXT: [[I1_NOT:%.*]] = xor i1 [[I1]], true -; CHECK-NEXT: call void @use1(i1 [[I1_NOT]]) -; CHECK-NEXT: [[I2:%.*]] = icmp eq i32 [[V2:%.*]], [[V3:%.*]] -; CHECK-NEXT: [[I3:%.*]] = and i1 [[I2]], [[I1]] -; CHECK-NEXT: [[I4:%.*]] = xor i1 [[I3]], true -; CHECK-NEXT: ret i1 [[I4]] +; CHECK-NEXT: [[I1:%.*]] = icmp ne i32 [[V0:%.*]], [[V1:%.*]] +; CHECK-NEXT: call void @use1(i1 [[I1]]) +; CHECK-NEXT: [[I2:%.*]] = icmp ne i32 [[V2:%.*]], [[V3:%.*]] +; CHECK-NEXT: [[I3_NOT:%.*]] = or i1 [[I2]], [[I1]] +; CHECK-NEXT: ret i1 [[I3_NOT]] ; %i1 = icmp eq i32 %v0, %v1 %i1.not = xor i1 %i1, -1 @@ -133,13 +131,11 @@ define i1 @t7(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { } define i1 @t8(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { ; CHECK-LABEL: @t8( -; CHECK-NEXT: [[I1:%.*]] = icmp eq i32 [[V0:%.*]], [[V1:%.*]] -; CHECK-NEXT: [[I2:%.*]] = icmp eq i32 [[V2:%.*]], [[V3:%.*]] -; CHECK-NEXT: [[I2_NOT:%.*]] = xor i1 [[I2]], true -; CHECK-NEXT: call void @use1(i1 [[I2_NOT]]) -; CHECK-NEXT: [[I3:%.*]] = and i1 [[I2]], [[I1]] -; CHECK-NEXT: [[I4:%.*]] = xor i1 [[I3]], true -; CHECK-NEXT: ret i1 [[I4]] +; CHECK-NEXT: [[I1:%.*]] = icmp ne i32 [[V0:%.*]], [[V1:%.*]] +; CHECK-NEXT: [[I2:%.*]] = icmp ne i32 [[V2:%.*]], [[V3:%.*]] +; CHECK-NEXT: call void @use1(i1 [[I2]]) +; CHECK-NEXT: [[I3_NOT:%.*]] = or i1 [[I2]], [[I1]] +; CHECK-NEXT: ret i1 [[I3_NOT]] ; %i1 = icmp eq i32 %v0, %v1 %i2 = icmp eq i32 %v2, %v3 @@ -151,15 +147,12 @@ define i1 @t8(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { } define i1 @t9(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { ; CHECK-LABEL: @t9( -; CHECK-NEXT: [[I1:%.*]] = icmp eq i32 [[V0:%.*]], [[V1:%.*]] -; CHECK-NEXT: [[I1_NOT:%.*]] = xor i1 [[I1]], true -; CHECK-NEXT: call void @use1(i1 [[I1_NOT]]) -; CHECK-NEXT: [[I2:%.*]] = icmp eq i32 [[V2:%.*]], [[V3:%.*]] -; CHECK-NEXT: [[I2_NOT:%.*]] = xor i1 [[I2]], true -; CHECK-NEXT: call void @use1(i1 [[I2_NOT]]) -; CHECK-NEXT: [[I3:%.*]] = and i1 [[I2]], [[I1]] -; CHECK-NEXT: [[I4:%.*]] = xor i1 [[I3]], true -; CHECK-NEXT: ret i1 [[I4]] +; CHECK-NEXT: [[I1:%.*]] = icmp ne i32 [[V0:%.*]], [[V1:%.*]] +; CHECK-NEXT: call void @use1(i1 [[I1]]) +; CHECK-NEXT: [[I2:%.*]] = icmp ne i32 [[V2:%.*]], [[V3:%.*]] +; CHECK-NEXT: call void @use1(i1 [[I2]]) +; CHECK-NEXT: [[I3_NOT:%.*]] = or i1 [[I2]], [[I1]] +; CHECK-NEXT: ret i1 [[I3_NOT]] ; %i1 = icmp eq i32 %v0, %v1 %i1.not = xor i1 %i1, -1 @@ -181,8 +174,7 @@ define i1 @n10(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { ; CHECK-NEXT: [[I2:%.*]] = icmp eq i32 [[V2:%.*]], [[V3:%.*]] ; CHECK-NEXT: [[I3:%.*]] = and i1 [[I2]], [[I1]] ; CHECK-NEXT: call void @use1(i1 [[I3]]) -; CHECK-NEXT: [[I4_DEMORGAN:%.*]] = and i1 [[I2]], [[I1]] -; CHECK-NEXT: [[I4:%.*]] = xor i1 [[I4_DEMORGAN]], true +; CHECK-NEXT: [[I4:%.*]] = xor i1 [[I3]], true ; CHECK-NEXT: ret i1 [[I4]] ; %i1 = icmp eq i32 %v0, %v1 @@ -196,14 +188,12 @@ define i1 @n10(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { ; All other uses can be adapted. define i1 @t11(i32 %v0, i32 %v1, i32 %v2, i32 %v3, i1 %v4, i1 %v5) { ; CHECK-LABEL: @t11( -; CHECK-NEXT: [[I1:%.*]] = icmp eq i32 [[V0:%.*]], [[V1:%.*]] -; CHECK-NEXT: [[I2:%.*]] = icmp eq i32 [[V2:%.*]], [[V3:%.*]] -; CHECK-NEXT: [[I3:%.*]] = and i1 [[I2]], [[I1]] -; CHECK-NEXT: [[I4_DEMORGAN:%.*]] = and i1 [[I2]], [[I1]] -; CHECK-NEXT: [[I4:%.*]] = xor i1 [[I4_DEMORGAN]], true -; CHECK-NEXT: [[I5:%.*]] = select i1 [[I3]], i1 [[V4:%.*]], i1 [[V5:%.*]] +; CHECK-NEXT: [[I1:%.*]] = icmp ne i32 [[V0:%.*]], [[V1:%.*]] +; CHECK-NEXT: [[I2:%.*]] = icmp ne i32 [[V2:%.*]], [[V3:%.*]] +; CHECK-NEXT: [[I3_NOT:%.*]] = or i1 [[I2]], [[I1]] +; CHECK-NEXT: [[I5:%.*]] = select i1 [[I3_NOT]], i1 [[V5:%.*]], i1 [[V4:%.*]] ; CHECK-NEXT: call void @use1(i1 [[I5]]) -; CHECK-NEXT: ret i1 [[I4]] +; CHECK-NEXT: ret i1 [[I3_NOT]] ; %i1 = icmp eq i32 %v0, %v1 %i2 = icmp eq i32 %v2, %v3 diff --git a/llvm/test/Transforms/InstCombine/sink-not-into-logical-and.ll b/llvm/test/Transforms/InstCombine/sink-not-into-logical-and.ll index 496b9cdc0873bf..f07840c8bbdc97 100644 --- a/llvm/test/Transforms/InstCombine/sink-not-into-logical-and.ll +++ b/llvm/test/Transforms/InstCombine/sink-not-into-logical-and.ll @@ -14,8 +14,8 @@ define i1 @t0(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { ; CHECK-LABEL: @t0( ; CHECK-NEXT: [[I1:%.*]] = icmp ne i32 [[V0:%.*]], [[V1:%.*]] ; CHECK-NEXT: [[I2:%.*]] = icmp ne i32 [[V2:%.*]], [[V3:%.*]] -; CHECK-NEXT: [[I3:%.*]] = select i1 [[I2]], i1 true, i1 [[I1]] -; CHECK-NEXT: ret i1 [[I3]] +; CHECK-NEXT: [[I3_NOT:%.*]] = select i1 [[I2]], i1 true, i1 [[I1]] +; CHECK-NEXT: ret i1 [[I3_NOT]] ; %i1 = icmp eq i32 %v0, %v1 %i2 = icmp eq i32 %v2, %v3 @@ -115,13 +115,11 @@ define i1 @n6(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { ; Hands have invertible uses define i1 @t7(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { ; CHECK-LABEL: @t7( -; CHECK-NEXT: [[I1:%.*]] = icmp eq i32 [[V0:%.*]], [[V1:%.*]] -; CHECK-NEXT: [[I1_NOT:%.*]] = xor i1 [[I1]], true -; CHECK-NEXT: call void @use1(i1 [[I1_NOT]]) -; CHECK-NEXT: [[I2:%.*]] = icmp eq i32 [[V2:%.*]], [[V3:%.*]] -; CHECK-NEXT: [[I3:%.*]] = select i1 [[I2]], i1 [[I1]], i1 false -; CHECK-NEXT: [[I4:%.*]] = xor i1 [[I3]], true -; CHECK-NEXT: ret i1 [[I4]] +; CHECK-NEXT: [[I1:%.*]] = icmp ne i32 [[V0:%.*]], [[V1:%.*]] +; CHECK-NEXT: call void @use1(i1 [[I1]]) +; CHECK-NEXT: [[I2:%.*]] = icmp ne i32 [[V2:%.*]], [[V3:%.*]] +; CHECK-NEXT: [[I3_NOT:%.*]] = select i1 [[I2]], i1 true, i1 [[I1]] +; CHECK-NEXT: ret i1 [[I3_NOT]] ; %i1 = icmp eq i32 %v0, %v1 %i1.not = xor i1 %i1, -1 @@ -136,8 +134,8 @@ define i1 @t8(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { ; CHECK-NEXT: [[I1:%.*]] = icmp ne i32 [[V0:%.*]], [[V1:%.*]] ; CHECK-NEXT: [[I2:%.*]] = icmp ne i32 [[V2:%.*]], [[V3:%.*]] ; CHECK-NEXT: call void @use1(i1 [[I2]]) -; CHECK-NEXT: [[I3:%.*]] = select i1 [[I2]], i1 true, i1 [[I1]] -; CHECK-NEXT: ret i1 [[I3]] +; CHECK-NEXT: [[I3_NOT:%.*]] = select i1 [[I2]], i1 true, i1 [[I1]] +; CHECK-NEXT: ret i1 [[I3_NOT]] ; %i1 = icmp eq i32 %v0, %v1 %i2 = icmp eq i32 %v2, %v3 @@ -149,15 +147,12 @@ define i1 @t8(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { } define i1 @t9(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { ; CHECK-LABEL: @t9( -; CHECK-NEXT: [[I1:%.*]] = icmp eq i32 [[V0:%.*]], [[V1:%.*]] -; CHECK-NEXT: [[I1_NOT:%.*]] = xor i1 [[I1]], true -; CHECK-NEXT: call void @use1(i1 [[I1_NOT]]) -; CHECK-NEXT: [[I2:%.*]] = icmp eq i32 [[V2:%.*]], [[V3:%.*]] -; CHECK-NEXT: [[I2_NOT:%.*]] = xor i1 [[I2]], true -; CHECK-NEXT: call void @use1(i1 [[I2_NOT]]) -; CHECK-NEXT: [[I3:%.*]] = select i1 [[I2]], i1 [[I1]], i1 false -; CHECK-NEXT: [[I4:%.*]] = xor i1 [[I3]], true -; CHECK-NEXT: ret i1 [[I4]] +; CHECK-NEXT: [[I1:%.*]] = icmp ne i32 [[V0:%.*]], [[V1:%.*]] +; CHECK-NEXT: call void @use1(i1 [[I1]]) +; CHECK-NEXT: [[I2:%.*]] = icmp ne i32 [[V2:%.*]], [[V3:%.*]] +; CHECK-NEXT: call void @use1(i1 [[I2]]) +; CHECK-NEXT: [[I3_NOT:%.*]] = select i1 [[I2]], i1 true, i1 [[I1]] +; CHECK-NEXT: ret i1 [[I3_NOT]] ; %i1 = icmp eq i32 %v0, %v1 %i1.not = xor i1 %i1, -1 @@ -193,13 +188,12 @@ define i1 @n10(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { ; All other uses can be adapted. define i1 @t11(i32 %v0, i32 %v1, i32 %v2, i32 %v3, i1 %v4, i1 %v5) { ; CHECK-LABEL: @t11( -; CHECK-NEXT: [[I1:%.*]] = icmp eq i32 [[V0:%.*]], [[V1:%.*]] -; CHECK-NEXT: [[I2:%.*]] = icmp eq i32 [[V2:%.*]], [[V3:%.*]] -; CHECK-NEXT: [[I3:%.*]] = select i1 [[I2]], i1 [[I1]], i1 false -; CHECK-NEXT: [[I4:%.*]] = xor i1 [[I3]], true -; CHECK-NEXT: [[I5:%.*]] = select i1 [[I3]], i1 [[V4:%.*]], i1 [[V5:%.*]] +; CHECK-NEXT: [[I1:%.*]] = icmp ne i32 [[V0:%.*]], [[V1:%.*]] +; CHECK-NEXT: [[I2:%.*]] = icmp ne i32 [[V2:%.*]], [[V3:%.*]] +; CHECK-NEXT: [[I3_NOT:%.*]] = select i1 [[I2]], i1 true, i1 [[I1]] +; CHECK-NEXT: [[I5:%.*]] = select i1 [[I3_NOT]], i1 [[V5:%.*]], i1 [[V4:%.*]] ; CHECK-NEXT: call void @use1(i1 [[I5]]) -; CHECK-NEXT: ret i1 [[I4]] +; CHECK-NEXT: ret i1 [[I3_NOT]] ; %i1 = icmp eq i32 %v0, %v1 %i2 = icmp eq i32 %v2, %v3 diff --git a/llvm/test/Transforms/InstCombine/sink-not-into-logical-or.ll b/llvm/test/Transforms/InstCombine/sink-not-into-logical-or.ll index 055e81c19bc8b4..017e6972f78b38 100644 --- a/llvm/test/Transforms/InstCombine/sink-not-into-logical-or.ll +++ b/llvm/test/Transforms/InstCombine/sink-not-into-logical-or.ll @@ -14,8 +14,8 @@ define i1 @t0(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { ; CHECK-LABEL: @t0( ; CHECK-NEXT: [[I1:%.*]] = icmp ne i32 [[V0:%.*]], [[V1:%.*]] ; CHECK-NEXT: [[I2:%.*]] = icmp ne i32 [[V2:%.*]], [[V3:%.*]] -; CHECK-NEXT: [[I3:%.*]] = select i1 [[I2]], i1 [[I1]], i1 false -; CHECK-NEXT: ret i1 [[I3]] +; CHECK-NEXT: [[I3_NOT:%.*]] = select i1 [[I2]], i1 [[I1]], i1 false +; CHECK-NEXT: ret i1 [[I3_NOT]] ; %i1 = icmp eq i32 %v0, %v1 %i2 = icmp eq i32 %v2, %v3 @@ -115,13 +115,11 @@ define i1 @n6(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { ; Hands have invertible uses define i1 @t7(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { ; CHECK-LABEL: @t7( -; CHECK-NEXT: [[I1:%.*]] = icmp eq i32 [[V0:%.*]], [[V1:%.*]] -; CHECK-NEXT: [[I1_NOT:%.*]] = xor i1 [[I1]], true -; CHECK-NEXT: call void @use1(i1 [[I1_NOT]]) -; CHECK-NEXT: [[I2:%.*]] = icmp eq i32 [[V2:%.*]], [[V3:%.*]] -; CHECK-NEXT: [[I3:%.*]] = select i1 [[I2]], i1 true, i1 [[I1]] -; CHECK-NEXT: [[I4:%.*]] = xor i1 [[I3]], true -; CHECK-NEXT: ret i1 [[I4]] +; CHECK-NEXT: [[I1:%.*]] = icmp ne i32 [[V0:%.*]], [[V1:%.*]] +; CHECK-NEXT: call void @use1(i1 [[I1]]) +; CHECK-NEXT: [[I2:%.*]] = icmp ne i32 [[V2:%.*]], [[V3:%.*]] +; CHECK-NEXT: [[I3_NOT:%.*]] = select i1 [[I2]], i1 [[I1]], i1 false +; CHECK-NEXT: ret i1 [[I3_NOT]] ; %i1 = icmp eq i32 %v0, %v1 %i1.not = xor i1 %i1, -1 @@ -136,8 +134,8 @@ define i1 @t8(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { ; CHECK-NEXT: [[I1:%.*]] = icmp ne i32 [[V0:%.*]], [[V1:%.*]] ; CHECK-NEXT: [[I2:%.*]] = icmp ne i32 [[V2:%.*]], [[V3:%.*]] ; CHECK-NEXT: call void @use1(i1 [[I2]]) -; CHECK-NEXT: [[I3:%.*]] = select i1 [[I2]], i1 [[I1]], i1 false -; CHECK-NEXT: ret i1 [[I3]] +; CHECK-NEXT: [[I3_NOT:%.*]] = select i1 [[I2]], i1 [[I1]], i1 false +; CHECK-NEXT: ret i1 [[I3_NOT]] ; %i1 = icmp eq i32 %v0, %v1 %i2 = icmp eq i32 %v2, %v3 @@ -149,15 +147,12 @@ define i1 @t8(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { } define i1 @t9(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { ; CHECK-LABEL: @t9( -; CHECK-NEXT: [[I1:%.*]] = icmp eq i32 [[V0:%.*]], [[V1:%.*]] -; CHECK-NEXT: [[I1_NOT:%.*]] = xor i1 [[I1]], true -; CHECK-NEXT: call void @use1(i1 [[I1_NOT]]) -; CHECK-NEXT: [[I2:%.*]] = icmp eq i32 [[V2:%.*]], [[V3:%.*]] -; CHECK-NEXT: [[I2_NOT:%.*]] = xor i1 [[I2]], true -; CHECK-NEXT: call void @use1(i1 [[I2_NOT]]) -; CHECK-NEXT: [[I3:%.*]] = select i1 [[I2]], i1 true, i1 [[I1]] -; CHECK-NEXT: [[I4:%.*]] = xor i1 [[I3]], true -; CHECK-NEXT: ret i1 [[I4]] +; CHECK-NEXT: [[I1:%.*]] = icmp ne i32 [[V0:%.*]], [[V1:%.*]] +; CHECK-NEXT: call void @use1(i1 [[I1]]) +; CHECK-NEXT: [[I2:%.*]] = icmp ne i32 [[V2:%.*]], [[V3:%.*]] +; CHECK-NEXT: call void @use1(i1 [[I2]]) +; CHECK-NEXT: [[I3_NOT:%.*]] = select i1 [[I2]], i1 [[I1]], i1 false +; CHECK-NEXT: ret i1 [[I3_NOT]] ; %i1 = icmp eq i32 %v0, %v1 %i1.not = xor i1 %i1, -1 @@ -193,13 +188,12 @@ define i1 @n10(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { ; All other uses can be adapted. define i1 @t11(i32 %v0, i32 %v1, i32 %v2, i32 %v3, i1 %v4, i1 %v5) { ; CHECK-LABEL: @t11( -; CHECK-NEXT: [[I1:%.*]] = icmp eq i32 [[V0:%.*]], [[V1:%.*]] -; CHECK-NEXT: [[I2:%.*]] = icmp eq i32 [[V2:%.*]], [[V3:%.*]] -; CHECK-NEXT: [[I3:%.*]] = select i1 [[I2]], i1 true, i1 [[I1]] -; CHECK-NEXT: [[I4:%.*]] = xor i1 [[I3]], true -; CHECK-NEXT: [[I5:%.*]] = select i1 [[I3]], i1 [[V4:%.*]], i1 [[V5:%.*]] +; CHECK-NEXT: [[I1:%.*]] = icmp ne i32 [[V0:%.*]], [[V1:%.*]] +; CHECK-NEXT: [[I2:%.*]] = icmp ne i32 [[V2:%.*]], [[V3:%.*]] +; CHECK-NEXT: [[I3_NOT:%.*]] = select i1 [[I2]], i1 [[I1]], i1 false +; CHECK-NEXT: [[I5:%.*]] = select i1 [[I3_NOT]], i1 [[V5:%.*]], i1 [[V4:%.*]] ; CHECK-NEXT: call void @use1(i1 [[I5]]) -; CHECK-NEXT: ret i1 [[I4]] +; CHECK-NEXT: ret i1 [[I3_NOT]] ; %i1 = icmp eq i32 %v0, %v1 %i2 = icmp eq i32 %v2, %v3 diff --git a/llvm/test/Transforms/InstCombine/sink-not-into-or.ll b/llvm/test/Transforms/InstCombine/sink-not-into-or.ll index 1e32220dda120d..0b758112f699e0 100644 --- a/llvm/test/Transforms/InstCombine/sink-not-into-or.ll +++ b/llvm/test/Transforms/InstCombine/sink-not-into-or.ll @@ -14,8 +14,8 @@ define i1 @t0(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { ; CHECK-LABEL: @t0( ; CHECK-NEXT: [[I1:%.*]] = icmp ne i32 [[V0:%.*]], [[V1:%.*]] ; CHECK-NEXT: [[I2:%.*]] = icmp ne i32 [[V2:%.*]], [[V3:%.*]] -; CHECK-NEXT: [[I4:%.*]] = and i1 [[I2]], [[I1]] -; CHECK-NEXT: ret i1 [[I4]] +; CHECK-NEXT: [[I3_NOT:%.*]] = and i1 [[I2]], [[I1]] +; CHECK-NEXT: ret i1 [[I3_NOT]] ; %i1 = icmp eq i32 %v0, %v1 %i2 = icmp eq i32 %v2, %v3 @@ -115,13 +115,11 @@ define i1 @n6(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { ; Hands have invertible uses define i1 @t7(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { ; CHECK-LABEL: @t7( -; CHECK-NEXT: [[I1:%.*]] = icmp eq i32 [[V0:%.*]], [[V1:%.*]] -; CHECK-NEXT: [[I1_NOT:%.*]] = xor i1 [[I1]], true -; CHECK-NEXT: call void @use1(i1 [[I1_NOT]]) -; CHECK-NEXT: [[I2:%.*]] = icmp eq i32 [[V2:%.*]], [[V3:%.*]] -; CHECK-NEXT: [[I3:%.*]] = or i1 [[I2]], [[I1]] -; CHECK-NEXT: [[I4:%.*]] = xor i1 [[I3]], true -; CHECK-NEXT: ret i1 [[I4]] +; CHECK-NEXT: [[I1:%.*]] = icmp ne i32 [[V0:%.*]], [[V1:%.*]] +; CHECK-NEXT: call void @use1(i1 [[I1]]) +; CHECK-NEXT: [[I2:%.*]] = icmp ne i32 [[V2:%.*]], [[V3:%.*]] +; CHECK-NEXT: [[I3_NOT:%.*]] = and i1 [[I2]], [[I1]] +; CHECK-NEXT: ret i1 [[I3_NOT]] ; %i1 = icmp eq i32 %v0, %v1 %i1.not = xor i1 %i1, -1 @@ -133,13 +131,11 @@ define i1 @t7(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { } define i1 @t8(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { ; CHECK-LABEL: @t8( -; CHECK-NEXT: [[I1:%.*]] = icmp eq i32 [[V0:%.*]], [[V1:%.*]] -; CHECK-NEXT: [[I2:%.*]] = icmp eq i32 [[V2:%.*]], [[V3:%.*]] -; CHECK-NEXT: [[I2_NOT:%.*]] = xor i1 [[I2]], true -; CHECK-NEXT: call void @use1(i1 [[I2_NOT]]) -; CHECK-NEXT: [[I3:%.*]] = or i1 [[I2]], [[I1]] -; CHECK-NEXT: [[I4:%.*]] = xor i1 [[I3]], true -; CHECK-NEXT: ret i1 [[I4]] +; CHECK-NEXT: [[I1:%.*]] = icmp ne i32 [[V0:%.*]], [[V1:%.*]] +; CHECK-NEXT: [[I2:%.*]] = icmp ne i32 [[V2:%.*]], [[V3:%.*]] +; CHECK-NEXT: call void @use1(i1 [[I2]]) +; CHECK-NEXT: [[I3_NOT:%.*]] = and i1 [[I2]], [[I1]] +; CHECK-NEXT: ret i1 [[I3_NOT]] ; %i1 = icmp eq i32 %v0, %v1 %i2 = icmp eq i32 %v2, %v3 @@ -151,15 +147,12 @@ define i1 @t8(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { } define i1 @t9(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { ; CHECK-LABEL: @t9( -; CHECK-NEXT: [[I1:%.*]] = icmp eq i32 [[V0:%.*]], [[V1:%.*]] -; CHECK-NEXT: [[I1_NOT:%.*]] = xor i1 [[I1]], true -; CHECK-NEXT: call void @use1(i1 [[I1_NOT]]) -; CHECK-NEXT: [[I2:%.*]] = icmp eq i32 [[V2:%.*]], [[V3:%.*]] -; CHECK-NEXT: [[I2_NOT:%.*]] = xor i1 [[I2]], true -; CHECK-NEXT: call void @use1(i1 [[I2_NOT]]) -; CHECK-NEXT: [[I3:%.*]] = or i1 [[I2]], [[I1]] -; CHECK-NEXT: [[I4:%.*]] = xor i1 [[I3]], true -; CHECK-NEXT: ret i1 [[I4]] +; CHECK-NEXT: [[I1:%.*]] = icmp ne i32 [[V0:%.*]], [[V1:%.*]] +; CHECK-NEXT: call void @use1(i1 [[I1]]) +; CHECK-NEXT: [[I2:%.*]] = icmp ne i32 [[V2:%.*]], [[V3:%.*]] +; CHECK-NEXT: call void @use1(i1 [[I2]]) +; CHECK-NEXT: [[I3_NOT:%.*]] = and i1 [[I2]], [[I1]] +; CHECK-NEXT: ret i1 [[I3_NOT]] ; %i1 = icmp eq i32 %v0, %v1 %i1.not = xor i1 %i1, -1 @@ -181,8 +174,7 @@ define i1 @n10(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { ; CHECK-NEXT: [[I2:%.*]] = icmp eq i32 [[V2:%.*]], [[V3:%.*]] ; CHECK-NEXT: [[I3:%.*]] = or i1 [[I2]], [[I1]] ; CHECK-NEXT: call void @use1(i1 [[I3]]) -; CHECK-NEXT: [[I4_DEMORGAN:%.*]] = or i1 [[I2]], [[I1]] -; CHECK-NEXT: [[I4:%.*]] = xor i1 [[I4_DEMORGAN]], true +; CHECK-NEXT: [[I4:%.*]] = xor i1 [[I3]], true ; CHECK-NEXT: ret i1 [[I4]] ; %i1 = icmp eq i32 %v0, %v1 @@ -196,14 +188,12 @@ define i1 @n10(i32 %v0, i32 %v1, i32 %v2, i32 %v3) { ; All other uses can be adapted. define i1 @t11(i32 %v0, i32 %v1, i32 %v2, i32 %v3, i1 %v4, i1 %v5) { ; CHECK-LABEL: @t11( -; CHECK-NEXT: [[I1:%.*]] = icmp eq i32 [[V0:%.*]], [[V1:%.*]] -; CHECK-NEXT: [[I2:%.*]] = icmp eq i32 [[V2:%.*]], [[V3:%.*]] -; CHECK-NEXT: [[I3:%.*]] = or i1 [[I2]], [[I1]] -; CHECK-NEXT: [[I4_DEMORGAN:%.*]] = or i1 [[I2]], [[I1]] -; CHECK-NEXT: [[I4:%.*]] = xor i1 [[I4_DEMORGAN]], true -; CHECK-NEXT: [[I5:%.*]] = select i1 [[I3]], i1 [[V4:%.*]], i1 [[V5:%.*]] +; CHECK-NEXT: [[I1:%.*]] = icmp ne i32 [[V0:%.*]], [[V1:%.*]] +; CHECK-NEXT: [[I2:%.*]] = icmp ne i32 [[V2:%.*]], [[V3:%.*]] +; CHECK-NEXT: [[I3_NOT:%.*]] = and i1 [[I2]], [[I1]] +; CHECK-NEXT: [[I5:%.*]] = select i1 [[I3_NOT]], i1 [[V5:%.*]], i1 [[V4:%.*]] ; CHECK-NEXT: call void @use1(i1 [[I5]]) -; CHECK-NEXT: ret i1 [[I4]] +; CHECK-NEXT: ret i1 [[I3_NOT]] ; %i1 = icmp eq i32 %v0, %v1 %i2 = icmp eq i32 %v2, %v3