[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