diff --git a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp --- a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp +++ b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp @@ -167,19 +167,39 @@ // %N = sub i32 0, %X // %C = icmp slt i32 %X, 0 // %NABS = select i1 %C, i32 %X, i32 %N + // We should also recognize some abs whose cond is compared to zero. In + // isEqualImpl, it will get true result for one abs select and one select + // which is not recognized as abs. See inverse predicate check in funciton + // isEqualImpl. we must recognize these abs too, otherwise we get different + // hash values for equal instructions and miss cse opportunity. Flavor = SPF_UNKNOWN; CmpInst::Predicate Pred; - if (match(Cond, m_ICmp(Pred, m_Specific(B), m_ZeroInt())) && - Pred == ICmpInst::ICMP_SLT && match(A, m_Neg(m_Specific(B)))) { - // ABS: B < 0 ? -B : B - Flavor = SPF_ABS; - return true; - } - if (match(Cond, m_ICmp(Pred, m_Specific(A), m_ZeroInt())) && - Pred == ICmpInst::ICMP_SLT && match(B, m_Neg(m_Specific(A)))) { - // NABS: A < 0 ? A : -A - Flavor = SPF_NABS; - return true; + if (match(A, m_Neg(m_Specific(B))) || match(B, m_Neg(m_Specific(A)))) { + if (match(Cond, m_ICmp(Pred, m_Specific(B), m_ZeroInt()))) { + if (Pred == ICmpInst::ICMP_SLT || Pred == ICmpInst::ICMP_SLE) { + // ABS: B <= 0 ? -B : B + // ABS: B < 0 ? -B : B + Flavor = SPF_ABS; + return true; + } else if (Pred == ICmpInst::ICMP_SGE || Pred == ICmpInst::ICMP_SGT) { + // NABS: B >= 0 ? -B : B + // NABS: B > 0 ? -B : B + Flavor = SPF_NABS; + return true; + } + } else if (match(Cond, m_ICmp(Pred, m_Specific(A), m_ZeroInt()))) { + if (Pred == ICmpInst::ICMP_SLT || Pred == ICmpInst::ICMP_SLE) { + // NABS: A <= 0 ? A : -A + // NABS: A < 0 ? A : -A + Flavor = SPF_NABS; + return true; + } else if (Pred == ICmpInst::ICMP_SGE || Pred == ICmpInst::ICMP_SGT) { + // ABS: A >= 0 ? A : -A + // ABS: A > 0 ? A : -A + Flavor = SPF_ABS; + return true; + } + } } if (!match(Cond, m_ICmp(Pred, m_Specific(A), m_Specific(B)))) { @@ -251,7 +271,8 @@ return hash_combine(Inst->getOpcode(), SPF, A, B); } if (SPF == SPF_ABS || SPF == SPF_NABS) { - // ABS/NABS always puts the input in A and its negation in B. + if (A > B) + std::swap(A, B); return hash_combine(Inst->getOpcode(), SPF, A, B); } @@ -379,10 +400,8 @@ return ((LHSA == RHSA && LHSB == RHSB) || (LHSA == RHSB && LHSB == RHSA)); - if (LSPF == SPF_ABS || LSPF == SPF_NABS) { - // Abs results are placed in a defined order by matchSelectPattern. - return LHSA == RHSA && LHSB == RHSB; - } + if (LSPF == SPF_ABS || LSPF == SPF_NABS) + return LHSA == RHSA || match(LHSA, m_Neg(m_Specific(RHSA))); // select Cond, A, B <--> select not(Cond), B, A if (CondL == CondR && LHSA == RHSA && LHSB == RHSB) diff --git a/llvm/test/Transforms/EarlyCSE/commute.ll b/llvm/test/Transforms/EarlyCSE/commute.ll --- a/llvm/test/Transforms/EarlyCSE/commute.ll +++ b/llvm/test/Transforms/EarlyCSE/commute.ll @@ -301,9 +301,39 @@ ret i8 %r } -; Abs/nabs may exist with non-canonical operands. Value tracking can match those. -; But we do not use value tracking, so we expect instcombine will canonicalize -; this code to a form that allows CSE. +define i8 @abs_swapped_sge(i8 %a) { +; CHECK-LABEL: @abs_swapped_sge( +; CHECK-NEXT: [[NEG:%.*]] = sub i8 0, [[A:%.*]] +; CHECK-NEXT: [[CMP1:%.*]] = icmp sge i8 [[A]], 0 +; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i8 [[A]], 0 +; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 [[NEG]] +; CHECK-NEXT: ret i8 0 +; + %neg = sub i8 0, %a + %cmp1 = icmp sge i8 %a, 0 + %cmp2 = icmp slt i8 %a, 0 + %m1 = select i1 %cmp1, i8 %a, i8 %neg + %m2 = select i1 %cmp2, i8 %neg, i8 %a + %r = xor i8 %m2, %m1 + ret i8 %r +} + +define i8 @nabs_swapped_sge(i8 %a) { +; CHECK-LABEL: @nabs_swapped_sge( +; CHECK-NEXT: [[NEG:%.*]] = sub i8 0, [[A:%.*]] +; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i8 [[A]], 0 +; CHECK-NEXT: [[CMP2:%.*]] = icmp sge i8 [[A]], 0 +; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 [[NEG]] +; CHECK-NEXT: ret i8 0 +; + %neg = sub i8 0, %a + %cmp1 = icmp slt i8 %a, 0 + %cmp2 = icmp sge i8 %a, 0 + %m1 = select i1 %cmp1, i8 %a, i8 %neg + %m2 = select i1 %cmp2, i8 %neg, i8 %a + %r = xor i8 %m2, %m1 + ret i8 %r +} define i8 @abs_swapped(i8 %a) { ; CHECK-LABEL: @abs_swapped( @@ -311,9 +341,7 @@ ; CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i8 [[A]], 0 ; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i8 [[A]], 0 ; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 [[NEG]] -; CHECK-NEXT: [[M2:%.*]] = select i1 [[CMP2]], i8 [[NEG]], i8 [[A]] -; CHECK-NEXT: [[R:%.*]] = or i8 [[M2]], [[M1]] -; CHECK-NEXT: ret i8 [[R]] +; CHECK-NEXT: ret i8 [[M1]] ; %neg = sub i8 0, %a %cmp1 = icmp sgt i8 %a, 0 @@ -341,19 +369,13 @@ ret i8 %r } -; Abs/nabs may exist with non-canonical operands. Value tracking can match those. -; But we do not use value tracking, so we expect instcombine will canonicalize -; this code to a form that allows CSE. - define i8 @nabs_swapped(i8 %a) { ; CHECK-LABEL: @nabs_swapped( ; CHECK-NEXT: [[NEG:%.*]] = sub i8 0, [[A:%.*]] ; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i8 [[A]], 0 ; CHECK-NEXT: [[CMP2:%.*]] = icmp sgt i8 [[A]], 0 ; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 [[NEG]] -; CHECK-NEXT: [[M2:%.*]] = select i1 [[CMP2]], i8 [[NEG]], i8 [[A]] -; CHECK-NEXT: [[R:%.*]] = xor i8 [[M2]], [[M1]] -; CHECK-NEXT: ret i8 [[R]] +; CHECK-NEXT: ret i8 0 ; %neg = sub i8 0, %a %cmp1 = icmp slt i8 %a, 0 @@ -694,9 +716,10 @@ ; CHECK-LABEL: @inverted_max( ; CHECK-NEXT: [[CMP:%.*]] = icmp sle i32 0, [[I:%.*]] ; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP]], i32 [[I]], i32 0 -; CHECK-NEXT: [[CMPINV:%.*]] = icmp sgt i32 0, [[I:%.*]] +; CHECK-NEXT: [[CMPINV:%.*]] = icmp sgt i32 0, [[I]] ; CHECK-NEXT: [[R:%.*]] = add i32 [[M1]], [[M1]] ; CHECK-NEXT: ret i32 [[R]] +; %cmp = icmp sle i32 0, %i %m1 = select i1 %cmp, i32 %i, i32 0 %cmpinv = icmp sgt i32 0, %i