[llvm] [KnownFPClass] Refine known classes for `KnownFPClass::bitcast` (PR #215708)

via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 18 20:22:27 PDT 2026


https://github.com/ZERICO2005 updated https://github.com/llvm/llvm-project/pull/215708

>From 50445c4a78ff38b278cd6cd63ec94ecf8bc37eb2 Mon Sep 17 00:00:00 2001
From: zerico <zerico2005 at gmail.com>
Date: Tue, 11 Aug 2026 16:00:45 -0600
Subject: [PATCH] [KnownFPClass] Refine known classes for bitcast

---
 llvm/lib/Support/KnownFPClass.cpp             |  82 ++--
 ...tcast.ll => nofpclass-bitcast-fp-to-fp.ll} |   5 +-
 .../Attributor/nofpclass-bitcast-int-to-fp.ll | 463 ++++++++++++++++++
 llvm/test/Transforms/Attributor/nofpclass.ll  | 266 ----------
 llvm/unittests/Support/CMakeLists.txt         |   1 +
 llvm/unittests/Support/KnownFPClassTest.cpp   | 118 +++++
 6 files changed, 630 insertions(+), 305 deletions(-)
 rename llvm/test/Transforms/Attributor/{nofpclass-bitcast.ll => nofpclass-bitcast-fp-to-fp.ll} (98%)
 create mode 100644 llvm/test/Transforms/Attributor/nofpclass-bitcast-int-to-fp.ll
 create mode 100644 llvm/unittests/Support/KnownFPClassTest.cpp

diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index ac9406ecd0280..60e323f08de16 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -235,6 +235,10 @@ KnownFPClass KnownFPClass::bitcast(const fltSemantics &FltSemantics,
          "Bitcast operand has incorrect bit width");
   KnownFPClass Known;
 
+  // Conflicting known bits do not describe a concrete value. Return unknown.
+  if (Bits.hasConflict())
+    return Known;
+
   // Transfer information from the sign bit.
   if (Bits.isNonNegative())
     Known.signBitMustBeZero();
@@ -242,41 +246,49 @@ KnownFPClass KnownFPClass::bitcast(const fltSemantics &FltSemantics,
     Known.signBitMustBeOne();
 
   if (APFloat::isIEEELikeFP(FltSemantics)) {
-    // 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
-    //     always be NaN
-    //   - 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(FltSemantics, Bits.One).isNaN())
-      Known.KnownFPClasses = fcNan;
-    else if (!APFloat(FltSemantics, ~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(FltSemantics).bitcastToAPInt());
-    InfKB.Zero.clearSignBit();
-    if (const auto InfResult = KnownBits::eq(Bits, InfKB)) {
-      assert(!InfResult.value());
-      Known.knownNot(fcInf);
-    } else if (Bits == InfKB) {
-      Known.KnownFPClasses = fcInf;
-    }
-
-    // Build KnownBits representing Zero and check if it must be equal or
-    // unequal to this value.
-    auto ZeroKB = KnownBits::makeConstant(
-        APFloat::getZero(FltSemantics).bitcastToAPInt());
-    ZeroKB.Zero.clearSignBit();
-    if (const auto ZeroResult = KnownBits::eq(Bits, ZeroKB)) {
-      assert(!ZeroResult.value());
-      Known.knownNot(fcZero);
-    } else if (Bits == ZeroKB) {
-      Known.KnownFPClasses = fcZero;
-    }
+    const unsigned MantissaBits = FltSemantics.precision - 1;
+    const APInt ExponentMask = APInt::getBitsSet(
+        FltSemantics.sizeInBits, MantissaBits, FltSemantics.sizeInBits - 1);
+    const APInt MantissaMask =
+        APInt::getLowBitsSet(FltSemantics.sizeInBits, MantissaBits);
+
+    const bool ExponentKnownAllZeros =
+        (Bits.Zero & ExponentMask) == ExponentMask;
+    const bool ExponentKnownAllOnes = (Bits.One & ExponentMask) == ExponentMask;
+    const bool ExponentKnownNotAllZeros = !(Bits.One & ExponentMask).isZero();
+    const bool ExponentKnownNotAllOnes = !(Bits.Zero & ExponentMask).isZero();
+
+    const bool MantissaKnownAllZeros =
+        (Bits.Zero & MantissaMask) == MantissaMask;
+    const bool MantissaKnownNotAllZeros = !(Bits.One & MantissaMask).isZero();
+
+    // Zero and subnormal require an exponent with all zero bits.
+    if (ExponentKnownNotAllZeros)
+      Known.knownNot(fcZero | fcSubnormal);
+
+    // Infinity and NaN require an exponent with all one bits.
+    if (ExponentKnownNotAllOnes)
+      Known.knownNot(fcInf | fcNan);
+
+    // Normal values have an exponent that is not all zeros or all ones.
+    if (ExponentKnownAllZeros || ExponentKnownAllOnes)
+      Known.knownNot(fcNormal);
+
+    // Zero and infinity require a mantissa with all zero bits.
+    if (MantissaKnownNotAllZeros)
+      Known.knownNot(fcZero | fcInf);
+
+    // Subnormal and NaN require a non-zero mantissa.
+    if (MantissaKnownAllZeros)
+      Known.knownNot(fcSubnormal | fcNan);
+
+    const bool QuietBitKnownSet = Bits.One[MantissaBits - 1];
+    const bool QuietBitKnownClear = Bits.Zero[MantissaBits - 1];
+
+    if (QuietBitKnownSet)
+      Known.knownNot(fcSNan);
+    else if (QuietBitKnownClear)
+      Known.knownNot(fcQNan);
   }
 
   return Known;
diff --git a/llvm/test/Transforms/Attributor/nofpclass-bitcast.ll b/llvm/test/Transforms/Attributor/nofpclass-bitcast-fp-to-fp.ll
similarity index 98%
rename from llvm/test/Transforms/Attributor/nofpclass-bitcast.ll
rename to llvm/test/Transforms/Attributor/nofpclass-bitcast-fp-to-fp.ll
index 7bf82f39b938c..9d72a1dc664e6 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-bitcast.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-bitcast-fp-to-fp.ll
@@ -1,5 +1,5 @@
 ; 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
+; RUN: opt -S -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal < %s | FileCheck %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(
@@ -224,6 +224,3 @@ define float @bitcast_umax_v32i1_not_nan(float nofpclass(nan) %x, float nofpclas
   %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: {{.*}}
diff --git a/llvm/test/Transforms/Attributor/nofpclass-bitcast-int-to-fp.ll b/llvm/test/Transforms/Attributor/nofpclass-bitcast-int-to-fp.ll
new file mode 100644
index 0000000000000..34f621bb8b4de
--- /dev/null
+++ b/llvm/test/Transforms/Attributor/nofpclass-bitcast-int-to-fp.ll
@@ -0,0 +1,463 @@
+; 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 %s
+
+define float @bitcast_to_float_unknown(i32 %x) {
+; CHECK-LABEL: define float @bitcast_to_float_unknown(
+; CHECK-SAME: i32 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[X]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %cast = bitcast i32 %x to float
+  ret float %cast
+}
+
+define float @bitcast_to_float_positive_one(i32 %x) {
+; CHECK-LABEL: define nofpclass(nan inf zero sub nnorm) float @bitcast_to_float_positive_one(
+; CHECK-SAME: i32 [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[MASKED:%.*]] = and i32 [[X]], 1065353216
+; CHECK-NEXT:    [[BITS:%.*]] = or i32 [[MASKED]], 1065353216
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[BITS]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %masked = and i32 %x, u0x3F800000
+  %bits = or i32 %masked, u0x3F800000
+  %cast = bitcast i32 %bits to float
+  ret float %cast
+}
+
+define float @bitcast_to_float_negative_inf(i32 %x) {
+; CHECK-LABEL: define nofpclass(nan pinf zero sub norm) float @bitcast_to_float_negative_inf(
+; CHECK-SAME: i32 [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[MASKED:%.*]] = and i32 [[X]], -8388608
+; CHECK-NEXT:    [[BITS:%.*]] = or i32 [[MASKED]], -8388608
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[BITS]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %masked = and i32 %x, u0xFF800000
+  %bits = or i32 %masked, u0xFF800000
+  %cast = bitcast i32 %bits to float
+  ret float %cast
+}
+
+; Test that no nofpclass deductions are made for poison values.
+define float @bitcast_to_float_poison(<1 x i32> %x, <1 x i32> %y) {
+; CHECK-LABEL: define float @bitcast_to_float_poison(
+; CHECK-SAME: <1 x i32> [[X:%.*]], <1 x i32> [[Y:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[POISON:%.*]] = shufflevector <1 x i32> [[X]], <1 x i32> [[Y]], <1 x i32> poison
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast <1 x i32> [[POISON]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %poison = shufflevector <1 x i32> %x, <1 x i32> %y, <1 x i32> poison
+  %cast = bitcast <1 x i32> %poison to float
+  ret float %cast
+}
+
+define float @bitcast_to_float_positive_sign(i32 %x) {
+; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float @bitcast_to_float_positive_sign(
+; CHECK-SAME: i32 [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[MASKED:%.*]] = and i32 [[X]], 2147483647
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[MASKED]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %masked = and i32 %x, u0x7FFFFFFF
+  %cast = bitcast i32 %masked to float
+  ret float %cast
+}
+
+define float @bitcast_to_float_negative_sign(i32 %x) {
+; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) float @bitcast_to_float_negative_sign(
+; CHECK-SAME: i32 [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[MASKED:%.*]] = or i32 [[X]], -2147483648
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[MASKED]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %masked = or i32 %x, u0x80000000
+  %cast = bitcast i32 %masked to float
+  ret float %cast
+}
+
+define float @bitcast_to_float_exponent_all_zeros(i32 %x) {
+; CHECK-LABEL: define nofpclass(nan inf norm) float @bitcast_to_float_exponent_all_zeros(
+; CHECK-SAME: i32 [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[MASKED:%.*]] = and i32 [[X]], -2139095041
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[MASKED]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %masked = and i32 %x, u0x807FFFFF
+  %cast = bitcast i32 %masked to float
+  ret float %cast
+}
+
+define float @bitcast_to_float_exponent_all_ones(i32 %x) {
+; CHECK-LABEL: define nofpclass(zero sub norm) float @bitcast_to_float_exponent_all_ones(
+; CHECK-SAME: i32 [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[MASKED:%.*]] = or i32 [[X]], 2139095040
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[MASKED]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %masked = or i32 %x, u0x7F800000
+  %cast = bitcast i32 %masked to float
+  ret float %cast
+}
+
+define float @bitcast_to_float_exponent_nonzero_not_all_ones(i32 %x) {
+; CHECK-LABEL: define nofpclass(nan inf zero sub) float @bitcast_to_float_exponent_nonzero_not_all_ones(
+; CHECK-SAME: i32 [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[SET:%.*]] = or i32 [[X]], 8388608
+; CHECK-NEXT:    [[MASKED:%.*]] = and i32 [[SET]], -16777217
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[MASKED]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %set = or i32 %x, u0x00800000
+  %masked = and i32 %set, u0xFEFFFFFF
+  %cast = bitcast i32 %masked to float
+  ret float %cast
+}
+
+define float @bitcast_to_float_exponent_nonzero(i32 %x) {
+; CHECK-LABEL: define nofpclass(zero sub) float @bitcast_to_float_exponent_nonzero(
+; CHECK-SAME: i32 [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[MASKED:%.*]] = or i32 [[X]], 8388608
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[MASKED]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %masked = or i32 %x, u0x00800000
+  %cast = bitcast i32 %masked to float
+  ret float %cast
+}
+
+define float @bitcast_to_float_exponent_not_all_ones(i32 %x) {
+; CHECK-LABEL: define nofpclass(nan inf) float @bitcast_to_float_exponent_not_all_ones(
+; CHECK-SAME: i32 [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[MASKED:%.*]] = and i32 [[X]], -8388609
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[MASKED]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %masked = and i32 %x, u0xFF7FFFFF
+  %cast = bitcast i32 %masked to float
+  ret float %cast
+}
+
+define float @bitcast_to_float_mantissa_all_zeros(i32 %x) {
+; CHECK-LABEL: define nofpclass(nan sub) float @bitcast_to_float_mantissa_all_zeros(
+; CHECK-SAME: i32 [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[MASKED:%.*]] = and i32 [[X]], -8388608
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[MASKED]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %masked = and i32 %x, u0xFF800000
+  %cast = bitcast i32 %masked to float
+  ret float %cast
+}
+
+define float @bitcast_to_float_mantissa_low_bit_zero(i32 %x) {
+; CHECK-LABEL: define float @bitcast_to_float_mantissa_low_bit_zero(
+; CHECK-SAME: i32 [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[MASKED:%.*]] = and i32 [[X]], -2
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[MASKED]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %masked = and i32 %x, u0xFFFFFFFE
+  %cast = bitcast i32 %masked to float
+  ret float %cast
+}
+
+define float @bitcast_to_float_mantissa_nonzero(i32 %x) {
+; CHECK-LABEL: define nofpclass(inf zero) float @bitcast_to_float_mantissa_nonzero(
+; CHECK-SAME: i32 [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[MASKED:%.*]] = or i32 [[X]], 1
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[MASKED]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %masked = or i32 %x, 1
+  %cast = bitcast i32 %masked to float
+  ret float %cast
+}
+
+define float @bitcast_to_float_quiet_bit_set(i32 %x) {
+; CHECK-LABEL: define nofpclass(snan inf zero) float @bitcast_to_float_quiet_bit_set(
+; CHECK-SAME: i32 [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[MASKED:%.*]] = or i32 [[X]], 4194304
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[MASKED]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %masked = or i32 %x, u0x00400000
+  %cast = bitcast i32 %masked to float
+  ret float %cast
+}
+
+define float @bitcast_to_float_quiet_bit_clear(i32 %x) {
+; CHECK-LABEL: define nofpclass(qnan) float @bitcast_to_float_quiet_bit_clear(
+; CHECK-SAME: i32 [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[MASKED:%.*]] = and i32 [[X]], -4194305
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[MASKED]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %masked = and i32 %x, u0xFFBFFFFF
+  %cast = bitcast i32 %masked to float
+  ret float %cast
+}
+
+define float @bitcast_to_float_inf_or_snan(i32 %x) {
+; CHECK-LABEL: define nofpclass(qnan zero sub norm) float @bitcast_to_float_inf_or_snan(
+; CHECK-SAME: i32 [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[QUIET_BIT_ZERO:%.*]] = and i32 [[X]], -4194305
+; CHECK-NEXT:    [[MASKED:%.*]] = or i32 [[QUIET_BIT_ZERO]], 2139095040
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[MASKED]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %quiet_bit_zero = and i32 %x, u0xFFBFFFFF
+  %masked = or i32 %quiet_bit_zero, u0x7F800000
+  %cast = bitcast i32 %masked to float
+  ret float %cast
+}
+
+define float @bitcast_to_float_snan(i32 %x) {
+; CHECK-LABEL: define nofpclass(qnan inf zero sub norm) float @bitcast_to_float_snan(
+; CHECK-SAME: i32 [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[QUIET_BIT_ZERO:%.*]] = and i32 [[X]], -4194305
+; CHECK-NEXT:    [[EXPONENT_ONE:%.*]] = or i32 [[QUIET_BIT_ZERO]], 2139095040
+; CHECK-NEXT:    [[MASKED:%.*]] = or i32 [[EXPONENT_ONE]], 1
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[MASKED]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %quiet_bit_zero = and i32 %x, u0xFFBFFFFF
+  %exponent_one = or i32 %quiet_bit_zero, u0x7F800000
+  %masked = or i32 %exponent_one, 1
+  %cast = bitcast i32 %masked to float
+  ret float %cast
+}
+
+define float @bitcast_to_float_lshr_1(i32 %arg) {
+; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float @bitcast_to_float_lshr_1(
+; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i32 [[ARG]], 1
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[SHR]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %shr = lshr i32 %arg, 1
+  %cast = bitcast i32 %shr to float
+  ret float %cast
+}
+
+define float @bitcast_to_float_lshr_2(i32 %arg) {
+; CHECK-LABEL: define nofpclass(nan inf nzero nsub nnorm) float @bitcast_to_float_lshr_2(
+; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i32 [[ARG]], 2
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[SHR]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %shr = lshr i32 %arg, 2
+  %cast = bitcast i32 %shr to float
+  ret float %cast
+}
+
+define float @bitcast_to_float_nan(i32 %arg) {
+; CHECK-LABEL: define nofpclass(inf zero sub norm) float @bitcast_to_float_nan(
+; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[ARG]], 2139095041
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[OR]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %or = or i32 %arg, 2139095041
+  %cast = bitcast i32 %or to float
+  ret float %cast
+}
+
+define float @bitcast_to_float_zero(i32 %arg) {
+; CHECK-LABEL: define nofpclass(nan inf sub norm) float @bitcast_to_float_zero(
+; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[SHL:%.*]] = shl i32 [[ARG]], 31
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[SHL]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %shl = shl i32 %arg, 31
+  %cast = bitcast i32 %shl to float
+  ret float %cast
+}
+
+define float @bitcast_to_float_nzero(i32 %arg) {
+; CHECK-LABEL: define nofpclass(zero sub) float @bitcast_to_float_nzero(
+; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[ARG]], 134217728
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[OR]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %or = or i32 %arg, 134217728
+  %cast = bitcast i32 %or to float
+  ret float %cast
+}
+
+define float @bitcast_to_float_inf(i32 %arg) {
+; CHECK-LABEL: define nofpclass(nan zero sub norm) float @bitcast_to_float_inf(
+; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[SHR:%.*]] = shl i32 [[ARG]], 31
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[SHR]], 2139095040
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[OR]] to float
+; CHECK-NEXT:    ret float [[CAST]]
+;
+  %shr = shl i32 %arg, 31
+  %or = or i32 %shr, 2139095040
+  %cast = bitcast i32 %or to float
+  ret float %cast
+}
+
+define double @bitcast_to_double_lshr_1(i64 %arg) {
+; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) double @bitcast_to_double_lshr_1(
+; CHECK-SAME: i64 [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i64 [[ARG]], 1
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i64 [[SHR]] to double
+; CHECK-NEXT:    ret double [[CAST]]
+;
+  %shr = lshr i64 %arg, 1
+  %cast = bitcast i64 %shr to double
+  ret double %cast
+}
+
+define double @bitcast_to_double_lshr_2(i64 %arg) {
+; CHECK-LABEL: define nofpclass(nan inf nzero nsub nnorm) double @bitcast_to_double_lshr_2(
+; CHECK-SAME: i64 [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i64 [[ARG]], 2
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i64 [[SHR]] to double
+; CHECK-NEXT:    ret double [[CAST]]
+;
+  %shr = lshr i64 %arg, 2
+  %cast = bitcast i64 %shr to double
+  ret double %cast
+}
+
+define double @bitcast_to_double_sign_1(i64 %arg) {
+; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) double @bitcast_to_double_sign_1(
+; CHECK-SAME: i64 [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[OR:%.*]] = or i64 [[ARG]], -9223372036854775808
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i64 [[OR]] to double
+; CHECK-NEXT:    ret double [[CAST]]
+;
+  %or = or i64 %arg, -9223372036854775808
+  %cast = bitcast i64 %or to double
+  ret double %cast
+}
+
+define double @bitcast_to_double_nan(i64 %arg) {
+; CHECK-LABEL: define nofpclass(inf zero sub norm) double @bitcast_to_double_nan(
+; CHECK-SAME: i64 [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[OR:%.*]] = or i64 [[ARG]], -4503599627370495
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i64 [[OR]] to double
+; CHECK-NEXT:    ret double [[CAST]]
+;
+  %or = or i64 %arg, -4503599627370495
+  %cast = bitcast i64 %or to double
+  ret double %cast
+}
+
+define double @bitcast_to_double_zero(i64 %arg) {
+; CHECK-LABEL: define nofpclass(nan inf sub norm) double @bitcast_to_double_zero(
+; CHECK-SAME: i64 [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[SHL:%.*]] = shl i64 [[ARG]], 63
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i64 [[SHL]] to double
+; CHECK-NEXT:    ret double [[CAST]]
+;
+  %shl = shl i64 %arg, 63
+  %cast = bitcast i64 %shl to double
+  ret double %cast
+}
+
+define double @bitcast_to_double_nzero(i64 %arg) {
+; CHECK-LABEL: define nofpclass(zero sub) double @bitcast_to_double_nzero(
+; CHECK-SAME: i64 [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[OR:%.*]] = or i64 [[ARG]], 1152921504606846976
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i64 [[OR]] to double
+; CHECK-NEXT:    ret double [[CAST]]
+;
+  %or = or i64 %arg, 1152921504606846976
+  %cast = bitcast i64 %or to double
+  ret double %cast
+}
+
+define double @bitcast_to_double_inf(i64 %arg) {
+; CHECK-LABEL: define nofpclass(nan zero sub norm) double @bitcast_to_double_inf(
+; CHECK-SAME: i64 [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[SHR:%.*]] = shl i64 [[ARG]], 63
+; CHECK-NEXT:    [[OR:%.*]] = or i64 [[SHR]], 9218868437227405312
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast i64 [[OR]] to double
+; CHECK-NEXT:    ret double [[CAST]]
+;
+  %shr = shl i64 %arg, 63
+  %or = or i64 %shr, 9218868437227405312
+  %cast = bitcast i64 %or to double
+  ret double %cast
+}
+
+; vectors
+
+define <2 x float> @bitcast_to_float_vect_sign_0(<2 x i32> %arg) {
+; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) <2 x float> @bitcast_to_float_vect_sign_0(
+; CHECK-SAME: <2 x i32> [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[SHR:%.*]] = lshr <2 x i32> [[ARG]], <i32 1, i32 2>
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast <2 x i32> [[SHR]] to <2 x float>
+; CHECK-NEXT:    ret <2 x float> [[CAST]]
+;
+  %shr = lshr <2 x i32> %arg, <i32 1, i32 2>
+  %cast = bitcast <2 x i32> %shr to <2 x float>
+  ret <2 x float> %cast
+}
+
+define <2 x float> @bitcast_to_float_vect_nnan(<2 x i32> %arg) {
+; CHECK-LABEL: define nofpclass(nan inf nzero nsub nnorm) <2 x float> @bitcast_to_float_vect_nnan(
+; CHECK-SAME: <2 x i32> [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[SHR:%.*]] = lshr <2 x i32> [[ARG]], splat (i32 4)
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast <2 x i32> [[SHR]] to <2 x float>
+; CHECK-NEXT:    ret <2 x float> [[CAST]]
+;
+  %shr = lshr <2 x i32> %arg, <i32 4, i32 4>
+  %cast = bitcast <2 x i32> %shr to <2 x float>
+  ret <2 x float> %cast
+}
+
+define <2 x float> @bitcast_to_float_vect_sign_1(<2 x i32> %arg) {
+; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) <2 x float> @bitcast_to_float_vect_sign_1(
+; CHECK-SAME: <2 x i32> [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[OR:%.*]] = or <2 x i32> [[ARG]], splat (i32 -2147483648)
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast <2 x i32> [[OR]] to <2 x float>
+; CHECK-NEXT:    ret <2 x float> [[CAST]]
+;
+  %or = or <2 x i32> %arg, <i32 -2147483648, i32 -2147483648>
+  %cast = bitcast <2 x i32> %or to <2 x float>
+  ret <2 x float> %cast
+}
+
+define <2 x float> @bitcast_to_float_vect_nan(<2 x i32> %arg) {
+; CHECK-LABEL: define nofpclass(inf zero sub norm) <2 x float> @bitcast_to_float_vect_nan(
+; CHECK-SAME: <2 x i32> [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[OR:%.*]] = or <2 x i32> [[ARG]], splat (i32 2139095041)
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast <2 x i32> [[OR]] to <2 x float>
+; CHECK-NEXT:    ret <2 x float> [[CAST]]
+;
+  %or = or <2 x i32> %arg, <i32 2139095041, i32 2139095041>
+  %cast = bitcast <2 x i32> %or to <2 x float>
+  ret <2 x float> %cast
+}
+
+define <2 x float> @bitcast_to_float_vect_conservative_1(<2 x i32> %arg) {
+; CHECK-LABEL: define <2 x float> @bitcast_to_float_vect_conservative_1(
+; CHECK-SAME: <2 x i32> [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[OR:%.*]] = or <2 x i32> [[ARG]], <i32 -2147483648, i32 0>
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast <2 x i32> [[OR]] to <2 x float>
+; CHECK-NEXT:    ret <2 x float> [[CAST]]
+;
+  %or = or <2 x i32> %arg, <i32 -2147483648, i32 0>
+  %cast = bitcast <2 x i32> %or to <2 x float>
+  ret <2 x float> %cast
+}
+
+define <2 x float> @bitcast_to_float_vect_conservative_2(<2 x i32> %arg) {
+; CHECK-LABEL: define <2 x float> @bitcast_to_float_vect_conservative_2(
+; CHECK-SAME: <2 x i32> [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[OR:%.*]] = or <2 x i32> [[ARG]], <i32 0, i32 2139095041>
+; CHECK-NEXT:    [[CAST:%.*]] = bitcast <2 x i32> [[OR]] to <2 x float>
+; CHECK-NEXT:    ret <2 x float> [[CAST]]
+;
+  %or = or <2 x i32> %arg, <i32 0, i32 2139095041>
+  %cast = bitcast <2 x i32> %or to <2 x float>
+  ret <2 x float> %cast
+}
diff --git a/llvm/test/Transforms/Attributor/nofpclass.ll b/llvm/test/Transforms/Attributor/nofpclass.ll
index 719896870a479..e41cf9fb46e2f 100644
--- a/llvm/test/Transforms/Attributor/nofpclass.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass.ll
@@ -2741,272 +2741,6 @@ entry:
   ret double %abs
 }
 
-define float @bitcast_to_float_sign_0(i32 %arg) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float @bitcast_to_float_sign_0
-; CHECK-SAME: (i32 [[ARG:%.*]]) #[[ATTR3]] {
-; CHECK-NEXT:    [[SHR:%.*]] = lshr i32 [[ARG]], 1
-; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[SHR]] to float
-; CHECK-NEXT:    ret float [[CAST]]
-;
-  %shr = lshr i32 %arg, 1
-  %cast = bitcast i32 %shr to float
-  ret float %cast
-}
-
-define float @bitcast_to_float_nnan(i32 %arg) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; CHECK-LABEL: define nofpclass(nan inf nzero nsub nnorm) float @bitcast_to_float_nnan
-; CHECK-SAME: (i32 [[ARG:%.*]]) #[[ATTR3]] {
-; CHECK-NEXT:    [[SHR:%.*]] = lshr i32 [[ARG]], 2
-; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[SHR]] to float
-; CHECK-NEXT:    ret float [[CAST]]
-;
-  %shr = lshr i32 %arg, 2
-  %cast = bitcast i32 %shr to float
-  ret float %cast
-}
-
-define float @bitcast_to_float_sign_1(i32 %arg) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) float @bitcast_to_float_sign_1
-; CHECK-SAME: (i32 [[ARG:%.*]]) #[[ATTR3]] {
-; CHECK-NEXT:    [[OR:%.*]] = or i32 [[ARG]], -2147483648
-; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[OR]] to float
-; CHECK-NEXT:    ret float [[CAST]]
-;
-  %or = or i32 %arg, -2147483648
-  %cast = bitcast i32 %or to float
-  ret float %cast
-}
-
-define float @bitcast_to_float_nan(i32 %arg) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; CHECK-LABEL: define nofpclass(inf zero sub norm) float @bitcast_to_float_nan
-; CHECK-SAME: (i32 [[ARG:%.*]]) #[[ATTR3]] {
-; CHECK-NEXT:    [[OR:%.*]] = or i32 [[ARG]], 2139095041
-; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[OR]] to float
-; CHECK-NEXT:    ret float [[CAST]]
-;
-  %or = or i32 %arg, 2139095041
-  %cast = bitcast i32 %or to float
-  ret float %cast
-}
-
-define float @bitcast_to_float_zero(i32 %arg) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; CHECK-LABEL: define nofpclass(nan inf sub norm) float @bitcast_to_float_zero
-; CHECK-SAME: (i32 [[ARG:%.*]]) #[[ATTR3]] {
-; CHECK-NEXT:    [[SHL:%.*]] = shl i32 [[ARG]], 31
-; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[SHL]] to float
-; CHECK-NEXT:    ret float [[CAST]]
-;
-  %shl = shl i32 %arg, 31
-  %cast = bitcast i32 %shl to float
-  ret float %cast
-}
-
-define float @bitcast_to_float_nzero(i32 %arg) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; CHECK-LABEL: define nofpclass(zero) float @bitcast_to_float_nzero
-; CHECK-SAME: (i32 [[ARG:%.*]]) #[[ATTR3]] {
-; CHECK-NEXT:    [[OR:%.*]] = or i32 [[ARG]], 134217728
-; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[OR]] to float
-; CHECK-NEXT:    ret float [[CAST]]
-;
-  %or = or i32 %arg, 134217728
-  %cast = bitcast i32 %or to float
-  ret float %cast
-}
-
-define float @bitcast_to_float_inf(i32 %arg) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; CHECK-LABEL: define nofpclass(nan zero sub norm) float @bitcast_to_float_inf
-; CHECK-SAME: (i32 [[ARG:%.*]]) #[[ATTR3]] {
-; CHECK-NEXT:    [[SHR:%.*]] = shl i32 [[ARG]], 31
-; CHECK-NEXT:    [[OR:%.*]] = or i32 [[SHR]], 2139095040
-; CHECK-NEXT:    [[CAST:%.*]] = bitcast i32 [[OR]] to float
-; CHECK-NEXT:    ret float [[CAST]]
-;
-  %shr = shl i32 %arg, 31
-  %or = or i32 %shr, 2139095040
-  %cast = bitcast i32 %or to float
-  ret float %cast
-}
-
-define double @bitcast_to_double_sign_0(i64 %arg) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) double @bitcast_to_double_sign_0
-; CHECK-SAME: (i64 [[ARG:%.*]]) #[[ATTR3]] {
-; CHECK-NEXT:    [[SHR:%.*]] = lshr i64 [[ARG]], 1
-; CHECK-NEXT:    [[CAST:%.*]] = bitcast i64 [[SHR]] to double
-; CHECK-NEXT:    ret double [[CAST]]
-;
-  %shr = lshr i64 %arg, 1
-  %cast = bitcast i64 %shr to double
-  ret double %cast
-}
-
-define double @bitcast_to_double_nnan(i64 %arg) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; CHECK-LABEL: define nofpclass(nan inf nzero nsub nnorm) double @bitcast_to_double_nnan
-; CHECK-SAME: (i64 [[ARG:%.*]]) #[[ATTR3]] {
-; CHECK-NEXT:    [[SHR:%.*]] = lshr i64 [[ARG]], 2
-; CHECK-NEXT:    [[CAST:%.*]] = bitcast i64 [[SHR]] to double
-; CHECK-NEXT:    ret double [[CAST]]
-;
-  %shr = lshr i64 %arg, 2
-  %cast = bitcast i64 %shr to double
-  ret double %cast
-}
-
-define double @bitcast_to_double_sign_1(i64 %arg) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) double @bitcast_to_double_sign_1
-; CHECK-SAME: (i64 [[ARG:%.*]]) #[[ATTR3]] {
-; CHECK-NEXT:    [[OR:%.*]] = or i64 [[ARG]], -9223372036854775808
-; CHECK-NEXT:    [[CAST:%.*]] = bitcast i64 [[OR]] to double
-; CHECK-NEXT:    ret double [[CAST]]
-;
-  %or = or i64 %arg, -9223372036854775808
-  %cast = bitcast i64 %or to double
-  ret double %cast
-}
-
-define double @bitcast_to_double_nan(i64 %arg) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; CHECK-LABEL: define nofpclass(inf zero sub norm) double @bitcast_to_double_nan
-; CHECK-SAME: (i64 [[ARG:%.*]]) #[[ATTR3]] {
-; CHECK-NEXT:    [[OR:%.*]] = or i64 [[ARG]], -4503599627370495
-; CHECK-NEXT:    [[CAST:%.*]] = bitcast i64 [[OR]] to double
-; CHECK-NEXT:    ret double [[CAST]]
-;
-  %or = or i64 %arg, -4503599627370495
-  %cast = bitcast i64 %or to double
-  ret double %cast
-}
-
-
-define double @bitcast_to_double_zero(i64 %arg) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; CHECK-LABEL: define nofpclass(nan inf sub norm) double @bitcast_to_double_zero
-; CHECK-SAME: (i64 [[ARG:%.*]]) #[[ATTR3]] {
-; CHECK-NEXT:    [[SHL:%.*]] = shl i64 [[ARG]], 63
-; CHECK-NEXT:    [[CAST:%.*]] = bitcast i64 [[SHL]] to double
-; CHECK-NEXT:    ret double [[CAST]]
-;
-  %shl = shl i64 %arg, 63
-  %cast = bitcast i64 %shl to double
-  ret double %cast
-}
-
-define double @bitcast_to_double_nzero(i64 %arg) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; CHECK-LABEL: define nofpclass(zero) double @bitcast_to_double_nzero
-; CHECK-SAME: (i64 [[ARG:%.*]]) #[[ATTR3]] {
-; CHECK-NEXT:    [[OR:%.*]] = or i64 [[ARG]], 1152921504606846976
-; CHECK-NEXT:    [[CAST:%.*]] = bitcast i64 [[OR]] to double
-; CHECK-NEXT:    ret double [[CAST]]
-;
-  %or = or i64 %arg, 1152921504606846976
-  %cast = bitcast i64 %or to double
-  ret double %cast
-}
-
-define double @bitcast_to_double_inf(i64 %arg) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; CHECK-LABEL: define nofpclass(nan zero sub norm) double @bitcast_to_double_inf
-; CHECK-SAME: (i64 [[ARG:%.*]]) #[[ATTR3]] {
-; CHECK-NEXT:    [[SHR:%.*]] = shl i64 [[ARG]], 63
-; CHECK-NEXT:    [[OR:%.*]] = or i64 [[SHR]], 9218868437227405312
-; CHECK-NEXT:    [[CAST:%.*]] = bitcast i64 [[OR]] to double
-; CHECK-NEXT:    ret double [[CAST]]
-;
-  %shr = shl i64 %arg, 63
-  %or = or i64 %shr, 9218868437227405312
-  %cast = bitcast i64 %or to double
-  ret double %cast
-}
-
-
-define <2 x float> @bitcast_to_float_vect_sign_0(<2 x i32> %arg) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) <2 x float> @bitcast_to_float_vect_sign_0
-; CHECK-SAME: (<2 x i32> [[ARG:%.*]]) #[[ATTR3]] {
-; CHECK-NEXT:    [[SHR:%.*]] = lshr <2 x i32> [[ARG]], <i32 1, i32 2>
-; CHECK-NEXT:    [[CAST:%.*]] = bitcast <2 x i32> [[SHR]] to <2 x float>
-; CHECK-NEXT:    ret <2 x float> [[CAST]]
-;
-  %shr = lshr <2 x i32> %arg, <i32 1, i32 2>
-  %cast = bitcast <2 x i32> %shr to <2 x float>
-  ret <2 x float> %cast
-}
-
-define <2 x float> @bitcast_to_float_vect_nnan(<2 x i32> %arg) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; CHECK-LABEL: define nofpclass(nan inf nzero nsub nnorm) <2 x float> @bitcast_to_float_vect_nnan
-; CHECK-SAME: (<2 x i32> [[ARG:%.*]]) #[[ATTR3]] {
-; CHECK-NEXT:    [[SHR:%.*]] = lshr <2 x i32> [[ARG]], splat (i32 4)
-; CHECK-NEXT:    [[CAST:%.*]] = bitcast <2 x i32> [[SHR]] to <2 x float>
-; CHECK-NEXT:    ret <2 x float> [[CAST]]
-;
-  %shr = lshr <2 x i32> %arg, <i32 4, i32 4>
-  %cast = bitcast <2 x i32> %shr to <2 x float>
-  ret <2 x float> %cast
-}
-
-define <2 x float> @bitcast_to_float_vect_sign_1(<2 x i32> %arg) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) <2 x float> @bitcast_to_float_vect_sign_1
-; CHECK-SAME: (<2 x i32> [[ARG:%.*]]) #[[ATTR3]] {
-; CHECK-NEXT:    [[OR:%.*]] = or <2 x i32> [[ARG]], splat (i32 -2147483648)
-; CHECK-NEXT:    [[CAST:%.*]] = bitcast <2 x i32> [[OR]] to <2 x float>
-; CHECK-NEXT:    ret <2 x float> [[CAST]]
-;
-  %or = or <2 x i32> %arg, <i32 -2147483648, i32 -2147483648>
-  %cast = bitcast <2 x i32> %or to <2 x float>
-  ret <2 x float> %cast
-}
-
-define <2 x float> @bitcast_to_float_vect_nan(<2 x i32> %arg) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; CHECK-LABEL: define nofpclass(inf zero sub norm) <2 x float> @bitcast_to_float_vect_nan
-; CHECK-SAME: (<2 x i32> [[ARG:%.*]]) #[[ATTR3]] {
-; CHECK-NEXT:    [[OR:%.*]] = or <2 x i32> [[ARG]], splat (i32 2139095041)
-; CHECK-NEXT:    [[CAST:%.*]] = bitcast <2 x i32> [[OR]] to <2 x float>
-; CHECK-NEXT:    ret <2 x float> [[CAST]]
-;
-  %or = or <2 x i32> %arg, <i32 2139095041, i32 2139095041>
-  %cast = bitcast <2 x i32> %or to <2 x float>
-  ret <2 x float> %cast
-}
-
-define <2 x float> @bitcast_to_float_vect_conservative_1(<2 x i32> %arg) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; CHECK-LABEL: define <2 x float> @bitcast_to_float_vect_conservative_1
-; CHECK-SAME: (<2 x i32> [[ARG:%.*]]) #[[ATTR3]] {
-; CHECK-NEXT:    [[OR:%.*]] = or <2 x i32> [[ARG]], <i32 -2147483648, i32 0>
-; CHECK-NEXT:    [[CAST:%.*]] = bitcast <2 x i32> [[OR]] to <2 x float>
-; CHECK-NEXT:    ret <2 x float> [[CAST]]
-;
-  %or = or <2 x i32> %arg, <i32 -2147483648, i32 0>
-  %cast = bitcast <2 x i32> %or to <2 x float>
-  ret <2 x float> %cast
-}
-
-define <2 x float> @bitcast_to_float_vect_conservative_2(<2 x i32> %arg) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; CHECK-LABEL: define <2 x float> @bitcast_to_float_vect_conservative_2
-; CHECK-SAME: (<2 x i32> [[ARG:%.*]]) #[[ATTR3]] {
-; CHECK-NEXT:    [[OR:%.*]] = or <2 x i32> [[ARG]], <i32 0, i32 2139095041>
-; CHECK-NEXT:    [[CAST:%.*]] = bitcast <2 x i32> [[OR]] to <2 x float>
-; CHECK-NEXT:    ret <2 x float> [[CAST]]
-;
-  %or = or <2 x i32> %arg, <i32 0, i32 2139095041>
-  %cast = bitcast <2 x i32> %or to <2 x float>
-  ret <2 x float> %cast
-}
-
 define float @fadd_double(float noundef %arg) {
 ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define noundef float @fadd_double
diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt
index e808e668a87ad..cc88a6c5670ca 100644
--- a/llvm/unittests/Support/CMakeLists.txt
+++ b/llvm/unittests/Support/CMakeLists.txt
@@ -57,6 +57,7 @@ add_llvm_unittest(SupportTests
   JobserverTest.cpp
   JSONTest.cpp
   KnownBitsTest.cpp
+  KnownFPClassTest.cpp
   LEB128Test.cpp
   LineIteratorTest.cpp
   LockFileManagerTest.cpp
diff --git a/llvm/unittests/Support/KnownFPClassTest.cpp b/llvm/unittests/Support/KnownFPClassTest.cpp
new file mode 100644
index 0000000000000..2322e41f9af32
--- /dev/null
+++ b/llvm/unittests/Support/KnownFPClassTest.cpp
@@ -0,0 +1,118 @@
+//===- KnownFPClassTest.cpp - KnownFPClass tests --------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/KnownFPClass.h"
+#include "llvm/ADT/APFloat.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/FloatingPointMode.h"
+#include "llvm/Support/KnownBits.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace {
+
+static void expectConstant(const char *SemanticsName, const char *ValueName,
+                           const fltSemantics &Semantics, APInt ValueBits,
+                           FPClassTest PositiveClass, bool Negative) {
+  if (Negative)
+    ValueBits.setBit(Semantics.sizeInBits - 1);
+
+  SCOPED_TRACE(testing::Message()
+               << SemanticsName << ' ' << (Negative ? "negative " : "positive ")
+               << ValueName);
+  KnownFPClass Known =
+      KnownFPClass::bitcast(Semantics, KnownBits::makeConstant(ValueBits));
+  FPClassTest ExpectedClass =
+      Negative ? llvm::fneg(PositiveClass) : PositiveClass;
+  EXPECT_EQ(ExpectedClass, Known.KnownFPClasses);
+  EXPECT_EQ(Negative, Known.SignBit);
+}
+
+TEST(KnownFPClassTest, BitcastExhaustiveIEEEHalf) {
+  const fltSemantics &Semantics = APFloat::IEEEhalf();
+
+  for (uint64_t RawBits = 0; RawBits != (1u << 16); ++RawBits) {
+    APInt ValueBits(16, RawBits);
+    KnownFPClass Known =
+        KnownFPClass::bitcast(Semantics, KnownBits::makeConstant(ValueBits));
+    KnownFPClass Expected(APFloat(Semantics, ValueBits));
+
+    ASSERT_EQ(Expected.KnownFPClasses, Known.KnownFPClasses) << RawBits;
+    ASSERT_EQ(Expected.SignBit, Known.SignBit) << RawBits;
+  }
+}
+
+TEST(KnownFPClassTest, BitcastConflict) {
+  const fltSemantics &Semantics = APFloat::IEEEsingle();
+  KnownBits Bits(Semantics.sizeInBits);
+  Bits.setAllConflict();
+
+  ASSERT_TRUE(Bits.hasConflict());
+  KnownFPClass Known = KnownFPClass::bitcast(Semantics, Bits);
+  EXPECT_EQ(fcAllFlags, Known.KnownFPClasses);
+  EXPECT_EQ(std::nullopt, Known.SignBit);
+}
+
+TEST(KnownFPClassTest, BitcastConstant) {
+  struct SemanticsCase {
+    const char *Name;
+    const fltSemantics *Semantics;
+  };
+
+  for (const SemanticsCase &TestCase :
+       {SemanticsCase{"ieee_binary16", &APFloat::IEEEhalf()},
+        SemanticsCase{"bfloat16", &APFloat::BFloat()},
+        SemanticsCase{"ieee_binary32", &APFloat::IEEEsingle()},
+        SemanticsCase{"ieee_binary64", &APFloat::IEEEdouble()},
+        SemanticsCase{"ieee_binary128", &APFloat::IEEEquad()}}) {
+    const fltSemantics &Semantics = *TestCase.Semantics;
+    const unsigned BitWidth = Semantics.sizeInBits;
+    const unsigned MantissaBits = Semantics.precision - 1;
+    const APInt ExponentMask =
+        APInt::getBitsSet(BitWidth, MantissaBits, BitWidth - 1);
+    const APInt MantissaMask = APInt::getLowBitsSet(BitWidth, MantissaBits);
+    const APInt QuietBit = APInt::getOneBitSet(BitWidth, MantissaBits - 1);
+
+    for (bool Negative : {false, true}) {
+      expectConstant(TestCase.Name, "0.0", Semantics, APInt::getZero(BitWidth),
+                     fcPosZero, Negative);
+      expectConstant(TestCase.Name, "min_subnormal", Semantics,
+                     APInt(BitWidth, 1), fcPosSubnormal, Negative);
+      expectConstant(TestCase.Name, "max_subnormal", Semantics, MantissaMask,
+                     fcPosSubnormal, Negative);
+      expectConstant(TestCase.Name, "min_normal", Semantics,
+                     APInt::getOneBitSet(BitWidth, MantissaBits), fcPosNormal,
+                     Negative);
+      expectConstant(TestCase.Name, "1.0", Semantics,
+                     APFloat::getOne(Semantics).bitcastToAPInt(), fcPosNormal,
+                     Negative);
+      expectConstant(TestCase.Name, "max_normal", Semantics,
+                     APFloat::getLargest(Semantics).bitcastToAPInt(),
+                     fcPosNormal, Negative);
+      expectConstant(TestCase.Name, "inf", Semantics, ExponentMask, fcPosInf,
+                     Negative);
+
+      // An sNaN has a clear quiet bit and a non-zero payload.
+      expectConstant(TestCase.Name, "snan_mostly_zero", Semantics,
+                     ExponentMask | APInt(BitWidth, 1), fcSNan, Negative);
+
+      // A qNaN has a set quiet bit. The remaining payload bits may be zero.
+      expectConstant(TestCase.Name, "qnan_mostly_zero", Semantics,
+                     ExponentMask | QuietBit, fcQNan, Negative);
+
+      expectConstant(TestCase.Name, "snan_mostly_one", Semantics,
+                     ExponentMask | (MantissaMask & ~QuietBit), fcSNan,
+                     Negative);
+      expectConstant(TestCase.Name, "qnan_mostly_one", Semantics,
+                     ExponentMask | MantissaMask, fcQNan, Negative);
+    }
+  }
+}
+
+} // end anonymous namespace



More information about the llvm-commits mailing list