[llvm] [CVP] Canonicalize signed minmax into unsigned (PR #82478)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Thu Feb 22 05:58:03 PST 2024


https://github.com/dtcxzyw updated https://github.com/llvm/llvm-project/pull/82478

>From 66f7186f018a9f4dbf2d7e9fd1be80ee34fb3c3c Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Thu, 22 Feb 2024 21:55:37 +0800
Subject: [PATCH 1/2] [CVP] Add pre-commit tests. NFC.

---
 .../CorrelatedValuePropagation/min-max.ll     | 71 ++++++++++++++++++-
 1 file changed, 70 insertions(+), 1 deletion(-)

diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/min-max.ll b/llvm/test/Transforms/CorrelatedValuePropagation/min-max.ll
index d21b8f2418c2ee..01dcedbdb658da 100644
--- a/llvm/test/Transforms/CorrelatedValuePropagation/min-max.ll
+++ b/llvm/test/Transforms/CorrelatedValuePropagation/min-max.ll
@@ -1,5 +1,6 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=correlated-propagation -S | FileCheck %s
+; RUN: opt < %s -passes=correlated-propagation -S | FileCheck %s --check-prefixes=CHECK,CHECK-CANONICALIZE-ON
+; RUN: opt < %s -passes=correlated-propagation -canonicalize-icmp-predicates-to-unsigned=false -S | FileCheck %s --check-prefixes=CHECK,CHECK-CANONICALIZE-OFF
 
 declare void @llvm.assume(i1)
 declare i8 @llvm.umin(i8, i8)
@@ -290,3 +291,71 @@ if.end:
   %phi = phi i64 [%val, %bb1], [0, %entry]
   ret i64 %phi
 }
+
+define i8 @test_smax_to_umax_nneg(i8 %a, i8 %b) {
+; CHECK-LABEL: @test_smax_to_umax_nneg(
+; CHECK-NEXT:    [[NNEG_A:%.*]] = and i8 [[A:%.*]], 127
+; CHECK-NEXT:    [[NNEG_B:%.*]] = and i8 [[B:%.*]], 127
+; CHECK-NEXT:    [[RET:%.*]] = call i8 @llvm.smax.i8(i8 [[NNEG_A]], i8 [[NNEG_B]])
+; CHECK-NEXT:    ret i8 [[RET]]
+;
+  %nneg_a = and i8 %a, 127
+  %nneg_b = and i8 %b, 127
+  %ret = call i8 @llvm.smax.i8(i8 %nneg_a, i8 %nneg_b)
+  ret i8 %ret
+}
+
+define i8 @test_smax_to_umax_neg(i8 %a, i8 %b) {
+; CHECK-LABEL: @test_smax_to_umax_neg(
+; CHECK-NEXT:    [[NEG_A:%.*]] = or i8 [[A:%.*]], -128
+; CHECK-NEXT:    [[NEG_B:%.*]] = or i8 [[B:%.*]], -128
+; CHECK-NEXT:    [[RET:%.*]] = call i8 @llvm.smax.i8(i8 [[NEG_A]], i8 [[NEG_B]])
+; CHECK-NEXT:    ret i8 [[RET]]
+;
+  %neg_a = or i8 %a, 128
+  %neg_b = or i8 %b, 128
+  %ret = call i8 @llvm.smax.i8(i8 %neg_a, i8 %neg_b)
+  ret i8 %ret
+}
+
+define i8 @test_smin_to_umin_nneg(i8 %a, i8 %b) {
+; CHECK-LABEL: @test_smin_to_umin_nneg(
+; CHECK-NEXT:    [[NNEG_A:%.*]] = and i8 [[A:%.*]], 127
+; CHECK-NEXT:    [[NNEG_B:%.*]] = and i8 [[B:%.*]], 127
+; CHECK-NEXT:    [[RET:%.*]] = call i8 @llvm.smin.i8(i8 [[NNEG_A]], i8 [[NNEG_B]])
+; CHECK-NEXT:    ret i8 [[RET]]
+;
+  %nneg_a = and i8 %a, 127
+  %nneg_b = and i8 %b, 127
+  %ret = call i8 @llvm.smin.i8(i8 %nneg_a, i8 %nneg_b)
+  ret i8 %ret
+}
+
+define i8 @test_smin_to_umin_neg(i8 %a, i8 %b) {
+; CHECK-LABEL: @test_smin_to_umin_neg(
+; CHECK-NEXT:    [[NEG_A:%.*]] = or i8 [[A:%.*]], -128
+; CHECK-NEXT:    [[NEG_B:%.*]] = or i8 [[B:%.*]], -128
+; CHECK-NEXT:    [[RET:%.*]] = call i8 @llvm.smin.i8(i8 [[NEG_A]], i8 [[NEG_B]])
+; CHECK-NEXT:    ret i8 [[RET]]
+;
+  %neg_a = or i8 %a, 128
+  %neg_b = or i8 %b, 128
+  %ret = call i8 @llvm.smin.i8(i8 %neg_a, i8 %neg_b)
+  ret i8 %ret
+}
+
+define i8 @test_umax_nneg(i8 %a, i8 %b) {
+; CHECK-LABEL: @test_umax_nneg(
+; CHECK-NEXT:    [[NNEG_A:%.*]] = and i8 [[A:%.*]], 127
+; CHECK-NEXT:    [[NNEG_B:%.*]] = and i8 [[B:%.*]], 127
+; CHECK-NEXT:    [[RET:%.*]] = call i8 @llvm.umax.i8(i8 [[NNEG_A]], i8 [[NNEG_B]])
+; CHECK-NEXT:    ret i8 [[RET]]
+;
+  %nneg_a = and i8 %a, 127
+  %nneg_b = and i8 %b, 127
+  %ret = call i8 @llvm.umax.i8(i8 %nneg_a, i8 %nneg_b)
+  ret i8 %ret
+}
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-CANONICALIZE-OFF: {{.*}}
+; CHECK-CANONICALIZE-ON: {{.*}}

>From d3411f473841200727ec610cd3d01d2d5e2331c4 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Thu, 22 Feb 2024 21:56:53 +0800
Subject: [PATCH 2/2] [CVP] Canonicalize signed minmax into unsigned

---
 .../Scalar/CorrelatedValuePropagation.cpp     | 21 ++++
 .../CorrelatedValuePropagation/min-max.ll     | 99 ++++++++++++-------
 2 files changed, 87 insertions(+), 33 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
index c71870bc1b6569..35f4d7b3e53382 100644
--- a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
+++ b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
@@ -90,6 +90,8 @@ STATISTIC(NumSaturating,
     "Number of saturating arithmetics converted to normal arithmetics");
 STATISTIC(NumNonNull, "Number of function pointer arguments marked non-null");
 STATISTIC(NumMinMax, "Number of llvm.[us]{min,max} intrinsics removed");
+STATISTIC(NumSMinMax,
+          "Number of llvm.s{min,max} intrinsics simplified to unsigned");
 STATISTIC(NumUDivURemsNarrowedExpanded,
           "Number of bound udiv's/urem's expanded");
 STATISTIC(NumZExt, "Number of non-negative deductions");
@@ -528,6 +530,7 @@ static bool processAbsIntrinsic(IntrinsicInst *II, LazyValueInfo *LVI) {
 }
 
 // See if this min/max intrinsic always picks it's one specific operand.
+// If not, check whether we can canonicalize signed minmax into unsigned version
 static bool processMinMaxIntrinsic(MinMaxIntrinsic *MM, LazyValueInfo *LVI) {
   CmpInst::Predicate Pred = CmpInst::getNonStrictPredicate(MM->getPredicate());
   ConstantRange LHS_CR = LVI->getConstantRangeAtUse(MM->getOperandUse(0),
@@ -546,6 +549,24 @@ static bool processMinMaxIntrinsic(MinMaxIntrinsic *MM, LazyValueInfo *LVI) {
     MM->eraseFromParent();
     return true;
   }
+
+  // To match the behavior of processICmp
+  if (!CanonicalizeICmpPredicatesToUnsigned)
+    return false;
+
+  if (MM->isSigned() &&
+      ConstantRange::areInsensitiveToSignednessOfICmpPredicate(LHS_CR,
+                                                               RHS_CR)) {
+    ++NumSMinMax;
+    IRBuilder<> B(MM);
+    MM->replaceAllUsesWith(B.CreateBinaryIntrinsic(
+        MM->getIntrinsicID() == Intrinsic::smin ? Intrinsic::umin
+                                                : Intrinsic::umax,
+        MM->getLHS(), MM->getRHS()));
+    MM->eraseFromParent();
+    return true;
+  }
+
   return false;
 }
 
diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/min-max.ll b/llvm/test/Transforms/CorrelatedValuePropagation/min-max.ll
index 01dcedbdb658da..02fc4f3719167d 100644
--- a/llvm/test/Transforms/CorrelatedValuePropagation/min-max.ll
+++ b/llvm/test/Transforms/CorrelatedValuePropagation/min-max.ll
@@ -174,11 +174,17 @@ define i8 @test14(i8 %x) {
   ret i8 %r
 }
 define i8 @test15(i8 %x) {
-; CHECK-LABEL: @test15(
-; CHECK-NEXT:    [[LIM:%.*]] = icmp sge i8 [[X:%.*]], 41
-; CHECK-NEXT:    call void @llvm.assume(i1 [[LIM]])
-; CHECK-NEXT:    [[R:%.*]] = call i8 @llvm.smin.i8(i8 [[X]], i8 42)
-; CHECK-NEXT:    ret i8 [[R]]
+; CHECK-CANONICALIZE-ON-LABEL: @test15(
+; CHECK-CANONICALIZE-ON-NEXT:    [[LIM:%.*]] = icmp sge i8 [[X:%.*]], 41
+; CHECK-CANONICALIZE-ON-NEXT:    call void @llvm.assume(i1 [[LIM]])
+; CHECK-CANONICALIZE-ON-NEXT:    [[TMP1:%.*]] = call i8 @llvm.umin.i8(i8 [[X]], i8 42)
+; CHECK-CANONICALIZE-ON-NEXT:    ret i8 [[TMP1]]
+;
+; CHECK-CANONICALIZE-OFF-LABEL: @test15(
+; CHECK-CANONICALIZE-OFF-NEXT:    [[LIM:%.*]] = icmp sge i8 [[X:%.*]], 41
+; CHECK-CANONICALIZE-OFF-NEXT:    call void @llvm.assume(i1 [[LIM]])
+; CHECK-CANONICALIZE-OFF-NEXT:    [[R:%.*]] = call i8 @llvm.smin.i8(i8 [[X]], i8 42)
+; CHECK-CANONICALIZE-OFF-NEXT:    ret i8 [[R]]
 ;
   %lim = icmp sge i8 %x, 41
   call void @llvm.assume(i1 %lim)
@@ -187,11 +193,17 @@ define i8 @test15(i8 %x) {
 }
 
 define i8 @test16(i8 %x) {
-; CHECK-LABEL: @test16(
-; CHECK-NEXT:    [[LIM:%.*]] = icmp sge i8 [[X:%.*]], 41
-; CHECK-NEXT:    call void @llvm.assume(i1 [[LIM]])
-; CHECK-NEXT:    [[R:%.*]] = call i8 @llvm.smax.i8(i8 [[X]], i8 42)
-; CHECK-NEXT:    ret i8 [[R]]
+; CHECK-CANONICALIZE-ON-LABEL: @test16(
+; CHECK-CANONICALIZE-ON-NEXT:    [[LIM:%.*]] = icmp sge i8 [[X:%.*]], 41
+; CHECK-CANONICALIZE-ON-NEXT:    call void @llvm.assume(i1 [[LIM]])
+; CHECK-CANONICALIZE-ON-NEXT:    [[TMP1:%.*]] = call i8 @llvm.umax.i8(i8 [[X]], i8 42)
+; CHECK-CANONICALIZE-ON-NEXT:    ret i8 [[TMP1]]
+;
+; CHECK-CANONICALIZE-OFF-LABEL: @test16(
+; CHECK-CANONICALIZE-OFF-NEXT:    [[LIM:%.*]] = icmp sge i8 [[X:%.*]], 41
+; CHECK-CANONICALIZE-OFF-NEXT:    call void @llvm.assume(i1 [[LIM]])
+; CHECK-CANONICALIZE-OFF-NEXT:    [[R:%.*]] = call i8 @llvm.smax.i8(i8 [[X]], i8 42)
+; CHECK-CANONICALIZE-OFF-NEXT:    ret i8 [[R]]
 ;
   %lim = icmp sge i8 %x, 41
   call void @llvm.assume(i1 %lim)
@@ -293,11 +305,17 @@ if.end:
 }
 
 define i8 @test_smax_to_umax_nneg(i8 %a, i8 %b) {
-; CHECK-LABEL: @test_smax_to_umax_nneg(
-; CHECK-NEXT:    [[NNEG_A:%.*]] = and i8 [[A:%.*]], 127
-; CHECK-NEXT:    [[NNEG_B:%.*]] = and i8 [[B:%.*]], 127
-; CHECK-NEXT:    [[RET:%.*]] = call i8 @llvm.smax.i8(i8 [[NNEG_A]], i8 [[NNEG_B]])
-; CHECK-NEXT:    ret i8 [[RET]]
+; CHECK-CANONICALIZE-ON-LABEL: @test_smax_to_umax_nneg(
+; CHECK-CANONICALIZE-ON-NEXT:    [[NNEG_A:%.*]] = and i8 [[A:%.*]], 127
+; CHECK-CANONICALIZE-ON-NEXT:    [[NNEG_B:%.*]] = and i8 [[B:%.*]], 127
+; CHECK-CANONICALIZE-ON-NEXT:    [[TMP1:%.*]] = call i8 @llvm.umax.i8(i8 [[NNEG_A]], i8 [[NNEG_B]])
+; CHECK-CANONICALIZE-ON-NEXT:    ret i8 [[TMP1]]
+;
+; CHECK-CANONICALIZE-OFF-LABEL: @test_smax_to_umax_nneg(
+; CHECK-CANONICALIZE-OFF-NEXT:    [[NNEG_A:%.*]] = and i8 [[A:%.*]], 127
+; CHECK-CANONICALIZE-OFF-NEXT:    [[NNEG_B:%.*]] = and i8 [[B:%.*]], 127
+; CHECK-CANONICALIZE-OFF-NEXT:    [[RET:%.*]] = call i8 @llvm.smax.i8(i8 [[NNEG_A]], i8 [[NNEG_B]])
+; CHECK-CANONICALIZE-OFF-NEXT:    ret i8 [[RET]]
 ;
   %nneg_a = and i8 %a, 127
   %nneg_b = and i8 %b, 127
@@ -306,11 +324,17 @@ define i8 @test_smax_to_umax_nneg(i8 %a, i8 %b) {
 }
 
 define i8 @test_smax_to_umax_neg(i8 %a, i8 %b) {
-; CHECK-LABEL: @test_smax_to_umax_neg(
-; CHECK-NEXT:    [[NEG_A:%.*]] = or i8 [[A:%.*]], -128
-; CHECK-NEXT:    [[NEG_B:%.*]] = or i8 [[B:%.*]], -128
-; CHECK-NEXT:    [[RET:%.*]] = call i8 @llvm.smax.i8(i8 [[NEG_A]], i8 [[NEG_B]])
-; CHECK-NEXT:    ret i8 [[RET]]
+; CHECK-CANONICALIZE-ON-LABEL: @test_smax_to_umax_neg(
+; CHECK-CANONICALIZE-ON-NEXT:    [[NEG_A:%.*]] = or i8 [[A:%.*]], -128
+; CHECK-CANONICALIZE-ON-NEXT:    [[NEG_B:%.*]] = or i8 [[B:%.*]], -128
+; CHECK-CANONICALIZE-ON-NEXT:    [[TMP1:%.*]] = call i8 @llvm.umax.i8(i8 [[NEG_A]], i8 [[NEG_B]])
+; CHECK-CANONICALIZE-ON-NEXT:    ret i8 [[TMP1]]
+;
+; CHECK-CANONICALIZE-OFF-LABEL: @test_smax_to_umax_neg(
+; CHECK-CANONICALIZE-OFF-NEXT:    [[NEG_A:%.*]] = or i8 [[A:%.*]], -128
+; CHECK-CANONICALIZE-OFF-NEXT:    [[NEG_B:%.*]] = or i8 [[B:%.*]], -128
+; CHECK-CANONICALIZE-OFF-NEXT:    [[RET:%.*]] = call i8 @llvm.smax.i8(i8 [[NEG_A]], i8 [[NEG_B]])
+; CHECK-CANONICALIZE-OFF-NEXT:    ret i8 [[RET]]
 ;
   %neg_a = or i8 %a, 128
   %neg_b = or i8 %b, 128
@@ -319,11 +343,17 @@ define i8 @test_smax_to_umax_neg(i8 %a, i8 %b) {
 }
 
 define i8 @test_smin_to_umin_nneg(i8 %a, i8 %b) {
-; CHECK-LABEL: @test_smin_to_umin_nneg(
-; CHECK-NEXT:    [[NNEG_A:%.*]] = and i8 [[A:%.*]], 127
-; CHECK-NEXT:    [[NNEG_B:%.*]] = and i8 [[B:%.*]], 127
-; CHECK-NEXT:    [[RET:%.*]] = call i8 @llvm.smin.i8(i8 [[NNEG_A]], i8 [[NNEG_B]])
-; CHECK-NEXT:    ret i8 [[RET]]
+; CHECK-CANONICALIZE-ON-LABEL: @test_smin_to_umin_nneg(
+; CHECK-CANONICALIZE-ON-NEXT:    [[NNEG_A:%.*]] = and i8 [[A:%.*]], 127
+; CHECK-CANONICALIZE-ON-NEXT:    [[NNEG_B:%.*]] = and i8 [[B:%.*]], 127
+; CHECK-CANONICALIZE-ON-NEXT:    [[TMP1:%.*]] = call i8 @llvm.umin.i8(i8 [[NNEG_A]], i8 [[NNEG_B]])
+; CHECK-CANONICALIZE-ON-NEXT:    ret i8 [[TMP1]]
+;
+; CHECK-CANONICALIZE-OFF-LABEL: @test_smin_to_umin_nneg(
+; CHECK-CANONICALIZE-OFF-NEXT:    [[NNEG_A:%.*]] = and i8 [[A:%.*]], 127
+; CHECK-CANONICALIZE-OFF-NEXT:    [[NNEG_B:%.*]] = and i8 [[B:%.*]], 127
+; CHECK-CANONICALIZE-OFF-NEXT:    [[RET:%.*]] = call i8 @llvm.smin.i8(i8 [[NNEG_A]], i8 [[NNEG_B]])
+; CHECK-CANONICALIZE-OFF-NEXT:    ret i8 [[RET]]
 ;
   %nneg_a = and i8 %a, 127
   %nneg_b = and i8 %b, 127
@@ -332,11 +362,17 @@ define i8 @test_smin_to_umin_nneg(i8 %a, i8 %b) {
 }
 
 define i8 @test_smin_to_umin_neg(i8 %a, i8 %b) {
-; CHECK-LABEL: @test_smin_to_umin_neg(
-; CHECK-NEXT:    [[NEG_A:%.*]] = or i8 [[A:%.*]], -128
-; CHECK-NEXT:    [[NEG_B:%.*]] = or i8 [[B:%.*]], -128
-; CHECK-NEXT:    [[RET:%.*]] = call i8 @llvm.smin.i8(i8 [[NEG_A]], i8 [[NEG_B]])
-; CHECK-NEXT:    ret i8 [[RET]]
+; CHECK-CANONICALIZE-ON-LABEL: @test_smin_to_umin_neg(
+; CHECK-CANONICALIZE-ON-NEXT:    [[NEG_A:%.*]] = or i8 [[A:%.*]], -128
+; CHECK-CANONICALIZE-ON-NEXT:    [[NEG_B:%.*]] = or i8 [[B:%.*]], -128
+; CHECK-CANONICALIZE-ON-NEXT:    [[TMP1:%.*]] = call i8 @llvm.umin.i8(i8 [[NEG_A]], i8 [[NEG_B]])
+; CHECK-CANONICALIZE-ON-NEXT:    ret i8 [[TMP1]]
+;
+; CHECK-CANONICALIZE-OFF-LABEL: @test_smin_to_umin_neg(
+; CHECK-CANONICALIZE-OFF-NEXT:    [[NEG_A:%.*]] = or i8 [[A:%.*]], -128
+; CHECK-CANONICALIZE-OFF-NEXT:    [[NEG_B:%.*]] = or i8 [[B:%.*]], -128
+; CHECK-CANONICALIZE-OFF-NEXT:    [[RET:%.*]] = call i8 @llvm.smin.i8(i8 [[NEG_A]], i8 [[NEG_B]])
+; CHECK-CANONICALIZE-OFF-NEXT:    ret i8 [[RET]]
 ;
   %neg_a = or i8 %a, 128
   %neg_b = or i8 %b, 128
@@ -356,6 +392,3 @@ define i8 @test_umax_nneg(i8 %a, i8 %b) {
   %ret = call i8 @llvm.umax.i8(i8 %nneg_a, i8 %nneg_b)
   ret i8 %ret
 }
-;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
-; CHECK-CANONICALIZE-OFF: {{.*}}
-; CHECK-CANONICALIZE-ON: {{.*}}



More information about the llvm-commits mailing list