Author: Sanjay Patel Date: 2021-01-25T07:52:50-05:00 New Revision: 09a136bcc6947128df86492d88f1733bdff745d1
URL: https://github.com/llvm/llvm-project/commit/09a136bcc6947128df86492d88f1733bdff745d1 DIFF: https://github.com/llvm/llvm-project/commit/09a136bcc6947128df86492d88f1733bdff745d1.diff LOG: [InstCombine] narrow min/max intrinsics with extended inputs We can sink extends after min/max if they match and would not change the sign-interpreted compare. The only combo that doesn't work is zext+smin/smax because the zexts could change a negative number into positive: https://alive2.llvm.org/ce/z/D6sz6J Sext+umax/umin works: define i32 @src(i8 %x, i8 %y) { %0: %sx = sext i8 %x to i32 %sy = sext i8 %y to i32 %m = umax i32 %sx, %sy ret i32 %m } => define i32 @tgt(i8 %x, i8 %y) { %0: %m = umax i8 %x, %y %r = sext i8 %m to i32 ret i32 %r } Transformation seems to be correct! 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 5ba51d255109..0b4246feecee 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -830,6 +830,30 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) { break; } + case Intrinsic::umax: + case Intrinsic::umin: { + Value *I0 = II->getArgOperand(0), *I1 = II->getArgOperand(1); + Value *X, *Y; + if (match(I0, m_ZExt(m_Value(X))) && match(I1, m_ZExt(m_Value(Y))) && + (I0->hasOneUse() || I1->hasOneUse()) && X->getType() == Y->getType()) { + Value *NarrowMaxMin = Builder.CreateBinaryIntrinsic(IID, X, Y); + return CastInst::Create(Instruction::ZExt, NarrowMaxMin, II->getType()); + } + // If both operands of unsigned min/max are sign-extended, it is still ok + // to narrow the operation. + LLVM_FALLTHROUGH; + } + case Intrinsic::smax: + case Intrinsic::smin: { + Value *I0 = II->getArgOperand(0), *I1 = II->getArgOperand(1); + Value *X, *Y; + if (match(I0, m_SExt(m_Value(X))) && match(I1, m_SExt(m_Value(Y))) && + (I0->hasOneUse() || I1->hasOneUse()) && X->getType() == Y->getType()) { + Value *NarrowMaxMin = Builder.CreateBinaryIntrinsic(IID, X, Y); + return CastInst::Create(Instruction::SExt, NarrowMaxMin, II->getType()); + } + break; + } case Intrinsic::bswap: { Value *IIOperand = II->getArgOperand(0); Value *X = nullptr; diff --git a/llvm/test/Transforms/InstCombine/minmax-intrinsics.ll b/llvm/test/Transforms/InstCombine/minmax-intrinsics.ll index bccfac81bdce..97ed799f32a8 100644 --- a/llvm/test/Transforms/InstCombine/minmax-intrinsics.ll +++ b/llvm/test/Transforms/InstCombine/minmax-intrinsics.ll @@ -50,9 +50,8 @@ define i8 @smax_known_bits(i8 %x, i8 %y) { define i8 @smax_sext(i5 %x, i5 %y) { ; CHECK-LABEL: @smax_sext( -; CHECK-NEXT: [[SX:%.*]] = sext i5 [[X:%.*]] to i8 -; CHECK-NEXT: [[SY:%.*]] = sext i5 [[Y:%.*]] to i8 -; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smax.i8(i8 [[SX]], i8 [[SY]]) +; CHECK-NEXT: [[TMP1:%.*]] = call i5 @llvm.smax.i5(i5 [[X:%.*]], i5 [[Y:%.*]]) +; CHECK-NEXT: [[M:%.*]] = sext i5 [[TMP1]] to i8 ; CHECK-NEXT: ret i8 [[M]] ; %sx = sext i5 %x to i8 @@ -61,12 +60,14 @@ define i8 @smax_sext(i5 %x, i5 %y) { ret i8 %m } +; Extra use is ok. + define i8 @smin_sext(i5 %x, i5 %y) { ; CHECK-LABEL: @smin_sext( -; CHECK-NEXT: [[SX:%.*]] = sext i5 [[X:%.*]] to i8 ; CHECK-NEXT: [[SY:%.*]] = sext i5 [[Y:%.*]] to i8 ; CHECK-NEXT: call void @use(i8 [[SY]]) -; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smin.i8(i8 [[SX]], i8 [[SY]]) +; CHECK-NEXT: [[TMP1:%.*]] = call i5 @llvm.smin.i5(i5 [[X:%.*]], i5 [[Y]]) +; CHECK-NEXT: [[M:%.*]] = sext i5 [[TMP1]] to i8 ; CHECK-NEXT: ret i8 [[M]] ; %sx = sext i5 %x to i8 @@ -76,12 +77,14 @@ define i8 @smin_sext(i5 %x, i5 %y) { ret i8 %m } +; Sext doesn't change unsigned min/max comparison of narrow values. + define i8 @umax_sext(i5 %x, i5 %y) { ; CHECK-LABEL: @umax_sext( ; CHECK-NEXT: [[SX:%.*]] = sext i5 [[X:%.*]] to i8 ; CHECK-NEXT: call void @use(i8 [[SX]]) -; CHECK-NEXT: [[SY:%.*]] = sext i5 [[Y:%.*]] to i8 -; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umax.i8(i8 [[SX]], i8 [[SY]]) +; CHECK-NEXT: [[TMP1:%.*]] = call i5 @llvm.umax.i5(i5 [[X]], i5 [[Y:%.*]]) +; CHECK-NEXT: [[M:%.*]] = sext i5 [[TMP1]] to i8 ; CHECK-NEXT: ret i8 [[M]] ; %sx = sext i5 %x to i8 @@ -93,9 +96,8 @@ define i8 @umax_sext(i5 %x, i5 %y) { define <3 x i8> @umin_sext(<3 x i5> %x, <3 x i5> %y) { ; CHECK-LABEL: @umin_sext( -; CHECK-NEXT: [[SX:%.*]] = sext <3 x i5> [[X:%.*]] to <3 x i8> -; CHECK-NEXT: [[SY:%.*]] = sext <3 x i5> [[Y:%.*]] to <3 x i8> -; CHECK-NEXT: [[M:%.*]] = call <3 x i8> @llvm.umin.v3i8(<3 x i8> [[SX]], <3 x i8> [[SY]]) +; CHECK-NEXT: [[TMP1:%.*]] = call <3 x i5> @llvm.umin.v3i5(<3 x i5> [[X:%.*]], <3 x i5> [[Y:%.*]]) +; CHECK-NEXT: [[M:%.*]] = sext <3 x i5> [[TMP1]] to <3 x i8> ; CHECK-NEXT: ret <3 x i8> [[M]] ; %sx = sext <3 x i5> %x to <3 x i8> @@ -104,6 +106,8 @@ define <3 x i8> @umin_sext(<3 x i5> %x, <3 x i5> %y) { ret <3 x i8> %m } +; Negative test - zext may change sign of inputs + define i8 @smax_zext(i5 %x, i5 %y) { ; CHECK-LABEL: @smax_zext( ; CHECK-NEXT: [[ZX:%.*]] = zext i5 [[X:%.*]] to i8 @@ -117,6 +121,8 @@ define i8 @smax_zext(i5 %x, i5 %y) { ret i8 %m } +; Negative test - zext may change sign of inputs + define i8 @smin_zext(i5 %x, i5 %y) { ; CHECK-LABEL: @smin_zext( ; CHECK-NEXT: [[ZX:%.*]] = zext i5 [[X:%.*]] to i8 @@ -132,9 +138,8 @@ define i8 @smin_zext(i5 %x, i5 %y) { define i8 @umax_zext(i5 %x, i5 %y) { ; CHECK-LABEL: @umax_zext( -; CHECK-NEXT: [[ZX:%.*]] = zext i5 [[X:%.*]] to i8 -; CHECK-NEXT: [[ZY:%.*]] = zext i5 [[Y:%.*]] to i8 -; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umax.i8(i8 [[ZX]], i8 [[ZY]]) +; CHECK-NEXT: [[TMP1:%.*]] = call i5 @llvm.umax.i5(i5 [[X:%.*]], i5 [[Y:%.*]]) +; CHECK-NEXT: [[M:%.*]] = zext i5 [[TMP1]] to i8 ; CHECK-NEXT: ret i8 [[M]] ; %zx = zext i5 %x to i8 @@ -145,9 +150,8 @@ define i8 @umax_zext(i5 %x, i5 %y) { define i8 @umin_zext(i5 %x, i5 %y) { ; CHECK-LABEL: @umin_zext( -; CHECK-NEXT: [[ZX:%.*]] = zext i5 [[X:%.*]] to i8 -; CHECK-NEXT: [[ZY:%.*]] = zext i5 [[Y:%.*]] to i8 -; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umin.i8(i8 [[ZX]], i8 [[ZY]]) +; CHECK-NEXT: [[TMP1:%.*]] = call i5 @llvm.umin.i5(i5 [[X:%.*]], i5 [[Y:%.*]]) +; CHECK-NEXT: [[M:%.*]] = zext i5 [[TMP1]] to i8 ; CHECK-NEXT: ret i8 [[M]] ; %zx = zext i5 %x to i8 @@ -156,6 +160,8 @@ define i8 @umin_zext(i5 %x, i5 %y) { ret i8 %m } +; Negative test - mismatched types + define i8 @umin_zext_types(i6 %x, i5 %y) { ; CHECK-LABEL: @umin_zext_types( ; CHECK-NEXT: [[ZX:%.*]] = zext i6 [[X:%.*]] to i8 @@ -169,6 +175,8 @@ define i8 @umin_zext_types(i6 %x, i5 %y) { ret i8 %m } +; Negative test - mismatched extends + define i8 @umin_ext(i5 %x, i5 %y) { ; CHECK-LABEL: @umin_ext( ; CHECK-NEXT: [[SX:%.*]] = sext i5 [[X:%.*]] to i8 @@ -182,6 +190,8 @@ define i8 @umin_ext(i5 %x, i5 %y) { ret i8 %m } +; Negative test - too many uses. + define i8 @umin_zext_uses(i5 %x, i5 %y) { ; CHECK-LABEL: @umin_zext_uses( ; CHECK-NEXT: [[ZX:%.*]] = zext i5 [[X:%.*]] to i8 _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits