diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -937,6 +937,16 @@ return nullptr; } +static BinaryOperator *tryInverseOperator(BinaryOperator *Op, + InstCombiner::BuilderTy &Builder) { + // xor (ashr %x, %y), -1 --> ashr (xor %x, -1), %y + Value *X, *Y; + if (match(Op, m_OneUse(m_Not(m_AShr(m_Value(X), m_Value(Y)))))) + return BinaryOperator::CreateAShr(Builder.CreateNot(X), Y); + + return nullptr; +} + Value *InstCombinerImpl::tryFactorizationFolds(BinaryOperator &I) { Value *LHS = I.getOperand(0), *RHS = I.getOperand(1); BinaryOperator *Op0 = dyn_cast(LHS); @@ -952,9 +962,51 @@ // The instruction has the form "(A op' B) op (C op' D)". Try to factorize // a common term. - if (Op0 && Op1 && LHSOpcode == RHSOpcode) - if (Value *V = tryFactorization(I, SQ, Builder, LHSOpcode, A, B, C, D)) - return V; + if (Op0 && Op1) { + if (LHSOpcode == RHSOpcode) + if (Value *V = tryFactorization(I, SQ, Builder, LHSOpcode, A, B, C, D)) + return V; + + // Try inverse LHS and RHS operators + Value *Result = nullptr; + Value *InversedA, *InversedB, *InversedC, *InversedD; + Instruction::BinaryOps InversedLHSOpcode, InversedRHSOpcode; + BinaryOperator *InversedOp0 = nullptr; + BinaryOperator *InversedOp1 = nullptr; + + InversedOp0 = tryInverseOperator(Op0, Builder); + if (InversedOp0) { + InversedLHSOpcode = getBinOpsForFactorization(TopLevelOpcode, InversedOp0, + InversedA, InversedB); + if (InversedLHSOpcode == RHSOpcode) + Result = tryFactorization(I, SQ, Builder, RHSOpcode, InversedA, + InversedB, C, D); + } + + if (!Result) { + InversedOp1 = tryInverseOperator(Op1, Builder); + if (InversedOp1) { + InversedRHSOpcode = getBinOpsForFactorization( + TopLevelOpcode, InversedOp1, InversedC, InversedD); + if (LHSOpcode == InversedRHSOpcode) + Result = tryFactorization(I, SQ, Builder, LHSOpcode, A, B, InversedC, + InversedD); + } + } + + if (!Result && InversedOp0 && InversedOp1 && + InversedLHSOpcode == InversedRHSOpcode) + Result = tryFactorization(I, SQ, Builder, InversedLHSOpcode, InversedA, + InversedB, InversedC, InversedD); + + if (InversedOp0) + InversedOp0->dropAllReferences(); + if (InversedOp1) + InversedOp1->dropAllReferences(); + + if (Result) + return Result; + } // The instruction has the form "(A op' B) op (C)". Try to factorize common // term. diff --git a/llvm/test/Transforms/InstCombine/and-or-shift.ll b/llvm/test/Transforms/InstCombine/and-or-shift.ll --- a/llvm/test/Transforms/InstCombine/and-or-shift.ll +++ b/llvm/test/Transforms/InstCombine/and-or-shift.ll @@ -6,10 +6,9 @@ define i8 @and_ashr_sub_nsw_x_not_x(i8 %x, i8 %y) { ; CHECK-LABEL: @and_ashr_sub_nsw_x_not_x( ; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 0, [[X:%.*]] -; CHECK-NEXT: [[SHR:%.*]] = ashr i8 [[SUB]], [[Y:%.*]] -; CHECK-NEXT: [[NOT_NOT:%.*]] = ashr i8 [[X]], [[Y]] -; CHECK-NEXT: [[SHR1:%.*]] = xor i8 [[NOT_NOT]], -1 -; CHECK-NEXT: [[AND:%.*]] = and i8 [[SHR]], [[SHR1]] +; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[X]], -1 +; CHECK-NEXT: [[SHR2:%.*]] = and i8 [[SUB]], [[TMP1]] +; CHECK-NEXT: [[AND:%.*]] = ashr i8 [[SHR2]], [[Y:%.*]] ; CHECK-NEXT: ret i8 [[AND]] ; %sub = sub nsw i8 0, %x @@ -23,10 +22,9 @@ define i8 @and_ashr_sub_nsw_x_not_x_commuted(i8 %x, i8 %y) { ; CHECK-LABEL: @and_ashr_sub_nsw_x_not_x_commuted( ; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 0, [[X:%.*]] -; CHECK-NEXT: [[SHR:%.*]] = ashr i8 [[SUB]], [[Y:%.*]] -; CHECK-NEXT: [[NOT_NOT:%.*]] = ashr i8 [[X]], [[Y]] -; CHECK-NEXT: [[SHR1:%.*]] = xor i8 [[NOT_NOT]], -1 -; CHECK-NEXT: [[AND:%.*]] = and i8 [[SHR]], [[SHR1]] +; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[X]], -1 +; CHECK-NEXT: [[SHR2:%.*]] = and i8 [[SUB]], [[TMP1]] +; CHECK-NEXT: [[AND:%.*]] = ashr i8 [[SHR2]], [[Y:%.*]] ; CHECK-NEXT: ret i8 [[AND]] ; %sub = sub nsw i8 0, %x @@ -72,10 +70,9 @@ define <4 x i8> @and_ashr_sub_nsw_x_not_x_vec(<4 x i8> %x, <4 x i8> %y) { ; CHECK-LABEL: @and_ashr_sub_nsw_x_not_x_vec( ; CHECK-NEXT: [[SUB:%.*]] = sub nsw <4 x i8> zeroinitializer, [[X:%.*]] -; CHECK-NEXT: [[SHR:%.*]] = ashr <4 x i8> [[SUB]], [[Y:%.*]] -; CHECK-NEXT: [[NOT_NOT:%.*]] = ashr <4 x i8> [[X]], [[Y]] -; CHECK-NEXT: [[SHR1:%.*]] = xor <4 x i8> [[NOT_NOT]], -; CHECK-NEXT: [[AND:%.*]] = and <4 x i8> [[SHR]], [[SHR1]] +; CHECK-NEXT: [[TMP1:%.*]] = xor <4 x i8> [[X]], +; CHECK-NEXT: [[SHR2:%.*]] = and <4 x i8> [[SUB]], [[TMP1]] +; CHECK-NEXT: [[AND:%.*]] = ashr <4 x i8> [[SHR2]], [[Y:%.*]] ; CHECK-NEXT: ret <4 x i8> [[AND]] ; %sub = sub nsw <4 x i8> , %x @@ -89,10 +86,9 @@ define <4 x i8> @and_ashr_sub_nsw_x_not_x_vec_commuted(<4 x i8> %x, <4 x i8> %y) { ; CHECK-LABEL: @and_ashr_sub_nsw_x_not_x_vec_commuted( ; CHECK-NEXT: [[SUB:%.*]] = sub nsw <4 x i8> zeroinitializer, [[X:%.*]] -; CHECK-NEXT: [[SHR:%.*]] = ashr <4 x i8> [[SUB]], [[Y:%.*]] -; CHECK-NEXT: [[NOT_NOT:%.*]] = ashr <4 x i8> [[X]], [[Y]] -; CHECK-NEXT: [[SHR1:%.*]] = xor <4 x i8> [[NOT_NOT]], -; CHECK-NEXT: [[AND:%.*]] = and <4 x i8> [[SHR]], [[SHR1]] +; CHECK-NEXT: [[TMP1:%.*]] = xor <4 x i8> [[X]], +; CHECK-NEXT: [[SHR2:%.*]] = and <4 x i8> [[SUB]], [[TMP1]] +; CHECK-NEXT: [[AND:%.*]] = ashr <4 x i8> [[SHR2]], [[Y:%.*]] ; CHECK-NEXT: ret <4 x i8> [[AND]] ; %sub = sub nsw <4 x i8> , %x @@ -140,10 +136,9 @@ define i8 @or_ashr_sub_nsw_x_not_x(i8 %x, i8 %y) { ; CHECK-LABEL: @or_ashr_sub_nsw_x_not_x( ; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 0, [[X:%.*]] -; CHECK-NEXT: [[SHR:%.*]] = ashr i8 [[SUB]], [[Y:%.*]] -; CHECK-NEXT: [[NOT_NOT:%.*]] = ashr i8 [[X]], [[Y]] -; CHECK-NEXT: [[SHR1:%.*]] = xor i8 [[NOT_NOT]], -1 -; CHECK-NEXT: [[OR:%.*]] = or i8 [[SHR]], [[SHR1]] +; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[X]], -1 +; CHECK-NEXT: [[SHR2:%.*]] = or i8 [[SUB]], [[TMP1]] +; CHECK-NEXT: [[OR:%.*]] = ashr i8 [[SHR2]], [[Y:%.*]] ; CHECK-NEXT: ret i8 [[OR]] ; %sub = sub nsw i8 0, %x @@ -157,10 +152,9 @@ define i8 @or_ashr_sub_nsw_x_not_x_commuted(i8 %x, i8 %y) { ; CHECK-LABEL: @or_ashr_sub_nsw_x_not_x_commuted( ; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 0, [[X:%.*]] -; CHECK-NEXT: [[SHR:%.*]] = ashr i8 [[SUB]], [[Y:%.*]] -; CHECK-NEXT: [[NOT_NOT:%.*]] = ashr i8 [[X]], [[Y]] -; CHECK-NEXT: [[SHR1:%.*]] = xor i8 [[NOT_NOT]], -1 -; CHECK-NEXT: [[OR:%.*]] = or i8 [[SHR]], [[SHR1]] +; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[X]], -1 +; CHECK-NEXT: [[SHR2:%.*]] = or i8 [[SUB]], [[TMP1]] +; CHECK-NEXT: [[OR:%.*]] = ashr i8 [[SHR2]], [[Y:%.*]] ; CHECK-NEXT: ret i8 [[OR]] ; %sub = sub nsw i8 0, %x @@ -207,10 +201,9 @@ define <4 x i8> @or_ashr_sub_nsw_x_not_x_vec(<4 x i8> %x, <4 x i8> %y) { ; CHECK-LABEL: @or_ashr_sub_nsw_x_not_x_vec( ; CHECK-NEXT: [[SUB:%.*]] = sub nsw <4 x i8> zeroinitializer, [[X:%.*]] -; CHECK-NEXT: [[SHR:%.*]] = ashr <4 x i8> [[SUB]], [[Y:%.*]] -; CHECK-NEXT: [[NOT_NOT:%.*]] = ashr <4 x i8> [[X]], [[Y]] -; CHECK-NEXT: [[SHR1:%.*]] = xor <4 x i8> [[NOT_NOT]], -; CHECK-NEXT: [[OR:%.*]] = or <4 x i8> [[SHR]], [[SHR1]] +; CHECK-NEXT: [[TMP1:%.*]] = xor <4 x i8> [[X]], +; CHECK-NEXT: [[SHR2:%.*]] = or <4 x i8> [[SUB]], [[TMP1]] +; CHECK-NEXT: [[OR:%.*]] = ashr <4 x i8> [[SHR2]], [[Y:%.*]] ; CHECK-NEXT: ret <4 x i8> [[OR]] ; %sub = sub nsw <4 x i8> , %x @@ -224,10 +217,9 @@ define <4 x i8> @or_ashr_sub_nsw_x_not_x_vec_commuted(<4 x i8> %x, <4 x i8> %y) { ; CHECK-LABEL: @or_ashr_sub_nsw_x_not_x_vec_commuted( ; CHECK-NEXT: [[SUB:%.*]] = sub nsw <4 x i8> zeroinitializer, [[X:%.*]] -; CHECK-NEXT: [[SHR:%.*]] = ashr <4 x i8> [[SUB]], [[Y:%.*]] -; CHECK-NEXT: [[NOT_NOT:%.*]] = ashr <4 x i8> [[X]], [[Y]] -; CHECK-NEXT: [[SHR1:%.*]] = xor <4 x i8> [[NOT_NOT]], -; CHECK-NEXT: [[OR:%.*]] = or <4 x i8> [[SHR]], [[SHR1]] +; CHECK-NEXT: [[TMP1:%.*]] = xor <4 x i8> [[X]], +; CHECK-NEXT: [[SHR2:%.*]] = or <4 x i8> [[SUB]], [[TMP1]] +; CHECK-NEXT: [[OR:%.*]] = ashr <4 x i8> [[SHR2]], [[Y:%.*]] ; CHECK-NEXT: ret <4 x i8> [[OR]] ; %sub = sub nsw <4 x i8> , %x