[llvm] ValueTracking: x - floor(x) cannot introduce overflow (PR #189003)

Matt Arsenault via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 2 11:58:40 PDT 2026


https://github.com/arsenm updated https://github.com/llvm/llvm-project/pull/189003

>From 0e0527a03b5cd1e9864ca5fc745e67b8f2fd8eea Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Fri, 27 Mar 2026 14:44:06 +0100
Subject: [PATCH 1/2] ValueTracking: x - floor(x) cannot introduce overflow

This returns a value with an absolute value less than 1 so it
should be possible to propagate no-infs.
---
 llvm/lib/Analysis/ValueTracking.cpp           | 10 ++++-
 .../Transforms/Attributor/nofpclass-fmul.ll   | 42 +++++++++++++++++++
 2 files changed, 51 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 340a616f13e19..2449e98ba3a8f 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -4975,7 +4975,15 @@ static constexpr KnownFPClass::MinMaxKind getMinMaxKind(Intrinsic::ID IID) {
 /// \return true if this is a floating point value that is known to have a
 /// magnitude smaller than 1. i.e., fabs(X) <= 1.0
 static bool isAbsoluteValueLessEqualOne(const Value *V) {
-  // TODO: Handle frexp and x - floor(x)?
+  // TODO: Handle frexp
+  // TODO: Other rounding intrinsics?
+
+  // fabs(x - floor(x)) <= 1
+  const Value *SubFloorX;
+  if (match(V, m_FSub(m_Value(SubFloorX),
+                      m_Intrinsic<Intrinsic::floor>(m_Deferred(SubFloorX)))))
+    return true;
+
   return match(V, m_Intrinsic<Intrinsic::amdgcn_trig_preop>(m_Value())) ||
          match(V, m_Intrinsic<Intrinsic::amdgcn_fract>(m_Value()));
 }
diff --git a/llvm/test/Transforms/Attributor/nofpclass-fmul.ll b/llvm/test/Transforms/Attributor/nofpclass-fmul.ll
index db2cb9ed9c2ba..1b1cf60a33500 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-fmul.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-fmul.ll
@@ -910,3 +910,45 @@ define float @ret_fmul__not_inf__neg1.5(float nofpclass(inf) %x) {
   %mul = fmul float %x, -1.5
   ret float %mul
 }
+
+define float @ret_fmul__not_inf__fsub_floor_pat(float nofpclass(inf) %x, float %y) {
+; CHECK-LABEL: define nofpclass(inf) float @ret_fmul__not_inf__fsub_floor_pat(
+; CHECK-SAME: float nofpclass(inf) [[X:%.*]], float [[Y:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[FLOOR_Y:%.*]] = call float @llvm.floor.f32(float [[Y]]) #[[ATTR2]]
+; CHECK-NEXT:    [[Y_SUB_FLOOR_Y:%.*]] = fsub float [[Y]], [[FLOOR_Y]]
+; CHECK-NEXT:    [[MUL:%.*]] = fmul float [[X]], [[Y_SUB_FLOOR_Y]]
+; CHECK-NEXT:    ret float [[MUL]]
+;
+  %floor.y = call float @llvm.floor.f32(float %y)
+  %y.sub.floor.y = fsub float %y, %floor.y
+  %mul = fmul float %x, %y.sub.floor.y
+  ret float %mul
+}
+
+define float @ret_fmul__not_inf__fsub_floor_pat_commute(float nofpclass(inf) %x, float %y) {
+; CHECK-LABEL: define nofpclass(inf) float @ret_fmul__not_inf__fsub_floor_pat_commute(
+; CHECK-SAME: float nofpclass(inf) [[X:%.*]], float [[Y:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[FLOOR_Y:%.*]] = call float @llvm.floor.f32(float [[Y]]) #[[ATTR2]]
+; CHECK-NEXT:    [[Y_SUB_FLOOR_Y:%.*]] = fsub float [[Y]], [[FLOOR_Y]]
+; CHECK-NEXT:    [[MUL:%.*]] = fmul float [[Y_SUB_FLOOR_Y]], [[X]]
+; CHECK-NEXT:    ret float [[MUL]]
+;
+  %floor.y = call float @llvm.floor.f32(float %y)
+  %y.sub.floor.y = fsub float %y, %floor.y
+  %mul = fmul float %y.sub.floor.y, %x
+  ret float %mul
+}
+
+define float @ret_fmul__not_inf__fsub_floor_pat_wrong_floor_val(float nofpclass(inf) %x, float %y, float %z) {
+; CHECK-LABEL: define float @ret_fmul__not_inf__fsub_floor_pat_wrong_floor_val(
+; CHECK-SAME: float nofpclass(inf) [[X:%.*]], float [[Y:%.*]], float [[Z:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[FLOOR_Y:%.*]] = call float @llvm.floor.f32(float [[Z]]) #[[ATTR2]]
+; CHECK-NEXT:    [[Y_SUB_FLOOR_Y:%.*]] = fsub float [[Y]], [[FLOOR_Y]]
+; CHECK-NEXT:    [[MUL:%.*]] = fmul float [[X]], [[Y_SUB_FLOOR_Y]]
+; CHECK-NEXT:    ret float [[MUL]]
+;
+  %floor.y = call float @llvm.floor.f32(float %z)
+  %y.sub.floor.y = fsub float %y, %floor.y
+  %mul = fmul float %x, %y.sub.floor.y
+  ret float %mul
+}

>From 8b6d5ed732c7ea99a18a1ae86afb6e5565fba50b Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Fri, 27 Mar 2026 20:11:20 +0100
Subject: [PATCH 2/2] Rename function to show nan doesn't matter

---
 llvm/lib/Analysis/ValueTracking.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 2449e98ba3a8f..a0fb28612c534 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -4973,8 +4973,8 @@ static constexpr KnownFPClass::MinMaxKind getMinMaxKind(Intrinsic::ID IID) {
 }
 
 /// \return true if this is a floating point value that is known to have a
-/// magnitude smaller than 1. i.e., fabs(X) <= 1.0
-static bool isAbsoluteValueLessEqualOne(const Value *V) {
+/// magnitude smaller than 1. i.e., fabs(X) <= 1.0 or is nan.
+static bool isAbsoluteValueULEOne(const Value *V) {
   // TODO: Handle frexp
   // TODO: Other rounding intrinsics?
 
@@ -5653,9 +5653,9 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
 
     /// Propgate no-infs if the other source is known smaller than one, such
     /// that this cannot introduce overflow.
-    if (KnownLHS.isKnownNever(fcInf) && isAbsoluteValueLessEqualOne(RHS))
+    if (KnownLHS.isKnownNever(fcInf) && isAbsoluteValueULEOne(RHS))
       Known.knownNot(fcInf);
-    else if (KnownRHS.isKnownNever(fcInf) && isAbsoluteValueLessEqualOne(LHS))
+    else if (KnownRHS.isKnownNever(fcInf) && isAbsoluteValueULEOne(LHS))
       Known.knownNot(fcInf);
 
     break;



More information about the llvm-commits mailing list