[llvm] 20902f0 - ValueTracking: Teach computeKnownFPClass to look at bitcast + integer max (#184073)

via llvm-commits llvm-commits at lists.llvm.org
Wed Mar 4 11:06:57 PST 2026


Author: Matt Arsenault
Date: 2026-03-04T19:06:47Z
New Revision: 20902f0b721ba6cf2fb134362d27144bd8584d53

URL: https://github.com/llvm/llvm-project/commit/20902f0b721ba6cf2fb134362d27144bd8584d53
DIFF: https://github.com/llvm/llvm-project/commit/20902f0b721ba6cf2fb134362d27144bd8584d53.diff

LOG: ValueTracking: Teach computeKnownFPClass to look at bitcast + integer max (#184073)

The returned class will still be one of the bitpatterns.

This pattern is used in rocm device libraries in assorted functions,
e.g.,

https://github.com/ROCm/llvm-project/blob/amd-staging/amd/device-libs/ocml/src/rlen3F.cl#L20

I believe it is blocking the eliminationg of finite checks in some of
the more complex functions.

Added: 
    llvm/test/Transforms/Attributor/nofpclass-bitcast.ll

Modified: 
    llvm/lib/Analysis/ValueTracking.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index f7e8745f947e2..f247ae87b72ca 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -5978,8 +5978,28 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
         !Src->getType()->isIntOrIntVectorTy())
       break;
 
-    const Type *Ty = Op->getType()->getScalarType();
-    KnownBits Bits(Ty->getScalarSizeInBits());
+    const Type *Ty = Op->getType();
+
+    Value *CastLHS, *CastRHS;
+
+    // Match bitcast(umax(bitcast(a), bitcast(b)))
+    if (match(Src, m_c_MaxOrMin(m_BitCast(m_Value(CastLHS)),
+                                m_BitCast(m_Value(CastRHS)))) &&
+        CastLHS->getType() == Ty && CastRHS->getType() == Ty) {
+      KnownFPClass KnownLHS, KnownRHS;
+      computeKnownFPClass(CastRHS, DemandedElts, InterestedClasses, KnownRHS, Q,
+                          Depth + 1);
+      if (!KnownRHS.isUnknown()) {
+        computeKnownFPClass(CastLHS, DemandedElts, InterestedClasses, KnownLHS,
+                            Q, Depth + 1);
+        Known = KnownLHS | KnownRHS;
+      }
+
+      return;
+    }
+
+    const Type *EltTy = Ty->getScalarType();
+    KnownBits Bits(EltTy->getPrimitiveSizeInBits());
     computeKnownBits(Src, DemandedElts, Bits, Q, Depth + 1);
 
     // Transfer information from the sign bit.
@@ -5988,7 +6008,7 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
     else if (Bits.isNegative())
       Known.signBitMustBeOne();
 
-    if (Ty->isIEEELikeFPTy()) {
+    if (EltTy->isIEEELikeFPTy()) {
       // IEEE floats are NaN when all bits of the exponent plus at least one of
       // the fraction bits are 1. This means:
       //   - If we assume unknown bits are 0 and the value is NaN, it will
@@ -5996,15 +6016,15 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
       //   - If we assume unknown bits are 1 and the value is not NaN, it can
       //     never be NaN
       // Note: They do not hold for x86_fp80 format.
-      if (APFloat(Ty->getFltSemantics(), Bits.One).isNaN())
+      if (APFloat(EltTy->getFltSemantics(), Bits.One).isNaN())
         Known.KnownFPClasses = fcNan;
-      else if (!APFloat(Ty->getFltSemantics(), ~Bits.Zero).isNaN())
+      else if (!APFloat(EltTy->getFltSemantics(), ~Bits.Zero).isNaN())
         Known.knownNot(fcNan);
 
       // Build KnownBits representing Inf and check if it must be equal or
       // unequal to this value.
       auto InfKB = KnownBits::makeConstant(
-          APFloat::getInf(Ty->getFltSemantics()).bitcastToAPInt());
+          APFloat::getInf(EltTy->getFltSemantics()).bitcastToAPInt());
       InfKB.Zero.clearSignBit();
       if (const auto InfResult = KnownBits::eq(Bits, InfKB)) {
         assert(!InfResult.value());
@@ -6016,7 +6036,7 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
       // Build KnownBits representing Zero and check if it must be equal or
       // unequal to this value.
       auto ZeroKB = KnownBits::makeConstant(
-          APFloat::getZero(Ty->getFltSemantics()).bitcastToAPInt());
+          APFloat::getZero(EltTy->getFltSemantics()).bitcastToAPInt());
       ZeroKB.Zero.clearSignBit();
       if (const auto ZeroResult = KnownBits::eq(Bits, ZeroKB)) {
         assert(!ZeroResult.value());

diff  --git a/llvm/test/Transforms/Attributor/nofpclass-bitcast.ll b/llvm/test/Transforms/Attributor/nofpclass-bitcast.ll
new file mode 100644
index 0000000000000..7bf82f39b938c
--- /dev/null
+++ b/llvm/test/Transforms/Attributor/nofpclass-bitcast.ll
@@ -0,0 +1,229 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal < %s | FileCheck --check-prefixes=CHECK,TUNIT %s
+
+define float @bitcast_umax_not_nan(float nofpclass(nan) %x, float nofpclass(nan) %y) {
+; CHECK-LABEL: define nofpclass(nan) float @bitcast_umax_not_nan(
+; CHECK-SAME: float nofpclass(nan) [[X:%.*]], float nofpclass(nan) [[Y:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT:    [[CAST_X:%.*]] = bitcast float [[X]] to i32
+; CHECK-NEXT:    [[CAST_Y:%.*]] = bitcast float [[Y]] to i32
+; CHECK-NEXT:    [[UMAX:%.*]] = call i32 @llvm.umax.i32(i32 [[CAST_X]], i32 [[CAST_Y]]) #[[ATTR2:[0-9]+]]
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[UMAX]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %cast.x = bitcast float %x to i32
+  %cast.y = bitcast float %y to i32
+  %umax = call i32 @llvm.umax.i32(i32 %cast.x, i32 %cast.y)
+  %cast = bitcast i32 %umax to float
+  ret float %cast
+}
+
+define float @bitcast_umin_not_nan(float nofpclass(nan) %x, float nofpclass(nan) %y) {
+; CHECK-LABEL: define nofpclass(nan) float @bitcast_umin_not_nan(
+; CHECK-SAME: float nofpclass(nan) [[X:%.*]], float nofpclass(nan) [[Y:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[CAST_X:%.*]] = bitcast float [[X]] to i32
+; CHECK-NEXT:    [[CAST_Y:%.*]] = bitcast float [[Y]] to i32
+; CHECK-NEXT:    [[UMIN:%.*]] = call i32 @llvm.umin.i32(i32 [[CAST_X]], i32 [[CAST_Y]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[UMIN]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %cast.x = bitcast float %x to i32
+  %cast.y = bitcast float %y to i32
+  %umin = call i32 @llvm.umin.i32(i32 %cast.x, i32 %cast.y)
+  %cast = bitcast i32 %umin to float
+  ret float %cast
+}
+
+define float @bitcast_smax_not_nan(float nofpclass(nan) %x, float nofpclass(nan) %y) {
+; CHECK-LABEL: define nofpclass(nan) float @bitcast_smax_not_nan(
+; CHECK-SAME: float nofpclass(nan) [[X:%.*]], float nofpclass(nan) [[Y:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[CAST_X:%.*]] = bitcast float [[X]] to i32
+; CHECK-NEXT:    [[CAST_Y:%.*]] = bitcast float [[Y]] to i32
+; CHECK-NEXT:    [[SMAX:%.*]] = call i32 @llvm.smax.i32(i32 [[CAST_X]], i32 [[CAST_Y]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[SMAX]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %cast.x = bitcast float %x to i32
+  %cast.y = bitcast float %y to i32
+  %smax = call i32 @llvm.smax.i32(i32 %cast.x, i32 %cast.y)
+  %cast = bitcast i32 %smax to float
+  ret float %cast
+}
+
+define float @bitcast_smin_not_nan(float nofpclass(nan) %x, float nofpclass(nan) %y) {
+; CHECK-LABEL: define nofpclass(nan) float @bitcast_smin_not_nan(
+; CHECK-SAME: float nofpclass(nan) [[X:%.*]], float nofpclass(nan) [[Y:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[CAST_X:%.*]] = bitcast float [[X]] to i32
+; CHECK-NEXT:    [[CAST_Y:%.*]] = bitcast float [[Y]] to i32
+; CHECK-NEXT:    [[SMIN:%.*]] = call i32 @llvm.smin.i32(i32 [[CAST_X]], i32 [[CAST_Y]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[SMIN]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %cast.x = bitcast float %x to i32
+  %cast.y = bitcast float %y to i32
+  %smin = call i32 @llvm.smin.i32(i32 %cast.x, i32 %cast.y)
+  %cast = bitcast i32 %smin to float
+  ret float %cast
+}
+
+define <2 x float> @bitcast_umax_not_nan_vec(<2 x float> nofpclass(nan) %x, <2 x float> nofpclass(nan) %y) {
+; CHECK-LABEL: define nofpclass(nan) <2 x float> @bitcast_umax_not_nan_vec(
+; CHECK-SAME: <2 x float> nofpclass(nan) [[X:%.*]], <2 x float> nofpclass(nan) [[Y:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[CAST_X:%.*]] = bitcast <2 x float> [[X]] to <2 x i32>
+; CHECK-NEXT:    [[CAST_Y:%.*]] = bitcast <2 x float> [[Y]] to <2 x i32>
+; CHECK-NEXT:    [[UMAX:%.*]] = call <2 x i32> @llvm.umax.v2i32(<2 x i32> [[CAST_X]], <2 x i32> [[CAST_Y]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast <2 x i32> [[UMAX]] to <2 x float>
+; CHECK-NEXT:    ret <2 x float> [[CAST]]
+;
+  %cast.x = bitcast <2 x float> %x to <2 x i32>
+  %cast.y = bitcast <2 x float> %y to <2 x i32>
+  %umax = call <2 x i32> @llvm.umax.v2i32(<2 x i32> %cast.x, <2 x i32> %cast.y)
+  %cast = bitcast <2 x i32> %umax to <2 x float>
+  ret <2 x float> %cast
+}
+
+define float @bitcast_umax_mismatch_
diff erent(float nofpclass(inf) %x, float nofpclass(nan) %y) {
+; CHECK-LABEL: define float @bitcast_umax_mismatch_
diff erent(
+; CHECK-SAME: float nofpclass(inf) [[X:%.*]], float nofpclass(nan) [[Y:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[CAST_X:%.*]] = bitcast float [[X]] to i32
+; CHECK-NEXT:    [[CAST_Y:%.*]] = bitcast float [[Y]] to i32
+; CHECK-NEXT:    [[UMAX:%.*]] = call i32 @llvm.umax.i32(i32 [[CAST_X]], i32 [[CAST_Y]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[UMAX]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %cast.x = bitcast float %x to i32
+  %cast.y = bitcast float %y to i32
+  %umax = call i32 @llvm.umax.i32(i32 %cast.x, i32 %cast.y)
+  %cast = bitcast i32 %umax to float
+  ret float %cast
+}
+
+define float @bitcast_umax_missing_lhs(float %x, float nofpclass(nan) %y) {
+; CHECK-LABEL: define float @bitcast_umax_missing_lhs(
+; CHECK-SAME: float [[X:%.*]], float nofpclass(nan) [[Y:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[CAST_X:%.*]] = bitcast float [[X]] to i32
+; CHECK-NEXT:    [[CAST_Y:%.*]] = bitcast float [[Y]] to i32
+; CHECK-NEXT:    [[UMAX:%.*]] = call i32 @llvm.umax.i32(i32 [[CAST_X]], i32 [[CAST_Y]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[UMAX]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %cast.x = bitcast float %x to i32
+  %cast.y = bitcast float %y to i32
+  %umax = call i32 @llvm.umax.i32(i32 %cast.x, i32 %cast.y)
+  %cast = bitcast i32 %umax to float
+  ret float %cast
+}
+
+define float @bitcast_umax_missing_rhs(float nofpclass(nan) %x, float %y) {
+; CHECK-LABEL: define float @bitcast_umax_missing_rhs(
+; CHECK-SAME: float nofpclass(nan) [[X:%.*]], float [[Y:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[CAST_X:%.*]] = bitcast float [[X]] to i32
+; CHECK-NEXT:    [[CAST_Y:%.*]] = bitcast float [[Y]] to i32
+; CHECK-NEXT:    [[UMAX:%.*]] = call i32 @llvm.umax.i32(i32 [[CAST_X]], i32 [[CAST_Y]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[UMAX]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %cast.x = bitcast float %x to i32
+  %cast.y = bitcast float %y to i32
+  %umax = call i32 @llvm.umax.i32(i32 %cast.x, i32 %cast.y)
+  %cast = bitcast i32 %umax to float
+  ret float %cast
+}
+
+define float @bitcast_umax_not_nan__not_elementwise_cast(<2 x half> nofpclass(nan) %x, <2 x half> nofpclass(nan) %y) {
+; CHECK-LABEL: define float @bitcast_umax_not_nan__not_elementwise_cast(
+; CHECK-SAME: <2 x half> nofpclass(nan) [[X:%.*]], <2 x half> nofpclass(nan) [[Y:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[CAST_X:%.*]] = bitcast <2 x half> [[X]] to i32
+; CHECK-NEXT:    [[CAST_Y:%.*]] = bitcast <2 x half> [[Y]] to i32
+; CHECK-NEXT:    [[UMAX:%.*]] = call i32 @llvm.umax.i32(i32 [[CAST_X]], i32 [[CAST_Y]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[UMAX]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %cast.x = bitcast <2 x half> %x to i32
+  %cast.y = bitcast <2 x half> %y to i32
+  %umax = call i32 @llvm.umax.i32(i32 %cast.x, i32 %cast.y)
+  %cast = bitcast i32 %umax to float
+  ret float %cast
+}
+
+define bfloat @bitcast_wrong_type_umax_not_nan(half nofpclass(nan) %x, half nofpclass(nan) %y) {
+; CHECK-LABEL: define bfloat @bitcast_wrong_type_umax_not_nan(
+; CHECK-SAME: half nofpclass(nan) [[X:%.*]], half nofpclass(nan) [[Y:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[CAST_X:%.*]] = bitcast half [[X]] to i16
+; CHECK-NEXT:    [[CAST_Y:%.*]] = bitcast half [[Y]] to i16
+; CHECK-NEXT:    [[UMAX:%.*]] = call i16 @llvm.umax.i16(i16 [[CAST_X]], i16 [[CAST_Y]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i16 [[UMAX]] to bfloat
+; CHECK-NEXT:    ret bfloat [[CAST]]
+;
+  %cast.x = bitcast half %x to i16
+  %cast.y = bitcast half %y to i16
+  %umax = call i16 @llvm.umax.i16(i16 %cast.x, i16 %cast.y)
+  %cast = bitcast i16 %umax to bfloat
+  ret bfloat %cast
+}
+
+define <2 x bfloat> @bitcast_wrong_type_umax_vec_not_nan(<2 x half> nofpclass(nan) %x, <2 x half> nofpclass(nan) %y) {
+; CHECK-LABEL: define <2 x bfloat> @bitcast_wrong_type_umax_vec_not_nan(
+; CHECK-SAME: <2 x half> nofpclass(nan) [[X:%.*]], <2 x half> nofpclass(nan) [[Y:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[CAST_X:%.*]] = bitcast <2 x half> [[X]] to <2 x i16>
+; CHECK-NEXT:    [[CAST_Y:%.*]] = bitcast <2 x half> [[Y]] to <2 x i16>
+; CHECK-NEXT:    [[UMAX:%.*]] = call <2 x i16> @llvm.umax.v2i16(<2 x i16> [[CAST_X]], <2 x i16> [[CAST_Y]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast <2 x i16> [[UMAX]] to <2 x bfloat>
+; CHECK-NEXT:    ret <2 x bfloat> [[CAST]]
+;
+  %cast.x = bitcast <2 x half> %x to <2 x i16>
+  %cast.y = bitcast <2 x half> %y to <2 x i16>
+  %umax = call <2 x i16> @llvm.umax.v2i16(<2 x i16> %cast.x, <2 x i16> %cast.y)
+  %cast = bitcast <2 x i16> %umax to <2 x bfloat>
+  ret <2 x bfloat> %cast
+}
+
+define half @bitcast_wrong_type_lhs_umax_not_nan(bfloat nofpclass(nan) %x, half nofpclass(nan) %y) {
+; CHECK-LABEL: define half @bitcast_wrong_type_lhs_umax_not_nan(
+; CHECK-SAME: bfloat nofpclass(nan) [[X:%.*]], half nofpclass(nan) [[Y:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[CAST_X:%.*]] = bitcast bfloat [[X]] to i16
+; CHECK-NEXT:    [[CAST_Y:%.*]] = bitcast half [[Y]] to i16
+; CHECK-NEXT:    [[UMAX:%.*]] = call i16 @llvm.umax.i16(i16 [[CAST_X]], i16 [[CAST_Y]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i16 [[UMAX]] to half
+; CHECK-NEXT:    ret half [[CAST]]
+;
+  %cast.x = bitcast bfloat %x to i16
+  %cast.y = bitcast half %y to i16
+  %umax = call i16 @llvm.umax.i16(i16 %cast.x, i16 %cast.y)
+  %cast = bitcast i16 %umax to half
+  ret half %cast
+}
+
+define half @bitcast_wrong_type_rhs_umax_not_nan(half nofpclass(nan) %x, bfloat nofpclass(nan) %y) {
+; CHECK-LABEL: define half @bitcast_wrong_type_rhs_umax_not_nan(
+; CHECK-SAME: half nofpclass(nan) [[X:%.*]], bfloat nofpclass(nan) [[Y:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[CAST_X:%.*]] = bitcast half [[X]] to i16
+; CHECK-NEXT:    [[CAST_Y:%.*]] = bitcast bfloat [[Y]] to i16
+; CHECK-NEXT:    [[UMAX:%.*]] = call i16 @llvm.umax.i16(i16 [[CAST_X]], i16 [[CAST_Y]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i16 [[UMAX]] to half
+; CHECK-NEXT:    ret half [[CAST]]
+;
+  %cast.x = bitcast half %x to i16
+  %cast.y = bitcast bfloat %y to i16
+  %umax = call i16 @llvm.umax.i16(i16 %cast.x, i16 %cast.y)
+  %cast = bitcast i16 %umax to half
+  ret half %cast
+}
+
+define float @bitcast_umax_v32i1_not_nan(float nofpclass(nan) %x, float nofpclass(nan) %y) {
+; CHECK-LABEL: define float @bitcast_umax_v32i1_not_nan(
+; CHECK-SAME: float nofpclass(nan) [[X:%.*]], float nofpclass(nan) [[Y:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[CAST_X:%.*]] = bitcast float [[X]] to <32 x i1>
+; CHECK-NEXT:    [[CAST_Y:%.*]] = bitcast float [[Y]] to <32 x i1>
+; CHECK-NEXT:    [[UMAX:%.*]] = call <32 x i1> @llvm.umax.v32i1(<32 x i1> [[CAST_X]], <32 x i1> [[CAST_Y]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast <32 x i1> [[UMAX]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %cast.x = bitcast float %x to <32 x i1>
+  %cast.y = bitcast float %y to <32 x i1>
+  %umax = call <32 x i1> @llvm.umax.v32i1(<32 x i1> %cast.x, <32 x i1> %cast.y)
+  %cast = bitcast <32 x i1> %umax to float
+  ret float %cast
+}
+
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; TUNIT: {{.*}}


        


More information about the llvm-commits mailing list