[InstSimplify] Fold min/max intrinsic based on icmp of operands
authorNikita Popov <nikita.ppv@gmail.com>
Thu, 13 Aug 2020 19:58:25 +0000 (21:58 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Wed, 26 Aug 2020 20:02:57 +0000 (22:02 +0200)
This is a reboot of D84655, now performing the inner icmp
simplification query without undef folds.

It should be possible to handle the current foldMinMaxSharedOp()
fold based on this, by moving the logic into icmp of min/max instead,
making it more general. We can't drop the folds for constant operands,
because those also allow undef, which we exclude here.

The tests use assumes for exhaustive coverage, and have a few
more examples of misc folds we get based on icmp simplification.

Differential Revision: https://reviews.llvm.org/D85929

llvm/lib/Analysis/InstructionSimplify.cpp
llvm/test/Transforms/InstSimplify/maxmin_intrinsics.ll

index 8870794..eb0d951 100644 (file)
@@ -5232,6 +5232,16 @@ static APInt getMaxMinLimit(Intrinsic::ID IID, unsigned BitWidth) {
   }
 }
 
+static ICmpInst::Predicate getMaxMinPredicate(Intrinsic::ID IID) {
+  switch (IID) {
+  case Intrinsic::smax: return ICmpInst::ICMP_SGE;
+  case Intrinsic::smin: return ICmpInst::ICMP_SLE;
+  case Intrinsic::umax: return ICmpInst::ICMP_UGE;
+  case Intrinsic::umin: return ICmpInst::ICMP_ULE;
+  default: llvm_unreachable("Unexpected intrinsic");
+  }
+}
+
 /// Given a min/max intrinsic, see if it can be removed based on having an
 /// operand that is another min/max intrinsic with shared operand(s). The caller
 /// is expected to swap the operand arguments to handle commutation.
@@ -5324,6 +5334,12 @@ static Value *simplifyBinaryIntrinsic(Function *F, Value *Op0, Value *Op1,
     if (Value *V = foldMinMaxSharedOp(IID, Op1, Op0))
       return V;
 
+    ICmpInst::Predicate Pred = getMaxMinPredicate(IID);
+    if (isICmpTrue(Pred, Op0, Op1, Q.getWithoutUndef(), RecursionLimit))
+      return Op0;
+    if (isICmpTrue(Pred, Op1, Op0, Q.getWithoutUndef(), RecursionLimit))
+      return Op1;
+
     break;
   }
   case Intrinsic::usub_with_overflow:
index 99239db..c2c0f9e 100644 (file)
@@ -1895,8 +1895,7 @@ define i8 @umin_assume_uge(i8 %x, i8 %y) {
 ; CHECK-LABEL: @umin_assume_uge(
 ; CHECK-NEXT:    [[C:%.*]] = icmp uge i8 [[X:%.*]], [[Y:%.*]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
-; CHECK-NEXT:    [[M:%.*]] = call i8 @llvm.umin.i8(i8 [[X]], i8 [[Y]])
-; CHECK-NEXT:    ret i8 [[M]]
+; CHECK-NEXT:    ret i8 [[Y]]
 ;
   %c = icmp uge i8 %x, %y
   call void @llvm.assume(i1 %c)
@@ -1908,8 +1907,7 @@ define i8 @umin_assume_ugt(i8 %x, i8 %y) {
 ; CHECK-LABEL: @umin_assume_ugt(
 ; CHECK-NEXT:    [[C:%.*]] = icmp ugt i8 [[X:%.*]], [[Y:%.*]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
-; CHECK-NEXT:    [[M:%.*]] = call i8 @llvm.umin.i8(i8 [[X]], i8 [[Y]])
-; CHECK-NEXT:    ret i8 [[M]]
+; CHECK-NEXT:    ret i8 [[Y]]
 ;
   %c = icmp ugt i8 %x, %y
   call void @llvm.assume(i1 %c)
@@ -1921,8 +1919,7 @@ define i8 @umin_assume_ule(i8 %x, i8 %y) {
 ; CHECK-LABEL: @umin_assume_ule(
 ; CHECK-NEXT:    [[C:%.*]] = icmp ule i8 [[X:%.*]], [[Y:%.*]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
-; CHECK-NEXT:    [[M:%.*]] = call i8 @llvm.umin.i8(i8 [[X]], i8 [[Y]])
-; CHECK-NEXT:    ret i8 [[M]]
+; CHECK-NEXT:    ret i8 [[X]]
 ;
   %c = icmp ule i8 %x, %y
   call void @llvm.assume(i1 %c)
@@ -1934,8 +1931,7 @@ define i8 @umin_assume_ult(i8 %x, i8 %y) {
 ; CHECK-LABEL: @umin_assume_ult(
 ; CHECK-NEXT:    [[C:%.*]] = icmp ult i8 [[X:%.*]], [[Y:%.*]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
-; CHECK-NEXT:    [[M:%.*]] = call i8 @llvm.umin.i8(i8 [[X]], i8 [[Y]])
-; CHECK-NEXT:    ret i8 [[M]]
+; CHECK-NEXT:    ret i8 [[X]]
 ;
   %c = icmp ult i8 %x, %y
   call void @llvm.assume(i1 %c)
@@ -1947,8 +1943,7 @@ define i8 @umax_assume_uge(i8 %x, i8 %y) {
 ; CHECK-LABEL: @umax_assume_uge(
 ; CHECK-NEXT:    [[C:%.*]] = icmp uge i8 [[X:%.*]], [[Y:%.*]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
-; CHECK-NEXT:    [[M:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 [[Y]])
-; CHECK-NEXT:    ret i8 [[M]]
+; CHECK-NEXT:    ret i8 [[X]]
 ;
   %c = icmp uge i8 %x, %y
   call void @llvm.assume(i1 %c)
@@ -1960,8 +1955,7 @@ define i8 @umax_assume_ugt(i8 %x, i8 %y) {
 ; CHECK-LABEL: @umax_assume_ugt(
 ; CHECK-NEXT:    [[C:%.*]] = icmp ugt i8 [[X:%.*]], [[Y:%.*]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
-; CHECK-NEXT:    [[M:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 [[Y]])
-; CHECK-NEXT:    ret i8 [[M]]
+; CHECK-NEXT:    ret i8 [[X]]
 ;
   %c = icmp ugt i8 %x, %y
   call void @llvm.assume(i1 %c)
@@ -1973,8 +1967,7 @@ define i8 @umax_assume_ule(i8 %x, i8 %y) {
 ; CHECK-LABEL: @umax_assume_ule(
 ; CHECK-NEXT:    [[C:%.*]] = icmp ule i8 [[X:%.*]], [[Y:%.*]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
-; CHECK-NEXT:    [[M:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 [[Y]])
-; CHECK-NEXT:    ret i8 [[M]]
+; CHECK-NEXT:    ret i8 [[Y]]
 ;
   %c = icmp ule i8 %x, %y
   call void @llvm.assume(i1 %c)
@@ -1986,8 +1979,7 @@ define i8 @umax_assume_ult(i8 %x, i8 %y) {
 ; CHECK-LABEL: @umax_assume_ult(
 ; CHECK-NEXT:    [[C:%.*]] = icmp ult i8 [[X:%.*]], [[Y:%.*]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
-; CHECK-NEXT:    [[M:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 [[Y]])
-; CHECK-NEXT:    ret i8 [[M]]
+; CHECK-NEXT:    ret i8 [[Y]]
 ;
   %c = icmp ult i8 %x, %y
   call void @llvm.assume(i1 %c)
@@ -1999,8 +1991,7 @@ define i8 @smin_assume_sge(i8 %x, i8 %y) {
 ; CHECK-LABEL: @smin_assume_sge(
 ; CHECK-NEXT:    [[C:%.*]] = icmp sge i8 [[X:%.*]], [[Y:%.*]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
-; CHECK-NEXT:    [[M:%.*]] = call i8 @llvm.smin.i8(i8 [[X]], i8 [[Y]])
-; CHECK-NEXT:    ret i8 [[M]]
+; CHECK-NEXT:    ret i8 [[Y]]
 ;
   %c = icmp sge i8 %x, %y
   call void @llvm.assume(i1 %c)
@@ -2012,8 +2003,7 @@ define i8 @smin_assume_sgt(i8 %x, i8 %y) {
 ; CHECK-LABEL: @smin_assume_sgt(
 ; CHECK-NEXT:    [[C:%.*]] = icmp sgt i8 [[X:%.*]], [[Y:%.*]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
-; CHECK-NEXT:    [[M:%.*]] = call i8 @llvm.smin.i8(i8 [[X]], i8 [[Y]])
-; CHECK-NEXT:    ret i8 [[M]]
+; CHECK-NEXT:    ret i8 [[Y]]
 ;
   %c = icmp sgt i8 %x, %y
   call void @llvm.assume(i1 %c)
@@ -2025,8 +2015,7 @@ define i8 @smin_assume_sle(i8 %x, i8 %y) {
 ; CHECK-LABEL: @smin_assume_sle(
 ; CHECK-NEXT:    [[C:%.*]] = icmp sle i8 [[X:%.*]], [[Y:%.*]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
-; CHECK-NEXT:    [[M:%.*]] = call i8 @llvm.smin.i8(i8 [[X]], i8 [[Y]])
-; CHECK-NEXT:    ret i8 [[M]]
+; CHECK-NEXT:    ret i8 [[X]]
 ;
   %c = icmp sle i8 %x, %y
   call void @llvm.assume(i1 %c)
@@ -2038,8 +2027,7 @@ define i8 @smin_assume_slt(i8 %x, i8 %y) {
 ; CHECK-LABEL: @smin_assume_slt(
 ; CHECK-NEXT:    [[C:%.*]] = icmp slt i8 [[X:%.*]], [[Y:%.*]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
-; CHECK-NEXT:    [[M:%.*]] = call i8 @llvm.smin.i8(i8 [[X]], i8 [[Y]])
-; CHECK-NEXT:    ret i8 [[M]]
+; CHECK-NEXT:    ret i8 [[X]]
 ;
   %c = icmp slt i8 %x, %y
   call void @llvm.assume(i1 %c)
@@ -2051,8 +2039,7 @@ define i8 @smax_assume_sge(i8 %x, i8 %y) {
 ; CHECK-LABEL: @smax_assume_sge(
 ; CHECK-NEXT:    [[C:%.*]] = icmp sge i8 [[X:%.*]], [[Y:%.*]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
-; CHECK-NEXT:    [[M:%.*]] = call i8 @llvm.smax.i8(i8 [[X]], i8 [[Y]])
-; CHECK-NEXT:    ret i8 [[M]]
+; CHECK-NEXT:    ret i8 [[X]]
 ;
   %c = icmp sge i8 %x, %y
   call void @llvm.assume(i1 %c)
@@ -2064,8 +2051,7 @@ define i8 @smax_assume_sgt(i8 %x, i8 %y) {
 ; CHECK-LABEL: @smax_assume_sgt(
 ; CHECK-NEXT:    [[C:%.*]] = icmp sgt i8 [[X:%.*]], [[Y:%.*]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
-; CHECK-NEXT:    [[M:%.*]] = call i8 @llvm.smax.i8(i8 [[X]], i8 [[Y]])
-; CHECK-NEXT:    ret i8 [[M]]
+; CHECK-NEXT:    ret i8 [[X]]
 ;
   %c = icmp sgt i8 %x, %y
   call void @llvm.assume(i1 %c)
@@ -2077,8 +2063,7 @@ define i8 @smax_assume_sle(i8 %x, i8 %y) {
 ; CHECK-LABEL: @smax_assume_sle(
 ; CHECK-NEXT:    [[C:%.*]] = icmp sle i8 [[X:%.*]], [[Y:%.*]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
-; CHECK-NEXT:    [[M:%.*]] = call i8 @llvm.smax.i8(i8 [[X]], i8 [[Y]])
-; CHECK-NEXT:    ret i8 [[M]]
+; CHECK-NEXT:    ret i8 [[Y]]
 ;
   %c = icmp sle i8 %x, %y
   call void @llvm.assume(i1 %c)
@@ -2090,8 +2075,7 @@ define i8 @smax_assume_slt(i8 %x, i8 %y) {
 ; CHECK-LABEL: @smax_assume_slt(
 ; CHECK-NEXT:    [[C:%.*]] = icmp slt i8 [[X:%.*]], [[Y:%.*]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
-; CHECK-NEXT:    [[M:%.*]] = call i8 @llvm.smax.i8(i8 [[X]], i8 [[Y]])
-; CHECK-NEXT:    ret i8 [[M]]
+; CHECK-NEXT:    ret i8 [[Y]]
 ;
   %c = icmp slt i8 %x, %y
   call void @llvm.assume(i1 %c)
@@ -2102,8 +2086,7 @@ define i8 @smax_assume_slt(i8 %x, i8 %y) {
 define i8 @umax_add_nuw_1(i8 %x) {
 ; CHECK-LABEL: @umax_add_nuw_1(
 ; CHECK-NEXT:    [[ADD:%.*]] = add nuw i8 [[X:%.*]], 1
-; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.umax.i8(i8 [[ADD]], i8 [[X]])
-; CHECK-NEXT:    ret i8 [[MAX]]
+; CHECK-NEXT:    ret i8 [[ADD]]
 ;
   %add = add nuw i8 %x, 1
   %max = call i8 @llvm.umax.i8(i8 %add, i8 %x)
@@ -2113,8 +2096,7 @@ define i8 @umax_add_nuw_1(i8 %x) {
 define i8 @umax_add_nuw_2(i8 %x) {
 ; CHECK-LABEL: @umax_add_nuw_2(
 ; CHECK-NEXT:    [[ADD:%.*]] = add nuw i8 [[X:%.*]], 42
-; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.umax.i8(i8 [[ADD]], i8 42)
-; CHECK-NEXT:    ret i8 [[MAX]]
+; CHECK-NEXT:    ret i8 [[ADD]]
 ;
   %add = add nuw i8 %x, 42
   %max = call i8 @llvm.umax.i8(i8 %add, i8 42)
@@ -2123,10 +2105,8 @@ define i8 @umax_add_nuw_2(i8 %x) {
 
 define i8 @umax_range_metadata(i8* %p1, i8* %p2) {
 ; CHECK-LABEL: @umax_range_metadata(
-; CHECK-NEXT:    [[X:%.*]] = load i8, i8* [[P1:%.*]], align 1, [[RNG0:!range !.*]]
-; CHECK-NEXT:    [[Y:%.*]] = load i8, i8* [[P2:%.*]], align 1, [[RNG1:!range !.*]]
-; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 [[Y]])
-; CHECK-NEXT:    ret i8 [[MAX]]
+; CHECK-NEXT:    [[Y:%.*]] = load i8, i8* [[P2:%.*]], align 1, [[RNG0:!range !.*]]
+; CHECK-NEXT:    ret i8 [[Y]]
 ;
   %x = load i8, i8* %p1, !range !{i8 0, i8 10}
   %y = load i8, i8* %p2, !range !{i8 20, i8 30}
@@ -2136,10 +2116,8 @@ define i8 @umax_range_metadata(i8* %p1, i8* %p2) {
 
 define i8 @umax_zext_sext(i4 %x) {
 ; CHECK-LABEL: @umax_zext_sext(
-; CHECK-NEXT:    [[ZEXT:%.*]] = zext i4 [[X:%.*]] to i8
-; CHECK-NEXT:    [[SEXT:%.*]] = sext i4 [[X]] to i8
-; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.umax.i8(i8 [[ZEXT]], i8 [[SEXT]])
-; CHECK-NEXT:    ret i8 [[MAX]]
+; CHECK-NEXT:    [[SEXT:%.*]] = sext i4 [[X:%.*]] to i8
+; CHECK-NEXT:    ret i8 [[SEXT]]
 ;
   %zext = zext i4 %x to i8
   %sext = sext i4 %x to i8
@@ -2149,9 +2127,7 @@ define i8 @umax_zext_sext(i4 %x) {
 
 define i8 @umax_lshr(i8 %x, i8 %y) {
 ; CHECK-LABEL: @umax_lshr(
-; CHECK-NEXT:    [[SHR:%.*]] = lshr i8 [[X:%.*]], [[Y:%.*]]
-; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 [[SHR]])
-; CHECK-NEXT:    ret i8 [[MAX]]
+; CHECK-NEXT:    ret i8 [[X:%.*]]
 ;
   %shr = lshr i8 %x, %y
   %max = call i8 @llvm.umax.i8(i8 %x, i8 %shr)