[llvm] [InstCombine] Fold sadd/ssub with overflow bitwise pattern (PR #216607)

via llvm-commits llvm-commits at lists.llvm.org
Sun Aug 16 15:05:37 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: AZero13 (AZero13)

<details>
<summary>Changes</summary>

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

---
Full diff: https://github.com/llvm/llvm-project/pull/216607.diff


3 Files Affected:

- (modified) llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp (+80) 
- (added) llvm/test/Transforms/InstCombine/sadd-with-overflow-bitwise.ll (+17) 
- (added) llvm/test/Transforms/InstCombine/ssub-with-overflow-bitwise.ll (+20) 


``````````diff
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index b6f4a55c07e8a..07dca2f154a6b 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -3375,11 +3375,91 @@ static Value *foldAndOrOfICmpEqConstantAndICmp(ICmpInst *LHS, ICmpInst *RHS,
 /// Fold (icmp)&(icmp) or (icmp)|(icmp) if possible.
 /// If IsLogical is true, then the and/or is in select form and the transform
 /// must be poison-safe.
+static Value *foldSignedAddSubBitwiseOverflowCheck(ICmpInst *Cmp0, ICmpInst *Cmp1,
+                                                   bool IsAnd,
+                                                   InstCombiner::BuilderTy &Builder) {
+  if (!IsAnd)
+    return nullptr;
+
+  auto IsSignBitCheck = [](ICmpInst *Cmp, bool &Expected) {
+    const APInt *C;
+    if (!match(Cmp->getOperand(1), m_APInt(C)))
+      return false;
+    bool TrueIfSigned;
+    if (!isSignBitCheck(Cmp->getPredicate(), *C, TrueIfSigned))
+      return false;
+    Expected = TrueIfSigned;
+    return true;
+  };
+
+  bool Cmp0Expected, Cmp1Expected;
+  if (!IsSignBitCheck(Cmp0, Cmp0Expected) || !IsSignBitCheck(Cmp1, Cmp1Expected))
+    return nullptr;
+
+  ICmpInst *SignEqCmp = nullptr;
+  ICmpInst *SignDiffCmp = nullptr;
+
+  if (!Cmp0Expected && Cmp1Expected) {
+    SignEqCmp = Cmp0;
+    SignDiffCmp = Cmp1;
+  } else if (Cmp0Expected && !Cmp1Expected) {
+    SignEqCmp = Cmp1;
+    SignDiffCmp = Cmp0;
+  }
+
+  auto MatchPattern = [&](ICmpInst *SignEq, ICmpInst *SignDiff, bool IsSub) -> Value * {
+    Value *A, *B;
+    if (!match(SignEq->getOperand(0), m_c_Xor(m_Value(A), m_Value(B))))
+      return nullptr;
+
+    Value *Math, *AOrB;
+    if (!match(SignDiff->getOperand(0), m_c_Xor(m_Value(Math), m_Value(AOrB))))
+      return nullptr;
+
+    if (AOrB != A && AOrB != B) {
+      std::swap(Math, AOrB);
+      if (AOrB != A && AOrB != B)
+        return nullptr;
+    }
+
+    if (IsSub) {
+      if (match(Math, m_Sub(m_Specific(A), m_Specific(B))) && AOrB == A)
+        ; // Matched sub
+      else if (match(Math, m_Sub(m_Specific(B), m_Specific(A))) && AOrB == B)
+        std::swap(A, B);
+      else
+        return nullptr;
+    } else {
+      if (!match(Math, m_c_Add(m_Specific(A), m_Specific(B))))
+        return nullptr;
+    }
+
+    Intrinsic::ID IID = IsSub ? Intrinsic::ssub_with_overflow : Intrinsic::sadd_with_overflow;
+    Function *F = Intrinsic::getOrInsertDeclaration(SignEq->getModule(), IID, A->getType());
+    Value *Call = Builder.CreateCall(F, {A, B});
+    return Builder.CreateExtractValue(Call, 1);
+  };
+
+  if (SignEqCmp && SignDiffCmp)
+    return MatchPattern(SignEqCmp, SignDiffCmp, /*IsSub=*/false);
+  else if (Cmp0Expected && Cmp1Expected) {
+    if (Value *V = MatchPattern(Cmp0, Cmp1, /*IsSub=*/true))
+      return V;
+    if (Value *V = MatchPattern(Cmp1, Cmp0, /*IsSub=*/true))
+      return V;
+  }
+  return nullptr;
+}
+
 Value *InstCombinerImpl::foldAndOrOfICmps(ICmpInst *LHS, ICmpInst *RHS,
                                           Instruction &I, bool IsAnd,
                                           bool IsLogical) {
   const SimplifyQuery Q = SQ.getWithInstruction(&I);
 
+  if (Value *V = foldSignedAddSubBitwiseOverflowCheck(LHS, RHS, IsAnd, Builder))
+    return V;
+
+
   ICmpInst::Predicate PredL = LHS->getPredicate(), PredR = RHS->getPredicate();
   Value *LHS0 = LHS->getOperand(0), *RHS0 = RHS->getOperand(0);
   Value *LHS1 = LHS->getOperand(1), *RHS1 = RHS->getOperand(1);
diff --git a/llvm/test/Transforms/InstCombine/sadd-with-overflow-bitwise.ll b/llvm/test/Transforms/InstCombine/sadd-with-overflow-bitwise.ll
new file mode 100644
index 0000000000000..4285ab13304cc
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/sadd-with-overflow-bitwise.ll
@@ -0,0 +1,17 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+define i1 @sadd_overflow_i8(i8 %a, i8 %b) {
+; CHECK-LABEL: @sadd_overflow_i8(
+; CHECK-NEXT:    [[TMP1:%.*]] = call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 [[B:%.*]], i8 [[A:%.*]])
+; CHECK-NEXT:    [[AND:%.*]] = extractvalue { i8, i1 } [[TMP1]], 1
+; CHECK-NEXT:    ret i1 [[AND]]
+;
+  %sum = add i8 %b, %a
+  %xor0 = xor i8 %b, %a
+  %cmp0 = icmp sgt i8 %xor0, -1
+  %xor1 = xor i8 %sum, %a
+  %cmp1 = icmp slt i8 %xor1, 0
+  %and = and i1 %cmp0, %cmp1
+  ret i1 %and
+}
diff --git a/llvm/test/Transforms/InstCombine/ssub-with-overflow-bitwise.ll b/llvm/test/Transforms/InstCombine/ssub-with-overflow-bitwise.ll
new file mode 100644
index 0000000000000..cb61911927922
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/ssub-with-overflow-bitwise.ll
@@ -0,0 +1,20 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+define i1 @ssub_overflow_i8(i8 %a, i8 %b) {
+; CHECK-LABEL: @ssub_overflow_i8(
+; CHECK-NEXT:    [[SUB:%.*]] = sub i8 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    [[XOR0:%.*]] = xor i8 [[A]], [[B]]
+; CHECK-NEXT:    [[XOR1:%.*]] = xor i8 [[A]], [[SUB]]
+; CHECK-NEXT:    [[TMP1:%.*]] = and i8 [[XOR0]], [[XOR1]]
+; CHECK-NEXT:    [[AND:%.*]] = icmp slt i8 [[TMP1]], 0
+; CHECK-NEXT:    ret i1 [[AND]]
+;
+  %sub = sub i8 %a, %b
+  %xor0 = xor i8 %a, %b
+  %cmp0 = icmp slt i8 %xor0, 0
+  %xor1 = xor i8 %a, %sub
+  %cmp1 = icmp slt i8 %xor1, 0
+  %and = and i1 %cmp0, %cmp1
+  ret i1 %and
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/216607


More information about the llvm-commits mailing list