[llvm] be7d08c - [InstCombine] Fold `sext(A < B) + zext(A > B)` into `ucmp/scmp(A, B)` (#103833)

via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 21 15:15:28 PDT 2024


Author: Volodymyr Vasylkun
Date: 2024-08-21T23:15:24+01:00
New Revision: be7d08cd59b0f23eea88e791b2413b44301949d3

URL: https://github.com/llvm/llvm-project/commit/be7d08cd59b0f23eea88e791b2413b44301949d3
DIFF: https://github.com/llvm/llvm-project/commit/be7d08cd59b0f23eea88e791b2413b44301949d3.diff

LOG: [InstCombine] Fold `sext(A < B) + zext(A > B)` into `ucmp/scmp(A, B)` (#103833)

This change also covers the fold of `zext(A > B) - zext(A < B)` since it
is already being canonicalized into the aforementioned pattern.

Proof: https://alive2.llvm.org/ce/z/AgnfMn

Added: 
    llvm/test/Transforms/InstCombine/sext-a-lt-b-plus-zext-a-gt-b-to-uscmp.ll

Modified: 
    llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
    llvm/test/Transforms/InstCombine/add.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index dd4a64050f878a..d7758b5fbf1786 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -1626,6 +1626,26 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
       A->getType()->isIntOrIntVectorTy(1))
     return replaceInstUsesWith(I, Constant::getNullValue(I.getType()));
 
+  // sext(A < B) + zext(A > B) => ucmp/scmp(A, B)
+  ICmpInst::Predicate LTPred, GTPred;
+  if (match(&I,
+            m_c_Add(m_SExt(m_c_ICmp(LTPred, m_Value(A), m_Value(B))),
+                    m_ZExt(m_c_ICmp(GTPred, m_Deferred(A), m_Deferred(B))))) &&
+      A->getType()->isIntOrIntVectorTy()) {
+    if (ICmpInst::isGT(LTPred)) {
+      std::swap(LTPred, GTPred);
+      std::swap(A, B);
+    }
+
+    if (ICmpInst::isLT(LTPred) && ICmpInst::isGT(GTPred) &&
+        ICmpInst::isSigned(LTPred) == ICmpInst::isSigned(GTPred))
+      return replaceInstUsesWith(
+          I, Builder.CreateIntrinsic(
+                 Ty,
+                 ICmpInst::isSigned(LTPred) ? Intrinsic::scmp : Intrinsic::ucmp,
+                 {A, B}));
+  }
+
   // A+B --> A|B iff A and B have no bits set in common.
   WithCache<const Value *> LHSCache(LHS), RHSCache(RHS);
   if (haveNoCommonBitsSet(LHSCache, RHSCache, SQ.getWithInstruction(&I)))

diff  --git a/llvm/test/Transforms/InstCombine/add.ll b/llvm/test/Transforms/InstCombine/add.ll
index 36da56d8441bf7..417c3a950d7805 100644
--- a/llvm/test/Transforms/InstCombine/add.ll
+++ b/llvm/test/Transforms/InstCombine/add.ll
@@ -1315,8 +1315,8 @@ define <2 x i8> @ashr_add_commute(<2 x i1> %x, <2 x i1> %y) {
 
 define i32 @cmp_math(i32 %x, i32 %y) {
 ; CHECK-LABEL: @cmp_math(
-; CHECK-NEXT:    [[LT:%.*]] = icmp ult i32 [[X:%.*]], [[Y:%.*]]
-; CHECK-NEXT:    [[R:%.*]] = zext i1 [[LT]] to i32
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp ult i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = zext i1 [[TMP1]] to i32
 ; CHECK-NEXT:    ret i32 [[R]]
 ;
   %gt = icmp ugt i32 %x, %y

diff  --git a/llvm/test/Transforms/InstCombine/sext-a-lt-b-plus-zext-a-gt-b-to-uscmp.ll b/llvm/test/Transforms/InstCombine/sext-a-lt-b-plus-zext-a-gt-b-to-uscmp.ll
new file mode 100644
index 00000000000000..02ae7ce82f13ce
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/sext-a-lt-b-plus-zext-a-gt-b-to-uscmp.ll
@@ -0,0 +1,184 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+; sext(A s< B) + zext(A s> B) => scmp(A, B)
+define i8 @signed_add(i32 %a, i32 %b) {
+; CHECK-LABEL: define i8 @signed_add(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = call i8 @llvm.scmp.i8.i32(i32 [[A]], i32 [[B]])
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %lt = icmp slt i32 %a, %b
+  %lt8 = sext i1 %lt to i8
+  %gt = icmp sgt i32 %a, %b
+  %gt8 = zext i1 %gt to i8
+  %r = add i8 %lt8, %gt8
+  ret i8 %r
+}
+
+; Unsigned version
+define i8 @unsigned_add(i32 %a, i32 %b) {
+; CHECK-LABEL: define i8 @unsigned_add(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = call i8 @llvm.ucmp.i8.i32(i32 [[A]], i32 [[B]])
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %lt = icmp ult i32 %a, %b
+  %lt8 = sext i1 %lt to i8
+  %gt = icmp ugt i32 %a, %b
+  %gt8 = zext i1 %gt to i8
+  %r = add i8 %lt8, %gt8
+  ret i8 %r
+}
+
+; Commuted operands
+define i8 @signed_add_commuted1(i32 %a, i32 %b) {
+; CHECK-LABEL: define i8 @signed_add_commuted1(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = call i8 @llvm.scmp.i8.i32(i32 [[B]], i32 [[A]])
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %lt = icmp slt i32 %a, %b
+  %lt8 = zext i1 %lt to i8
+  %gt = icmp sgt i32 %a, %b
+  %gt8 = sext i1 %gt to i8
+  %r = add i8 %lt8, %gt8
+  ret i8 %r
+}
+
+define i8 @signed_add_commuted2(i32 %a, i32 %b) {
+; CHECK-LABEL: define i8 @signed_add_commuted2(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = call i8 @llvm.scmp.i8.i32(i32 [[A]], i32 [[B]])
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %lt = icmp sgt i32 %b, %a
+  %lt8 = sext i1 %lt to i8
+  %gt = icmp sgt i32 %a, %b
+  %gt8 = zext i1 %gt to i8
+  %r = add i8 %lt8, %gt8
+  ret i8 %r
+}
+
+; zext(A s> B) - zext(A s< B) => scmp(A, B)
+define i8 @signed_sub(i32 %a, i32 %b) {
+; CHECK-LABEL: define i8 @signed_sub(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = call i8 @llvm.scmp.i8.i32(i32 [[A]], i32 [[B]])
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %lt = icmp slt i32 %a, %b
+  %lt8 = zext i1 %lt to i8
+  %gt = icmp sgt i32 %a, %b
+  %gt8 = zext i1 %gt to i8
+  %r = sub i8 %gt8, %lt8
+  ret i8 %r
+}
+
+; Unsigned version
+define i8 @unsigned_sub(i32 %a, i32 %b) {
+; CHECK-LABEL: define i8 @unsigned_sub(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = call i8 @llvm.ucmp.i8.i32(i32 [[A]], i32 [[B]])
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %lt = icmp ult i32 %a, %b
+  %lt8 = zext i1 %lt to i8
+  %gt = icmp ugt i32 %a, %b
+  %gt8 = zext i1 %gt to i8
+  %r = sub i8 %gt8, %lt8
+  ret i8 %r
+}
+
+; Negative test: incorrect predicates
+define i8 @signed_add_neg1(i32 %a, i32 %b) {
+; CHECK-LABEL: define i8 @signed_add_neg1(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:    [[LT:%.*]] = icmp sgt i32 [[A]], [[B]]
+; CHECK-NEXT:    [[LT8:%.*]] = sext i1 [[LT]] to i8
+; CHECK-NEXT:    [[GT:%.*]] = icmp sgt i32 [[A]], [[B]]
+; CHECK-NEXT:    [[GT8:%.*]] = zext i1 [[GT]] to i8
+; CHECK-NEXT:    [[R:%.*]] = add nsw i8 [[LT8]], [[GT8]]
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %lt = icmp sgt i32 %a, %b
+  %lt8 = sext i1 %lt to i8
+  %gt = icmp sgt i32 %a, %b
+  %gt8 = zext i1 %gt to i8
+  %r = add i8 %lt8, %gt8
+  ret i8 %r
+}
+
+define i8 @signed_add_neg2(i32 %a, i32 %b) {
+; CHECK-LABEL: define i8 @signed_add_neg2(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:    [[LT:%.*]] = icmp slt i32 [[A]], [[B]]
+; CHECK-NEXT:    [[LT8:%.*]] = sext i1 [[LT]] to i8
+; CHECK-NEXT:    [[GT:%.*]] = icmp ne i32 [[A]], [[B]]
+; CHECK-NEXT:    [[GT8:%.*]] = zext i1 [[GT]] to i8
+; CHECK-NEXT:    [[R:%.*]] = add nsw i8 [[LT8]], [[GT8]]
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %lt = icmp slt i32 %a, %b
+  %lt8 = sext i1 %lt to i8
+  %gt = icmp ne i32 %a, %b
+  %gt8 = zext i1 %gt to i8
+  %r = add i8 %lt8, %gt8
+  ret i8 %r
+}
+
+; Negative test: mismatched signedness of predicates
+define i8 @signed_add_neg3(i32 %a, i32 %b) {
+; CHECK-LABEL: define i8 @signed_add_neg3(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:    [[LT:%.*]] = icmp slt i32 [[A]], [[B]]
+; CHECK-NEXT:    [[LT8:%.*]] = sext i1 [[LT]] to i8
+; CHECK-NEXT:    [[GT:%.*]] = icmp ugt i32 [[A]], [[B]]
+; CHECK-NEXT:    [[GT8:%.*]] = zext i1 [[GT]] to i8
+; CHECK-NEXT:    [[R:%.*]] = add nsw i8 [[LT8]], [[GT8]]
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %lt = icmp slt i32 %a, %b
+  %lt8 = sext i1 %lt to i8
+  %gt = icmp ugt i32 %a, %b
+  %gt8 = zext i1 %gt to i8
+  %r = add i8 %lt8, %gt8
+  ret i8 %r
+}
+
+; Negative test: zext instead of sext or vice-versa (NOT commuted operands)
+define i8 @signed_add_neg4(i32 %a, i32 %b) {
+; CHECK-LABEL: define i8 @signed_add_neg4(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:    [[LT:%.*]] = icmp slt i32 [[A]], [[B]]
+; CHECK-NEXT:    [[LT8:%.*]] = sext i1 [[LT]] to i8
+; CHECK-NEXT:    [[GT:%.*]] = icmp sgt i32 [[A]], [[B]]
+; CHECK-NEXT:    [[GT8:%.*]] = sext i1 [[GT]] to i8
+; CHECK-NEXT:    [[R:%.*]] = add nsw i8 [[LT8]], [[GT8]]
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %lt = icmp slt i32 %a, %b
+  %lt8 = sext i1 %lt to i8
+  %gt = icmp sgt i32 %a, %b
+  %gt8 = sext i1 %gt to i8
+  %r = add i8 %lt8, %gt8
+  ret i8 %r
+}
+
+define i8 @signed_add_neg5(i32 %a, i32 %b) {
+; CHECK-LABEL: define i8 @signed_add_neg5(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:    [[LT:%.*]] = icmp slt i32 [[A]], [[B]]
+; CHECK-NEXT:    [[LT8:%.*]] = zext i1 [[LT]] to i8
+; CHECK-NEXT:    [[GT:%.*]] = icmp sgt i32 [[A]], [[B]]
+; CHECK-NEXT:    [[GT8:%.*]] = zext i1 [[GT]] to i8
+; CHECK-NEXT:    [[R:%.*]] = add nuw nsw i8 [[LT8]], [[GT8]]
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %lt = icmp slt i32 %a, %b
+  %lt8 = zext i1 %lt to i8
+  %gt = icmp sgt i32 %a, %b
+  %gt8 = zext i1 %gt to i8
+  %r = add i8 %lt8, %gt8
+  ret i8 %r
+}


        


More information about the llvm-commits mailing list