[llvm] [InstCombine] Fold fptoui (fdiv (uitofp X), C) to udiv X, C (PR #205853)

Sungbin Jo via llvm-commits llvm-commits at lists.llvm.org
Sun Jul 5 02:26:24 PDT 2026


https://github.com/goranmoomin updated https://github.com/llvm/llvm-project/pull/205853

>From 256ce1fac43e286908f523d40ca131281b3a644a Mon Sep 17 00:00:00 2001
From: Sungbin Jo <goranmoomin at daum.net>
Date: Fri, 26 Jun 2026 01:06:46 +0900
Subject: [PATCH 1/3] [InstCombine] Fold fptoui (fdiv (uitofp X), C) to udiv X,
 C

Fixes #205305.
---
 .../InstCombine/InstCombineCasts.cpp          |  52 +++++
 .../Transforms/InstCombine/fptoui-of-fdiv.ll  | 199 ++++++++++++++++++
 2 files changed, 251 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/fptoui-of-fdiv.ll

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
index 7e747ddb9013e..f7dbed2a4a26b 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
@@ -2507,10 +2507,62 @@ static Instruction *foldFPtoI(Instruction &FI, InstCombiner &IC) {
   return nullptr;
 }
 
+/// fptoui (fdiv (uitofp X), C) --> udiv X, C
+///
+/// This is safe if C > 0 and the integer width N <= the FP mantissa width p.
+/// Then uitofp is exact and the rounded quotient never crosses an integer,
+/// i.e. floor(rne_p(X/C)) == floor(X/C).
+/// See #205305 for detailed reasoning.
+static Instruction *foldFPToUIOfFDiv(FPToUIInst &FI, InstCombinerImpl &IC) {
+  auto *FDiv = dyn_cast<BinaryOperator>(FI.getOperand(0));
+  if (!FDiv || FDiv->getOpcode() != Instruction::FDiv || !FDiv->hasOneUse())
+    return nullptr;
+
+  auto *UIToFP = dyn_cast<UIToFPInst>(FDiv->getOperand(0));
+  if (!UIToFP || !UIToFP->hasOneUse())
+    return nullptr;
+  Value *X = UIToFP->getOperand(0);
+  Type *IntTy = X->getType();
+  if (!IntTy->isIntOrIntVectorTy())
+    return nullptr;
+
+  if (FI.getType() != IntTy)
+    return nullptr;
+
+  Type *FPTy = FDiv->getType();
+  unsigned IntWidth = IntTy->getScalarSizeInBits();
+  int MantissaWidth = FPTy->getScalarType()->getFPMantissaWidth();
+  if (MantissaWidth < 0 || IntWidth > static_cast<unsigned>(MantissaWidth))
+    return nullptr;
+
+  auto *CFP = dyn_cast<ConstantFP>(FDiv->getOperand(1));
+  if (!CFP)
+    return nullptr;
+
+  APFloat APF = CFP->getValueAPF();
+  if (!APF.isInteger())
+    return nullptr;
+
+  APSInt Divisor(IntTy->getScalarSizeInBits());
+  bool IsExact = false;
+  APF.convertToInteger(Divisor, APFloat::rmTowardZero, &IsExact);
+  if (!IsExact)
+    return nullptr;
+
+  if (Divisor.isZero())
+    return nullptr;
+
+  Constant *C = ConstantInt::get(IntTy, Divisor);
+  return BinaryOperator::CreateUDiv(X, C);
+}
+
 Instruction *InstCombinerImpl::visitFPToUI(FPToUIInst &FI) {
   if (Instruction *I = foldItoFPtoI(FI))
     return I;
 
+  if (Instruction *I = foldFPToUIOfFDiv(FI, *this))
+    return I;
+
   if (Instruction *I = foldFPtoI(FI, *this))
     return I;
 
diff --git a/llvm/test/Transforms/InstCombine/fptoui-of-fdiv.ll b/llvm/test/Transforms/InstCombine/fptoui-of-fdiv.ll
new file mode 100644
index 0000000000000..90d5d6538b2c8
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/fptoui-of-fdiv.ll
@@ -0,0 +1,199 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+; fptoui (fdiv (uitofp X), C) -> udiv X, C
+; Sound when C >= 1 and N <= p (N = integer width, p = FP mantissa width).
+
+define i32 @i32_double_const(i32 %x) {
+; CHECK-LABEL: define i32 @i32_double_const(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = udiv i32 [[X]], 7
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %a = uitofp i32 %x to double
+  %d = fdiv double %a, 7.000000e+00
+  %r = fptoui double %d to i32
+  ret i32 %r
+}
+
+define i16 @i16_float_const(i16 %x) {
+; CHECK-LABEL: define i16 @i16_float_const(
+; CHECK-SAME: i16 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = udiv i16 [[X]], 3
+; CHECK-NEXT:    ret i16 [[R]]
+;
+  %a = uitofp i16 %x to float
+  %d = fdiv float %a, 3.000000e+00
+  %r = fptoui float %d to i16
+  ret i16 %r
+}
+
+define <2 x i32> @vec_i32_double_const(<2 x i32> %x) {
+; CHECK-LABEL: define <2 x i32> @vec_i32_double_const(
+; CHECK-SAME: <2 x i32> [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = udiv <2 x i32> [[X]], splat (i32 7)
+; CHECK-NEXT:    ret <2 x i32> [[R]]
+;
+  %a = uitofp <2 x i32> %x to <2 x double>
+  %d = fdiv <2 x double> %a, splat (double 7.000000e+00)
+  %r = fptoui <2 x double> %d to <2 x i32>
+  ret <2 x i32> %r
+}
+
+define i32 @divisor_one(i32 %x) {
+; CHECK-LABEL: define i32 @divisor_one(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    ret i32 [[X]]
+;
+  %a = uitofp i32 %x to double
+  %d = fdiv double %a, 1.000000e+00
+  %r = fptoui double %d to i32
+  ret i32 %r
+}
+
+define i64 @i64_double_not_exact(i64 %x) {
+; CHECK-LABEL: define i64 @i64_double_not_exact(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = uitofp i64 [[X]] to double
+; CHECK-NEXT:    [[D:%.*]] = fdiv double [[A]], 7.000000e+00
+; CHECK-NEXT:    [[R:%.*]] = fptoui double [[D]] to i64
+; CHECK-NEXT:    ret i64 [[R]]
+;
+  %a = uitofp i64 %x to double
+  %d = fdiv double %a, 7.000000e+00
+  %r = fptoui double %d to i64
+  ret i64 %r
+}
+
+define i32 @i32_float_not_exact(i32 %x) {
+; CHECK-LABEL: define i32 @i32_float_not_exact(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = uitofp i32 [[X]] to float
+; CHECK-NEXT:    [[D:%.*]] = fdiv float [[A]], 7.000000e+00
+; CHECK-NEXT:    [[R:%.*]] = fptoui float [[D]] to i32
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %a = uitofp i32 %x to float
+  %d = fdiv float %a, 7.000000e+00
+  %r = fptoui float %d to i32
+  ret i32 %r
+}
+
+define i32 @fractional_divisor(i32 %x) {
+; CHECK-LABEL: define i32 @fractional_divisor(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = uitofp i32 [[X]] to double
+; CHECK-NEXT:    [[D:%.*]] = fdiv double [[A]], 2.500000e+00
+; CHECK-NEXT:    [[R:%.*]] = fptoui double [[D]] to i32
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %a = uitofp i32 %x to double
+  %d = fdiv double %a, 2.500000e+00
+  %r = fptoui double %d to i32
+  ret i32 %r
+}
+
+define i32 @zero_divisor(i32 %x) {
+; CHECK-LABEL: define i32 @zero_divisor(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = uitofp i32 [[X]] to double
+; CHECK-NEXT:    [[D:%.*]] = fdiv double [[A]], 0.000000e+00
+; CHECK-NEXT:    [[R:%.*]] = fptoui double [[D]] to i32
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %a = uitofp i32 %x to double
+  %d = fdiv double %a, 0.000000e+00
+  %r = fptoui double %d to i32
+  ret i32 %r
+}
+
+define i64 @width_mismatch(i32 %x) {
+; CHECK-LABEL: define i64 @width_mismatch(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = uitofp i32 [[X]] to double
+; CHECK-NEXT:    [[D:%.*]] = fdiv double [[A]], 7.000000e+00
+; CHECK-NEXT:    [[R:%.*]] = fptoui double [[D]] to i64
+; CHECK-NEXT:    ret i64 [[R]]
+;
+  %a = uitofp i32 %x to double
+  %d = fdiv double %a, 7.000000e+00
+  %r = fptoui double %d to i64
+  ret i64 %r
+}
+
+define i32 @variable_divisor(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @variable_divisor(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = uitofp i32 [[X]] to double
+; CHECK-NEXT:    [[B:%.*]] = uitofp i32 [[Y]] to double
+; CHECK-NEXT:    [[D:%.*]] = fdiv double [[A]], [[B]]
+; CHECK-NEXT:    [[R:%.*]] = fptoui double [[D]] to i32
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %a = uitofp i32 %x to double
+  %b = uitofp i32 %y to double
+  %d = fdiv double %a, %b
+  %r = fptoui double %d to i32
+  ret i32 %r
+}
+
+define i32 @signed_source(i32 %x) {
+; CHECK-LABEL: define i32 @signed_source(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = sitofp i32 [[X]] to double
+; CHECK-NEXT:    [[D:%.*]] = fdiv double [[A]], 7.000000e+00
+; CHECK-NEXT:    [[R:%.*]] = fptoui double [[D]] to i32
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %a = sitofp i32 %x to double
+  %d = fdiv double %a, 7.000000e+00
+  %r = fptoui double %d to i32
+  ret i32 %r
+}
+
+define i32 @overflow_divisor(i32 %x) {
+; CHECK-LABEL: define i32 @overflow_divisor(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = uitofp i32 [[X]] to double
+; CHECK-NEXT:    [[D:%.*]] = fdiv double [[A]], f0x4202A05F1FF80000
+; CHECK-NEXT:    [[R:%.*]] = fptoui double [[D]] to i32
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %a = uitofp i32 %x to double
+  %d = fdiv double %a, 9999999999.0
+  %r = fptoui double %d to i32
+  ret i32 %r
+}
+
+define i128 @ppc_fp128_unknown_precision(i128 %x) {
+; CHECK-LABEL: define i128 @ppc_fp128_unknown_precision(
+; CHECK-SAME: i128 [[X:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = uitofp i128 [[X]] to ppc_fp128
+; CHECK-NEXT:    [[D:%.*]] = fdiv ppc_fp128 [[A]], 7.000000e+00
+; CHECK-NEXT:    [[R:%.*]] = fptoui ppc_fp128 [[D]] to i128
+; CHECK-NEXT:    ret i128 [[R]]
+;
+  %a = uitofp i128 %x to ppc_fp128
+  %d = fdiv ppc_fp128 %a, 7.000000e+00
+  %r = fptoui ppc_fp128 %d to i128
+  ret i128 %r
+}
+
+; Unsound when N(=32) > p(=24) even if %x canBeCastedExactlyIntToFP (%x mod 256 = 0).
+define i32 @unsound_i32_float_shifted(i24 %a) {
+; CHECK-LABEL: define i32 @unsound_i32_float_shifted(
+; CHECK-SAME: i24 [[A:%.*]]) {
+; CHECK-NEXT:    [[Z:%.*]] = zext i24 [[A]] to i32
+; CHECK-NEXT:    [[X:%.*]] = shl nuw i32 [[Z]], 8
+; CHECK-NEXT:    [[F:%.*]] = uitofp i32 [[X]] to float
+; CHECK-NEXT:    [[D:%.*]] = fdiv float [[F]], 3.000000e+00
+; CHECK-NEXT:    [[R:%.*]] = fptoui float [[D]] to i32
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %z = zext i24 %a to i32
+  %x = shl i32 %z, 8
+  %f = uitofp i32 %x to float
+  %d = fdiv float %f, 3.000000e+00
+  %r = fptoui float %d to i32
+  ret i32 %r
+}

>From 7506720aca1068105c6d3914f9611b4a46d99dac Mon Sep 17 00:00:00 2001
From: Sungbin Jo <goranmoomin at daum.net>
Date: Mon, 29 Jun 2026 14:54:30 +0900
Subject: [PATCH 2/3] [InstCombine] Extend fptoui-of-fdiv fold to handle the
 signed case

---
 .../InstCombine/InstCombineCasts.cpp          |  37 +++-
 .../Transforms/InstCombine/fptoui-of-fdiv.ll  | 188 +++++++++++++++++-
 2 files changed, 212 insertions(+), 13 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
index f7dbed2a4a26b..bd90d5d0d965c 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
@@ -2507,21 +2507,27 @@ static Instruction *foldFPtoI(Instruction &FI, InstCombiner &IC) {
   return nullptr;
 }
 
-/// fptoui (fdiv (uitofp X), C) --> udiv X, C
+/// fpto{u/s}i (fdiv ({u/s}itofp X), C) --> {u/s}div X, C
 ///
-/// This is safe if C > 0 and the integer width N <= the FP mantissa width p.
-/// Then uitofp is exact and the rounded quotient never crosses an integer,
+/// Given N = integer width, p = FP mantissa width, this is safe if:
+/// Unsigned: C > 0 and N <= p.
+/// Signed: C != 0, N - 1 <= p, and not (X == INT_MIN and C == -1).
+/// Then {u/s}itofp is exact and the rounded quotient never crosses an integer,
 /// i.e. floor(rne_p(X/C)) == floor(X/C).
+///
 /// See #205305 for detailed reasoning.
-static Instruction *foldFPToUIOfFDiv(FPToUIInst &FI, InstCombinerImpl &IC) {
+static Instruction *foldFPToIOfFDiv(Instruction &FI, InstCombinerImpl &IC,
+                                    bool IsSigned) {
   auto *FDiv = dyn_cast<BinaryOperator>(FI.getOperand(0));
   if (!FDiv || FDiv->getOpcode() != Instruction::FDiv || !FDiv->hasOneUse())
     return nullptr;
 
-  auto *UIToFP = dyn_cast<UIToFPInst>(FDiv->getOperand(0));
-  if (!UIToFP || !UIToFP->hasOneUse())
+  auto *IToFP = dyn_cast<CastInst>(FDiv->getOperand(0));
+  if (!IToFP || !IToFP->hasOneUse())
+    return nullptr;
+  if (IsSigned ? !isa<SIToFPInst>(IToFP) : !isa<UIToFPInst>(IToFP))
     return nullptr;
-  Value *X = UIToFP->getOperand(0);
+  Value *X = IToFP->getOperand(0);
   Type *IntTy = X->getType();
   if (!IntTy->isIntOrIntVectorTy())
     return nullptr;
@@ -2532,7 +2538,7 @@ static Instruction *foldFPToUIOfFDiv(FPToUIInst &FI, InstCombinerImpl &IC) {
   Type *FPTy = FDiv->getType();
   unsigned IntWidth = IntTy->getScalarSizeInBits();
   int MantissaWidth = FPTy->getScalarType()->getFPMantissaWidth();
-  if (MantissaWidth < 0 || IntWidth > static_cast<unsigned>(MantissaWidth))
+  if (MantissaWidth < (int)IntWidth - IsSigned)
     return nullptr;
 
   auto *CFP = dyn_cast<ConstantFP>(FDiv->getOperand(1));
@@ -2543,7 +2549,7 @@ static Instruction *foldFPToUIOfFDiv(FPToUIInst &FI, InstCombinerImpl &IC) {
   if (!APF.isInteger())
     return nullptr;
 
-  APSInt Divisor(IntTy->getScalarSizeInBits());
+  APSInt Divisor(IntWidth, !IsSigned);
   bool IsExact = false;
   APF.convertToInteger(Divisor, APFloat::rmTowardZero, &IsExact);
   if (!IsExact)
@@ -2552,15 +2558,21 @@ static Instruction *foldFPToUIOfFDiv(FPToUIInst &FI, InstCombinerImpl &IC) {
   if (Divisor.isZero())
     return nullptr;
 
+  // sdiv INT_MIN, -1 is UB, not poison, so this isn't valid if X == INT_MIN.
+  // fdiv X, -1 gets transformed to fneg anyways, so we do not handle C == -1.
+  if (IsSigned && Divisor.isAllOnes())
+    return nullptr;
+
   Constant *C = ConstantInt::get(IntTy, Divisor);
-  return BinaryOperator::CreateUDiv(X, C);
+  return IsSigned ? BinaryOperator::CreateSDiv(X, C)
+                  : BinaryOperator::CreateUDiv(X, C);
 }
 
 Instruction *InstCombinerImpl::visitFPToUI(FPToUIInst &FI) {
   if (Instruction *I = foldItoFPtoI(FI))
     return I;
 
-  if (Instruction *I = foldFPToUIOfFDiv(FI, *this))
+  if (Instruction *I = foldFPToIOfFDiv(FI, *this, /*IsSigned=*/false))
     return I;
 
   if (Instruction *I = foldFPtoI(FI, *this))
@@ -2573,6 +2585,9 @@ Instruction *InstCombinerImpl::visitFPToSI(FPToSIInst &FI) {
   if (Instruction *I = foldItoFPtoI(FI))
     return I;
 
+  if (Instruction *I = foldFPToIOfFDiv(FI, *this, /*IsSigned=*/true))
+    return I;
+
   if (Instruction *I = foldFPtoI(FI, *this))
     return I;
 
diff --git a/llvm/test/Transforms/InstCombine/fptoui-of-fdiv.ll b/llvm/test/Transforms/InstCombine/fptoui-of-fdiv.ll
index 90d5d6538b2c8..242ae60f9a003 100644
--- a/llvm/test/Transforms/InstCombine/fptoui-of-fdiv.ll
+++ b/llvm/test/Transforms/InstCombine/fptoui-of-fdiv.ll
@@ -1,8 +1,10 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
 ; RUN: opt < %s -passes=instcombine -S | FileCheck %s
 
-; fptoui (fdiv (uitofp X), C) -> udiv X, C
-; Sound when C >= 1 and N <= p (N = integer width, p = FP mantissa width).
+; fpto{u/s}i (fdiv ({u/s}itofp X), C) -> {u/s}div X, C
+; Unsigned: sound when C >= 1 and N <= p.
+; Signed: sound when C not in {0, -1} and N - 1 <= p.
+; (N = integer width, p = FP mantissa width.)
 
 define i32 @i32_double_const(i32 %x) {
 ; CHECK-LABEL: define i32 @i32_double_const(
@@ -197,3 +199,185 @@ define i32 @unsound_i32_float_shifted(i24 %a) {
   %r = fptoui float %d to i32
   ret i32 %r
 }
+
+define i32 @si32_double_const(i32 %x) {
+; CHECK-LABEL: define i32 @si32_double_const(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = sdiv i32 [[X]], 7
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %a = sitofp i32 %x to double
+  %d = fdiv double %a, 7.000000e+00
+  %r = fptosi double %d to i32
+  ret i32 %r
+}
+
+define i32 @si32_double_neg_divisor(i32 %x) {
+; CHECK-LABEL: define i32 @si32_double_neg_divisor(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = sdiv i32 [[X]], -7
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %a = sitofp i32 %x to double
+  %d = fdiv double %a, -7.000000e+00
+  %r = fptosi double %d to i32
+  ret i32 %r
+}
+
+define i16 @si16_float_const(i16 %x) {
+; CHECK-LABEL: define i16 @si16_float_const(
+; CHECK-SAME: i16 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = sdiv i16 [[X]], 3
+; CHECK-NEXT:    ret i16 [[R]]
+;
+  %a = sitofp i16 %x to float
+  %d = fdiv float %a, 3.000000e+00
+  %r = fptosi float %d to i16
+  ret i16 %r
+}
+
+define i24 @si24_float_const(i24 %x) {
+; CHECK-LABEL: define i24 @si24_float_const(
+; CHECK-SAME: i24 [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = sdiv i24 [[X]], 3
+; CHECK-NEXT:    ret i24 [[R]]
+;
+  %a = sitofp i24 %x to float
+  %d = fdiv float %a, 3.000000e+00
+  %r = fptosi float %d to i24
+  ret i24 %r
+}
+
+define <2 x i32> @si32_double_vec(<2 x i32> %x) {
+; CHECK-LABEL: define <2 x i32> @si32_double_vec(
+; CHECK-SAME: <2 x i32> [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = sdiv <2 x i32> [[X]], splat (i32 7)
+; CHECK-NEXT:    ret <2 x i32> [[R]]
+;
+  %a = sitofp <2 x i32> %x to <2 x double>
+  %d = fdiv <2 x double> %a, splat (double 7.000000e+00)
+  %r = fptosi <2 x double> %d to <2 x i32>
+  ret <2 x i32> %r
+}
+
+define i32 @si32_divisor_one(i32 %x) {
+; CHECK-LABEL: define i32 @si32_divisor_one(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    ret i32 [[X]]
+;
+  %a = sitofp i32 %x to double
+  %d = fdiv double %a, 1.000000e+00
+  %r = fptosi double %d to i32
+  ret i32 %r
+}
+
+; Do not transform fptosi (fdiv (sitofp INT_MIN), -1) (poison) into sdiv INT_MIN, -1 (UB).
+; fdiv X, -1.0 is canonicalized to fneg before our transform runs.
+define i32 @si32_divisor_neg_one(i32 %x) {
+; CHECK-LABEL: define i32 @si32_divisor_neg_one(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = sitofp i32 [[X]] to double
+; CHECK-NEXT:    [[D:%.*]] = fneg double [[A]]
+; CHECK-NEXT:    [[R:%.*]] = fptosi double [[D]] to i32
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %a = sitofp i32 %x to double
+  %d = fdiv double %a, -1.000000e+00
+  %r = fptosi double %d to i32
+  ret i32 %r
+}
+
+define i64 @si64_double_not_exact(i64 %x) {
+; CHECK-LABEL: define i64 @si64_double_not_exact(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = sitofp i64 [[X]] to double
+; CHECK-NEXT:    [[D:%.*]] = fdiv double [[A]], 7.000000e+00
+; CHECK-NEXT:    [[R:%.*]] = fptosi double [[D]] to i64
+; CHECK-NEXT:    ret i64 [[R]]
+;
+  %a = sitofp i64 %x to double
+  %d = fdiv double %a, 7.000000e+00
+  %r = fptosi double %d to i64
+  ret i64 %r
+}
+
+define i32 @si32_float_not_exact(i32 %x) {
+; CHECK-LABEL: define i32 @si32_float_not_exact(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = sitofp i32 [[X]] to float
+; CHECK-NEXT:    [[D:%.*]] = fdiv float [[A]], 7.000000e+00
+; CHECK-NEXT:    [[R:%.*]] = fptosi float [[D]] to i32
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %a = sitofp i32 %x to float
+  %d = fdiv float %a, 7.000000e+00
+  %r = fptosi float %d to i32
+  ret i32 %r
+}
+
+define i32 @si32_fractional_divisor(i32 %x) {
+; CHECK-LABEL: define i32 @si32_fractional_divisor(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = sitofp i32 [[X]] to double
+; CHECK-NEXT:    [[D:%.*]] = fdiv double [[A]], 2.500000e+00
+; CHECK-NEXT:    [[R:%.*]] = fptosi double [[D]] to i32
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %a = sitofp i32 %x to double
+  %d = fdiv double %a, 2.500000e+00
+  %r = fptosi double %d to i32
+  ret i32 %r
+}
+
+define i32 @si32_zero_divisor(i32 %x) {
+; CHECK-LABEL: define i32 @si32_zero_divisor(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    ret i32 0
+;
+  %a = sitofp i32 %x to double
+  %d = fdiv double %a, 0.000000e+00
+  %r = fptosi double %d to i32
+  ret i32 %r
+}
+
+define i64 @si32_width_mismatch(i32 %x) {
+; CHECK-LABEL: define i64 @si32_width_mismatch(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = sitofp i32 [[X]] to double
+; CHECK-NEXT:    [[D:%.*]] = fdiv double [[A]], 7.000000e+00
+; CHECK-NEXT:    [[R:%.*]] = fptosi double [[D]] to i64
+; CHECK-NEXT:    ret i64 [[R]]
+;
+  %a = sitofp i32 %x to double
+  %d = fdiv double %a, 7.000000e+00
+  %r = fptosi double %d to i64
+  ret i64 %r
+}
+
+define i32 @si32_unsigned_source(i32 %x) {
+; CHECK-LABEL: define i32 @si32_unsigned_source(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = uitofp i32 [[X]] to double
+; CHECK-NEXT:    [[D:%.*]] = fdiv double [[A]], 7.000000e+00
+; CHECK-NEXT:    [[R:%.*]] = fptosi double [[D]] to i32
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %a = uitofp i32 %x to double
+  %d = fdiv double %a, 7.000000e+00
+  %r = fptosi double %d to i32
+  ret i32 %r
+}
+
+define i32 @si32_overflow_divisor(i32 %x) {
+; CHECK-LABEL: define i32 @si32_overflow_divisor(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = sitofp i32 [[X]] to double
+; CHECK-NEXT:    [[D:%.*]] = fdiv double [[A]], f0x4202A05F1FF80000
+; CHECK-NEXT:    [[R:%.*]] = fptosi double [[D]] to i32
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %a = sitofp i32 %x to double
+  %d = fdiv double %a, 9999999999.0
+  %r = fptosi double %d to i32
+  ret i32 %r
+}

>From 00e331097c0f0c1faa6efa917b6f11af14e29e26 Mon Sep 17 00:00:00 2001
From: Sungbin Jo <goranmoomin at daum.net>
Date: Sun, 5 Jul 2026 18:18:42 +0900
Subject: [PATCH 3/3] [InstCombine] Simplify fpto{u,s}i-of-fdiv fold with
 pattern matchers

---
 .../InstCombine/InstCombineCasts.cpp          | 78 ++++++++-----------
 .../Transforms/InstCombine/fptoui-of-fdiv.ll  | 12 +++
 2 files changed, 46 insertions(+), 44 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
index bd90d5d0d965c..66612976e9854 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
@@ -2504,54 +2504,50 @@ static Instruction *foldFPtoI(Instruction &FI, InstCombiner &IC) {
   if (FPClass.isKnownNever(Mask))
     return IC.replaceInstUsesWith(FI, ConstantInt::getNullValue(FI.getType()));
 
-  return nullptr;
-}
-
-/// fpto{u/s}i (fdiv ({u/s}itofp X), C) --> {u/s}div X, C
-///
-/// Given N = integer width, p = FP mantissa width, this is safe if:
-/// Unsigned: C > 0 and N <= p.
-/// Signed: C != 0, N - 1 <= p, and not (X == INT_MIN and C == -1).
-/// Then {u/s}itofp is exact and the rounded quotient never crosses an integer,
-/// i.e. floor(rne_p(X/C)) == floor(X/C).
-///
-/// See #205305 for detailed reasoning.
-static Instruction *foldFPToIOfFDiv(Instruction &FI, InstCombinerImpl &IC,
-                                    bool IsSigned) {
-  auto *FDiv = dyn_cast<BinaryOperator>(FI.getOperand(0));
-  if (!FDiv || FDiv->getOpcode() != Instruction::FDiv || !FDiv->hasOneUse())
-    return nullptr;
-
-  auto *IToFP = dyn_cast<CastInst>(FDiv->getOperand(0));
-  if (!IToFP || !IToFP->hasOneUse())
-    return nullptr;
-  if (IsSigned ? !isa<SIToFPInst>(IToFP) : !isa<UIToFPInst>(IToFP))
-    return nullptr;
-  Value *X = IToFP->getOperand(0);
+  // fpto{u/s}i (fdiv ({u/s}itofp X to F), C_fp) --> {u/s}div X, C
+  //
+  // F has precision p (significand bits incl. hidden bit); C_fp is the exact FP
+  // value of the integer constant C. Given N = integer width, this is safe if:
+  //   Unsigned: C > 0 and N <= p.
+  //   Signed:   C != 0 and N - 1 <= p, excluding (X == INT_MIN, C == -1) since
+  //             sdiv INT_MIN, -1 is UB while the FP path only yields poison.
+  //             fdiv X, -1 gets transformed to fneg in InstCombine regardless.
+  //
+  // The bounds make {u/s}itofp and C_fp exact (every |int| <= 2^p is exact),
+  // and ensure the rounded quotient never crosses an integer boundary:
+  //   Rounding lemma: for 0 <= A <= 2^p, 1 <= B <= 2^p, q = floor(A/B),
+  //     trunc(R_p(A/B)) = q.
+  //   For r = A - qB > 0, m = q+1, half-gap H(m) <= q/2^p and
+  //   m - A/B = (B-r)/B >= 1/B > q/2^p >= H(m), so R_p(A/B) < m; q = 0 is
+  //   similar (H(1) = 2^(-p-1) < 2^-p <= 1/B).
+  //   Signed case: by symmetry R_p(-z) = -R_p(z), so fptosi yields s*q = sdiv.
+  bool IsSigned = FI.getOpcode() == Instruction::FPToSI;
+  Value *X;
+  const APFloat *APF;
+  if (IsSigned) {
+    if (!match(FI.getOperand(0),
+               m_OneUse(m_FDiv(m_SIToFP(m_Value(X)), m_APFloat(APF)))))
+      return nullptr;
+  } else {
+    if (!match(FI.getOperand(0),
+               m_OneUse(m_FDiv(m_UIToFP(m_Value(X)), m_APFloat(APF)))))
+      return nullptr;
+  }
   Type *IntTy = X->getType();
-  if (!IntTy->isIntOrIntVectorTy())
-    return nullptr;
-
-  if (FI.getType() != IntTy)
+  if (!IntTy->isIntOrIntVectorTy() || FI.getType() != IntTy)
     return nullptr;
 
-  Type *FPTy = FDiv->getType();
   unsigned IntWidth = IntTy->getScalarSizeInBits();
-  int MantissaWidth = FPTy->getScalarType()->getFPMantissaWidth();
-  if (MantissaWidth < (int)IntWidth - IsSigned)
+  unsigned Precision = APFloat::semanticsPrecision(APF->getSemantics());
+  if (Precision + IsSigned < IntWidth)
     return nullptr;
 
-  auto *CFP = dyn_cast<ConstantFP>(FDiv->getOperand(1));
-  if (!CFP)
-    return nullptr;
-
-  APFloat APF = CFP->getValueAPF();
-  if (!APF.isInteger())
+  if (!APF->isInteger())
     return nullptr;
 
   APSInt Divisor(IntWidth, !IsSigned);
   bool IsExact = false;
-  APF.convertToInteger(Divisor, APFloat::rmTowardZero, &IsExact);
+  APF->convertToInteger(Divisor, APFloat::rmTowardZero, &IsExact);
   if (!IsExact)
     return nullptr;
 
@@ -2572,9 +2568,6 @@ Instruction *InstCombinerImpl::visitFPToUI(FPToUIInst &FI) {
   if (Instruction *I = foldItoFPtoI(FI))
     return I;
 
-  if (Instruction *I = foldFPToIOfFDiv(FI, *this, /*IsSigned=*/false))
-    return I;
-
   if (Instruction *I = foldFPtoI(FI, *this))
     return I;
 
@@ -2585,9 +2578,6 @@ Instruction *InstCombinerImpl::visitFPToSI(FPToSIInst &FI) {
   if (Instruction *I = foldItoFPtoI(FI))
     return I;
 
-  if (Instruction *I = foldFPToIOfFDiv(FI, *this, /*IsSigned=*/true))
-    return I;
-
   if (Instruction *I = foldFPtoI(FI, *this))
     return I;
 
diff --git a/llvm/test/Transforms/InstCombine/fptoui-of-fdiv.ll b/llvm/test/Transforms/InstCombine/fptoui-of-fdiv.ll
index 242ae60f9a003..180b5915c05b1 100644
--- a/llvm/test/Transforms/InstCombine/fptoui-of-fdiv.ll
+++ b/llvm/test/Transforms/InstCombine/fptoui-of-fdiv.ll
@@ -42,6 +42,18 @@ define <2 x i32> @vec_i32_double_const(<2 x i32> %x) {
   ret <2 x i32> %r
 }
 
+define <4 x i16> @vec_i16_float_neg_divisor(<4 x i16> %x) {
+; CHECK-LABEL: define <4 x i16> @vec_i16_float_neg_divisor(
+; CHECK-SAME: <4 x i16> [[X:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = sdiv <4 x i16> [[X]], splat (i16 -3)
+; CHECK-NEXT:    ret <4 x i16> [[R]]
+;
+  %a = sitofp <4 x i16> %x to <4 x float>
+  %d = fdiv <4 x float> %a, splat (float -3.000000e+00)
+  %r = fptosi <4 x float> %d to <4 x i16>
+  ret <4 x i16> %r
+}
+
 define i32 @divisor_one(i32 %x) {
 ; CHECK-LABEL: define i32 @divisor_one(
 ; CHECK-SAME: i32 [[X:%.*]]) {



More information about the llvm-commits mailing list