[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