Index: llvm/trunk/lib/Transforms/Scalar/EarlyCSE.cpp =================================================================== --- llvm/trunk/lib/Transforms/Scalar/EarlyCSE.cpp +++ llvm/trunk/lib/Transforms/Scalar/EarlyCSE.cpp @@ -27,6 +27,7 @@ #include "llvm/Analysis/MemorySSAUpdater.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/TargetTransformInfo.h" +#include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DataLayout.h" @@ -142,6 +143,19 @@ return hash_combine(Inst->getOpcode(), Pred, LHS, RHS); } + // Hash min/max (cmp + select) to allow for commuted operands, non-canonical + // compare predicate (eg, the compare for smin may use 'sgt' rather than + // 'slt'), and non-canonical operands in the compare. + Value *A, *B; + SelectPatternFlavor SPF = matchSelectPattern(Inst, A, B).Flavor; + // TODO: We should also detect abs and FP min/max. + if (SPF == SPF_SMIN || SPF == SPF_SMAX || + SPF == SPF_UMIN || SPF == SPF_UMAX) { + if (A > B) + std::swap(A, B); + return hash_combine(Inst->getOpcode(), SPF, A, B); + } + if (CastInst *CI = dyn_cast(Inst)) return hash_combine(CI->getOpcode(), CI->getType(), CI->getOperand(0)); @@ -200,6 +214,19 @@ LHSCmp->getSwappedPredicate() == RHSCmp->getPredicate(); } + // Min/max can occur with commuted operands, non-canonical predicates, and/or + // non-canonical operands. + Value *LHSA, *LHSB; + SelectPatternFlavor LSPF = matchSelectPattern(LHSI, LHSA, LHSB).Flavor; + // TODO: We should also detect abs and FP min/max. + if (LSPF == SPF_SMIN || LSPF == SPF_SMAX || + LSPF == SPF_UMIN || LSPF == SPF_UMAX) { + Value *RHSA, *RHSB; + SelectPatternFlavor RSPF = matchSelectPattern(RHSI, RHSA, RHSB).Flavor; + return (LSPF == RSPF && ((LHSA == RHSA && LHSB == RHSB) || + (LHSA == RHSB && LHSB == RHSA))); + } + return false; } Index: llvm/trunk/test/Transforms/EarlyCSE/commute.ll =================================================================== --- llvm/trunk/test/Transforms/EarlyCSE/commute.ll +++ llvm/trunk/test/Transforms/EarlyCSE/commute.ll @@ -78,8 +78,7 @@ ; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i8 %a, %b ; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i8 %b, %a ; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %a, i8 %b -; CHECK-NEXT: [[M2:%.*]] = select i1 [[CMP2]], i8 %b, i8 %a -; CHECK-NEXT: [[R:%.*]] = mul i8 [[M1]], [[M2]] +; CHECK-NEXT: [[R:%.*]] = mul i8 [[M1]], [[M1]] ; CHECK-NEXT: ret i8 [[R]] ; %cmp1 = icmp slt i8 %a, %b @@ -97,9 +96,7 @@ ; CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i8 %a, %b ; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i8 %a, %b ; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %b, i8 %a -; CHECK-NEXT: [[M2:%.*]] = select i1 [[CMP2]], i8 %a, i8 %b -; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[M2]], [[M1]] -; CHECK-NEXT: ret i1 [[R]] +; CHECK-NEXT: ret i1 true ; %cmp1 = icmp sgt i8 %a, %b %cmp2 = icmp slt i8 %a, %b @@ -114,9 +111,7 @@ ; CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i8 %a, %b ; CHECK-NEXT: [[CMP2:%.*]] = icmp sgt i8 %b, %a ; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %a, i8 %b -; CHECK-NEXT: [[M2:%.*]] = select i1 [[CMP2]], i8 %b, i8 %a -; CHECK-NEXT: [[R:%.*]] = urem i8 [[M2]], [[M1]] -; CHECK-NEXT: ret i8 [[R]] +; CHECK-NEXT: ret i8 0 ; %cmp1 = icmp sgt i8 %a, %b %cmp2 = icmp sgt i8 %b, %a @@ -131,9 +126,7 @@ ; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i8 %a, %b ; CHECK-NEXT: [[CMP2:%.*]] = icmp sgt i8 %a, %b ; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %b, i8 %a -; CHECK-NEXT: [[M2:%.*]] = select i1 [[CMP2]], i8 %a, i8 %b -; CHECK-NEXT: [[R:%.*]] = sdiv i8 [[M1]], [[M2]] -; CHECK-NEXT: ret i8 [[R]] +; CHECK-NEXT: ret i8 1 ; %cmp1 = icmp slt i8 %a, %b %cmp2 = icmp sgt i8 %a, %b @@ -148,9 +141,7 @@ ; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i8 %a, %b ; CHECK-NEXT: [[CMP2:%.*]] = icmp ult i8 %b, %a ; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %a, i8 %b -; CHECK-NEXT: [[M2:%.*]] = select i1 [[CMP2]], i8 %b, i8 %a -; CHECK-NEXT: [[R:%.*]] = sub i8 [[M2]], [[M1]] -; CHECK-NEXT: ret i8 [[R]] +; CHECK-NEXT: ret i8 0 ; %cmp1 = icmp ult i8 %a, %b %cmp2 = icmp ult i8 %b, %a @@ -167,9 +158,7 @@ ; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt <2 x i8> %a, %b ; CHECK-NEXT: [[CMP2:%.*]] = icmp ult <2 x i8> %a, %b ; CHECK-NEXT: [[M1:%.*]] = select <2 x i1> [[CMP1]], <2 x i8> %b, <2 x i8> %a -; CHECK-NEXT: [[M2:%.*]] = select <2 x i1> [[CMP2]], <2 x i8> %a, <2 x i8> %b -; CHECK-NEXT: [[R:%.*]] = sub <2 x i8> [[M2]], [[M1]] -; CHECK-NEXT: ret <2 x i8> [[R]] +; CHECK-NEXT: ret <2 x i8> zeroinitializer ; %cmp1 = icmp ugt <2 x i8> %a, %b %cmp2 = icmp ult <2 x i8> %a, %b @@ -184,9 +173,7 @@ ; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 %a, %b ; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 %b, %a ; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %a, i8 %b -; CHECK-NEXT: [[M2:%.*]] = select i1 [[CMP2]], i8 %b, i8 %a -; CHECK-NEXT: [[R:%.*]] = udiv i8 [[M1]], [[M2]] -; CHECK-NEXT: ret i8 [[R]] +; CHECK-NEXT: ret i8 1 ; %cmp1 = icmp ugt i8 %a, %b %cmp2 = icmp ugt i8 %b, %a @@ -201,8 +188,7 @@ ; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i8 %a, %b ; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 %a, %b ; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 %b, i8 %a -; CHECK-NEXT: [[M2:%.*]] = select i1 [[CMP2]], i8 %a, i8 %b -; CHECK-NEXT: [[R:%.*]] = add i8 [[M2]], [[M1]] +; CHECK-NEXT: [[R:%.*]] = add i8 [[M1]], [[M1]] ; CHECK-NEXT: ret i8 [[R]] ; %cmp1 = icmp ult i8 %a, %b @@ -213,3 +199,22 @@ ret i8 %r } +; Min/max may exist with non-canonical operands. Value tracking can match those. + +define i8 @smax_nsw(i8 %a, i8 %b) { +; CHECK-LABEL: @smax_nsw( +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 %a, %b +; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i8 %a, %b +; CHECK-NEXT: [[CMP2:%.*]] = icmp sgt i8 [[SUB]], 0 +; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 0, i8 [[SUB]] +; CHECK-NEXT: ret i8 0 +; + %sub = sub nsw i8 %a, %b + %cmp1 = icmp slt i8 %a, %b + %cmp2 = icmp sgt i8 %sub, 0 + %m1 = select i1 %cmp1, i8 0, i8 %sub + %m2 = select i1 %cmp2, i8 %sub, i8 0 + %r = sub i8 %m2, %m1 + ret i8 %r +} +