[llvm] 6063e6b - [InstCombine] move add after min/max intrinsic
Sanjay Patel via llvm-commits
llvm-commits at lists.llvm.org
Sun Sep 26 06:49:18 PDT 2021
Author: Sanjay Patel
Date: 2021-09-26T09:49:10-04:00
New Revision: 6063e6b499c7829b941f94456af493a1ecb93ea1
URL: https://github.com/llvm/llvm-project/commit/6063e6b499c7829b941f94456af493a1ecb93ea1
DIFF: https://github.com/llvm/llvm-project/commit/6063e6b499c7829b941f94456af493a1ecb93ea1.diff
LOG: [InstCombine] move add after min/max intrinsic
This is another regression noted with the proposal to canonicalize
to the min/max intrinsics in D98152.
Here are Alive2 attempts to show correctness without specifying
exact constants:
https://alive2.llvm.org/ce/z/bvfCwh (smax)
https://alive2.llvm.org/ce/z/of7eqy (smin)
https://alive2.llvm.org/ce/z/2Xtxoh (umax)
https://alive2.llvm.org/ce/z/Rm4Ad8 (umin)
(if you comment out the assume and/or no-wrap, you should see failures)
The different output for the umin test is due to a fold added with
c4fc2cb5b2d98125 :
// umin(x, 1) == zext(x != 0)
We probably want to adjust that, so it applies more generally
(umax --> sext or patterns where we can fold to select-of-constants).
Some folds that were ok when starting with cmp+select may increase
instruction count for the equivalent intrinsic, so we have to decide
if it's worth altering a min/max.
Differential Revision: https://reviews.llvm.org/D110038
Added:
Modified:
llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
llvm/test/Transforms/InstCombine/minmax-intrinsics.ll
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index 5efb29b8a9d8..8231add66337 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -754,6 +754,45 @@ static Optional<bool> getKnownSign(Value *Op, Instruction *CxtI,
ICmpInst::ICMP_SLT, Op, Constant::getNullValue(Op->getType()), CxtI, DL);
}
+/// Try to canonicalize min/max(X + C0, C1) as min/max(X, C1 - C0) + C0. This
+/// can trigger other combines.
+static Instruction *moveAddAfterMinMax(IntrinsicInst *II,
+ InstCombiner::BuilderTy &Builder) {
+ Intrinsic::ID MinMaxID = II->getIntrinsicID();
+ assert((MinMaxID == Intrinsic::smax || MinMaxID == Intrinsic::smin ||
+ MinMaxID == Intrinsic::umax || MinMaxID == Intrinsic::umin) &&
+ "Expected a min or max intrinsic");
+
+ // TODO: Match vectors with undef elements, but undef may not propagate.
+ Value *Op0 = II->getArgOperand(0), *Op1 = II->getArgOperand(1);
+ Value *X;
+ const APInt *C0, *C1;
+ if (!match(Op0, m_OneUse(m_Add(m_Value(X), m_APInt(C0)))) ||
+ !match(Op1, m_APInt(C1)))
+ return nullptr;
+
+ // Check for necessary no-wrap and overflow constraints.
+ bool IsSigned = MinMaxID == Intrinsic::smax || MinMaxID == Intrinsic::smin;
+ auto *Add = cast<BinaryOperator>(Op0);
+ if ((IsSigned && !Add->hasNoSignedWrap()) ||
+ (!IsSigned && !Add->hasNoUnsignedWrap()))
+ return nullptr;
+
+ // If the constant
diff erence overflows, then instsimplify should reduce the
+ // min/max to the add or C1.
+ bool Overflow;
+ APInt CDiff =
+ IsSigned ? C1->ssub_ov(*C0, Overflow) : C1->usub_ov(*C0, Overflow);
+ assert(!Overflow && "Expected simplify of min/max");
+
+ // min/max (add X, C0), C1 --> add (min/max X, C1 - C0), C0
+ // Note: the "mismatched" no-overflow setting does not propagate.
+ Constant *NewMinMaxC = ConstantInt::get(II->getType(), CDiff);
+ Value *NewMinMax = Builder.CreateBinaryIntrinsic(MinMaxID, X, NewMinMaxC);
+ return IsSigned ? BinaryOperator::CreateNSWAdd(NewMinMax, Add->getOperand(1))
+ : BinaryOperator::CreateNUWAdd(NewMinMax, Add->getOperand(1));
+}
+
/// If we have a clamp pattern like max (min X, 42), 41 -- where the output
/// can only be one of two possible constant values -- turn that into a select
/// of constants.
@@ -1101,6 +1140,9 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
if (Instruction *I = moveNotAfterMinMax(I1, I0))
return I;
+ if (Instruction *I = moveAddAfterMinMax(II, Builder))
+ return I;
+
// smax(X, -X) --> abs(X)
// smin(X, -X) --> -abs(X)
// umax(X, -X) --> -abs(X)
diff --git a/llvm/test/Transforms/InstCombine/minmax-intrinsics.ll b/llvm/test/Transforms/InstCombine/minmax-intrinsics.ll
index b84eee089a43..7b76c3ad7422 100644
--- a/llvm/test/Transforms/InstCombine/minmax-intrinsics.ll
+++ b/llvm/test/Transforms/InstCombine/minmax-intrinsics.ll
@@ -1869,8 +1869,8 @@ define void @cmyk_commute11(i8 %r, i8 %g, i8 %b) {
define i8 @smax_offset(i8 %x) {
; CHECK-LABEL: @smax_offset(
-; CHECK-NEXT: [[A:%.*]] = add nsw i8 [[X:%.*]], 3
-; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smax.i8(i8 [[A]], i8 -124)
+; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.smax.i8(i8 [[X:%.*]], i8 -127)
+; CHECK-NEXT: [[M:%.*]] = add nsw i8 [[TMP1]], 3
; CHECK-NEXT: ret i8 [[M]]
;
%a = add nsw i8 %x, 3
@@ -1878,6 +1878,8 @@ define i8 @smax_offset(i8 %x) {
ret i8 %m
}
+; This is handled by InstSimplify; testing here to confirm assert.
+
define i8 @smax_offset_limit(i8 %x) {
; CHECK-LABEL: @smax_offset_limit(
; CHECK-NEXT: [[A:%.*]] = add nsw i8 [[X:%.*]], 3
@@ -1888,6 +1890,8 @@ define i8 @smax_offset_limit(i8 %x) {
ret i8 %m
}
+; This is handled by InstSimplify; testing here to confirm assert.
+
define i8 @smax_offset_overflow(i8 %x) {
; CHECK-LABEL: @smax_offset_overflow(
; CHECK-NEXT: [[A:%.*]] = add nsw i8 [[X:%.*]], 3
@@ -1898,6 +1902,8 @@ define i8 @smax_offset_overflow(i8 %x) {
ret i8 %m
}
+; negative test - require nsw
+
define i8 @smax_offset_may_wrap(i8 %x) {
; CHECK-LABEL: @smax_offset_may_wrap(
; CHECK-NEXT: [[A:%.*]] = add i8 [[X:%.*]], 3
@@ -1909,6 +1915,8 @@ define i8 @smax_offset_may_wrap(i8 %x) {
ret i8 %m
}
+; negative test
+
define i8 @smax_offset_uses(i8 %x) {
; CHECK-LABEL: @smax_offset_uses(
; CHECK-NEXT: [[A:%.*]] = add nsw i8 [[X:%.*]], 3
@@ -1924,8 +1932,8 @@ define i8 @smax_offset_uses(i8 %x) {
define <3 x i8> @smin_offset(<3 x i8> %x) {
; CHECK-LABEL: @smin_offset(
-; CHECK-NEXT: [[A:%.*]] = add nuw nsw <3 x i8> [[X:%.*]], <i8 124, i8 124, i8 124>
-; CHECK-NEXT: [[M:%.*]] = call <3 x i8> @llvm.smin.v3i8(<3 x i8> [[A]], <3 x i8> <i8 -3, i8 -3, i8 -3>)
+; CHECK-NEXT: [[TMP1:%.*]] = call <3 x i8> @llvm.smin.v3i8(<3 x i8> [[X:%.*]], <3 x i8> <i8 -127, i8 -127, i8 -127>)
+; CHECK-NEXT: [[M:%.*]] = or <3 x i8> [[TMP1]], <i8 124, i8 124, i8 124>
; CHECK-NEXT: ret <3 x i8> [[M]]
;
%a = add nsw nuw <3 x i8> %x, <i8 124, i8 124, i8 124>
@@ -1933,6 +1941,8 @@ define <3 x i8> @smin_offset(<3 x i8> %x) {
ret <3 x i8> %m
}
+; This is handled by InstSimplify; testing here to confirm assert.
+
define i8 @smin_offset_limit(i8 %x) {
; CHECK-LABEL: @smin_offset_limit(
; CHECK-NEXT: ret i8 -3
@@ -1942,6 +1952,8 @@ define i8 @smin_offset_limit(i8 %x) {
ret i8 %m
}
+; This is handled by InstSimplify; testing here to confirm assert.
+
define i8 @smin_offset_overflow(i8 %x) {
; CHECK-LABEL: @smin_offset_overflow(
; CHECK-NEXT: ret i8 -3
@@ -1951,6 +1963,8 @@ define i8 @smin_offset_overflow(i8 %x) {
ret i8 %m
}
+; negative test - require nsw
+
define i8 @smin_offset_may_wrap(i8 %x) {
; CHECK-LABEL: @smin_offset_may_wrap(
; CHECK-NEXT: [[A:%.*]] = add nuw i8 [[X:%.*]], 124
@@ -1962,6 +1976,8 @@ define i8 @smin_offset_may_wrap(i8 %x) {
ret i8 %m
}
+; negative test
+
define i8 @smin_offset_uses(i8 %x) {
; CHECK-LABEL: @smin_offset_uses(
; CHECK-NEXT: [[A:%.*]] = add nsw i8 [[X:%.*]], 124
@@ -1975,10 +1991,12 @@ define i8 @smin_offset_uses(i8 %x) {
ret i8 %m
}
+; Note: 'nsw' must not propagate here.
+
define <3 x i8> @umax_offset(<3 x i8> %x) {
; CHECK-LABEL: @umax_offset(
-; CHECK-NEXT: [[A:%.*]] = add nuw nsw <3 x i8> [[X:%.*]], <i8 127, i8 127, i8 127>
-; CHECK-NEXT: [[M:%.*]] = call <3 x i8> @llvm.umax.v3i8(<3 x i8> [[A]], <3 x i8> <i8 -126, i8 -126, i8 -126>)
+; CHECK-NEXT: [[TMP1:%.*]] = call <3 x i8> @llvm.umax.v3i8(<3 x i8> [[X:%.*]], <3 x i8> <i8 3, i8 3, i8 3>)
+; CHECK-NEXT: [[M:%.*]] = add nuw <3 x i8> [[TMP1]], <i8 127, i8 127, i8 127>
; CHECK-NEXT: ret <3 x i8> [[M]]
;
%a = add nsw nuw <3 x i8> %x, <i8 127, i8 127, i8 127>
@@ -1986,6 +2004,8 @@ define <3 x i8> @umax_offset(<3 x i8> %x) {
ret <3 x i8> %m
}
+; This is handled by InstSimplify; testing here to confirm assert.
+
define i8 @umax_offset_limit(i8 %x) {
; CHECK-LABEL: @umax_offset_limit(
; CHECK-NEXT: [[A:%.*]] = add nuw i8 [[X:%.*]], 3
@@ -1996,6 +2016,8 @@ define i8 @umax_offset_limit(i8 %x) {
ret i8 %m
}
+; This is handled by InstSimplify; testing here to confirm assert.
+
define i8 @umax_offset_overflow(i8 %x) {
; CHECK-LABEL: @umax_offset_overflow(
; CHECK-NEXT: [[A:%.*]] = add nuw i8 [[X:%.*]], 3
@@ -2006,6 +2028,8 @@ define i8 @umax_offset_overflow(i8 %x) {
ret i8 %m
}
+; negative test - require nuw
+
define i8 @umax_offset_may_wrap(i8 %x) {
; CHECK-LABEL: @umax_offset_may_wrap(
; CHECK-NEXT: [[A:%.*]] = add i8 [[X:%.*]], 3
@@ -2017,6 +2041,8 @@ define i8 @umax_offset_may_wrap(i8 %x) {
ret i8 %m
}
+; negative test
+
define i8 @umax_offset_uses(i8 %x) {
; CHECK-LABEL: @umax_offset_uses(
; CHECK-NEXT: [[A:%.*]] = add nuw i8 [[X:%.*]], 3
@@ -2032,8 +2058,8 @@ define i8 @umax_offset_uses(i8 %x) {
define i8 @umin_offset(i8 %x) {
; CHECK-LABEL: @umin_offset(
-; CHECK-NEXT: [[A:%.*]] = add nuw i8 [[X:%.*]], -5
-; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umin.i8(i8 [[A]], i8 -4)
+; CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i8 [[X:%.*]], 0
+; CHECK-NEXT: [[M:%.*]] = select i1 [[DOTNOT]], i8 -5, i8 -4
; CHECK-NEXT: ret i8 [[M]]
;
%a = add nuw i8 %x, 251
@@ -2041,6 +2067,8 @@ define i8 @umin_offset(i8 %x) {
ret i8 %m
}
+; This is handled by InstSimplify; testing here to confirm assert.
+
define i8 @umin_offset_limit(i8 %x) {
; CHECK-LABEL: @umin_offset_limit(
; CHECK-NEXT: ret i8 -4
@@ -2050,6 +2078,8 @@ define i8 @umin_offset_limit(i8 %x) {
ret i8 %m
}
+; This is handled by InstSimplify; testing here to confirm assert.
+
define i8 @umin_offset_overflow(i8 %x) {
; CHECK-LABEL: @umin_offset_overflow(
; CHECK-NEXT: ret i8 -4
@@ -2059,6 +2089,8 @@ define i8 @umin_offset_overflow(i8 %x) {
ret i8 %m
}
+; negative test - require nuw
+
define i8 @umin_offset_may_wrap(i8 %x) {
; CHECK-LABEL: @umin_offset_may_wrap(
; CHECK-NEXT: [[A:%.*]] = add nsw i8 [[X:%.*]], -5
@@ -2070,6 +2102,8 @@ define i8 @umin_offset_may_wrap(i8 %x) {
ret i8 %m
}
+; negative test
+
define i8 @umin_offset_uses(i8 %x) {
; CHECK-LABEL: @umin_offset_uses(
; CHECK-NEXT: [[A:%.*]] = add nuw i8 [[X:%.*]], -5
@@ -2083,6 +2117,8 @@ define i8 @umin_offset_uses(i8 %x) {
ret i8 %m
}
+; TODO: This could transform, but undef element must not propagate to the new add.
+
define <3 x i8> @umax_vector_splat_undef(<3 x i8> %x) {
; CHECK-LABEL: @umax_vector_splat_undef(
; CHECK-NEXT: [[A:%.*]] = add nuw <3 x i8> [[X:%.*]], <i8 undef, i8 64, i8 64>
More information about the llvm-commits
mailing list