[llvm] [InstSimplify] Simplify `uadd.sat(X, Y) u>= X + Y` and `usub.sat(X, Y) u<= X, Y` (PR #104698)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Sun Aug 18 04:09:08 PDT 2024


https://github.com/dtcxzyw created https://github.com/llvm/llvm-project/pull/104698

These patterns are found in harfbuzz/typst.

Alive2: https://alive2.llvm.org/ce/z/cxyjYV


>From b659f185aa1b0a986d5b6bdfafeac551ea5d434b Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sun, 18 Aug 2024 18:53:38 +0800
Subject: [PATCH 1/2] [InstSimplify] Add pre-commit tests. NFC.

---
 .../InstSimplify/saturating-add-sub.ll        | 136 ++++++++++++++++++
 1 file changed, 136 insertions(+)

diff --git a/llvm/test/Transforms/InstSimplify/saturating-add-sub.ll b/llvm/test/Transforms/InstSimplify/saturating-add-sub.ll
index 40b22c619f7686..947585b76708e6 100644
--- a/llvm/test/Transforms/InstSimplify/saturating-add-sub.ll
+++ b/llvm/test/Transforms/InstSimplify/saturating-add-sub.ll
@@ -932,3 +932,139 @@ define i1 @usub_uge_fail(i8 %x, i8 %y) {
   %cmp = icmp uge i8 %sat, %x
   ret i1 %cmp
 }
+
+define i1 @icmp_ult_uaddsat_add(i32 %x, i32 %y) {
+; CHECK-LABEL: @icmp_ult_uaddsat_add(
+; CHECK-NEXT:    [[UADDSAT:%.*]] = call i32 @llvm.uadd.sat.i32(i32 [[X:%.*]], i32 [[Y:%.*]])
+; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i32 [[UADDSAT]], [[ADD]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %uaddsat = call i32 @llvm.uadd.sat.i32(i32 %x, i32 %y)
+  %add = add i32 %x, %y
+  %cmp = icmp ult i32 %uaddsat, %add
+  ret i1 %cmp
+}
+
+define i1 @icmp_uge_uaddsat_add(i32 %x, i32 %y) {
+; CHECK-LABEL: @icmp_uge_uaddsat_add(
+; CHECK-NEXT:    [[UADDSAT:%.*]] = call i32 @llvm.uadd.sat.i32(i32 [[X:%.*]], i32 [[Y:%.*]])
+; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp uge i32 [[UADDSAT]], [[ADD]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %uaddsat = call i32 @llvm.uadd.sat.i32(i32 %x, i32 %y)
+  %add = add i32 %x, %y
+  %cmp = icmp uge i32 %uaddsat, %add
+  ret i1 %cmp
+}
+
+define i1 @icmp_ugt_uaddsat_add_commuted1(i32 %x, i32 %y) {
+; CHECK-LABEL: @icmp_ugt_uaddsat_add_commuted1(
+; CHECK-NEXT:    [[UADDSAT:%.*]] = call i32 @llvm.uadd.sat.i32(i32 [[X:%.*]], i32 [[Y:%.*]])
+; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i32 [[ADD]], [[UADDSAT]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %uaddsat = call i32 @llvm.uadd.sat.i32(i32 %x, i32 %y)
+  %add = add i32 %x, %y
+  %cmp = icmp ugt i32 %add, %uaddsat
+  ret i1 %cmp
+}
+
+define i1 @icmp_ult_uaddsat_add_commuted2(i32 %x, i32 %y) {
+; CHECK-LABEL: @icmp_ult_uaddsat_add_commuted2(
+; CHECK-NEXT:    [[XX:%.*]] = mul i32 [[X:%.*]], 998244353
+; CHECK-NEXT:    [[YY:%.*]] = mul i32 [[Y:%.*]], 998244353
+; CHECK-NEXT:    [[UADDSAT:%.*]] = call i32 @llvm.uadd.sat.i32(i32 [[XX]], i32 [[YY]])
+; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[YY]], [[XX]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i32 [[UADDSAT]], [[ADD]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %xx = mul i32 %x, 998244353
+  %yy = mul i32 %y, 998244353
+  %uaddsat = call i32 @llvm.uadd.sat.i32(i32 %xx, i32 %yy)
+  %add = add i32 %yy, %xx ; thwart complexity-based canonicalization
+  %cmp = icmp ult i32 %uaddsat, %add
+  ret i1 %cmp
+}
+
+define i1 @icmp_ule_usubsat_sub(i32 %x, i32 %y) {
+; CHECK-LABEL: @icmp_ule_usubsat_sub(
+; CHECK-NEXT:    [[USUBSAT:%.*]] = call i32 @llvm.usub.sat.i32(i32 [[X:%.*]], i32 [[Y:%.*]])
+; CHECK-NEXT:    [[ADD:%.*]] = sub i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ule i32 [[USUBSAT]], [[ADD]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %usubsat = call i32 @llvm.usub.sat.i32(i32 %x, i32 %y)
+  %add = sub i32 %x, %y
+  %cmp = icmp ule i32 %usubsat, %add
+  ret i1 %cmp
+}
+
+define i1 @icmp_ugt_usubsat_sub(i32 %x, i32 %y) {
+; CHECK-LABEL: @icmp_ugt_usubsat_sub(
+; CHECK-NEXT:    [[USUBSAT:%.*]] = call i32 @llvm.usub.sat.i32(i32 [[X:%.*]], i32 [[Y:%.*]])
+; CHECK-NEXT:    [[ADD:%.*]] = sub i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i32 [[USUBSAT]], [[ADD]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %usubsat = call i32 @llvm.usub.sat.i32(i32 %x, i32 %y)
+  %add = sub i32 %x, %y
+  %cmp = icmp ugt i32 %usubsat, %add
+  ret i1 %cmp
+}
+
+; Negative tests
+
+define i1 @icmp_ult_uaddsat_add_mismatch(i32 %x, i32 %y, i32 %z) {
+; CHECK-LABEL: @icmp_ult_uaddsat_add_mismatch(
+; CHECK-NEXT:    [[UADDSAT:%.*]] = call i32 @llvm.uadd.sat.i32(i32 [[X:%.*]], i32 [[Z:%.*]])
+; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[X]], [[Y:%.*]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i32 [[UADDSAT]], [[ADD]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %uaddsat = call i32 @llvm.uadd.sat.i32(i32 %x, i32 %z)
+  %add = add i32 %x, %y
+  %cmp = icmp ult i32 %uaddsat, %add
+  ret i1 %cmp
+}
+
+define i1 @icmp_ult_uaddsat_add_wrong_pred(i32 %x, i32 %y) {
+; CHECK-LABEL: @icmp_ult_uaddsat_add_wrong_pred(
+; CHECK-NEXT:    [[UADDSAT:%.*]] = call i32 @llvm.uadd.sat.i32(i32 [[X:%.*]], i32 [[Y:%.*]])
+; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ule i32 [[UADDSAT]], [[ADD]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %uaddsat = call i32 @llvm.uadd.sat.i32(i32 %x, i32 %y)
+  %add = add i32 %x, %y
+  %cmp = icmp ule i32 %uaddsat, %add
+  ret i1 %cmp
+}
+
+define i1 @icmp_ult_uaddsat_add_wrong_op(i32 %x, i32 %y) {
+; CHECK-LABEL: @icmp_ult_uaddsat_add_wrong_op(
+; CHECK-NEXT:    [[UADDSAT:%.*]] = call i32 @llvm.uadd.sat.i32(i32 [[X:%.*]], i32 [[Y:%.*]])
+; CHECK-NEXT:    [[SUB:%.*]] = sub i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i32 [[UADDSAT]], [[SUB]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %uaddsat = call i32 @llvm.uadd.sat.i32(i32 %x, i32 %y)
+  %sub = sub i32 %x, %y
+  %cmp = icmp ult i32 %uaddsat, %sub
+  ret i1 %cmp
+}
+
+define i1 @icmp_ule_usubsat_sub_commuted(i32 %x, i32 %y) {
+; CHECK-LABEL: @icmp_ule_usubsat_sub_commuted(
+; CHECK-NEXT:    [[USUBSAT:%.*]] = call i32 @llvm.usub.sat.i32(i32 [[X:%.*]], i32 [[Y:%.*]])
+; CHECK-NEXT:    [[ADD:%.*]] = sub i32 [[Y]], [[X]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ule i32 [[USUBSAT]], [[ADD]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %usubsat = call i32 @llvm.usub.sat.i32(i32 %x, i32 %y)
+  %add = sub i32 %y, %x
+  %cmp = icmp ule i32 %usubsat, %add
+  ret i1 %cmp
+}

>From 8151c01b6d378edab481dff1726e0a0406d4f14b Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sun, 18 Aug 2024 18:54:53 +0800
Subject: [PATCH 2/2] [InstSimplify] Simplify `uadd.sat(X, Y) uge X + Y` and
 `usub.sat(X, Y) ule X - Y`

---
 llvm/lib/Analysis/InstructionSimplify.cpp     | 16 ++++++++++
 .../InstSimplify/saturating-add-sub.ll        | 32 ++++---------------
 2 files changed, 22 insertions(+), 26 deletions(-)

diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index eff8a7cfc8ce6c..32a9f1ab34fb3f 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -3718,6 +3718,14 @@ static Value *simplifyICmpWithIntrinsicOnLHS(CmpInst::Predicate Pred,
       if (Pred == ICmpInst::ICMP_ULT)
         return ConstantInt::getFalse(getCompareTy(II));
     }
+    // uadd.sat(X, Y) uge X + Y
+    if (match(RHS, m_c_Add(m_Specific(II->getArgOperand(0)),
+                           m_Specific(II->getArgOperand(1))))) {
+      if (Pred == ICmpInst::ICMP_UGE)
+        return ConstantInt::getTrue(getCompareTy(II));
+      if (Pred == ICmpInst::ICMP_ULT)
+        return ConstantInt::getFalse(getCompareTy(II));
+    }
     return nullptr;
   case Intrinsic::usub_sat:
     // usub.sat(X, Y) ule X
@@ -3727,6 +3735,14 @@ static Value *simplifyICmpWithIntrinsicOnLHS(CmpInst::Predicate Pred,
       if (Pred == ICmpInst::ICMP_UGT)
         return ConstantInt::getFalse(getCompareTy(II));
     }
+    // usub.sat(X, Y) ule X - Y
+    if (match(RHS, m_Sub(m_Specific(II->getArgOperand(0)),
+                         m_Specific(II->getArgOperand(1))))) {
+      if (Pred == ICmpInst::ICMP_ULE)
+        return ConstantInt::getTrue(getCompareTy(II));
+      if (Pred == ICmpInst::ICMP_UGT)
+        return ConstantInt::getFalse(getCompareTy(II));
+    }
     return nullptr;
   default:
     return nullptr;
diff --git a/llvm/test/Transforms/InstSimplify/saturating-add-sub.ll b/llvm/test/Transforms/InstSimplify/saturating-add-sub.ll
index 947585b76708e6..ea139f2411cc3d 100644
--- a/llvm/test/Transforms/InstSimplify/saturating-add-sub.ll
+++ b/llvm/test/Transforms/InstSimplify/saturating-add-sub.ll
@@ -935,10 +935,7 @@ define i1 @usub_uge_fail(i8 %x, i8 %y) {
 
 define i1 @icmp_ult_uaddsat_add(i32 %x, i32 %y) {
 ; CHECK-LABEL: @icmp_ult_uaddsat_add(
-; CHECK-NEXT:    [[UADDSAT:%.*]] = call i32 @llvm.uadd.sat.i32(i32 [[X:%.*]], i32 [[Y:%.*]])
-; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[X]], [[Y]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i32 [[UADDSAT]], [[ADD]]
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 false
 ;
   %uaddsat = call i32 @llvm.uadd.sat.i32(i32 %x, i32 %y)
   %add = add i32 %x, %y
@@ -948,10 +945,7 @@ define i1 @icmp_ult_uaddsat_add(i32 %x, i32 %y) {
 
 define i1 @icmp_uge_uaddsat_add(i32 %x, i32 %y) {
 ; CHECK-LABEL: @icmp_uge_uaddsat_add(
-; CHECK-NEXT:    [[UADDSAT:%.*]] = call i32 @llvm.uadd.sat.i32(i32 [[X:%.*]], i32 [[Y:%.*]])
-; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[X]], [[Y]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp uge i32 [[UADDSAT]], [[ADD]]
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 true
 ;
   %uaddsat = call i32 @llvm.uadd.sat.i32(i32 %x, i32 %y)
   %add = add i32 %x, %y
@@ -961,10 +955,7 @@ define i1 @icmp_uge_uaddsat_add(i32 %x, i32 %y) {
 
 define i1 @icmp_ugt_uaddsat_add_commuted1(i32 %x, i32 %y) {
 ; CHECK-LABEL: @icmp_ugt_uaddsat_add_commuted1(
-; CHECK-NEXT:    [[UADDSAT:%.*]] = call i32 @llvm.uadd.sat.i32(i32 [[X:%.*]], i32 [[Y:%.*]])
-; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[X]], [[Y]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i32 [[ADD]], [[UADDSAT]]
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 false
 ;
   %uaddsat = call i32 @llvm.uadd.sat.i32(i32 %x, i32 %y)
   %add = add i32 %x, %y
@@ -974,12 +965,7 @@ define i1 @icmp_ugt_uaddsat_add_commuted1(i32 %x, i32 %y) {
 
 define i1 @icmp_ult_uaddsat_add_commuted2(i32 %x, i32 %y) {
 ; CHECK-LABEL: @icmp_ult_uaddsat_add_commuted2(
-; CHECK-NEXT:    [[XX:%.*]] = mul i32 [[X:%.*]], 998244353
-; CHECK-NEXT:    [[YY:%.*]] = mul i32 [[Y:%.*]], 998244353
-; CHECK-NEXT:    [[UADDSAT:%.*]] = call i32 @llvm.uadd.sat.i32(i32 [[XX]], i32 [[YY]])
-; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[YY]], [[XX]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i32 [[UADDSAT]], [[ADD]]
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 false
 ;
   %xx = mul i32 %x, 998244353
   %yy = mul i32 %y, 998244353
@@ -991,10 +977,7 @@ define i1 @icmp_ult_uaddsat_add_commuted2(i32 %x, i32 %y) {
 
 define i1 @icmp_ule_usubsat_sub(i32 %x, i32 %y) {
 ; CHECK-LABEL: @icmp_ule_usubsat_sub(
-; CHECK-NEXT:    [[USUBSAT:%.*]] = call i32 @llvm.usub.sat.i32(i32 [[X:%.*]], i32 [[Y:%.*]])
-; CHECK-NEXT:    [[ADD:%.*]] = sub i32 [[X]], [[Y]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ule i32 [[USUBSAT]], [[ADD]]
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 true
 ;
   %usubsat = call i32 @llvm.usub.sat.i32(i32 %x, i32 %y)
   %add = sub i32 %x, %y
@@ -1004,10 +987,7 @@ define i1 @icmp_ule_usubsat_sub(i32 %x, i32 %y) {
 
 define i1 @icmp_ugt_usubsat_sub(i32 %x, i32 %y) {
 ; CHECK-LABEL: @icmp_ugt_usubsat_sub(
-; CHECK-NEXT:    [[USUBSAT:%.*]] = call i32 @llvm.usub.sat.i32(i32 [[X:%.*]], i32 [[Y:%.*]])
-; CHECK-NEXT:    [[ADD:%.*]] = sub i32 [[X]], [[Y]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i32 [[USUBSAT]], [[ADD]]
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 false
 ;
   %usubsat = call i32 @llvm.usub.sat.i32(i32 %x, i32 %y)
   %add = sub i32 %x, %y



More information about the llvm-commits mailing list