[llvm] [InstCombine] Factorise add/sub and max/min using distributivity (PR #101507)

Jorge Botto via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 1 11:49:53 PDT 2024


https://github.com/jf-botto updated https://github.com/llvm/llvm-project/pull/101507

>From 0448916556f9490f887473531a628495b6464f44 Mon Sep 17 00:00:00 2001
From: Jorge Botto <jorge.botto.16 at ucl.ac.uk>
Date: Thu, 1 Aug 2024 18:42:53 +0100
Subject: [PATCH 1/2] Precommit test

---
 .../InstCombine/intrinsic-distributive.ll     | 226 ++++++++++++++++++
 1 file changed, 226 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/intrinsic-distributive.ll

diff --git a/llvm/test/Transforms/InstCombine/intrinsic-distributive.ll b/llvm/test/Transforms/InstCombine/intrinsic-distributive.ll
new file mode 100644
index 0000000000000..b09a7e21ca97d
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/intrinsic-distributive.ll
@@ -0,0 +1,226 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -S -passes=instcombine < %s 2>&1 | FileCheck %s
+
+define i8 @umin_of_umax(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i8 @umin_of_umax(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[MAX1:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 [[Z]])
+; CHECK-NEXT:    [[MAX2:%.*]] = call i8 @llvm.umax.i8(i8 [[Y]], i8 [[Z]])
+; CHECK-NEXT:    [[MIN:%.*]] = call i8 @llvm.umin.i8(i8 [[MAX1]], i8 [[MAX2]])
+; CHECK-NEXT:    ret i8 [[MIN]]
+;
+  %max1 = call i8 @llvm.umax.i8(i8 %x, i8 %z)
+  %max2 = call i8 @llvm.umax.i8(i8 %y, i8 %z)
+  %min = call i8 @llvm.umin.i8(i8 %max1, i8 %max2)
+  ret i8 %min
+}
+
+define i8 @umin_of_umax_comm(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i8 @umin_of_umax_comm(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[MAX1:%.*]] = call i8 @llvm.umax.i8(i8 [[Z]], i8 [[X]])
+; CHECK-NEXT:    [[MAX2:%.*]] = call i8 @llvm.umax.i8(i8 [[Z]], i8 [[Y]])
+; CHECK-NEXT:    [[MIN:%.*]] = call i8 @llvm.umin.i8(i8 [[MAX1]], i8 [[MAX2]])
+; CHECK-NEXT:    ret i8 [[MIN]]
+;
+  %max1 = call i8 @llvm.umax.i8(i8 %z, i8 %x)
+  %max2 = call i8 @llvm.umax.i8(i8 %z, i8 %y)
+  %min = call i8 @llvm.umin.i8(i8 %max1, i8 %max2)
+  ret i8 %min
+}
+
+define i8 @smin_of_smax(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i8 @smin_of_smax(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[MAX1:%.*]] = call i8 @llvm.smax.i8(i8 [[X]], i8 [[Z]])
+; CHECK-NEXT:    [[MAX2:%.*]] = call i8 @llvm.smax.i8(i8 [[Y]], i8 [[Z]])
+; CHECK-NEXT:    [[MIN:%.*]] = call i8 @llvm.smin.i8(i8 [[MAX1]], i8 [[MAX2]])
+; CHECK-NEXT:    ret i8 [[MIN]]
+;
+  %max1 = call i8 @llvm.smax.i8(i8 %x, i8 %z)
+  %max2 = call i8 @llvm.smax.i8(i8 %y, i8 %z)
+  %min = call i8 @llvm.smin.i8(i8 %max1, i8 %max2)
+  ret i8 %min
+}
+
+define i8 @smin_of_smax_comm(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i8 @smin_of_smax_comm(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[MAX1:%.*]] = call i8 @llvm.smax.i8(i8 [[Z]], i8 [[X]])
+; CHECK-NEXT:    [[MAX2:%.*]] = call i8 @llvm.smax.i8(i8 [[Z]], i8 [[Y]])
+; CHECK-NEXT:    [[MIN:%.*]] = call i8 @llvm.smin.i8(i8 [[MAX1]], i8 [[MAX2]])
+; CHECK-NEXT:    ret i8 [[MIN]]
+;
+  %max1 = call i8 @llvm.smax.i8(i8 %z, i8 %x)
+  %max2 = call i8 @llvm.smax.i8(i8 %z, i8 %y)
+  %min = call i8 @llvm.smin.i8(i8 %max1, i8 %max2)
+  ret i8 %min
+}
+
+define i8 @umax_of_umin(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i8 @umax_of_umin(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[MIN1:%.*]] = call i8 @llvm.umin.i8(i8 [[X]], i8 [[Z]])
+; CHECK-NEXT:    [[MIN2:%.*]] = call i8 @llvm.umin.i8(i8 [[Y]], i8 [[Z]])
+; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.umax.i8(i8 [[MIN1]], i8 [[MIN2]])
+; CHECK-NEXT:    ret i8 [[MAX]]
+;
+  %min1 = call i8 @llvm.umin.i8(i8 %x, i8 %z)
+  %min2 = call i8 @llvm.umin.i8(i8 %y, i8 %z)
+  %max = call i8 @llvm.umax.i8(i8 %min1, i8 %min2)
+  ret i8 %max
+}
+
+define i8 @umax_of_umin_comm(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i8 @umax_of_umin_comm(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[MIN1:%.*]] = call i8 @llvm.umin.i8(i8 [[Z]], i8 [[X]])
+; CHECK-NEXT:    [[MIN2:%.*]] = call i8 @llvm.umin.i8(i8 [[Z]], i8 [[Y]])
+; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.umax.i8(i8 [[MIN1]], i8 [[MIN2]])
+; CHECK-NEXT:    ret i8 [[MAX]]
+;
+  %min1 = call i8 @llvm.umin.i8(i8 %z, i8 %x)
+  %min2 = call i8 @llvm.umin.i8(i8 %z, i8 %y)
+  %max = call i8 @llvm.umax.i8(i8 %min1, i8 %min2)
+  ret i8 %max
+}
+
+define i8 @smax_of_smin(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i8 @smax_of_smin(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[MIN1:%.*]] = call i8 @llvm.smin.i8(i8 [[X]], i8 [[Z]])
+; CHECK-NEXT:    [[MIN2:%.*]] = call i8 @llvm.smin.i8(i8 [[Y]], i8 [[Z]])
+; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.smax.i8(i8 [[MIN1]], i8 [[MIN2]])
+; CHECK-NEXT:    ret i8 [[MAX]]
+;
+  %min1 = call i8 @llvm.smin.i8(i8 %x, i8 %z)
+  %min2 = call i8 @llvm.smin.i8(i8 %y, i8 %z)
+  %max = call i8 @llvm.smax.i8(i8 %min1, i8 %min2)
+  ret i8 %max
+}
+
+define i8 @smax_of_smin_comm(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i8 @smax_of_smin_comm(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[MIN1:%.*]] = call i8 @llvm.smin.i8(i8 [[Z]], i8 [[X]])
+; CHECK-NEXT:    [[MIN2:%.*]] = call i8 @llvm.smin.i8(i8 [[Z]], i8 [[Y]])
+; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.smax.i8(i8 [[MIN1]], i8 [[MIN2]])
+; CHECK-NEXT:    ret i8 [[MAX]]
+;
+  %min1 = call i8 @llvm.smin.i8(i8 %z, i8 %x)
+  %min2 = call i8 @llvm.smin.i8(i8 %z, i8 %y)
+  %max = call i8 @llvm.smax.i8(i8 %min1, i8 %min2)
+  ret i8 %max
+}
+
+define i8 @umax_of_uadd_sat(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i8 @umax_of_uadd_sat(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[ADD1:%.*]] = call i8 @llvm.uadd.sat.i8(i8 [[X]], i8 [[Z]])
+; CHECK-NEXT:    [[ADD2:%.*]] = call i8 @llvm.uadd.sat.i8(i8 [[Y]], i8 [[Z]])
+; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.umax.i8(i8 [[ADD1]], i8 [[ADD2]])
+; CHECK-NEXT:    ret i8 [[MAX]]
+;
+  %add1 = call i8 @llvm.uadd.sat.i8(i8 %x, i8 %z)
+  %add2 = call i8 @llvm.uadd.sat.i8(i8 %y, i8 %z)
+  %max = call i8 @llvm.umax.i8(i8 %add1, i8 %add2)
+  ret i8 %max
+}
+
+define i8 @umax_of_uadd_sat_comm(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i8 @umax_of_uadd_sat_comm(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[ADD1:%.*]] = call i8 @llvm.uadd.sat.i8(i8 [[Z]], i8 [[X]])
+; CHECK-NEXT:    [[ADD2:%.*]] = call i8 @llvm.uadd.sat.i8(i8 [[Z]], i8 [[Y]])
+; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.umax.i8(i8 [[ADD1]], i8 [[ADD2]])
+; CHECK-NEXT:    ret i8 [[MAX]]
+;
+  %add1 = call i8 @llvm.uadd.sat.i8(i8 %z, i8 %x)
+  %add2 = call i8 @llvm.uadd.sat.i8(i8 %z, i8 %y)
+  %max = call i8 @llvm.umax.i8(i8 %add1, i8 %add2)
+  ret i8 %max
+}
+
+define i8 @umin_of_uadd_sat(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i8 @umin_of_uadd_sat(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[ADD1:%.*]] = call i8 @llvm.uadd.sat.i8(i8 [[X]], i8 [[Z]])
+; CHECK-NEXT:    [[ADD2:%.*]] = call i8 @llvm.uadd.sat.i8(i8 [[Y]], i8 [[Z]])
+; CHECK-NEXT:    [[MIN:%.*]] = call i8 @llvm.umin.i8(i8 [[ADD1]], i8 [[ADD2]])
+; CHECK-NEXT:    ret i8 [[MIN]]
+;
+  %add1 = call i8 @llvm.uadd.sat.i8(i8 %x, i8 %z)
+  %add2 = call i8 @llvm.uadd.sat.i8(i8 %y, i8 %z)
+  %min = call i8 @llvm.umin.i8(i8 %add1, i8 %add2)
+  ret i8 %min
+}
+
+define i8 @umin_of_uadd_sat_comm(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i8 @umin_of_uadd_sat_comm(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[ADD1:%.*]] = call i8 @llvm.uadd.sat.i8(i8 [[Z]], i8 [[X]])
+; CHECK-NEXT:    [[ADD2:%.*]] = call i8 @llvm.uadd.sat.i8(i8 [[Z]], i8 [[Y]])
+; CHECK-NEXT:    [[MIN:%.*]] = call i8 @llvm.umin.i8(i8 [[ADD1]], i8 [[ADD2]])
+; CHECK-NEXT:    ret i8 [[MIN]]
+;
+  %add1 = call i8 @llvm.uadd.sat.i8(i8 %z, i8 %x)
+  %add2 = call i8 @llvm.uadd.sat.i8(i8 %z, i8 %y)
+  %min = call i8 @llvm.umin.i8(i8 %add1, i8 %add2)
+  ret i8 %min
+}
+
+define i8 @smax_of_sadd_sat(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i8 @smax_of_sadd_sat(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[ADD1:%.*]] = call i8 @llvm.sadd.sat.i8(i8 [[X]], i8 [[Z]])
+; CHECK-NEXT:    [[ADD2:%.*]] = call i8 @llvm.sadd.sat.i8(i8 [[Y]], i8 [[Z]])
+; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.smax.i8(i8 [[ADD1]], i8 [[ADD2]])
+; CHECK-NEXT:    ret i8 [[MAX]]
+;
+  %add1 = call i8 @llvm.sadd.sat.i8(i8 %x, i8 %z)
+  %add2 = call i8 @llvm.sadd.sat.i8(i8 %y, i8 %z)
+  %max = call i8 @llvm.smax.i8(i8 %add1, i8 %add2)
+  ret i8 %max
+}
+
+define i8 @smax_of_sadd_sat_comm(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i8 @smax_of_sadd_sat_comm(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[ADD1:%.*]] = call i8 @llvm.sadd.sat.i8(i8 [[Z]], i8 [[X]])
+; CHECK-NEXT:    [[ADD2:%.*]] = call i8 @llvm.sadd.sat.i8(i8 [[Z]], i8 [[Y]])
+; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.smax.i8(i8 [[ADD1]], i8 [[ADD2]])
+; CHECK-NEXT:    ret i8 [[MAX]]
+;
+  %add1 = call i8 @llvm.sadd.sat.i8(i8 %z, i8 %x)
+  %add2 = call i8 @llvm.sadd.sat.i8(i8 %z, i8 %y)
+  %max = call i8 @llvm.smax.i8(i8 %add1, i8 %add2)
+  ret i8 %max
+}
+
+define i8 @smin_of_sadd_sat(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i8 @smin_of_sadd_sat(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[ADD1:%.*]] = call i8 @llvm.sadd.sat.i8(i8 [[X]], i8 [[Z]])
+; CHECK-NEXT:    [[ADD2:%.*]] = call i8 @llvm.sadd.sat.i8(i8 [[Y]], i8 [[Z]])
+; CHECK-NEXT:    [[MIN:%.*]] = call i8 @llvm.smin.i8(i8 [[ADD1]], i8 [[ADD2]])
+; CHECK-NEXT:    ret i8 [[MIN]]
+;
+  %add1 = call i8 @llvm.sadd.sat.i8(i8 %x, i8 %z)
+  %add2 = call i8 @llvm.sadd.sat.i8(i8 %y, i8 %z)
+  %min = call i8 @llvm.smin.i8(i8 %add1, i8 %add2)
+  ret i8 %min
+}
+
+define i8 @smin_of_sadd_sat_comm(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i8 @smin_of_sadd_sat_comm(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[ADD1:%.*]] = call i8 @llvm.sadd.sat.i8(i8 [[Z]], i8 [[X]])
+; CHECK-NEXT:    [[ADD2:%.*]] = call i8 @llvm.sadd.sat.i8(i8 [[Z]], i8 [[Y]])
+; CHECK-NEXT:    [[MIN:%.*]] = call i8 @llvm.smin.i8(i8 [[ADD1]], i8 [[ADD2]])
+; CHECK-NEXT:    ret i8 [[MIN]]
+;
+  %add1 = call i8 @llvm.sadd.sat.i8(i8 %z, i8 %x)
+  %add2 = call i8 @llvm.sadd.sat.i8(i8 %z, i8 %y)
+  %min = call i8 @llvm.smin.i8(i8 %add1, i8 %add2)
+  ret i8 %min
+}

>From 4780a3920ea470dd019eebeb7796de7e0003dc17 Mon Sep 17 00:00:00 2001
From: Jorge Botto <jorge.botto.16 at ucl.ac.uk>
Date: Thu, 1 Aug 2024 19:39:10 +0100
Subject: [PATCH 2/2] Adding missed optimisation

---
 .../InstCombine/InstCombineCalls.cpp          | 75 +++++++++++++++++
 .../InstCombine/intrinsic-distributive.ll     | 80 ++++++++-----------
 2 files changed, 107 insertions(+), 48 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index cc68fd4cf1c1b..fba2583405869 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -1505,6 +1505,78 @@ foldMinimumOverTrailingOrLeadingZeroCount(Value *I0, Value *I1,
       ConstantInt::getTrue(ZeroUndef->getType()));
 }
 
+/// Return whether "X LOp (Y ROp Z)" is always equal to
+/// "(X LOp Y) ROp (X LOp Z)".
+static bool leftDistributesOverRightIntrinsic(Intrinsic::ID LOp,
+                                              Intrinsic::ID ROp) {
+  switch (LOp) {
+  case Intrinsic::umax:
+    return ROp == Intrinsic::umin;
+  case Intrinsic::smax:
+    return ROp == Intrinsic::smin;
+  case Intrinsic::umin:
+    return ROp == Intrinsic::umax;
+  case Intrinsic::smin:
+    return ROp == Intrinsic::smax;
+  case Intrinsic::uadd_sat:
+    return ROp == Intrinsic::umax || ROp == Intrinsic::umin;
+  case Intrinsic::sadd_sat:
+    return ROp == Intrinsic::smax || ROp == Intrinsic::smin;
+  default:
+    return false;
+  }
+}
+
+// Attempts to factorise a common term
+// in an instruction that has the form "(A op' B) op (C op' D)
+static Value *
+foldIntrinsicUsingDistributiveLaws(IntrinsicInst *II,
+                                   InstCombiner::BuilderTy &Builder) {
+  Value *LHS = II->getOperand(0), *RHS = II->getOperand(1);
+  Intrinsic::ID TopLevelOpcode = II->getIntrinsicID();
+
+  if (LHS && RHS) {
+    IntrinsicInst *Op0 = dyn_cast<IntrinsicInst>(LHS);
+    IntrinsicInst *Op1 = dyn_cast<IntrinsicInst>(RHS);
+
+    if (!Op0 || !Op1)
+      return nullptr;
+
+    if (Op0->getIntrinsicID() != Op1->getIntrinsicID())
+      return nullptr;
+
+    Intrinsic::ID InnerOpcode = Op0->getIntrinsicID();
+
+    if (!leftDistributesOverRightIntrinsic(InnerOpcode, TopLevelOpcode))
+      return nullptr;
+
+    assert(II->isCommutative() && Op0->isCommutative() &&
+           "Only inner and outer commutative op codes are supported.");
+
+    Value *A = Op0->getOperand(0);
+    Value *B = Op0->getOperand(1);
+    Value *C = Op1->getOperand(0);
+    Value *D = Op1->getOperand(1);
+
+    if (A == C || A == D) {
+      if (A != C)
+        std::swap(C, D);
+
+      Value *NewIntrinsic = Builder.CreateBinaryIntrinsic(TopLevelOpcode, B, D);
+      return Builder.CreateBinaryIntrinsic(InnerOpcode, NewIntrinsic, A);
+    }
+
+    if (B == D || B == C) {
+      if (B != D)
+        std::swap(C, D);
+
+      Value *NewIntrinsic = Builder.CreateBinaryIntrinsic(TopLevelOpcode, A, C);
+      return Builder.CreateBinaryIntrinsic(InnerOpcode, NewIntrinsic, B);
+    }
+  }
+  return nullptr;
+}
+
 /// CallInst simplification. This mostly only handles folding of intrinsic
 /// instructions. For normal calls, it allows visitCallBase to do the heavy
 /// lifting.
@@ -1929,6 +2001,9 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
       }
     }
 
+    if (Value *V = foldIntrinsicUsingDistributiveLaws(II, Builder))
+      return replaceInstUsesWith(*II, V);
+
     break;
   }
   case Intrinsic::bitreverse: {
diff --git a/llvm/test/Transforms/InstCombine/intrinsic-distributive.ll b/llvm/test/Transforms/InstCombine/intrinsic-distributive.ll
index b09a7e21ca97d..8c8ee8c3e5fa8 100644
--- a/llvm/test/Transforms/InstCombine/intrinsic-distributive.ll
+++ b/llvm/test/Transforms/InstCombine/intrinsic-distributive.ll
@@ -4,9 +4,8 @@
 define i8 @umin_of_umax(i8 %x, i8 %y, i8 %z) {
 ; CHECK-LABEL: define i8 @umin_of_umax(
 ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
-; CHECK-NEXT:    [[MAX1:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 [[Z]])
-; CHECK-NEXT:    [[MAX2:%.*]] = call i8 @llvm.umax.i8(i8 [[Y]], i8 [[Z]])
-; CHECK-NEXT:    [[MIN:%.*]] = call i8 @llvm.umin.i8(i8 [[MAX1]], i8 [[MAX2]])
+; CHECK-NEXT:    [[TMP1:%.*]] = call i8 @llvm.umin.i8(i8 [[X]], i8 [[Y]])
+; CHECK-NEXT:    [[MIN:%.*]] = call i8 @llvm.umax.i8(i8 [[TMP1]], i8 [[Z]])
 ; CHECK-NEXT:    ret i8 [[MIN]]
 ;
   %max1 = call i8 @llvm.umax.i8(i8 %x, i8 %z)
@@ -18,9 +17,8 @@ define i8 @umin_of_umax(i8 %x, i8 %y, i8 %z) {
 define i8 @umin_of_umax_comm(i8 %x, i8 %y, i8 %z) {
 ; CHECK-LABEL: define i8 @umin_of_umax_comm(
 ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
-; CHECK-NEXT:    [[MAX1:%.*]] = call i8 @llvm.umax.i8(i8 [[Z]], i8 [[X]])
-; CHECK-NEXT:    [[MAX2:%.*]] = call i8 @llvm.umax.i8(i8 [[Z]], i8 [[Y]])
-; CHECK-NEXT:    [[MIN:%.*]] = call i8 @llvm.umin.i8(i8 [[MAX1]], i8 [[MAX2]])
+; CHECK-NEXT:    [[TMP1:%.*]] = call i8 @llvm.umin.i8(i8 [[X]], i8 [[Y]])
+; CHECK-NEXT:    [[MIN:%.*]] = call i8 @llvm.umax.i8(i8 [[TMP1]], i8 [[Z]])
 ; CHECK-NEXT:    ret i8 [[MIN]]
 ;
   %max1 = call i8 @llvm.umax.i8(i8 %z, i8 %x)
@@ -32,9 +30,8 @@ define i8 @umin_of_umax_comm(i8 %x, i8 %y, i8 %z) {
 define i8 @smin_of_smax(i8 %x, i8 %y, i8 %z) {
 ; CHECK-LABEL: define i8 @smin_of_smax(
 ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
-; CHECK-NEXT:    [[MAX1:%.*]] = call i8 @llvm.smax.i8(i8 [[X]], i8 [[Z]])
-; CHECK-NEXT:    [[MAX2:%.*]] = call i8 @llvm.smax.i8(i8 [[Y]], i8 [[Z]])
-; CHECK-NEXT:    [[MIN:%.*]] = call i8 @llvm.smin.i8(i8 [[MAX1]], i8 [[MAX2]])
+; CHECK-NEXT:    [[TMP1:%.*]] = call i8 @llvm.smin.i8(i8 [[X]], i8 [[Y]])
+; CHECK-NEXT:    [[MIN:%.*]] = call i8 @llvm.smax.i8(i8 [[TMP1]], i8 [[Z]])
 ; CHECK-NEXT:    ret i8 [[MIN]]
 ;
   %max1 = call i8 @llvm.smax.i8(i8 %x, i8 %z)
@@ -46,9 +43,8 @@ define i8 @smin_of_smax(i8 %x, i8 %y, i8 %z) {
 define i8 @smin_of_smax_comm(i8 %x, i8 %y, i8 %z) {
 ; CHECK-LABEL: define i8 @smin_of_smax_comm(
 ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
-; CHECK-NEXT:    [[MAX1:%.*]] = call i8 @llvm.smax.i8(i8 [[Z]], i8 [[X]])
-; CHECK-NEXT:    [[MAX2:%.*]] = call i8 @llvm.smax.i8(i8 [[Z]], i8 [[Y]])
-; CHECK-NEXT:    [[MIN:%.*]] = call i8 @llvm.smin.i8(i8 [[MAX1]], i8 [[MAX2]])
+; CHECK-NEXT:    [[TMP1:%.*]] = call i8 @llvm.smin.i8(i8 [[X]], i8 [[Y]])
+; CHECK-NEXT:    [[MIN:%.*]] = call i8 @llvm.smax.i8(i8 [[TMP1]], i8 [[Z]])
 ; CHECK-NEXT:    ret i8 [[MIN]]
 ;
   %max1 = call i8 @llvm.smax.i8(i8 %z, i8 %x)
@@ -60,9 +56,8 @@ define i8 @smin_of_smax_comm(i8 %x, i8 %y, i8 %z) {
 define i8 @umax_of_umin(i8 %x, i8 %y, i8 %z) {
 ; CHECK-LABEL: define i8 @umax_of_umin(
 ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
-; CHECK-NEXT:    [[MIN1:%.*]] = call i8 @llvm.umin.i8(i8 [[X]], i8 [[Z]])
-; CHECK-NEXT:    [[MIN2:%.*]] = call i8 @llvm.umin.i8(i8 [[Y]], i8 [[Z]])
-; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.umax.i8(i8 [[MIN1]], i8 [[MIN2]])
+; CHECK-NEXT:    [[TMP1:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 [[Y]])
+; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.umin.i8(i8 [[TMP1]], i8 [[Z]])
 ; CHECK-NEXT:    ret i8 [[MAX]]
 ;
   %min1 = call i8 @llvm.umin.i8(i8 %x, i8 %z)
@@ -74,9 +69,8 @@ define i8 @umax_of_umin(i8 %x, i8 %y, i8 %z) {
 define i8 @umax_of_umin_comm(i8 %x, i8 %y, i8 %z) {
 ; CHECK-LABEL: define i8 @umax_of_umin_comm(
 ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
-; CHECK-NEXT:    [[MIN1:%.*]] = call i8 @llvm.umin.i8(i8 [[Z]], i8 [[X]])
-; CHECK-NEXT:    [[MIN2:%.*]] = call i8 @llvm.umin.i8(i8 [[Z]], i8 [[Y]])
-; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.umax.i8(i8 [[MIN1]], i8 [[MIN2]])
+; CHECK-NEXT:    [[TMP1:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 [[Y]])
+; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.umin.i8(i8 [[TMP1]], i8 [[Z]])
 ; CHECK-NEXT:    ret i8 [[MAX]]
 ;
   %min1 = call i8 @llvm.umin.i8(i8 %z, i8 %x)
@@ -88,9 +82,8 @@ define i8 @umax_of_umin_comm(i8 %x, i8 %y, i8 %z) {
 define i8 @smax_of_smin(i8 %x, i8 %y, i8 %z) {
 ; CHECK-LABEL: define i8 @smax_of_smin(
 ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
-; CHECK-NEXT:    [[MIN1:%.*]] = call i8 @llvm.smin.i8(i8 [[X]], i8 [[Z]])
-; CHECK-NEXT:    [[MIN2:%.*]] = call i8 @llvm.smin.i8(i8 [[Y]], i8 [[Z]])
-; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.smax.i8(i8 [[MIN1]], i8 [[MIN2]])
+; CHECK-NEXT:    [[TMP1:%.*]] = call i8 @llvm.smax.i8(i8 [[X]], i8 [[Y]])
+; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.smin.i8(i8 [[TMP1]], i8 [[Z]])
 ; CHECK-NEXT:    ret i8 [[MAX]]
 ;
   %min1 = call i8 @llvm.smin.i8(i8 %x, i8 %z)
@@ -102,9 +95,8 @@ define i8 @smax_of_smin(i8 %x, i8 %y, i8 %z) {
 define i8 @smax_of_smin_comm(i8 %x, i8 %y, i8 %z) {
 ; CHECK-LABEL: define i8 @smax_of_smin_comm(
 ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
-; CHECK-NEXT:    [[MIN1:%.*]] = call i8 @llvm.smin.i8(i8 [[Z]], i8 [[X]])
-; CHECK-NEXT:    [[MIN2:%.*]] = call i8 @llvm.smin.i8(i8 [[Z]], i8 [[Y]])
-; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.smax.i8(i8 [[MIN1]], i8 [[MIN2]])
+; CHECK-NEXT:    [[TMP1:%.*]] = call i8 @llvm.smax.i8(i8 [[X]], i8 [[Y]])
+; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.smin.i8(i8 [[TMP1]], i8 [[Z]])
 ; CHECK-NEXT:    ret i8 [[MAX]]
 ;
   %min1 = call i8 @llvm.smin.i8(i8 %z, i8 %x)
@@ -116,9 +108,8 @@ define i8 @smax_of_smin_comm(i8 %x, i8 %y, i8 %z) {
 define i8 @umax_of_uadd_sat(i8 %x, i8 %y, i8 %z) {
 ; CHECK-LABEL: define i8 @umax_of_uadd_sat(
 ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
-; CHECK-NEXT:    [[ADD1:%.*]] = call i8 @llvm.uadd.sat.i8(i8 [[X]], i8 [[Z]])
-; CHECK-NEXT:    [[ADD2:%.*]] = call i8 @llvm.uadd.sat.i8(i8 [[Y]], i8 [[Z]])
-; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.umax.i8(i8 [[ADD1]], i8 [[ADD2]])
+; CHECK-NEXT:    [[TMP1:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 [[Y]])
+; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.uadd.sat.i8(i8 [[TMP1]], i8 [[Z]])
 ; CHECK-NEXT:    ret i8 [[MAX]]
 ;
   %add1 = call i8 @llvm.uadd.sat.i8(i8 %x, i8 %z)
@@ -130,9 +121,8 @@ define i8 @umax_of_uadd_sat(i8 %x, i8 %y, i8 %z) {
 define i8 @umax_of_uadd_sat_comm(i8 %x, i8 %y, i8 %z) {
 ; CHECK-LABEL: define i8 @umax_of_uadd_sat_comm(
 ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
-; CHECK-NEXT:    [[ADD1:%.*]] = call i8 @llvm.uadd.sat.i8(i8 [[Z]], i8 [[X]])
-; CHECK-NEXT:    [[ADD2:%.*]] = call i8 @llvm.uadd.sat.i8(i8 [[Z]], i8 [[Y]])
-; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.umax.i8(i8 [[ADD1]], i8 [[ADD2]])
+; CHECK-NEXT:    [[TMP1:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 [[Y]])
+; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.uadd.sat.i8(i8 [[TMP1]], i8 [[Z]])
 ; CHECK-NEXT:    ret i8 [[MAX]]
 ;
   %add1 = call i8 @llvm.uadd.sat.i8(i8 %z, i8 %x)
@@ -144,9 +134,8 @@ define i8 @umax_of_uadd_sat_comm(i8 %x, i8 %y, i8 %z) {
 define i8 @umin_of_uadd_sat(i8 %x, i8 %y, i8 %z) {
 ; CHECK-LABEL: define i8 @umin_of_uadd_sat(
 ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
-; CHECK-NEXT:    [[ADD1:%.*]] = call i8 @llvm.uadd.sat.i8(i8 [[X]], i8 [[Z]])
-; CHECK-NEXT:    [[ADD2:%.*]] = call i8 @llvm.uadd.sat.i8(i8 [[Y]], i8 [[Z]])
-; CHECK-NEXT:    [[MIN:%.*]] = call i8 @llvm.umin.i8(i8 [[ADD1]], i8 [[ADD2]])
+; CHECK-NEXT:    [[TMP1:%.*]] = call i8 @llvm.umin.i8(i8 [[X]], i8 [[Y]])
+; CHECK-NEXT:    [[MIN:%.*]] = call i8 @llvm.uadd.sat.i8(i8 [[TMP1]], i8 [[Z]])
 ; CHECK-NEXT:    ret i8 [[MIN]]
 ;
   %add1 = call i8 @llvm.uadd.sat.i8(i8 %x, i8 %z)
@@ -158,9 +147,8 @@ define i8 @umin_of_uadd_sat(i8 %x, i8 %y, i8 %z) {
 define i8 @umin_of_uadd_sat_comm(i8 %x, i8 %y, i8 %z) {
 ; CHECK-LABEL: define i8 @umin_of_uadd_sat_comm(
 ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
-; CHECK-NEXT:    [[ADD1:%.*]] = call i8 @llvm.uadd.sat.i8(i8 [[Z]], i8 [[X]])
-; CHECK-NEXT:    [[ADD2:%.*]] = call i8 @llvm.uadd.sat.i8(i8 [[Z]], i8 [[Y]])
-; CHECK-NEXT:    [[MIN:%.*]] = call i8 @llvm.umin.i8(i8 [[ADD1]], i8 [[ADD2]])
+; CHECK-NEXT:    [[TMP1:%.*]] = call i8 @llvm.umin.i8(i8 [[X]], i8 [[Y]])
+; CHECK-NEXT:    [[MIN:%.*]] = call i8 @llvm.uadd.sat.i8(i8 [[TMP1]], i8 [[Z]])
 ; CHECK-NEXT:    ret i8 [[MIN]]
 ;
   %add1 = call i8 @llvm.uadd.sat.i8(i8 %z, i8 %x)
@@ -172,9 +160,8 @@ define i8 @umin_of_uadd_sat_comm(i8 %x, i8 %y, i8 %z) {
 define i8 @smax_of_sadd_sat(i8 %x, i8 %y, i8 %z) {
 ; CHECK-LABEL: define i8 @smax_of_sadd_sat(
 ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
-; CHECK-NEXT:    [[ADD1:%.*]] = call i8 @llvm.sadd.sat.i8(i8 [[X]], i8 [[Z]])
-; CHECK-NEXT:    [[ADD2:%.*]] = call i8 @llvm.sadd.sat.i8(i8 [[Y]], i8 [[Z]])
-; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.smax.i8(i8 [[ADD1]], i8 [[ADD2]])
+; CHECK-NEXT:    [[TMP1:%.*]] = call i8 @llvm.smax.i8(i8 [[X]], i8 [[Y]])
+; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.sadd.sat.i8(i8 [[TMP1]], i8 [[Z]])
 ; CHECK-NEXT:    ret i8 [[MAX]]
 ;
   %add1 = call i8 @llvm.sadd.sat.i8(i8 %x, i8 %z)
@@ -186,9 +173,8 @@ define i8 @smax_of_sadd_sat(i8 %x, i8 %y, i8 %z) {
 define i8 @smax_of_sadd_sat_comm(i8 %x, i8 %y, i8 %z) {
 ; CHECK-LABEL: define i8 @smax_of_sadd_sat_comm(
 ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
-; CHECK-NEXT:    [[ADD1:%.*]] = call i8 @llvm.sadd.sat.i8(i8 [[Z]], i8 [[X]])
-; CHECK-NEXT:    [[ADD2:%.*]] = call i8 @llvm.sadd.sat.i8(i8 [[Z]], i8 [[Y]])
-; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.smax.i8(i8 [[ADD1]], i8 [[ADD2]])
+; CHECK-NEXT:    [[TMP1:%.*]] = call i8 @llvm.smax.i8(i8 [[X]], i8 [[Y]])
+; CHECK-NEXT:    [[MAX:%.*]] = call i8 @llvm.sadd.sat.i8(i8 [[TMP1]], i8 [[Z]])
 ; CHECK-NEXT:    ret i8 [[MAX]]
 ;
   %add1 = call i8 @llvm.sadd.sat.i8(i8 %z, i8 %x)
@@ -200,9 +186,8 @@ define i8 @smax_of_sadd_sat_comm(i8 %x, i8 %y, i8 %z) {
 define i8 @smin_of_sadd_sat(i8 %x, i8 %y, i8 %z) {
 ; CHECK-LABEL: define i8 @smin_of_sadd_sat(
 ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
-; CHECK-NEXT:    [[ADD1:%.*]] = call i8 @llvm.sadd.sat.i8(i8 [[X]], i8 [[Z]])
-; CHECK-NEXT:    [[ADD2:%.*]] = call i8 @llvm.sadd.sat.i8(i8 [[Y]], i8 [[Z]])
-; CHECK-NEXT:    [[MIN:%.*]] = call i8 @llvm.smin.i8(i8 [[ADD1]], i8 [[ADD2]])
+; CHECK-NEXT:    [[TMP1:%.*]] = call i8 @llvm.smin.i8(i8 [[X]], i8 [[Y]])
+; CHECK-NEXT:    [[MIN:%.*]] = call i8 @llvm.sadd.sat.i8(i8 [[TMP1]], i8 [[Z]])
 ; CHECK-NEXT:    ret i8 [[MIN]]
 ;
   %add1 = call i8 @llvm.sadd.sat.i8(i8 %x, i8 %z)
@@ -214,9 +199,8 @@ define i8 @smin_of_sadd_sat(i8 %x, i8 %y, i8 %z) {
 define i8 @smin_of_sadd_sat_comm(i8 %x, i8 %y, i8 %z) {
 ; CHECK-LABEL: define i8 @smin_of_sadd_sat_comm(
 ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
-; CHECK-NEXT:    [[ADD1:%.*]] = call i8 @llvm.sadd.sat.i8(i8 [[Z]], i8 [[X]])
-; CHECK-NEXT:    [[ADD2:%.*]] = call i8 @llvm.sadd.sat.i8(i8 [[Z]], i8 [[Y]])
-; CHECK-NEXT:    [[MIN:%.*]] = call i8 @llvm.smin.i8(i8 [[ADD1]], i8 [[ADD2]])
+; CHECK-NEXT:    [[TMP1:%.*]] = call i8 @llvm.smin.i8(i8 [[X]], i8 [[Y]])
+; CHECK-NEXT:    [[MIN:%.*]] = call i8 @llvm.sadd.sat.i8(i8 [[TMP1]], i8 [[Z]])
 ; CHECK-NEXT:    ret i8 [[MIN]]
 ;
   %add1 = call i8 @llvm.sadd.sat.i8(i8 %z, i8 %x)



More information about the llvm-commits mailing list