[llvm] [InstCombine] Treat umax as select(icmp eq x, 0), 1, x) in binop select fold. (PR #65978)

David Green via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 11 09:49:04 PDT 2023


https://github.com/davemgreen created https://github.com/llvm/llvm-project/pull/65978:

There is an existing instcombine in SimplifySelectsFeedingBinaryOp for folding `(A ? B : C) binop (A ? E : F) -> A ? (B binop E) : (C binop F)`. However this will not combine if the select `(x>=1 ? x : 1)` has been converted to a `umax(x, 1)`. This adds code to treat the umax as a `select(x==0,1,x)` if it matches the other operand, allowing binops to fold into the select/umax pair.

>From d94b9fe9cdd4d9db9307cdf504d0c41730389acb Mon Sep 17 00:00:00 2001
From: David Green <david.green at arm.com>
Date: Mon, 11 Sep 2023 17:14:13 +0100
Subject: [PATCH 1/2] [Instcombine] Add tests for treating umax as a select.
 NFC

---
 .../Transforms/InstCombine/binop-select.ll    | 63 +++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/binop-select.ll b/llvm/test/Transforms/InstCombine/binop-select.ll
index a59e19897f061d1..bb82857db10f4fd 100644
--- a/llvm/test/Transforms/InstCombine/binop-select.ll
+++ b/llvm/test/Transforms/InstCombine/binop-select.ll
@@ -403,3 +403,66 @@ define i32 @ashr_sel_op1_use(i1 %b) {
   %r = ashr i32 -2, %s
   ret i32 %r
 }
+
+
+define i32 @umax_as_select_sub(i32 %a) {
+; CHECK-LABEL: @umax_as_select_sub(
+; CHECK-NEXT:    [[C_NOT:%.*]] = icmp eq i32 [[A:%.*]], 0
+; CHECK-NEXT:    [[S:%.*]] = select i1 [[C_NOT]], i32 0, i32 2
+; CHECK-NEXT:    [[M:%.*]] = call i32 @llvm.umax.i32(i32 [[A]], i32 1)
+; CHECK-NEXT:    [[B:%.*]] = sub i32 [[S]], [[M]]
+; CHECK-NEXT:    ret i32 [[B]]
+;
+  %c = icmp ugt i32 %a, 0
+  %s = select i1 %c, i32 2, i32 0
+  %m = call i32 @llvm.umax.i32(i32 %a, i32 1)
+  %b = sub i32 %s, %m
+  ret i32 %b
+}
+
+define i32 @umax_as_select_sub_c(i32 %a) {
+; CHECK-LABEL: @umax_as_select_sub_c(
+; CHECK-NEXT:    [[C_NOT:%.*]] = icmp eq i32 [[A:%.*]], 0
+; CHECK-NEXT:    [[S_NEG:%.*]] = select i1 [[C_NOT]], i32 0, i32 -2
+; CHECK-NEXT:    [[M:%.*]] = call i32 @llvm.umax.i32(i32 [[A]], i32 1)
+; CHECK-NEXT:    [[B:%.*]] = add i32 [[S_NEG]], [[M]]
+; CHECK-NEXT:    ret i32 [[B]]
+;
+  %c = icmp ugt i32 %a, 0
+  %s = select i1 %c, i32 2, i32 0
+  %m = call i32 @llvm.umax.i32(i32 %a, i32 1)
+  %b = sub i32 %m, %s
+  ret i32 %b
+}
+
+define i32 @umax_as_select_add(i32 %a, i32 %x, i32 %y) {
+; CHECK-LABEL: @umax_as_select_add(
+; CHECK-NEXT:    [[C_NOT:%.*]] = icmp eq i32 [[A:%.*]], 0
+; CHECK-NEXT:    [[S:%.*]] = select i1 [[C_NOT]], i32 [[Y:%.*]], i32 [[X:%.*]]
+; CHECK-NEXT:    [[M:%.*]] = call i32 @llvm.umax.i32(i32 [[A]], i32 1)
+; CHECK-NEXT:    [[B:%.*]] = add i32 [[S]], [[M]]
+; CHECK-NEXT:    ret i32 [[B]]
+;
+  %c = icmp ugt i32 %a, 0
+  %s = select i1 %c, i32 %x, i32 %y
+  %m = call i32 @llvm.umax.i32(i32 %a, i32 1)
+  %b = add i32 %s, %m
+  ret i32 %b
+}
+
+define i32 @umax_as_select_mul(i32 %a) {
+; CHECK-LABEL: @umax_as_select_mul(
+; CHECK-NEXT:    [[C_NOT:%.*]] = icmp eq i32 [[A:%.*]], 0
+; CHECK-NEXT:    [[S:%.*]] = select i1 [[C_NOT]], i32 0, i32 2
+; CHECK-NEXT:    [[M:%.*]] = call i32 @llvm.umax.i32(i32 [[A]], i32 1)
+; CHECK-NEXT:    [[B:%.*]] = mul i32 [[S]], [[M]]
+; CHECK-NEXT:    ret i32 [[B]]
+;
+  %c = icmp ugt i32 %a, 0
+  %s = select i1 %c, i32 2, i32 0
+  %m = select i1 %c, i32 %a, i32 1
+  %b = mul i32 %s, %m
+  ret i32 %b
+}
+
+declare i32 @llvm.umax.i32(i32, i32)

>From 88831735addd78e40f2046e8a38353c22a1c4305 Mon Sep 17 00:00:00 2001
From: David Green <david.green at arm.com>
Date: Mon, 11 Sep 2023 17:24:40 +0100
Subject: [PATCH 2/2] [InstCombine] Treat umax as select(icmp eq x, 0), 1, x)
 in binop select fold.

There is an existing combine in instcombine for combining
`(A ? B : C) binop (A ? E : F) -> A ? (B binop E) : (C binop F)`.
However this will not combine if the select `(x>=1 ? x : 1)` has
been converted to a `umax(x, 1)`. This adds code to treat the umax
as a select, allowing binops to fold into the select.
---
 .../InstCombine/InstructionCombining.cpp      | 20 +++++++++++++++++++
 .../Transforms/InstCombine/binop-select.ll    | 17 ++++++----------
 2 files changed, 26 insertions(+), 11 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index ed8709ea4c051f7..dc551a8dba1900f 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -1103,9 +1103,29 @@ Value *InstCombinerImpl::SimplifySelectsFeedingBinaryOp(BinaryOperator &I,
   Value *A, *B, *C, *D, *E, *F;
   bool LHSIsSelect = match(LHS, m_Select(m_Value(A), m_Value(B), m_Value(C)));
   bool RHSIsSelect = match(RHS, m_Select(m_Value(D), m_Value(E), m_Value(F)));
+
   if (!LHSIsSelect && !RHSIsSelect)
     return nullptr;
 
+  // Treat umax(x, 1) as select(icmp(eq, x, 0), 1, x), if it matches the other
+  // predicate.
+  auto TryMatchSelectFromUMax = [](bool LHSIsSelect, Value *RHS, bool &RHSIsSelect,
+                                   Value *A, Value *B, Value *C, Value *&D,
+                                   Value *&E, Value *&F) {
+    CmpInst::Predicate Pred;
+    Value *X;
+    if (LHSIsSelect && !RHSIsSelect &&
+        match(RHS, m_UMax(m_Value(X), m_One())) &&
+        match(A, m_c_ICmp(Pred, m_Specific(X), m_Zero())) &&
+        Pred == ICmpInst::ICMP_EQ) {
+      RHSIsSelect = true;
+      match(RHS, m_UMax(m_Value(F), m_Value(E)));
+      D = A;
+    }
+  };
+  TryMatchSelectFromUMax(LHSIsSelect, RHS, RHSIsSelect, A, B, C, D, E, F);
+  TryMatchSelectFromUMax(RHSIsSelect, LHS, LHSIsSelect, D, E, F, A, B, C);
+
   FastMathFlags FMF;
   BuilderTy::FastMathFlagGuard Guard(Builder);
   if (isa<FPMathOperator>(&I)) {
diff --git a/llvm/test/Transforms/InstCombine/binop-select.ll b/llvm/test/Transforms/InstCombine/binop-select.ll
index bb82857db10f4fd..b064b3447e8135b 100644
--- a/llvm/test/Transforms/InstCombine/binop-select.ll
+++ b/llvm/test/Transforms/InstCombine/binop-select.ll
@@ -408,9 +408,8 @@ define i32 @ashr_sel_op1_use(i1 %b) {
 define i32 @umax_as_select_sub(i32 %a) {
 ; CHECK-LABEL: @umax_as_select_sub(
 ; CHECK-NEXT:    [[C_NOT:%.*]] = icmp eq i32 [[A:%.*]], 0
-; CHECK-NEXT:    [[S:%.*]] = select i1 [[C_NOT]], i32 0, i32 2
-; CHECK-NEXT:    [[M:%.*]] = call i32 @llvm.umax.i32(i32 [[A]], i32 1)
-; CHECK-NEXT:    [[B:%.*]] = sub i32 [[S]], [[M]]
+; CHECK-NEXT:    [[TMP1:%.*]] = sub i32 2, [[A]]
+; CHECK-NEXT:    [[B:%.*]] = select i1 [[C_NOT]], i32 -1, i32 [[TMP1]]
 ; CHECK-NEXT:    ret i32 [[B]]
 ;
   %c = icmp ugt i32 %a, 0
@@ -423,9 +422,8 @@ define i32 @umax_as_select_sub(i32 %a) {
 define i32 @umax_as_select_sub_c(i32 %a) {
 ; CHECK-LABEL: @umax_as_select_sub_c(
 ; CHECK-NEXT:    [[C_NOT:%.*]] = icmp eq i32 [[A:%.*]], 0
-; CHECK-NEXT:    [[S_NEG:%.*]] = select i1 [[C_NOT]], i32 0, i32 -2
-; CHECK-NEXT:    [[M:%.*]] = call i32 @llvm.umax.i32(i32 [[A]], i32 1)
-; CHECK-NEXT:    [[B:%.*]] = add i32 [[S_NEG]], [[M]]
+; CHECK-NEXT:    [[TMP1:%.*]] = add i32 [[A]], -2
+; CHECK-NEXT:    [[B:%.*]] = select i1 [[C_NOT]], i32 1, i32 [[TMP1]]
 ; CHECK-NEXT:    ret i32 [[B]]
 ;
   %c = icmp ugt i32 %a, 0
@@ -452,11 +450,8 @@ define i32 @umax_as_select_add(i32 %a, i32 %x, i32 %y) {
 
 define i32 @umax_as_select_mul(i32 %a) {
 ; CHECK-LABEL: @umax_as_select_mul(
-; CHECK-NEXT:    [[C_NOT:%.*]] = icmp eq i32 [[A:%.*]], 0
-; CHECK-NEXT:    [[S:%.*]] = select i1 [[C_NOT]], i32 0, i32 2
-; CHECK-NEXT:    [[M:%.*]] = call i32 @llvm.umax.i32(i32 [[A]], i32 1)
-; CHECK-NEXT:    [[B:%.*]] = mul i32 [[S]], [[M]]
-; CHECK-NEXT:    ret i32 [[B]]
+; CHECK-NEXT:    [[TMP1:%.*]] = shl i32 [[A:%.*]], 1
+; CHECK-NEXT:    ret i32 [[TMP1]]
 ;
   %c = icmp ugt i32 %a, 0
   %s = select i1 %c, i32 2, i32 0



More information about the llvm-commits mailing list