[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 13:20:31 PDT 2026


https://github.com/lucasly-ba updated https://github.com/llvm/llvm-project/pull/210588

>From c639ba06621dfd94bcad5b9ad73b25b3fd5a3072 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          | 48 +++++++++
 .../InstCombine/fptoui-of-uitofp-add.ll       | 98 +++++++++++++++++++
 2 files changed, 146 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..b9355d5b4acbc 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
@@ -2580,10 +2580,58 @@ 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;
+
+  const fltSemantics &Sem = C->getSemantics();
+
+  // Need a finite C in [0, 1).
+  if (!C->isFinite() || C->isNegative() ||
+      C->compare(APFloat(Sem, 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.
+  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