https://github.com/hulxv updated https://github.com/llvm/llvm-project/pull/215729
>From e5578acf2972df97f7919b0e9e198a55bc45d4a0 Mon Sep 17 00:00:00 2001 From: hulxv <[email protected]> Date: Wed, 12 Aug 2026 03:12:47 +0300 Subject: [PATCH] [compiler-rt][builtins] libc-backed float80-bfloat16/float16 conversion builtins --- compiler-rt/lib/builtins/CMakeLists.txt | 3 + compiler-rt/lib/builtins/extendhfxf2.cpp | 25 +++++ compiler-rt/lib/builtins/truncxfbf2.cpp | 28 ++++++ compiler-rt/lib/builtins/truncxfhf2.cpp | 25 +++++ libc/shared/builtins.h | 3 + libc/shared/builtins/extendhfxf2.h | 35 +++++++ libc/shared/builtins/truncxfbf2.h | 35 +++++++ libc/shared/builtins/truncxfhf2.h | 35 +++++++ libc/src/__support/FPUtil/FPBits.h | 3 + libc/src/__support/FPUtil/dyadic_float.h | 64 ++++++++----- libc/src/__support/builtins/CMakeLists.txt | 35 +++++++ libc/src/__support/builtins/extendhfxf2.h | 41 ++++++++ .../src/__support/builtins/fpconvert_helper.h | 93 +++++++++++++++---- libc/src/__support/builtins/truncxfbf2.h | 39 ++++++++ libc/src/__support/builtins/truncxfhf2.h | 40 ++++++++ libc/test/shared/CMakeLists.txt | 3 + libc/test/shared/shared_builtins_test.cpp | 13 +++ 17 files changed, 479 insertions(+), 41 deletions(-) create mode 100644 compiler-rt/lib/builtins/extendhfxf2.cpp create mode 100644 compiler-rt/lib/builtins/truncxfbf2.cpp create mode 100644 compiler-rt/lib/builtins/truncxfhf2.cpp create mode 100644 libc/shared/builtins/extendhfxf2.h create mode 100644 libc/shared/builtins/truncxfbf2.h create mode 100644 libc/shared/builtins/truncxfhf2.h create mode 100644 libc/src/__support/builtins/extendhfxf2.h create mode 100644 libc/src/__support/builtins/truncxfbf2.h create mode 100644 libc/src/__support/builtins/truncxfhf2.h diff --git a/compiler-rt/lib/builtins/CMakeLists.txt b/compiler-rt/lib/builtins/CMakeLists.txt index 8d3eee6f777be..840451b36e154 100644 --- a/compiler-rt/lib/builtins/CMakeLists.txt +++ b/compiler-rt/lib/builtins/CMakeLists.txt @@ -295,6 +295,7 @@ if(COMPILER_RT_USE_LIBC_MATH) use_libc_builtin(GENERIC_SOURCES divsf3) use_libc_builtin(GENERIC_TF_SOURCES divtf3) use_libc_builtin(GENERIC_TF_SOURCES extenddftf2) + use_libc_builtin(x86_80_BIT_SOURCES extendhfxf2) use_libc_builtin(GENERIC_SOURCES extendsfdf2) use_libc_builtin(GENERIC_TF_SOURCES extendsftf2) use_libc_builtin(x86_80_BIT_SOURCES extendxftf2) @@ -340,6 +341,8 @@ if(COMPILER_RT_USE_LIBC_MATH) use_libc_builtin(GENERIC_TF_SOURCES trunctfdf2) use_libc_builtin(GENERIC_TF_SOURCES trunctfsf2) use_libc_builtin(x86_80_BIT_SOURCES trunctfxf2) + use_libc_builtin(BF16_SOURCES truncxfbf2) + use_libc_builtin(x86_80_BIT_SOURCES truncxfhf2) use_libc_builtin(x86_80_BIT_SOURCES floatdixf) use_libc_builtin(x86_80_BIT_SOURCES floattixf) use_libc_builtin(x86_80_BIT_SOURCES floatundixf) diff --git a/compiler-rt/lib/builtins/extendhfxf2.cpp b/compiler-rt/lib/builtins/extendhfxf2.cpp new file mode 100644 index 0000000000000..ea3b14bcd6fe5 --- /dev/null +++ b/compiler-rt/lib/builtins/extendhfxf2.cpp @@ -0,0 +1,25 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements compiler-rt's __extendhfxf2, extend float16 to long +/// double, on top of LLVM-libc's shared::extendhfxf2. +/// +//===----------------------------------------------------------------------===// + +#include "int_lib.h" +#define SRC_HALF +#define DST_DOUBLE +#include "fp_extend.h" + +#include "fp_libc_config.h" +#include "shared/builtins/extendhfxf2.h" + +extern "C" COMPILER_RT_ABI xf_float __extendhfxf2(src_t a) { + return LIBC_NAMESPACE::shared::extendhfxf2(__builtin_bit_cast(uint16_t, a)); +} diff --git a/compiler-rt/lib/builtins/truncxfbf2.cpp b/compiler-rt/lib/builtins/truncxfbf2.cpp new file mode 100644 index 0000000000000..847feb2a17bc3 --- /dev/null +++ b/compiler-rt/lib/builtins/truncxfbf2.cpp @@ -0,0 +1,28 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements compiler-rt's __truncxfbf2, truncate long double to +/// bfloat16, on top of LLVM-libc's shared::truncxfbf2. +/// +//===----------------------------------------------------------------------===// + +#include "int_lib.h" + +#if defined(CRT_HAS_TF_MODE) && __LDBL_MANT_DIG__ == 64 && defined(__x86_64__) +#define SRC_80 +#define DST_BFLOAT +#include "fp_trunc.h" + +#include "fp_libc_config.h" +#include "shared/builtins/truncxfbf2.h" + +extern "C" COMPILER_RT_ABI dst_t __truncxfbf2(long double a) { + return __builtin_bit_cast(dst_t, LIBC_NAMESPACE::shared::truncxfbf2(a)); +} +#endif diff --git a/compiler-rt/lib/builtins/truncxfhf2.cpp b/compiler-rt/lib/builtins/truncxfhf2.cpp new file mode 100644 index 0000000000000..a89fe329bad65 --- /dev/null +++ b/compiler-rt/lib/builtins/truncxfhf2.cpp @@ -0,0 +1,25 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements compiler-rt's __truncxfhf2, truncate long double to +/// float16, on top of LLVM-libc's shared::truncxfhf2. +/// +//===----------------------------------------------------------------------===// + +#include "int_lib.h" +#define SRC_SINGLE +#define DST_HALF +#include "fp_trunc.h" + +#include "fp_libc_config.h" +#include "shared/builtins/truncxfhf2.h" + +extern "C" COMPILER_RT_ABI dst_t __truncxfhf2(xf_float a) { + return __builtin_bit_cast(dst_t, LIBC_NAMESPACE::shared::truncxfhf2(a)); +} diff --git a/libc/shared/builtins.h b/libc/shared/builtins.h index f44f793736702..d895249fd35b7 100644 --- a/libc/shared/builtins.h +++ b/libc/shared/builtins.h @@ -24,6 +24,7 @@ #include "builtins/divsf3.h" #include "builtins/divtf3.h" #include "builtins/extenddftf2.h" +#include "builtins/extendhfxf2.h" #include "builtins/extendsfdf2.h" #include "builtins/extendsftf2.h" #include "builtins/extendxftf2.h" @@ -90,6 +91,8 @@ #include "builtins/trunctfdf2.h" #include "builtins/trunctfsf2.h" #include "builtins/trunctfxf2.h" +#include "builtins/truncxfbf2.h" +#include "builtins/truncxfhf2.h" #include "builtins/unorddf2.h" #include "builtins/unordsf2.h" #include "builtins/unordtf2.h" diff --git a/libc/shared/builtins/extendhfxf2.h b/libc/shared/builtins/extendhfxf2.h new file mode 100644 index 0000000000000..ace2f1143528a --- /dev/null +++ b/libc/shared/builtins/extendhfxf2.h @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This header exposes LLVM-libc's __extendhfxf2 implementation as +/// shared::extendhfxf2 so that it can be reused by compiler-rt's builtins. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SHARED_BUILTINS_EXTENDHFXF2_H +#define LLVM_LIBC_SHARED_BUILTINS_EXTENDHFXF2_H + +#include "src/__support/macros/properties/types.h" + +#ifdef LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80 + +#include "shared/libc_common.h" +#include "src/__support/builtins/extendhfxf2.h" + +namespace LIBC_NAMESPACE_DECL { +namespace shared { + +using builtins::extendhfxf2; + +} // namespace shared +} // namespace LIBC_NAMESPACE_DECL + +#endif // LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80 + +#endif // LLVM_LIBC_SHARED_BUILTINS_EXTENDHFXF2_H diff --git a/libc/shared/builtins/truncxfbf2.h b/libc/shared/builtins/truncxfbf2.h new file mode 100644 index 0000000000000..b17cdb3161e5d --- /dev/null +++ b/libc/shared/builtins/truncxfbf2.h @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This header exposes LLVM-libc's __truncxfbf2 implementation as +/// shared::truncxfbf2 so that it can be reused by compiler-rt's builtins. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SHARED_BUILTINS_TRUNCXFBF2_H +#define LLVM_LIBC_SHARED_BUILTINS_TRUNCXFBF2_H + +#include "src/__support/macros/properties/types.h" + +#ifdef LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80 + +#include "shared/libc_common.h" +#include "src/__support/builtins/truncxfbf2.h" + +namespace LIBC_NAMESPACE_DECL { +namespace shared { + +using builtins::truncxfbf2; + +} // namespace shared +} // namespace LIBC_NAMESPACE_DECL + +#endif // LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80 + +#endif // LLVM_LIBC_SHARED_BUILTINS_TRUNCXFBF2_H diff --git a/libc/shared/builtins/truncxfhf2.h b/libc/shared/builtins/truncxfhf2.h new file mode 100644 index 0000000000000..714891883b5e8 --- /dev/null +++ b/libc/shared/builtins/truncxfhf2.h @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This header exposes LLVM-libc's __truncxfhf2 implementation as +/// shared::truncxfhf2 so that it can be reused by compiler-rt's builtins. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SHARED_BUILTINS_TRUNCXFHF2_H +#define LLVM_LIBC_SHARED_BUILTINS_TRUNCXFHF2_H + +#include "src/__support/macros/properties/types.h" + +#ifdef LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80 + +#include "shared/libc_common.h" +#include "src/__support/builtins/truncxfhf2.h" + +namespace LIBC_NAMESPACE_DECL { +namespace shared { + +using builtins::truncxfhf2; + +} // namespace shared +} // namespace LIBC_NAMESPACE_DECL + +#endif // LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80 + +#endif // LLVM_LIBC_SHARED_BUILTINS_TRUNCXFHF2_H diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h index 58222f1a7ae15..0c088ba89d6bc 100644 --- a/libc/src/__support/FPUtil/FPBits.h +++ b/libc/src/__support/FPUtil/FPBits.h @@ -786,6 +786,9 @@ struct FPRep : public FPRepImpl<fp_type, FPRep<fp_type>> { } // namespace internal +// Like 'FPBits' but named by 'FPType', so no native C++ type need exist. +template <FPType fp_type> using FPRep = internal::FPRep<fp_type>; + // Returns the FPType corresponding to C++ type T on the host. template <typename T> LIBC_INLINE static constexpr FPType get_fp_type() { using UnqualT = cpp::remove_cv_t<T>; diff --git a/libc/src/__support/FPUtil/dyadic_float.h b/libc/src/__support/FPUtil/dyadic_float.h index 2effcfcb4a34d..b171b6b169d2b 100644 --- a/libc/src/__support/FPUtil/dyadic_float.h +++ b/libc/src/__support/FPUtil/dyadic_float.h @@ -187,42 +187,47 @@ template <size_t Bits> struct DyadicFloat { return DyadicFloat(result_sign, result_exponent, result_mantissa); } - template <typename T, bool ShouldSignalExceptions> - LIBC_INLINE LIBC_CONSTEXPR_DEFAULT cpp::enable_if_t< - cpp::is_floating_point_v<T> && (FPBits<T>::FRACTION_LEN < Bits), T> - generic_as() const { - using FPBits = FPBits<T>; - using StorageType = typename FPBits::StorageType; - - constexpr int EXTRA_FRACTION_LEN = Bits - 1 - FPBits::FRACTION_LEN; + // Round to the destination format named by DstType and return its raw + // storage bits. This is the integer core of generic_as(): it never names a + // native destination type, so it can target a format that has no usable C++ + // type (e.g. float16 where _Float16 is unavailable, or where a native + // conversion would lower to a circular compiler-rt builtin). + template <FPType DstType, bool ShouldSignalExceptions> + LIBC_INLINE LIBC_CONSTEXPR_DEFAULT typename FPRep<DstType>::StorageType + generic_as_bits() const { + using DstRep = FPRep<DstType>; + using StorageType = typename DstRep::StorageType; + static_assert(DstRep::FRACTION_LEN < Bits); + + constexpr int EXTRA_FRACTION_LEN = Bits - 1 - DstRep::FRACTION_LEN; if (mantissa == 0) - return FPBits::zero(sign).get_val(); + return DstRep::zero(sign).uintval(); int unbiased_exp = get_unbiased_exponent(); - if (unbiased_exp + FPBits::EXP_BIAS >= FPBits::MAX_BIASED_EXPONENT) { + if (unbiased_exp + DstRep::EXP_BIAS >= DstRep::MAX_BIASED_EXPONENT) { if constexpr (ShouldSignalExceptions) { set_errno_if_required(ERANGE); raise_except_if_required(FE_OVERFLOW | FE_INEXACT); } #ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY - return FPBits::inf(sign).get_val(); + return DstRep::inf(sign).uintval(); #else // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY switch (quick_get_round()) { case FE_TONEAREST: - return FPBits::inf(sign).get_val(); + return DstRep::inf(sign).uintval(); case FE_TOWARDZERO: - return FPBits::max_normal(sign).get_val(); + return DstRep::max_normal(sign).uintval(); case FE_DOWNWARD: if (sign.is_pos()) - return FPBits::max_normal(Sign::POS).get_val(); - return FPBits::inf(Sign::NEG).get_val(); + return DstRep::max_normal(Sign::POS).uintval(); + return DstRep::inf(Sign::NEG).uintval(); case FE_UPWARD: if (sign.is_neg()) - return FPBits::max_normal(Sign::NEG).get_val(); - return FPBits::inf(Sign::POS).get_val(); + return DstRep::max_normal(Sign::NEG).uintval(); + return DstRep::inf(Sign::POS).uintval(); default: __builtin_unreachable(); } @@ -235,10 +240,10 @@ template <size_t Bits> struct DyadicFloat { bool sticky = false; bool underflow = false; - if (unbiased_exp < -FPBits::EXP_BIAS - FPBits::FRACTION_LEN) { + if (unbiased_exp < -DstRep::EXP_BIAS - DstRep::FRACTION_LEN) { sticky = true; underflow = true; - } else if (unbiased_exp == -FPBits::EXP_BIAS - FPBits::FRACTION_LEN) { + } else if (unbiased_exp == -DstRep::EXP_BIAS - DstRep::FRACTION_LEN) { round = true; // underflow is detected pre-rounding FE_UNDERFLOW may be raised // even if rounding produces a non-underflow result @@ -248,12 +253,12 @@ template <size_t Bits> struct DyadicFloat { } else { int extra_fraction_len = EXTRA_FRACTION_LEN; - if (unbiased_exp < 1 - FPBits::EXP_BIAS) { + if (unbiased_exp < 1 - DstRep::EXP_BIAS) { underflow = true; - extra_fraction_len += 1 - FPBits::EXP_BIAS - unbiased_exp; + extra_fraction_len += 1 - DstRep::EXP_BIAS - unbiased_exp; } else { out_biased_exp = - static_cast<StorageType>(unbiased_exp + FPBits::EXP_BIAS); + static_cast<StorageType>(unbiased_exp + DstRep::EXP_BIAS); } MantissaType round_mask = MantissaType(1) << (extra_fraction_len - 1); @@ -267,7 +272,7 @@ template <size_t Bits> struct DyadicFloat { bool lsb = (out_mantissa & 1) != 0; StorageType result = - FPBits::create_value(sign, out_biased_exp, out_mantissa).uintval(); + DstRep::create_value(sign, out_biased_exp, out_mantissa).uintval(); #ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY if (round && (lsb || sticky)) @@ -293,7 +298,7 @@ template <size_t Bits> struct DyadicFloat { if (ShouldSignalExceptions && (round || sticky)) { int excepts = FE_INEXACT; - if (FPBits(result).is_inf()) { + if (DstRep(result).is_inf()) { set_errno_if_required(ERANGE); excepts |= FE_OVERFLOW; } else if (underflow) { @@ -303,7 +308,16 @@ template <size_t Bits> struct DyadicFloat { raise_except_if_required(excepts); } - return FPBits(result).get_val(); + return result; + } + + template <typename T, bool ShouldSignalExceptions> + LIBC_INLINE LIBC_CONSTEXPR_DEFAULT cpp::enable_if_t< + cpp::is_floating_point_v<T> && (FPBits<T>::FRACTION_LEN < Bits), T> + generic_as() const { + return FPBits<T>( + generic_as_bits<get_fp_type<T>(), ShouldSignalExceptions>()) + .get_val(); } template <typename T, bool ShouldSignalExceptions, diff --git a/libc/src/__support/builtins/CMakeLists.txt b/libc/src/__support/builtins/CMakeLists.txt index 436d14ebcdd93..ede1ad0be1c9f 100644 --- a/libc/src/__support/builtins/CMakeLists.txt +++ b/libc/src/__support/builtins/CMakeLists.txt @@ -818,3 +818,38 @@ add_header_library( libc.src.__support.macros.config libc.src.__support.macros.properties.types ) + +add_header_library( + extendhfxf2 + HDRS + extendhfxf2.h + DEPENDS + libc.hdr.stdint_proxy + libc.src.__support.FPUtil.FPBits + libc.src.__support.builtins.fpconvert_helper + libc.src.__support.macros.config + libc.src.__support.macros.properties.types +) + +add_header_library( + truncxfhf2 + HDRS + truncxfhf2.h + DEPENDS + libc.hdr.stdint_proxy + libc.src.__support.FPUtil.FPBits + libc.src.__support.builtins.fpconvert_helper + libc.src.__support.macros.config + libc.src.__support.macros.properties.types +) + +add_header_library( + truncxfbf2 + HDRS + truncxfbf2.h + DEPENDS + libc.hdr.stdint_proxy + libc.src.__support.FPUtil.bfloat16 + libc.src.__support.macros.config + libc.src.__support.macros.properties.types +) diff --git a/libc/src/__support/builtins/extendhfxf2.h b/libc/src/__support/builtins/extendhfxf2.h new file mode 100644 index 0000000000000..1e2f8f45dcda6 --- /dev/null +++ b/libc/src/__support/builtins/extendhfxf2.h @@ -0,0 +1,41 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This header exposes LLVM-libc's __extendhfxf2 implementation as +/// builtins::extendhfxf2 so that it can be reused by compiler-rt's builtins. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC___SUPPORT_BUILTINS_EXTENDHFXF2_H +#define LLVM_LIBC_SRC___SUPPORT_BUILTINS_EXTENDHFXF2_H + +#include "src/__support/macros/properties/types.h" + +#ifdef LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80 + +#include "hdr/stdint_proxy.h" +#include "src/__support/FPUtil/FPBits.h" +#include "src/__support/builtins/fpconvert_helper.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { +namespace builtins { + +// Extend float16 to long double; mirrors compiler-rt's __extendhfxf2. +LIBC_INLINE long double extendhfxf2(uint16_t bits) { + return fpconvert_from_bits<long double, fputil::FPType::IEEE754_Binary16>( + bits); +} + +} // namespace builtins +} // namespace LIBC_NAMESPACE_DECL + +#endif // LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80 + +#endif // LLVM_LIBC_SRC___SUPPORT_BUILTINS_EXTENDHFXF2_H diff --git a/libc/src/__support/builtins/fpconvert_helper.h b/libc/src/__support/builtins/fpconvert_helper.h index f892bc4d2a1ce..ede5c7b7bb587 100644 --- a/libc/src/__support/builtins/fpconvert_helper.h +++ b/libc/src/__support/builtins/fpconvert_helper.h @@ -17,6 +17,7 @@ #define LLVM_LIBC_SRC___SUPPORT_BUILTINS_FPCONVERT_HELPER_H #include "hdr/fenv_macros.h" +#include "hdr/stdint_proxy.h" #include "src/__support/CPP/algorithm.h" #include "src/__support/CPP/bit.h" #include "src/__support/CPP/type_traits.h" @@ -42,39 +43,99 @@ namespace builtins { // Convert the floating-point value x from From to To (extend or truncate). // Narrowing rounds to nearest, ties to even; mirrors compiler-rt __extend* / // __trunc*. -template <typename To, typename From> -LIBC_INLINE constexpr To fpconvert(From x) { - using FromBits = fputil::FPBits<From>; +namespace internal { + +// Shared conversion body. The source is named by FPType, so it needs no +// native C++ type; only the destination does. +template <typename To, fputil::FPType FromFPType> +LIBC_INLINE constexpr To +fpconvert(typename fputil::FPRep<FromFPType>::StorageType bits) { + using FromRep = fputil::FPRep<FromFPType>; using ToBits = fputil::FPBits<To>; using ToStorageType = typename ToBits::StorageType; - FromBits x_bits(x); + FromRep x_bits(bits); + if (x_bits.is_nan()) { + typename FromRep::StorageType x_frac = x_bits.get_mantissa(); + if constexpr (ToBits::FRACTION_LEN >= FromRep::FRACTION_LEN) { + ToStorageType to_frac = static_cast<ToStorageType>(x_frac) + << (ToBits::FRACTION_LEN - FromRep::FRACTION_LEN); + return ToBits::signaling_nan(x_bits.sign(), to_frac).get_val(); + } + ToStorageType to_frac = static_cast<ToStorageType>( + x_frac >> (FromRep::FRACTION_LEN - ToBits::FRACTION_LEN)); + return ToBits::quiet_nan(x_bits.sign(), to_frac).get_val(); + } + + if (x_bits.is_inf()) + return ToBits::inf(x_bits.sign()).get_val(); + + // Zero and subnormals fall through: DyadicFloat gives a zero mantissa for + // zero, which as<To>() maps back to a correctly-signed zero. Built from + // parts so no From value is materialized. + constexpr size_t MAX_FRACTION_LEN = + cpp::max(ToBits::FRACTION_LEN, FromRep::FRACTION_LEN); + using DyadicType = fputil::DyadicFloat<cpp::bit_ceil(MAX_FRACTION_LEN)>; + DyadicType xd( + x_bits.sign(), x_bits.get_explicit_exponent() - FromRep::FRACTION_LEN, + typename DyadicType::MantissaType(x_bits.get_explicit_mantissa())); + return xd.template as<To, /*ShouldSignalExceptions=*/true>(); +} + +} // namespace internal + +// Convert a floating-point value from From to To (extend or truncate). +template <typename To, typename From> +LIBC_INLINE constexpr To fpconvert(From x) { if constexpr (cpp::is_same_v<To, From>) return x; + else + return internal::fpconvert<To, fputil::get_fp_type<From>()>( + cpp::bit_cast<typename fputil::FPBits<From>::StorageType>(x)); +} + +// Same, for a source delivered as raw bits. Keeps _Float16 out of the +// signature, which would otherwise lower to a circular __extendhfsf2. +template <typename To, fputil::FPType FromFPType> +LIBC_INLINE constexpr To +fpconvert_from_bits(typename fputil::FPRep<FromFPType>::StorageType bits) { + return internal::fpconvert<To, FromFPType>(bits); +} + +// Truncate x to the destination format named by ToFPType and return its raw +// bits. The mirror of fpconvert_from_bits for narrowing: the destination is +// named by FPType, so no native type is produced -- truncating to float16 needs +// no _Float16, which would otherwise lower to a circular __trunc*hf2. +template <fputil::FPType ToFPType, typename From> +LIBC_INLINE constexpr typename fputil::FPRep<ToFPType>::StorageType +fpconvert_to_bits(From x) { + using ToRep = fputil::FPRep<ToFPType>; + using ToStorageType = typename ToRep::StorageType; + using FromBits = fputil::FPBits<From>; + + FromBits x_bits(x); if (x_bits.is_nan()) { typename FromBits::StorageType x_frac = x_bits.get_mantissa(); - if constexpr (ToBits::FRACTION_LEN >= FromBits::FRACTION_LEN) { - ToStorageType to_frac = - static_cast<ToStorageType>(x_frac) - << (ToBits::FRACTION_LEN - FromBits::FRACTION_LEN); - return ToBits::signaling_nan(x_bits.sign(), to_frac).get_val(); + if constexpr (ToRep::FRACTION_LEN >= FromBits::FRACTION_LEN) { + ToStorageType to_frac = static_cast<ToStorageType>(x_frac) + << (ToRep::FRACTION_LEN - FromBits::FRACTION_LEN); + return ToRep::signaling_nan(x_bits.sign(), to_frac).uintval(); } ToStorageType to_frac = static_cast<ToStorageType>( - x_frac >> (FromBits::FRACTION_LEN - ToBits::FRACTION_LEN)); - return ToBits::quiet_nan(x_bits.sign(), to_frac).get_val(); + x_frac >> (FromBits::FRACTION_LEN - ToRep::FRACTION_LEN)); + return ToRep::quiet_nan(x_bits.sign(), to_frac).uintval(); } if (x_bits.is_inf()) - return ToBits::inf(x_bits.sign()).get_val(); + return ToRep::inf(x_bits.sign()).uintval(); - // Zero and subnormals fall through: DyadicFloat(x) gives a zero mantissa for - // zero, which as<To>() maps back to a correctly-signed zero. constexpr size_t MAX_FRACTION_LEN = - cpp::max(ToBits::FRACTION_LEN, FromBits::FRACTION_LEN); + cpp::max(ToRep::FRACTION_LEN, FromBits::FRACTION_LEN); fputil::DyadicFloat<cpp::bit_ceil(MAX_FRACTION_LEN)> xd(x); - return xd.template as<To, /*ShouldSignalExceptions=*/true>(); + return xd.template generic_as_bits<ToFPType, + /*ShouldSignalExceptions=*/true>(); } } // namespace builtins diff --git a/libc/src/__support/builtins/truncxfbf2.h b/libc/src/__support/builtins/truncxfbf2.h new file mode 100644 index 0000000000000..2b9fe17e840e7 --- /dev/null +++ b/libc/src/__support/builtins/truncxfbf2.h @@ -0,0 +1,39 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This header exposes LLVM-libc's __truncxfbf2 implementation as +/// builtins::truncxfbf2 so that it can be reused by compiler-rt's builtins. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC___SUPPORT_BUILTINS_TRUNCXFBF2_H +#define LLVM_LIBC_SRC___SUPPORT_BUILTINS_TRUNCXFBF2_H + +#include "src/__support/macros/properties/types.h" + +#ifdef LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80 + +#include "hdr/stdint_proxy.h" +#include "src/__support/FPUtil/bfloat16.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { +namespace builtins { + +// Truncate long double to bfloat16; mirrors compiler-rt's __truncxfbf2. +LIBC_INLINE uint16_t truncxfbf2(long double x) { + return fputil::cast<bfloat16>(x).bits; +} + +} // namespace builtins +} // namespace LIBC_NAMESPACE_DECL + +#endif // LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80 + +#endif // LLVM_LIBC_SRC___SUPPORT_BUILTINS_TRUNCXFBF2_H diff --git a/libc/src/__support/builtins/truncxfhf2.h b/libc/src/__support/builtins/truncxfhf2.h new file mode 100644 index 0000000000000..e84120f24e950 --- /dev/null +++ b/libc/src/__support/builtins/truncxfhf2.h @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This header exposes LLVM-libc's __truncxfhf2 implementation as +/// builtins::truncxfhf2 so that it can be reused by compiler-rt's builtins. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC___SUPPORT_BUILTINS_TRUNCXFHF2_H +#define LLVM_LIBC_SRC___SUPPORT_BUILTINS_TRUNCXFHF2_H + +#include "src/__support/macros/properties/types.h" + +#ifdef LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80 + +#include "hdr/stdint_proxy.h" +#include "src/__support/FPUtil/FPBits.h" +#include "src/__support/builtins/fpconvert_helper.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { +namespace builtins { + +// Truncate long double to float16; mirrors compiler-rt's __truncxfhf2. +LIBC_INLINE uint16_t truncxfhf2(long double x) { + return fpconvert_to_bits<fputil::FPType::IEEE754_Binary16>(x); +} + +} // namespace builtins +} // namespace LIBC_NAMESPACE_DECL + +#endif // LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80 + +#endif // LLVM_LIBC_SRC___SUPPORT_BUILTINS_TRUNCXFHF2_H diff --git a/libc/test/shared/CMakeLists.txt b/libc/test/shared/CMakeLists.txt index 5633813dcc0ae..c940b0a8009ee 100644 --- a/libc/test/shared/CMakeLists.txt +++ b/libc/test/shared/CMakeLists.txt @@ -835,6 +835,7 @@ add_fp_unittest( libc.src.__support.builtins.divsf3 libc.src.__support.builtins.divtf3 libc.src.__support.builtins.extenddftf2 + libc.src.__support.builtins.extendhfxf2 libc.src.__support.builtins.extendsfdf2 libc.src.__support.builtins.extendsftf2 libc.src.__support.builtins.extendxftf2 @@ -901,6 +902,8 @@ add_fp_unittest( libc.src.__support.builtins.trunctfdf2 libc.src.__support.builtins.trunctfsf2 libc.src.__support.builtins.trunctfxf2 + libc.src.__support.builtins.truncxfbf2 + libc.src.__support.builtins.truncxfhf2 libc.src.__support.builtins.unorddf2 libc.src.__support.builtins.unordsf2 libc.src.__support.builtins.unordtf2 diff --git a/libc/test/shared/shared_builtins_test.cpp b/libc/test/shared/shared_builtins_test.cpp index beca828800def..6a114182d6582 100644 --- a/libc/test/shared/shared_builtins_test.cpp +++ b/libc/test/shared/shared_builtins_test.cpp @@ -208,6 +208,12 @@ TEST(LlvmLibcSharedBuiltinsTest, ExtendConversion) { EXPECT_FP_EQ(float128(1.5), shared::extendxftf2(1.5L)); #endif // LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80 #endif // LIBC_TYPES_HAS_NATIVE_FLOAT128 +#endif // LIBC_TYPES_HAS_FLOAT128 +#endif // LIBC_TYPES_HAS_NATIVE_FLOAT128 +#endif // LIBC_TYPES_HAS_FLOAT128 +#ifdef LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80 + EXPECT_FP_EQ(1.5L, shared::extendhfxf2(static_cast<uint16_t>(0x3E00))); +#endif // LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80 } TEST(LlvmLibcSharedBuiltinsTest, TruncateConversion) { @@ -219,6 +225,13 @@ TEST(LlvmLibcSharedBuiltinsTest, TruncateConversion) { EXPECT_FP_EQ(1.5L, shared::trunctfxf2(float128(1.5))); #endif // LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80 #endif // LIBC_TYPES_HAS_NATIVE_FLOAT128 +#endif // LIBC_TYPES_HAS_FLOAT128 +#endif // LIBC_TYPES_HAS_NATIVE_FLOAT128 +#endif // LIBC_TYPES_HAS_FLOAT128 +#ifdef LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80 + EXPECT_EQ(static_cast<uint16_t>(0x3FC0), shared::truncxfbf2(1.5L)); + EXPECT_EQ(static_cast<uint16_t>(0x3E00), shared::truncxfhf2(1.5L)); +#endif // LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80 } TEST(LlvmLibcSharedBuiltinsTest, SingleCompare) { _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
