[llvm] 1eb155e - [InstCombine] Fold `-X / -Y` to `X / Y` when it cannot overflow (#224025)

via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 29 18:43:42 PDT 2026


Author: Harrison Hao
Date: 2026-09-30T09:43:34+08:00
New Revision: 1eb155e606fa9bde58c48fe1248f7ad4d6d0c8bc

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

LOG: [InstCombine] Fold `-X / -Y` to `X / Y` when it cannot overflow (#224025)

Fold:
```
  (-X) / (-Y) -> X / Y
```
Do not combine when X may be INT_MIN and Y may be -1, as the result
could introduce signed division overflow.

For AMDGPU example:
https://godbolt.org/z/6jj6E8T5T

Added: 
    

Modified: 
    llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
    llvm/test/Transforms/InstCombine/div.ll
    llvm/test/Transforms/InstCombine/sdiv-canonicalize.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
index 16a7b98666d74..fb412b35f00ae 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
@@ -1962,8 +1962,21 @@ Instruction *InstCombinerImpl::visitSDiv(BinaryOperator &I) {
     }
   }
 
-  // -X / Y --> -(X / Y)
   Value *Y;
+  // -X / -Y --> X / Y, unless X == INT_MIN and Y == -1.
+  if (Value *NegOp0 = dyn_castNegVal(Op0))
+    if (Value *NegOp1 = dyn_castNegVal(Op1))
+      if (!computeKnownBits(NegOp0, &I)
+               .getSignedMinValue()
+               .isMinSignedValue() ||
+          (match(Op0, m_NSWNeg(m_Value())) &&
+           !computeKnownBits(NegOp1, &I).Zero.isZero())) {
+        auto *BO = BinaryOperator::CreateSDiv(NegOp0, NegOp1);
+        BO->setIsExact(I.isExact());
+        return BO;
+      }
+
+  // -X / Y --> -(X / Y)
   if (match(&I, m_SDiv(m_OneUse(m_NSWNeg(m_Value(X))), m_Value(Y))))
     return BinaryOperator::CreateNSWNeg(
         Builder.CreateSDiv(X, Y, I.getName(), I.isExact()));

diff  --git a/llvm/test/Transforms/InstCombine/div.ll b/llvm/test/Transforms/InstCombine/div.ll
index 75a6c897e6133..65f2f37bdd2b8 100644
--- a/llvm/test/Transforms/InstCombine/div.ll
+++ b/llvm/test/Transforms/InstCombine/div.ll
@@ -539,12 +539,12 @@ define <2 x i64> @sdiv_exact_negated_dividend_constant_divisor_vec(<2 x i64> %x)
   ret <2 x i64> %div
 }
 
-; Can't negate signed min vector element.
-
+; Negating the signed min divisor wraps back to itself. If the dividend is
+; signed min, the source is poison due to the nsw negation, so producing 1
+; in the result is a valid refinement.
 define <2 x i8> @sdiv_exact_negated_dividend_constant_divisor_vec_overflow(<2 x i8> %x) {
 ; CHECK-LABEL: @sdiv_exact_negated_dividend_constant_divisor_vec_overflow(
-; CHECK-NEXT:    [[DIV1:%.*]] = sdiv exact <2 x i8> [[X:%.*]], <i8 -128, i8 42>
-; CHECK-NEXT:    [[DIV:%.*]] = sub nsw <2 x i8> zeroinitializer, [[DIV1]]
+; CHECK-NEXT:    [[DIV:%.*]] = sdiv exact <2 x i8> [[X:%.*]], <i8 -128, i8 -42>
 ; CHECK-NEXT:    ret <2 x i8> [[DIV]]
 ;
   %neg = sub nsw <2 x i8> zeroinitializer, %x

diff  --git a/llvm/test/Transforms/InstCombine/sdiv-canonicalize.ll b/llvm/test/Transforms/InstCombine/sdiv-canonicalize.ll
index f427dbd1ac97b..ba8712bbb7902 100644
--- a/llvm/test/Transforms/InstCombine/sdiv-canonicalize.ll
+++ b/llvm/test/Transforms/InstCombine/sdiv-canonicalize.ll
@@ -147,6 +147,86 @@ define i32 @sdiv_abs_extra_use(i32 %x) {
   ret i32 %r
 }
 
+define i32 @sdiv_neg_neg_unknown(i32 %x, i32 %y) {
+; CHECK-LABEL: @sdiv_neg_neg_unknown(
+; CHECK-NEXT:    [[NEG_Y:%.*]] = sub nsw i32 0, [[Y:%.*]]
+; CHECK-NEXT:    [[SDIV1:%.*]] = sdiv i32 [[X:%.*]], [[NEG_Y]]
+; CHECK-NEXT:    [[SDIV:%.*]] = sub nsw i32 0, [[SDIV1]]
+; CHECK-NEXT:    ret i32 [[SDIV]]
+;
+  %neg.x = sub nsw i32 0, %x
+  %neg.y = sub nsw i32 0, %y
+  %sdiv = sdiv i32 %neg.x, %neg.y
+  ret i32 %sdiv
+}
+
+define i32 @sdiv_neg_neg_x_not_signmask(i32 %a, i32 %y) {
+; CHECK-LABEL: @sdiv_neg_neg_x_not_signmask(
+; CHECK-NEXT:    [[X:%.*]] = and i32 [[A:%.*]], 2147483647
+; CHECK-NEXT:    [[SDIV:%.*]] = sdiv i32 [[X]], [[Y:%.*]]
+; CHECK-NEXT:    ret i32 [[SDIV]]
+;
+  %x = and i32 %a, 2147483647
+  %neg.x = sub nsw i32 0, %x
+  %neg.y = sub nsw i32 0, %y
+  %sdiv = sdiv i32 %neg.x, %neg.y
+  ret i32 %sdiv
+}
+
+define i32 @sdiv_neg_neg_y_not_allones(i32 %x, i32 %b) {
+; CHECK-LABEL: @sdiv_neg_neg_y_not_allones(
+; CHECK-NEXT:    [[Y:%.*]] = and i32 [[B:%.*]], -2
+; CHECK-NEXT:    [[SDIV:%.*]] = sdiv i32 [[X:%.*]], [[Y]]
+; CHECK-NEXT:    ret i32 [[SDIV]]
+;
+  %y = and i32 %b, -2
+  %neg.x = sub nsw i32 0, %x
+  %neg.y = sub nsw i32 0, %y
+  %sdiv = sdiv i32 %neg.x, %neg.y
+  ret i32 %sdiv
+}
+
+define i32 @sdiv_neg_neg_exact(i32 %x, i32 %b) {
+; CHECK-LABEL: @sdiv_neg_neg_exact(
+; CHECK-NEXT:    [[Y:%.*]] = and i32 [[B:%.*]], -2
+; CHECK-NEXT:    [[SDIV:%.*]] = sdiv exact i32 [[X:%.*]], [[Y]]
+; CHECK-NEXT:    ret i32 [[SDIV]]
+;
+  %y = and i32 %b, -2
+  %neg.x = sub nsw i32 0, %x
+  %neg.y = sub nsw i32 0, %y
+  %sdiv = sdiv exact i32 %neg.x, %neg.y
+  ret i32 %sdiv
+}
+
+define <2 x i32> @sdiv_neg_neg_vec(<2 x i32> %x, <2 x i32> %b) {
+; CHECK-LABEL: @sdiv_neg_neg_vec(
+; CHECK-NEXT:    [[Y:%.*]] = and <2 x i32> [[B:%.*]], splat (i32 -2)
+; CHECK-NEXT:    [[SDIV:%.*]] = sdiv <2 x i32> [[X:%.*]], [[Y]]
+; CHECK-NEXT:    ret <2 x i32> [[SDIV]]
+;
+  %y = and <2 x i32> %b, splat (i32 -2)
+  %neg.x = sub nsw <2 x i32> zeroinitializer, %x
+  %neg.y = sub nsw <2 x i32> zeroinitializer, %y
+  %sdiv = sdiv <2 x i32> %neg.x, %neg.y
+  ret <2 x i32> %sdiv
+}
+
+define i32 @sdiv_neg_neg_missing_nsw(i32 %x, i32 %b) {
+; CHECK-LABEL: @sdiv_neg_neg_missing_nsw(
+; CHECK-NEXT:    [[Y:%.*]] = and i32 [[B:%.*]], -2
+; CHECK-NEXT:    [[NEG_X:%.*]] = sub i32 0, [[X:%.*]]
+; CHECK-NEXT:    [[NEG_Y:%.*]] = sub i32 0, [[Y]]
+; CHECK-NEXT:    [[SDIV:%.*]] = sdiv i32 [[NEG_X]], [[NEG_Y]]
+; CHECK-NEXT:    ret i32 [[SDIV]]
+;
+  %y = and i32 %b, -2
+  %neg.x = sub i32 0, %x
+  %neg.y = sub i32 0, %y
+  %sdiv = sdiv i32 %neg.x, %neg.y
+  ret i32 %sdiv
+}
+
 !0 = !{!"function_entry_count", i64 1000}
 ;.
 ; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }


        


More information about the llvm-commits mailing list