From 86b4d8645fc1b86693fef564cef68f24599c930f Mon Sep 17 00:00:00 2001 From: Sanjay Patel Date: Wed, 8 Feb 2023 16:47:55 -0500 Subject: [PATCH] [InstCombine] canonicalize cmp+select as umin/umax (V == 0) ? 1 : V --> umax(V, 1) (V == UMAX) ? UMAX-1 : V --> umin(V, UMAX-1) https://alive2.llvm.org/ce/z/pfDBAf This is one pair of the variants discussed in issue #60374. Enhancements for the other end of the constant range and signed variants are potential follow-ups, but that may require more work because we canonicalize at least one min/max like that to icmp+zext. --- llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp | 17 +++++++++++++++-- llvm/test/Transforms/InstCombine/div-shift.ll | 3 +-- llvm/test/Transforms/InstCombine/select.ll | 16 +++++++++++----- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp index e7d8208..894b11c 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp @@ -1593,13 +1593,26 @@ static Instruction *foldSelectZeroOrOnes(ICmpInst *Cmp, Value *TVal, return nullptr; } -static Value *foldSelectInstWithICmpConst(SelectInst &SI, ICmpInst *ICI) { +static Value *foldSelectInstWithICmpConst(SelectInst &SI, ICmpInst *ICI, + InstCombiner::BuilderTy &Builder) { const APInt *CmpC; Value *V; CmpInst::Predicate Pred; if (!match(ICI, m_ICmp(Pred, m_Value(V), m_APInt(CmpC)))) return nullptr; + // Match clamp away from min/max value as a max/min operation. + Value *TVal = SI.getTrueValue(); + Value *FVal = SI.getFalseValue(); + if (Pred == ICmpInst::ICMP_EQ && V == FVal) { + // (V == 0) ? 1 : V --> umax(V, 1) + if (CmpC->isMinValue() && match(TVal, m_SpecificInt(*CmpC + 1))) + return Builder.CreateBinaryIntrinsic(Intrinsic::umax, V, TVal); + // (V == UMAX) ? UMAX-1 : V --> umin(V, UMAX-1) + if (CmpC->isMaxValue() && match(TVal, m_SpecificInt(*CmpC - 1))) + return Builder.CreateBinaryIntrinsic(Intrinsic::umin, V, TVal); + } + BinaryOperator *BO; const APInt *C; CmpInst::Predicate CPred; @@ -1632,7 +1645,7 @@ Instruction *InstCombinerImpl::foldSelectInstWithICmp(SelectInst &SI, if (Instruction *NewSPF = canonicalizeSPF(SI, *ICI, *this)) return NewSPF; - if (Value *V = foldSelectInstWithICmpConst(SI, ICI)) + if (Value *V = foldSelectInstWithICmpConst(SI, ICI, Builder)) return replaceInstUsesWith(SI, V); if (Value *V = canonicalizeClampLike(SI, *ICI, Builder)) diff --git a/llvm/test/Transforms/InstCombine/div-shift.ll b/llvm/test/Transforms/InstCombine/div-shift.ll index b87ace5..efdaa0c 100644 --- a/llvm/test/Transforms/InstCombine/div-shift.ll +++ b/llvm/test/Transforms/InstCombine/div-shift.ll @@ -90,8 +90,7 @@ define i32 @t5(i1 %x, i1 %y, i32 %V) { define i32 @t6(i32 %x, i32 %z) { ; CHECK-LABEL: @t6( -; CHECK-NEXT: [[X_IS_ZERO:%.*]] = icmp eq i32 [[X:%.*]], 0 -; CHECK-NEXT: [[DIVISOR:%.*]] = select i1 [[X_IS_ZERO]], i32 1, i32 [[X]] +; CHECK-NEXT: [[DIVISOR:%.*]] = call i32 @llvm.umax.i32(i32 [[X:%.*]], i32 1) ; CHECK-NEXT: [[Y:%.*]] = udiv i32 [[Z:%.*]], [[DIVISOR]] ; CHECK-NEXT: ret i32 [[Y]] ; diff --git a/llvm/test/Transforms/InstCombine/select.ll b/llvm/test/Transforms/InstCombine/select.ll index 96303ba..87a26cf 100644 --- a/llvm/test/Transforms/InstCombine/select.ll +++ b/llvm/test/Transforms/InstCombine/select.ll @@ -3399,8 +3399,7 @@ define @scalable_non_zero( %x) { define i32 @clamp_zero(i32 %x) { ; CHECK-LABEL: @clamp_zero( -; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[X:%.*]], 0 -; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i32 1, i32 [[X]] +; CHECK-NEXT: [[SEL:%.*]] = call i32 @llvm.umax.i32(i32 [[X:%.*]], i32 1) ; CHECK-NEXT: ret i32 [[SEL]] ; %cmp = icmp eq i32 %x, 0 @@ -3412,7 +3411,7 @@ define i32 @clamp_zero_use(i32 %x) { ; CHECK-LABEL: @clamp_zero_use( ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[X:%.*]], 0 ; CHECK-NEXT: call void @use1(i1 [[CMP]]) -; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i32 1, i32 [[X]] +; CHECK-NEXT: [[SEL:%.*]] = call i32 @llvm.umax.i32(i32 [[X]], i32 1) ; CHECK-NEXT: ret i32 [[SEL]] ; %cmp = icmp eq i32 %x, 0 @@ -3421,6 +3420,8 @@ define i32 @clamp_zero_use(i32 %x) { ret i32 %sel } +; negative test - wrong cmp constant + define i32 @not_clamp_zero1(i32 %x) { ; CHECK-LABEL: @not_clamp_zero1( ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[X:%.*]], 2 @@ -3432,6 +3433,8 @@ define i32 @not_clamp_zero1(i32 %x) { ret i32 %sel } +; negative test - wrong select constant + define i32 @not_clamp_zero2(i32 %x) { ; CHECK-LABEL: @not_clamp_zero2( ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[X:%.*]], 0 @@ -3445,8 +3448,7 @@ define i32 @not_clamp_zero2(i32 %x) { define <2 x i8> @clamp_umaxval(<2 x i8> %x) { ; CHECK-LABEL: @clamp_umaxval( -; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i8> [[X:%.*]], -; CHECK-NEXT: [[SEL:%.*]] = select <2 x i1> [[CMP]], <2 x i8> , <2 x i8> [[X]] +; CHECK-NEXT: [[SEL:%.*]] = call <2 x i8> @llvm.umin.v2i8(<2 x i8> [[X:%.*]], <2 x i8> ) ; CHECK-NEXT: ret <2 x i8> [[SEL]] ; %cmp = icmp eq <2 x i8> %x, @@ -3454,6 +3456,8 @@ define <2 x i8> @clamp_umaxval(<2 x i8> %x) { ret <2 x i8> %sel } +; negative test - wrong cmp constant + define i8 @not_clamp_umax1(i8 %x) { ; CHECK-LABEL: @not_clamp_umax1( ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[X:%.*]], -3 @@ -3465,6 +3469,8 @@ define i8 @not_clamp_umax1(i8 %x) { ret i8 %sel } +; negative test - wrong select constant + define i8 @not_clamp_umax2(i8 %x) { ; CHECK-LABEL: @not_clamp_umax2( ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[X:%.*]], -1 -- 2.7.4