[llvm] [InstCombine] Fold `A == MIN_INT ? MAX_INT : 0 - A` to `ssub_sat 0, A` (PR #194519)

Ming Yan via llvm-commits llvm-commits at lists.llvm.org
Sun May 3 22:57:33 PDT 2026


https://github.com/NexMing updated https://github.com/llvm/llvm-project/pull/194519

>From 9ebc5095d0559df204c4f43ed6a540c663770920 Mon Sep 17 00:00:00 2001
From: yanming <ming.yan at terapines.com>
Date: Tue, 28 Apr 2026 11:04:16 +0800
Subject: [PATCH 1/4] [InstCombine] Fold `A == MIN_INT ? MAX_INT : 0 - A` to
 `ssub_sat 0, A`

---
 llvm/include/llvm/IR/PatternMatch.h           | 13 ++++++
 .../InstCombine/InstCombineSelect.cpp         |  9 ++++
 .../InstCombine/saturating-add-sub.ll         | 44 +++++++++++++++++++
 3 files changed, 66 insertions(+)

diff --git a/llvm/include/llvm/IR/PatternMatch.h b/llvm/include/llvm/IR/PatternMatch.h
index de24f4da336c2..8305f4dda7aff 100644
--- a/llvm/include/llvm/IR/PatternMatch.h
+++ b/llvm/include/llvm/IR/PatternMatch.h
@@ -510,6 +510,19 @@ inline api_pred_ty<is_maxsignedvalue> m_MaxSignedValue(const APInt *&V) {
   return V;
 }
 
+struct is_minsignedvalue {
+  bool isValue(const APInt &C) const { return C.isMinSignedValue(); }
+};
+/// Match an integer or vector with values having only the high bit set and
+/// all other bits cleared (0x80...).
+/// For vectors, this includes constants with undefined elements.
+inline cst_pred_ty<is_minsignedvalue> m_MinSignedValue() {
+  return cst_pred_ty<is_minsignedvalue>();
+}
+inline api_pred_ty<is_minsignedvalue> m_MinSignedValue(const APInt *&V) {
+  return V;
+}
+
 struct is_negative {
   bool isValue(const APInt &C) const { return C.isNegative(); }
 };
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index bffc887d93535..56f51bc67cfaa 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -2154,6 +2154,15 @@ static Instruction *foldSelectICmpEq(SelectInst &SI, ICmpInst *ICI,
   if (Pred == ICmpInst::ICMP_NE)
     std::swap(TrueVal, FalseVal);
 
+  /// `A == MIN_INT ? MAX_INT : 0 - A` --> `ssub_sat 0, A`
+  if (match(CmpRHS, m_MinSignedValue()) && match(TrueVal, m_MaxSignedValue()) &&
+      match(FalseVal, m_Sub(m_ZeroInt(), m_Specific(CmpLHS)))) {
+    return IC.replaceInstUsesWith(
+        SI, IC.Builder.CreateBinaryIntrinsic(
+                Intrinsic::ssub_sat,
+                ConstantInt::getNullValue(CmpLHS->getType()), CmpLHS));
+  }
+
   if (Instruction *Res =
           foldSelectWithExtremeEqCond(CmpLHS, CmpRHS, TrueVal, FalseVal))
     return Res;
diff --git a/llvm/test/Transforms/InstCombine/saturating-add-sub.ll b/llvm/test/Transforms/InstCombine/saturating-add-sub.ll
index 5ac8588a0ed83..f1cc997a41ba1 100644
--- a/llvm/test/Transforms/InstCombine/saturating-add-sub.ll
+++ b/llvm/test/Transforms/InstCombine/saturating-add-sub.ll
@@ -3087,3 +3087,47 @@ define i8 @no_fold_umin_to_uadd_sat_if_not_zero(i8 %a, i8 %b) {
   %r = trunc nuw i16 %cmp to i8
   ret i8 %r
 }
+
+define i8 @fold_icmpeq_to_ssub_sat(i8 %a) {
+; CHECK-LABEL: @fold_icmpeq_to_ssub_sat(
+; CHECK-NEXT:    [[SEL:%.*]] = call i8 @llvm.ssub.sat.i8(i8 0, i8 [[A:%.*]])
+; CHECK-NEXT:    ret i8 [[SEL]]
+;
+  %cmp = icmp eq i8 %a, -128
+  %sub = sub i8 0, %a
+  %sel = select i1 %cmp, i8 127, i8 %sub
+  ret i8 %sel
+}
+
+define i8 @fold_icmpne_to_ssub_sat(i8 %a) {
+; CHECK-LABEL: @fold_icmpne_to_ssub_sat(
+; CHECK-NEXT:    [[SEL:%.*]] = call i8 @llvm.ssub.sat.i8(i8 0, i8 [[A:%.*]])
+; CHECK-NEXT:    ret i8 [[SEL]]
+;
+  %cmp = icmp ne i8 %a, -128
+  %sub = sub i8 0, %a
+  %sel = select i1 %cmp, i8 %sub, i8 127
+  ret i8 %sel
+}
+
+define <4 x i8> @fold_icmpeq_to_ssub_sat_vec(<4 x i8> %a) {
+; CHECK-LABEL: @fold_icmpeq_to_ssub_sat_vec(
+; CHECK-NEXT:    [[SEL:%.*]] = call <4 x i8> @llvm.ssub.sat.v4i8(<4 x i8> zeroinitializer, <4 x i8> [[A:%.*]])
+; CHECK-NEXT:    ret <4 x i8> [[SEL]]
+;
+  %cmp = icmp eq <4 x i8> %a, splat (i8 -128)
+  %sub = sub <4 x i8> zeroinitializer, %a
+  %sel = select <4 x i1> %cmp, <4 x i8> splat (i8 127), <4 x i8> %sub
+  ret <4 x i8> %sel
+}
+
+define <4 x i8> @fold_icmpne_to_ssub_sat_vec(<4 x i8> %a) {
+; CHECK-LABEL: @fold_icmpne_to_ssub_sat_vec(
+; CHECK-NEXT:    [[SEL:%.*]] = call <4 x i8> @llvm.ssub.sat.v4i8(<4 x i8> zeroinitializer, <4 x i8> [[A:%.*]])
+; CHECK-NEXT:    ret <4 x i8> [[SEL]]
+;
+  %cmp = icmp ne <4 x i8> %a, splat (i8 -128)
+  %sub = sub <4 x i8> zeroinitializer, %a
+  %sel = select <4 x i1> %cmp, <4 x i8> %sub, <4 x i8> splat (i8 127)
+  ret <4 x i8> %sel
+}

>From f4ade443fe99de6048a1b61b907ee164757ab34a Mon Sep 17 00:00:00 2001
From: yanming <ming.yan at terapines.com>
Date: Tue, 28 Apr 2026 11:41:52 +0800
Subject: [PATCH 2/4] Fix tests.

---
 .../Transforms/LoopVectorize/ARM/mve-qabs.ll  | 24 +++++--------------
 1 file changed, 6 insertions(+), 18 deletions(-)

diff --git a/llvm/test/Transforms/LoopVectorize/ARM/mve-qabs.ll b/llvm/test/Transforms/LoopVectorize/ARM/mve-qabs.ll
index 8a79ea62361a4..4a314f6845f4a 100644
--- a/llvm/test/Transforms/LoopVectorize/ARM/mve-qabs.ll
+++ b/llvm/test/Transforms/LoopVectorize/ARM/mve-qabs.ll
@@ -29,9 +29,7 @@ define void @arm_abs_q7(ptr nocapture readonly %pSrc, ptr nocapture %pDst, i32 %
 ; CHECK-NEXT:    [[NEXT_GEP7:%.*]] = getelementptr i8, ptr [[PDST]], i32 [[INDEX]]
 ; CHECK-NEXT:    [[WIDE_LOAD:%.*]] = load <16 x i8>, ptr [[NEXT_GEP]], align 1
 ; CHECK-NEXT:    [[TMP1:%.*]] = icmp sgt <16 x i8> [[WIDE_LOAD]], zeroinitializer
-; CHECK-NEXT:    [[TMP2:%.*]] = icmp eq <16 x i8> [[WIDE_LOAD]], splat (i8 -128)
-; CHECK-NEXT:    [[TMP3:%.*]] = sub <16 x i8> zeroinitializer, [[WIDE_LOAD]]
-; CHECK-NEXT:    [[TMP4:%.*]] = select <16 x i1> [[TMP2]], <16 x i8> splat (i8 127), <16 x i8> [[TMP3]]
+; CHECK-NEXT:    [[TMP4:%.*]] = call <16 x i8> @llvm.ssub.sat.v16i8(<16 x i8> zeroinitializer, <16 x i8> [[WIDE_LOAD]])
 ; CHECK-NEXT:    [[TMP5:%.*]] = select <16 x i1> [[TMP1]], <16 x i8> [[WIDE_LOAD]], <16 x i8> [[TMP4]]
 ; CHECK-NEXT:    store <16 x i8> [[TMP5]], ptr [[NEXT_GEP7]], align 1
 ; CHECK-NEXT:    [[INDEX_NEXT]] = add nuw i32 [[INDEX]], 16
@@ -52,9 +50,7 @@ define void @arm_abs_q7(ptr nocapture readonly %pSrc, ptr nocapture %pDst, i32 %
 ; CHECK-NEXT:    [[INCDEC_PTR]] = getelementptr inbounds nuw i8, ptr [[PSRC_ADDR_022]], i32 1
 ; CHECK-NEXT:    [[TMP7:%.*]] = load i8, ptr [[PSRC_ADDR_022]], align 1
 ; CHECK-NEXT:    [[CMP1:%.*]] = icmp sgt i8 [[TMP7]], 0
-; CHECK-NEXT:    [[CMP5:%.*]] = icmp eq i8 [[TMP7]], -128
-; CHECK-NEXT:    [[SUB:%.*]] = sub i8 0, [[TMP7]]
-; CHECK-NEXT:    [[COND:%.*]] = select i1 [[CMP5]], i8 127, i8 [[SUB]]
+; CHECK-NEXT:    [[COND:%.*]] = call i8 @llvm.ssub.sat.i8(i8 0, i8 [[TMP7]])
 ; CHECK-NEXT:    [[COND11:%.*]] = select i1 [[CMP1]], i8 [[TMP7]], i8 [[COND]]
 ; CHECK-NEXT:    [[INCDEC_PTR13]] = getelementptr inbounds nuw i8, ptr [[PDST_ADDR_020]], i32 1
 ; CHECK-NEXT:    store i8 [[COND11]], ptr [[PDST_ADDR_020]], align 1
@@ -124,9 +120,7 @@ define void @arm_abs_q15(ptr nocapture readonly %pSrc, ptr nocapture %pDst, i32
 ; CHECK-NEXT:    [[NEXT_GEP8:%.*]] = getelementptr i8, ptr [[PDST]], i32 [[OFFSET_IDX7]]
 ; CHECK-NEXT:    [[WIDE_LOAD:%.*]] = load <8 x i16>, ptr [[NEXT_GEP]], align 2
 ; CHECK-NEXT:    [[TMP3:%.*]] = icmp sgt <8 x i16> [[WIDE_LOAD]], zeroinitializer
-; CHECK-NEXT:    [[TMP4:%.*]] = icmp eq <8 x i16> [[WIDE_LOAD]], splat (i16 -32768)
-; CHECK-NEXT:    [[TMP5:%.*]] = sub <8 x i16> zeroinitializer, [[WIDE_LOAD]]
-; CHECK-NEXT:    [[TMP6:%.*]] = select <8 x i1> [[TMP4]], <8 x i16> splat (i16 32767), <8 x i16> [[TMP5]]
+; CHECK-NEXT:    [[TMP6:%.*]] = call <8 x i16> @llvm.ssub.sat.v8i16(<8 x i16> zeroinitializer, <8 x i16> [[WIDE_LOAD]])
 ; CHECK-NEXT:    [[TMP7:%.*]] = select <8 x i1> [[TMP3]], <8 x i16> [[WIDE_LOAD]], <8 x i16> [[TMP6]]
 ; CHECK-NEXT:    store <8 x i16> [[TMP7]], ptr [[NEXT_GEP8]], align 2
 ; CHECK-NEXT:    [[INDEX_NEXT]] = add nuw i32 [[INDEX]], 8
@@ -147,9 +141,7 @@ define void @arm_abs_q15(ptr nocapture readonly %pSrc, ptr nocapture %pDst, i32
 ; CHECK-NEXT:    [[INCDEC_PTR]] = getelementptr inbounds nuw i8, ptr [[PSRC_ADDR_023]], i32 2
 ; CHECK-NEXT:    [[TMP9:%.*]] = load i16, ptr [[PSRC_ADDR_023]], align 2
 ; CHECK-NEXT:    [[CMP1:%.*]] = icmp sgt i16 [[TMP9]], 0
-; CHECK-NEXT:    [[CMP5:%.*]] = icmp eq i16 [[TMP9]], -32768
-; CHECK-NEXT:    [[SUB:%.*]] = sub i16 0, [[TMP9]]
-; CHECK-NEXT:    [[COND:%.*]] = select i1 [[CMP5]], i16 32767, i16 [[SUB]]
+; CHECK-NEXT:    [[COND:%.*]] = call i16 @llvm.ssub.sat.i16(i16 0, i16 [[TMP9]])
 ; CHECK-NEXT:    [[COND11:%.*]] = select i1 [[CMP1]], i16 [[TMP9]], i16 [[COND]]
 ; CHECK-NEXT:    [[INCDEC_PTR13]] = getelementptr inbounds nuw i8, ptr [[PDST_ADDR_021]], i32 2
 ; CHECK-NEXT:    store i16 [[COND11]], ptr [[PDST_ADDR_021]], align 2
@@ -219,9 +211,7 @@ define void @arm_abs_q31(ptr nocapture readonly %pSrc, ptr nocapture %pDst, i32
 ; CHECK-NEXT:    [[NEXT_GEP8:%.*]] = getelementptr i8, ptr [[PDST]], i32 [[OFFSET_IDX7]]
 ; CHECK-NEXT:    [[WIDE_LOAD:%.*]] = load <4 x i32>, ptr [[NEXT_GEP]], align 4
 ; CHECK-NEXT:    [[TMP3:%.*]] = icmp sgt <4 x i32> [[WIDE_LOAD]], zeroinitializer
-; CHECK-NEXT:    [[TMP4:%.*]] = icmp eq <4 x i32> [[WIDE_LOAD]], splat (i32 -2147483648)
-; CHECK-NEXT:    [[TMP5:%.*]] = sub nsw <4 x i32> zeroinitializer, [[WIDE_LOAD]]
-; CHECK-NEXT:    [[TMP6:%.*]] = select <4 x i1> [[TMP4]], <4 x i32> splat (i32 2147483647), <4 x i32> [[TMP5]]
+; CHECK-NEXT:    [[TMP6:%.*]] = call <4 x i32> @llvm.ssub.sat.v4i32(<4 x i32> zeroinitializer, <4 x i32> [[WIDE_LOAD]])
 ; CHECK-NEXT:    [[TMP7:%.*]] = select <4 x i1> [[TMP3]], <4 x i32> [[WIDE_LOAD]], <4 x i32> [[TMP6]]
 ; CHECK-NEXT:    store <4 x i32> [[TMP7]], ptr [[NEXT_GEP8]], align 4
 ; CHECK-NEXT:    [[INDEX_NEXT]] = add nuw i32 [[INDEX]], 4
@@ -242,9 +232,7 @@ define void @arm_abs_q31(ptr nocapture readonly %pSrc, ptr nocapture %pDst, i32
 ; CHECK-NEXT:    [[INCDEC_PTR]] = getelementptr inbounds nuw i8, ptr [[PSRC_ADDR_017]], i32 4
 ; CHECK-NEXT:    [[TMP9:%.*]] = load i32, ptr [[PSRC_ADDR_017]], align 4
 ; CHECK-NEXT:    [[CMP1:%.*]] = icmp sgt i32 [[TMP9]], 0
-; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i32 [[TMP9]], -2147483648
-; CHECK-NEXT:    [[SUB:%.*]] = sub nsw i32 0, [[TMP9]]
-; CHECK-NEXT:    [[COND:%.*]] = select i1 [[CMP2]], i32 2147483647, i32 [[SUB]]
+; CHECK-NEXT:    [[COND:%.*]] = call i32 @llvm.ssub.sat.i32(i32 0, i32 [[TMP9]])
 ; CHECK-NEXT:    [[COND6:%.*]] = select i1 [[CMP1]], i32 [[TMP9]], i32 [[COND]]
 ; CHECK-NEXT:    [[INCDEC_PTR7]] = getelementptr inbounds nuw i8, ptr [[PDST_ADDR_015]], i32 4
 ; CHECK-NEXT:    store i32 [[COND6]], ptr [[PDST_ADDR_015]], align 4

>From c2a1c17affec5486c5f1641043b9c0e297af866b Mon Sep 17 00:00:00 2001
From: yanming <ming.yan at terapines.com>
Date: Wed, 29 Apr 2026 10:19:44 +0800
Subject: [PATCH 3/4] Update code.

---
 llvm/include/llvm/IR/PatternMatch.h           | 13 ----
 .../InstCombine/InstCombineSelect.cpp         | 60 +++++++++++++++----
 2 files changed, 47 insertions(+), 26 deletions(-)

diff --git a/llvm/include/llvm/IR/PatternMatch.h b/llvm/include/llvm/IR/PatternMatch.h
index 8305f4dda7aff..de24f4da336c2 100644
--- a/llvm/include/llvm/IR/PatternMatch.h
+++ b/llvm/include/llvm/IR/PatternMatch.h
@@ -510,19 +510,6 @@ inline api_pred_ty<is_maxsignedvalue> m_MaxSignedValue(const APInt *&V) {
   return V;
 }
 
-struct is_minsignedvalue {
-  bool isValue(const APInt &C) const { return C.isMinSignedValue(); }
-};
-/// Match an integer or vector with values having only the high bit set and
-/// all other bits cleared (0x80...).
-/// For vectors, this includes constants with undefined elements.
-inline cst_pred_ty<is_minsignedvalue> m_MinSignedValue() {
-  return cst_pred_ty<is_minsignedvalue>();
-}
-inline api_pred_ty<is_minsignedvalue> m_MinSignedValue(const APInt *&V) {
-  return V;
-}
-
 struct is_negative {
   bool isValue(const APInt &C) const { return C.isNegative(); }
 };
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 56f51bc67cfaa..d9a06a84bdd54 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -1037,10 +1037,10 @@ static Instruction *foldSelectZeroOrFixedOp(SelectInst &SI,
 
 /// Transform patterns such as (a > b) ? a - b : 0 into usub.sat(a, b).
 /// There are 8 commuted/swapped variants of this pattern.
-static Value *canonicalizeSaturatedSubtract(const ICmpInst *ICI,
-                                            const Value *TrueVal,
-                                            const Value *FalseVal,
-                                            InstCombiner::BuilderTy &Builder) {
+static Value *
+canonicalizeSaturatedSubtractUnsigned(const ICmpInst *ICI, const Value *TrueVal,
+                                      const Value *FalseVal,
+                                      InstCombiner::BuilderTy &Builder) {
   ICmpInst::Predicate Pred = ICI->getPredicate();
   Value *A = ICI->getOperand(0);
   Value *B = ICI->getOperand(1);
@@ -1104,6 +1104,49 @@ static Value *canonicalizeSaturatedSubtract(const ICmpInst *ICI,
   return Result;
 }
 
+static Value *
+canonicalizeSaturatedSubtractSigned(const ICmpInst *ICI, const Value *TrueVal,
+                                    const Value *FalseVal,
+                                    InstCombiner::BuilderTy &Builder) {
+  ICmpInst::Predicate Pred = ICI->getPredicate();
+  Value *CmpLHS = ICI->getOperand(0);
+  Value *CmpRHS = ICI->getOperand(1);
+
+  // `A != B ? X : Y` --> `A == B ? Y : X`
+  // This canonicalization allows us to handle more patterns with fewer checks.
+  if (Pred == ICmpInst::ICMP_NE) {
+    Pred = ICmpInst::getInversePredicate(Pred);
+    std::swap(TrueVal, FalseVal);
+  }
+
+  if (Pred == ICmpInst::ICMP_EQ) {
+    // `A == MIN_INT ? MAX_INT : 0 - A` --> `ssub_sat 0, A`
+    if (match(CmpRHS, m_SignMask()) && match(TrueVal, m_MaxSignedValue()) &&
+        match(FalseVal, m_Neg(m_Specific(CmpLHS)))) {
+      return Builder.CreateBinaryIntrinsic(
+          Intrinsic::ssub_sat, ConstantInt::getNullValue(CmpLHS->getType()),
+          CmpLHS);
+    }
+  }
+
+  return nullptr;
+}
+
+static Value *canonicalizeSaturatedSubtract(const ICmpInst *ICI,
+                                            const Value *TrueVal,
+                                            const Value *FalseVal,
+                                            InstCombiner::BuilderTy &Builder) {
+  if (Value *V = canonicalizeSaturatedSubtractUnsigned(ICI, TrueVal, FalseVal,
+                                                       Builder))
+    return V;
+
+  if (Value *V =
+          canonicalizeSaturatedSubtractSigned(ICI, TrueVal, FalseVal, Builder))
+    return V;
+
+  return nullptr;
+}
+
 static Value *
 canonicalizeSaturatedAddUnsigned(ICmpInst *Cmp, Value *TVal, Value *FVal,
                                  InstCombiner::BuilderTy &Builder) {
@@ -2154,15 +2197,6 @@ static Instruction *foldSelectICmpEq(SelectInst &SI, ICmpInst *ICI,
   if (Pred == ICmpInst::ICMP_NE)
     std::swap(TrueVal, FalseVal);
 
-  /// `A == MIN_INT ? MAX_INT : 0 - A` --> `ssub_sat 0, A`
-  if (match(CmpRHS, m_MinSignedValue()) && match(TrueVal, m_MaxSignedValue()) &&
-      match(FalseVal, m_Sub(m_ZeroInt(), m_Specific(CmpLHS)))) {
-    return IC.replaceInstUsesWith(
-        SI, IC.Builder.CreateBinaryIntrinsic(
-                Intrinsic::ssub_sat,
-                ConstantInt::getNullValue(CmpLHS->getType()), CmpLHS));
-  }
-
   if (Instruction *Res =
           foldSelectWithExtremeEqCond(CmpLHS, CmpRHS, TrueVal, FalseVal))
     return Res;

>From 7a0ed7d1b4e44841ff8f3106accf846a73a27245 Mon Sep 17 00:00:00 2001
From: Ming Yan <ming.yan at terapines.com>
Date: Mon, 4 May 2026 13:54:59 +0800
Subject: [PATCH 4/4] Update code.

---
 .../InstCombine/InstCombineSelect.cpp           | 17 ++++++++---------
 1 file changed, 8 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 16b7f8b7e9d9e..ecd20b2a248e9 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -1115,18 +1115,17 @@ canonicalizeSaturatedSubtractSigned(const ICmpInst *ICI, const Value *TrueVal,
   // `A != B ? X : Y` --> `A == B ? Y : X`
   // This canonicalization allows us to handle more patterns with fewer checks.
   if (Pred == ICmpInst::ICMP_NE) {
-    Pred = ICmpInst::getInversePredicate(Pred);
+    Pred = ICmpInst::ICMP_EQ;
     std::swap(TrueVal, FalseVal);
   }
 
-  if (Pred == ICmpInst::ICMP_EQ) {
-    // `A == MIN_INT ? MAX_INT : 0 - A` --> `ssub_sat 0, A`
-    if (match(CmpRHS, m_SignMask()) && match(TrueVal, m_MaxSignedValue()) &&
-        match(FalseVal, m_Neg(m_Specific(CmpLHS)))) {
-      return Builder.CreateBinaryIntrinsic(
-          Intrinsic::ssub_sat, ConstantInt::getNullValue(CmpLHS->getType()),
-          CmpLHS);
-    }
+  // `A == MIN_INT ? MAX_INT : 0 - A` --> `ssub_sat 0, A`
+  if (Pred == ICmpInst::ICMP_EQ && match(CmpRHS, m_SignMask()) &&
+      match(TrueVal, m_MaxSignedValue()) &&
+      match(FalseVal, m_Neg(m_Specific(CmpLHS)))) {
+    return Builder.CreateBinaryIntrinsic(
+        Intrinsic::ssub_sat, ConstantInt::getNullValue(CmpLHS->getType()),
+        CmpLHS);
   }
 
   return nullptr;



More information about the llvm-commits mailing list