[llvm-branch-commits] [compiler-rt] [libc] [compiler-rt][builtins] libc-backed float80-bfloat16/float16 conversion builtins (PR #215729)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Aug 12 01:11:26 PDT 2026
https://github.com/hulxv updated https://github.com/llvm/llvm-project/pull/215729
>From e5578acf2972df97f7919b0e9e198a55bc45d4a0 Mon Sep 17 00:00:00 2001
From: hulxv <hulxxv at gmail.com>
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) {
More information about the llvm-branch-commits
mailing list