[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!
This commit is contained in:
Sanjay Patel 2021-01-24 12:57:11 -05:00
parent 07b60d0060
commit 09a136bcc6
2 changed files with 50 additions and 16 deletions

View File

@ -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;

View File

@ -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