[llvm] [InstCombine] Simplify fractions when there is no overflow (PR #210516)

via llvm-commits llvm-commits at lists.llvm.org
Sat Jul 18 08:44:00 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: AZero13 (AZero13)

<details>
<summary>Changes</summary>

Use Greatest Common Factor to avoid making bigger immediates.

Alive2 Proof: https://alive2.llvm.org/ce/z/3gEVTV

Resolves [#<!-- -->210452](https://github.com/llvm/llvm-project/issues/210452)

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


2 Files Affected:

- (modified) llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp (+44) 
- (modified) llvm/test/Transforms/InstCombine/div.ll (+228) 


``````````diff
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
index f63bff2f16e7a..0669496fc87b7 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
@@ -1402,6 +1402,28 @@ Instruction *InstCombinerImpl::commonIDivTransforms(BinaryOperator &I) {
         Mul->setHasNoSignedWrap(OBO->hasNoSignedWrap());
         return Mul;
       }
+
+      // (X * C1) / C2 -> (X * (C1/D)) / (C2/D) if D = gcd(C1, C2) > 1.
+      if (Op0->hasOneUse()) {
+        APInt GCD = IsSigned
+                        ? APIntOps::GreatestCommonDivisor(C1->abs(), C2->abs())
+                        : APIntOps::GreatestCommonDivisor(*C1, *C2);
+        if (GCD.ugt(1)) {
+          APInt NewC1 = IsSigned ? C1->sdiv(GCD) : C1->udiv(GCD);
+          APInt NewC2 = IsSigned ? C2->sdiv(GCD) : C2->udiv(GCD);
+
+          auto *OldMul = cast<OverflowingBinaryOperator>(Op0);
+          Value *NewMul = Builder.CreateMul(X, ConstantInt::get(Ty, NewC1), "",
+                                            OldMul->hasNoUnsignedWrap(),
+                                            OldMul->hasNoSignedWrap());
+
+          Constant *NewDivisor = ConstantInt::get(Ty, NewC2);
+          auto *NewDiv =
+              BinaryOperator::Create(I.getOpcode(), NewMul, NewDivisor);
+          NewDiv->setIsExact(I.isExact());
+          return NewDiv;
+        }
+      }
     }
 
     if ((IsSigned && match(Op0, m_NSWShl(m_Value(X), m_APInt(C1))) &&
@@ -1428,6 +1450,28 @@ Instruction *InstCombinerImpl::commonIDivTransforms(BinaryOperator &I) {
         Mul->setHasNoSignedWrap(OBO->hasNoSignedWrap());
         return Mul;
       }
+
+      // (X << C1) / C2 -> (X << (C1 - K)) / (C2 / (1 << K))
+      // Where K = min(C1, countr_zero(C2)), the shared power of 2.
+      if (Op0->hasOneUse()) {
+        unsigned ShiftAmt = static_cast<unsigned>(C1->getZExtValue());
+        unsigned K = std::min(C2->countr_zero(), ShiftAmt);
+        if (K > 0) {
+          unsigned NewShiftAmt = ShiftAmt - K;
+          APInt NewC2 = IsSigned ? C2->ashr(K) : C2->lshr(K);
+
+          auto *OldShift = cast<OverflowingBinaryOperator>(Op0);
+          Value *NewShift = Builder.CreateShl(
+              X, ConstantInt::get(Ty, NewShiftAmt), "",
+              OldShift->hasNoUnsignedWrap(), OldShift->hasNoSignedWrap());
+
+          Constant *NewDivisor = ConstantInt::get(Ty, NewC2);
+          auto *NewDiv =
+              BinaryOperator::Create(I.getOpcode(), NewShift, NewDivisor);
+          NewDiv->setIsExact(I.isExact());
+          return NewDiv;
+        }
+      }
     }
 
     // Distribute div over add to eliminate a matching div/mul pair:
diff --git a/llvm/test/Transforms/InstCombine/div.ll b/llvm/test/Transforms/InstCombine/div.ll
index 2adf6036d4bcf..221e58e0c69a5 100644
--- a/llvm/test/Transforms/InstCombine/div.ll
+++ b/llvm/test/Transforms/InstCombine/div.ll
@@ -2033,6 +2033,234 @@ define <2 x i32> @sdiv_select_one_false_poison_vec(<2 x i32> %a, i1 %b) {
   ret <2 x i32> %div
 }
 
+define i32 @sdiv_mul_nsw_gcd(i32 %x) {
+; CHECK-LABEL: @sdiv_mul_nsw_gcd(
+; CHECK-NEXT:    [[M:%.*]] = mul nsw i32 [[X:%.*]], 150
+; CHECK-NEXT:    [[D:%.*]] = sdiv i32 [[M]], 100
+; CHECK-NEXT:    ret i32 [[D]]
+;
+  %m = mul nsw i32 %x, 150
+  %d = sdiv i32 %m, 100
+  ret i32 %d
+}
+
+define i32 @sdiv_mul_nsw_gcd_neg_c1(i32 %x) {
+; CHECK-LABEL: @sdiv_mul_nsw_gcd_neg_c1(
+; CHECK-NEXT:    [[M:%.*]] = mul nsw i32 [[X:%.*]], -150
+; CHECK-NEXT:    [[D:%.*]] = sdiv i32 [[M]], 100
+; CHECK-NEXT:    ret i32 [[D]]
+;
+  %m = mul nsw i32 %x, -150
+  %d = sdiv i32 %m, 100
+  ret i32 %d
+}
+
+define i32 @sdiv_mul_nsw_gcd_neg_c2(i32 %x) {
+; CHECK-LABEL: @sdiv_mul_nsw_gcd_neg_c2(
+; CHECK-NEXT:    [[M:%.*]] = mul nsw i32 [[X:%.*]], 150
+; CHECK-NEXT:    [[D:%.*]] = sdiv i32 [[M]], -100
+; CHECK-NEXT:    ret i32 [[D]]
+;
+  %m = mul nsw i32 %x, 150
+  %d = sdiv i32 %m, -100
+  ret i32 %d
+}
+
+define i32 @sdiv_mul_nsw_gcd_neg_both(i32 %x) {
+; CHECK-LABEL: @sdiv_mul_nsw_gcd_neg_both(
+; CHECK-NEXT:    [[M:%.*]] = mul nsw i32 [[X:%.*]], -150
+; CHECK-NEXT:    [[D:%.*]] = sdiv i32 [[M]], -100
+; CHECK-NEXT:    ret i32 [[D]]
+;
+  %m = mul nsw i32 %x, -150
+  %d = sdiv i32 %m, -100
+  ret i32 %d
+}
+
+define i32 @udiv_mul_nuw_gcd(i32 %x) {
+; CHECK-LABEL: @udiv_mul_nuw_gcd(
+; CHECK-NEXT:    [[M:%.*]] = mul nuw i32 [[X:%.*]], 150
+; CHECK-NEXT:    [[D1:%.*]] = udiv i32 [[M]], 100
+; CHECK-NEXT:    ret i32 [[D1]]
+;
+  %m = mul nuw i32 %x, 150
+  %d = udiv i32 %m, 100
+  ret i32 %d
+}
+
+define i32 @udiv_mul_nuw_nsw_gcd(i32 %x) {
+; CHECK-LABEL: @udiv_mul_nuw_nsw_gcd(
+; CHECK-NEXT:    [[M:%.*]] = mul nuw nsw i32 [[X:%.*]], 150
+; CHECK-NEXT:    [[D1:%.*]] = udiv i32 [[M]], 100
+; CHECK-NEXT:    ret i32 [[D1]]
+;
+  %m = mul nuw nsw i32 %x, 150
+  %d = udiv i32 %m, 100
+  ret i32 %d
+}
+
+define i32 @sdiv_mul_nsw_exact_gcd(i32 %x) {
+; CHECK-LABEL: @sdiv_mul_nsw_exact_gcd(
+; CHECK-NEXT:    [[M:%.*]] = mul nsw i32 [[X:%.*]], 150
+; CHECK-NEXT:    [[D:%.*]] = sdiv exact i32 [[M]], 100
+; CHECK-NEXT:    ret i32 [[D]]
+;
+  %m = mul nsw i32 %x, 150
+  %d = sdiv exact i32 %m, 100
+  ret i32 %d
+}
+
+define i32 @udiv_mul_nuw_exact_gcd(i32 %x) {
+; CHECK-LABEL: @udiv_mul_nuw_exact_gcd(
+; CHECK-NEXT:    [[M:%.*]] = mul nuw i32 [[X:%.*]], 150
+; CHECK-NEXT:    [[D1:%.*]] = udiv exact i32 [[M]], 100
+; CHECK-NEXT:    ret i32 [[D1]]
+;
+  %m = mul nuw i32 %x, 150
+  %d = udiv exact i32 %m, 100
+  ret i32 %d
+}
+
+define i32 @sdiv_shl_nsw_gcd(i32 %x) {
+; CHECK-LABEL: @sdiv_shl_nsw_gcd(
+; CHECK-NEXT:    [[SHL:%.*]] = shl nsw i32 [[X:%.*]], 2
+; CHECK-NEXT:    [[D:%.*]] = sdiv i32 [[SHL]], 6
+; CHECK-NEXT:    ret i32 [[D]]
+;
+  %shl = shl nsw i32 %x, 2
+  %d = sdiv i32 %shl, 6
+  ret i32 %d
+}
+
+define i32 @udiv_shl_nuw_gcd(i32 %x) {
+; CHECK-LABEL: @udiv_shl_nuw_gcd(
+; CHECK-NEXT:    [[SHL:%.*]] = shl nuw i32 [[X:%.*]], 2
+; CHECK-NEXT:    [[D:%.*]] = udiv i32 [[SHL]], 6
+; CHECK-NEXT:    ret i32 [[D]]
+;
+  %shl = shl nuw i32 %x, 2
+  %d = udiv i32 %shl, 6
+  ret i32 %d
+}
+
+define i32 @sdiv_shl_nsw_exact_gcd(i32 %x) {
+; CHECK-LABEL: @sdiv_shl_nsw_exact_gcd(
+; CHECK-NEXT:    [[SHL:%.*]] = shl nsw i32 [[X:%.*]], 2
+; CHECK-NEXT:    [[D:%.*]] = sdiv exact i32 [[SHL]], 6
+; CHECK-NEXT:    ret i32 [[D]]
+;
+  %shl = shl nsw i32 %x, 2
+  %d = sdiv exact i32 %shl, 6
+  ret i32 %d
+}
+
+; Vector splat test
+define <2 x i32> @sdiv_mul_nsw_gcd_splat(<2 x i32> %x) {
+; CHECK-LABEL: @sdiv_mul_nsw_gcd_splat(
+; CHECK-NEXT:    [[M:%.*]] = mul nsw <2 x i32> [[X:%.*]], splat (i32 150)
+; CHECK-NEXT:    [[D:%.*]] = sdiv <2 x i32> [[M]], splat (i32 100)
+; CHECK-NEXT:    ret <2 x i32> [[D]]
+;
+  %m = mul nsw <2 x i32> %x, <i32 150, i32 150>
+  %d = sdiv <2 x i32> %m, <i32 100, i32 100>
+  ret <2 x i32> %d
+}
+
+; Vector non-splat test (should not fold)
+define <2 x i32> @sdiv_mul_nsw_gcd_non_splat(<2 x i32> %x) {
+; CHECK-LABEL: @sdiv_mul_nsw_gcd_non_splat(
+; CHECK-NEXT:    [[M:%.*]] = mul nsw <2 x i32> [[X:%.*]], <i32 150, i32 90>
+; CHECK-NEXT:    [[D:%.*]] = sdiv <2 x i32> [[M]], <i32 100, i32 60>
+; CHECK-NEXT:    ret <2 x i32> [[D]]
+;
+  %m = mul nsw <2 x i32> %x, <i32 150, i32 90>
+  %d = sdiv <2 x i32> %m, <i32 100, i32 60>
+  ret <2 x i32> %d
+}
+
+; Negative tests
+
+define i32 @sdiv_mul_no_nsw_gcd(i32 %x) {
+; CHECK-LABEL: @sdiv_mul_no_nsw_gcd(
+; CHECK-NEXT:    [[M:%.*]] = mul i32 [[X:%.*]], 150
+; CHECK-NEXT:    [[D:%.*]] = sdiv i32 [[M]], 100
+; CHECK-NEXT:    ret i32 [[D]]
+;
+  %m = mul i32 %x, 150
+  %d = sdiv i32 %m, 100
+  ret i32 %d
+}
+
+define i32 @udiv_mul_no_nuw_gcd(i32 %x) {
+; CHECK-LABEL: @udiv_mul_no_nuw_gcd(
+; CHECK-NEXT:    [[M:%.*]] = mul i32 [[X:%.*]], 150
+; CHECK-NEXT:    [[D:%.*]] = udiv i32 [[M]], 100
+; CHECK-NEXT:    ret i32 [[D]]
+;
+  %m = mul i32 %x, 150
+  %d = udiv i32 %m, 100
+  ret i32 %d
+}
+
+define i32 @sdiv_shl_no_nsw_gcd(i32 %x) {
+; CHECK-LABEL: @sdiv_shl_no_nsw_gcd(
+; CHECK-NEXT:    [[SHL:%.*]] = shl i32 [[X:%.*]], 2
+; CHECK-NEXT:    [[D:%.*]] = sdiv i32 [[SHL]], 6
+; CHECK-NEXT:    ret i32 [[D]]
+;
+  %shl = shl i32 %x, 2
+  %d = sdiv i32 %shl, 6
+  ret i32 %d
+}
+
+define i32 @udiv_shl_no_nuw_gcd(i32 %x) {
+; CHECK-LABEL: @udiv_shl_no_nuw_gcd(
+; CHECK-NEXT:    [[SHL:%.*]] = shl i32 [[X:%.*]], 2
+; CHECK-NEXT:    [[D:%.*]] = udiv i32 [[SHL]], 6
+; CHECK-NEXT:    ret i32 [[D]]
+;
+  %shl = shl i32 %x, 2
+  %d = udiv i32 %shl, 6
+  ret i32 %d
+}
+
+define i32 @sdiv_mul_nsw_gcd_multi_use(i32 %x) {
+; CHECK-LABEL: @sdiv_mul_nsw_gcd_multi_use(
+; CHECK-NEXT:    [[M:%.*]] = mul nsw i32 [[X:%.*]], 150
+; CHECK-NEXT:    call void @use(i32 [[M]])
+; CHECK-NEXT:    [[D:%.*]] = sdiv i32 [[M]], 100
+; CHECK-NEXT:    ret i32 [[D]]
+;
+  %m = mul nsw i32 %x, 150
+  call void @use(i32 %m)
+  %d = sdiv i32 %m, 100
+  ret i32 %d
+}
+
+define i32 @sdiv_shl_nsw_gcd_multi_use(i32 %x) {
+; CHECK-LABEL: @sdiv_shl_nsw_gcd_multi_use(
+; CHECK-NEXT:    [[SHL:%.*]] = shl nsw i32 [[X:%.*]], 2
+; CHECK-NEXT:    call void @use(i32 [[SHL]])
+; CHECK-NEXT:    [[D:%.*]] = sdiv i32 [[SHL]], 6
+; CHECK-NEXT:    ret i32 [[D]]
+;
+  %shl = shl nsw i32 %x, 2
+  call void @use(i32 %shl)
+  %d = sdiv i32 %shl, 6
+  ret i32 %d
+}
+
+define i32 @sdiv_mul_nsw_gcd_is_one(i32 %x) {
+; CHECK-LABEL: @sdiv_mul_nsw_gcd_is_one(
+; CHECK-NEXT:    [[M:%.*]] = mul nsw i32 [[X:%.*]], 150
+; CHECK-NEXT:    [[D:%.*]] = sdiv i32 [[M]], 101
+; CHECK-NEXT:    ret i32 [[D]]
+;
+  %m = mul nsw i32 %x, 150
+  %d = sdiv i32 %m, 101
+  ret i32 %d
+}
+
 !0 = !{!"function_entry_count", i64 1000}
 ;.
 ; CHECK: [[META0:![0-9]+]] = !{!"function_entry_count", i64 1000}

``````````

</details>


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


More information about the llvm-commits mailing list