[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 09:02:43 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/4] [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/4] [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/4] [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:%.*]]) {
>From 25c230a1af7a63784646ae10b91b3317a713ff0b Mon Sep 17 00:00:00 2001
From: Sungbin Jo <goranmoomin at daum.net>
Date: Mon, 6 Jul 2026 01:02:28 +0900
Subject: [PATCH 4/4] [InstCombine] Drop redundant integer type check in
fpto{u,s}i-of-fdiv fold
---
llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
index 66612976e9854..50032718efc41 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
@@ -2534,7 +2534,7 @@ static Instruction *foldFPtoI(Instruction &FI, InstCombiner &IC) {
return nullptr;
}
Type *IntTy = X->getType();
- if (!IntTy->isIntOrIntVectorTy() || FI.getType() != IntTy)
+ if (FI.getType() != IntTy)
return nullptr;
unsigned IntWidth = IntTy->getScalarSizeInBits();
More information about the llvm-commits
mailing list