[llvm] 09a136b - [InstCombine] narrow min/max intrinsics with extended inputs

Sanjay Patel via llvm-commits llvm-commits at lists.llvm.org
Mon Jan 25 04:58:31 PST 2021


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


        


More information about the llvm-commits mailing list