[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