[llvm] [InstCombine] Fold fptoui(fadd(uitofp(X), C)) to X (PR #210588)
Lucas Ly Ba via llvm-commits
llvm-commits at lists.llvm.org
Sun Jul 19 04:21:40 PDT 2026
https://github.com/lucasly-ba created https://github.com/llvm/llvm-project/pull/210588
X comes from a uitofp so it's already an integer. If C is in [0, 1) and X's type fits exactly in the float, adding C can't push the value up to the next integer, so rounding back down with fptoui just gives X. Shows up with the (unsigned)((float)x + 0.5f) rounding idiom.
Fixes #210536
>From 66a00acc52e1721060ca0eb7bf2ff135d5028cea Mon Sep 17 00:00:00 2001
From: Lucas Ly Ba <hi at lucaslyba.com>
Date: Sun, 19 Jul 2026 00:25:01 +0200
Subject: [PATCH] [InstCombine] Fold fptoui(fadd(uitofp(X), C)) to X
X comes from a uitofp so it's already an integer. If C is in [0, 1) and X's
type fits exactly in the float, adding C can't push the value up to the next
integer, so rounding back down with fptoui just gives X. Shows up with the
(unsigned)((float)x + 0.5f) rounding idiom.
Fixes #210536
---
.../InstCombine/InstCombineCasts.cpp | 47 +++++++++
.../InstCombine/fptoui-of-uitofp-add.ll | 98 +++++++++++++++++++
2 files changed, 145 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/fptoui-of-uitofp-add.ll
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
index fb196aec7946b..54bf79c6b4df4 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
@@ -2580,10 +2580,57 @@ static Instruction *foldFPtoI(Instruction &FI, InstCombiner &IC) {
: BinaryOperator::CreateUDiv(X, C);
}
+// fptoui (fadd (uitofp X), C) --> X
+//
+// X is already an integer, so if 0 <= C < 1 and X fits exactly in the float,
+// adding C can't reach the next integer and fptoui just gives X back. This is
+// the (unsigned)((float)x + 0.5f) rounding trick.
+static Value *foldFPToUIOfUIToFPInc(FPToUIInst &FI) {
+ Value *X;
+ const APFloat *C;
+ if (!match(FI.getOperand(0), m_FAdd(m_UIToFP(m_Value(X)), m_APFloat(C))))
+ return nullptr;
+
+ Type *IntTy = FI.getType();
+ if (X->getType() != IntTy)
+ return nullptr;
+
+ // Need a finite C in [0, 1).
+ if (!C->isFinite() || C->isNegative() ||
+ C->compare(APFloat(C->getSemantics(), 1)) != APFloat::cmpLessThan)
+ return nullptr;
+
+ // The uitofp has to be lossless, so the whole int type must fit in the
+ // mantissa.
+ unsigned Width = IntTy->getScalarSizeInBits();
+ int Mantissa =
+ FI.getOperand(0)->getType()->getScalarType()->getFPMantissaWidth();
+ if (Mantissa < 0 || Width > static_cast<unsigned>(Mantissa))
+ return nullptr;
+
+ // Also check the biggest value + C doesn't round up to 2^Width. Testing the
+ // top of the range is enough since the spacing between floats only grows.
+ const fltSemantics &Sem = C->getSemantics();
+ APFloat Top(Sem);
+ Top.convertFromAPInt(APInt::getMaxValue(Width), /*IsSigned=*/false,
+ APFloat::rmNearestTiesToEven);
+ Top.add(*C, APFloat::rmNearestTiesToEven);
+ APFloat Bound(Sem);
+ Bound.convertFromAPInt(APInt::getOneBitSet(Width + 1, Width),
+ /*IsSigned=*/false, APFloat::rmNearestTiesToEven);
+ if (Top.compare(Bound) != APFloat::cmpLessThan)
+ return nullptr;
+
+ return X;
+}
+
Instruction *InstCombinerImpl::visitFPToUI(FPToUIInst &FI) {
if (Instruction *I = foldItoFPtoI(FI))
return I;
+ if (Value *V = foldFPToUIOfUIToFPInc(FI))
+ return replaceInstUsesWith(FI, V);
+
if (Instruction *I = foldFPtoI(FI, *this))
return I;
diff --git a/llvm/test/Transforms/InstCombine/fptoui-of-uitofp-add.ll b/llvm/test/Transforms/InstCombine/fptoui-of-uitofp-add.ll
new file mode 100644
index 0000000000000..07e95b47a46e3
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/fptoui-of-uitofp-add.ll
@@ -0,0 +1,98 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+; fptoui(fadd(uitofp(x), C)) -> x when C is in [0, 1), x fits exactly in the
+; float, and adding C can't round up to the next integer.
+
+define i16 @half(i16 %x) {
+; CHECK-LABEL: @half(
+; CHECK-NEXT: ret i16 [[X:%.*]]
+;
+ %f = uitofp i16 %x to float
+ %a = fadd float %f, 5.000000e-01
+ %b = fptoui float %a to i16
+ ret i16 %b
+}
+
+define i16 @zero(i16 %x) {
+; CHECK-LABEL: @zero(
+; CHECK-NEXT: ret i16 [[X:%.*]]
+;
+ %f = uitofp i16 %x to float
+ %a = fadd float %f, 0.000000e+00
+ %b = fptoui float %a to i16
+ ret i16 %b
+}
+
+define i16 @quarter(i16 %x) {
+; CHECK-LABEL: @quarter(
+; CHECK-NEXT: ret i16 [[X:%.*]]
+;
+ %f = uitofp i16 %x to float
+ %a = fadd float %f, 2.500000e-01
+ %b = fptoui float %a to i16
+ ret i16 %b
+}
+
+; i32 fits exactly in a double.
+define i32 @i32_double(i32 %x) {
+; CHECK-LABEL: @i32_double(
+; CHECK-NEXT: ret i32 [[X:%.*]]
+;
+ %f = uitofp i32 %x to double
+ %a = fadd double %f, 5.000000e-01
+ %b = fptoui double %a to i32
+ ret i32 %b
+}
+
+define <2 x i16> @vec(<2 x i16> %x) {
+; CHECK-LABEL: @vec(
+; CHECK-NEXT: ret <2 x i16> [[X:%.*]]
+;
+ %f = uitofp <2 x i16> %x to <2 x float>
+ %a = fadd <2 x float> %f, splat (float 5.000000e-01)
+ %b = fptoui <2 x float> %a to <2 x i16>
+ ret <2 x i16> %b
+}
+
+; Negative: i32 is not exactly representable in a float.
+define i32 @negative_i32_float(i32 %x) {
+; CHECK-LABEL: @negative_i32_float(
+; CHECK-NEXT: [[F:%.*]] = uitofp i32 [[X:%.*]] to float
+; CHECK-NEXT: [[A:%.*]] = fadd float [[F]], 5.000000e-01
+; CHECK-NEXT: [[B:%.*]] = fptoui float [[A]] to i32
+; CHECK-NEXT: ret i32 [[B]]
+;
+ %f = uitofp i32 %x to float
+ %a = fadd float %f, 5.000000e-01
+ %b = fptoui float %a to i32
+ ret i32 %b
+}
+
+; Negative: C is not less than 1.
+define i16 @negative_one(i16 %x) {
+; CHECK-LABEL: @negative_one(
+; CHECK-NEXT: [[F:%.*]] = uitofp i16 [[X:%.*]] to float
+; CHECK-NEXT: [[A:%.*]] = fadd float [[F]], 1.000000e+00
+; CHECK-NEXT: [[B:%.*]] = fptoui float [[A]] to i16
+; CHECK-NEXT: ret i16 [[B]]
+;
+ %f = uitofp i16 %x to float
+ %a = fadd float %f, 1.000000e+00
+ %b = fptoui float %a to i16
+ ret i16 %b
+}
+
+; Negative: C is negative.
+define i16 @negative_c(i16 %x) {
+; CHECK-LABEL: @negative_c(
+; CHECK-NEXT: [[F:%.*]] = uitofp i16 [[X:%.*]] to float
+; CHECK-NEXT: [[A:%.*]] = fadd float [[F]], -5.000000e-01
+; CHECK-NEXT: [[B:%.*]] = fptoui float [[A]] to i16
+; CHECK-NEXT: ret i16 [[B]]
+;
+ %f = uitofp i16 %x to float
+ %a = fadd float %f, -5.000000e-01
+ %b = fptoui float %a to i16
+ ret i16 %b
+}
More information about the llvm-commits
mailing list