[libc-commits] [compiler-rt] [libc] [compiler-rt][builtins] libc-backed floating-point extend/trunct builtins (PR #209984)

Simon Tatham via libc-commits libc-commits at lists.llvm.org
Mon Jul 20 08:51:57 PDT 2026


================
@@ -0,0 +1,79 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Shared float-to-float extend/truncate conversions, rounding to nearest
+/// (ties to even) when narrowing.  These mirror compiler-rt's __extend<a><b>2 /
+/// __trunc<a><b>2 builtins, so they can be reused by compiler-rt's builtins.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_BUILTINS_FPCONVERT_HELPER_H
+#define LLVM_LIBC_SRC___SUPPORT_BUILTINS_FPCONVERT_HELPER_H
+
+#include "hdr/fenv_macros.h"
+#include "src/__support/CPP/algorithm.h"
+#include "src/__support/CPP/bit.h"
+#include "src/__support/CPP/type_traits.h"
+#include "src/__support/FPUtil/FEnvImpl.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/dyadic_float.h"
+#include "src/__support/macros/attributes.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace builtins {
+
+// TODO: use fputil::cast after adding Float128/64/32/16 classes currently, we
+// are using this helper to avoid an infinity loop that happens because
+// fputil::cast is calling the builtins if the target doesn't have FPU.
+//
+// assembly (each body below compiles to a self-call):
+//   // double f(float128 x) { return cast<double>(x); }
+//   trunc_tf_df:  callq __trunctfdf2 at PLT
+//   // float128 g(double x) { return cast<float128>(x); }
+//   ext_df_tf:    jmp   __extenddftf2 at PLT   // TAILCALL
+
+// 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>;
+  using ToBits = fputil::FPBits<To>;
+  using ToStorageType = typename ToBits::StorageType;
+
+  FromBits x_bits(x);
+
+  if (x_bits.is_nan()) {
+    if (x_bits.is_signaling_nan()) {
+      fputil::raise_except_if_required(FE_INVALID);
+      return ToBits::quiet_nan().get_val();
+    }
+    typename FromBits::StorageType x_mant = x_bits.get_mantissa();
+    if (FromBits::FRACTION_LEN > ToBits::FRACTION_LEN)
+      x_mant >>= FromBits::FRACTION_LEN - ToBits::FRACTION_LEN;
----------------
statham-arm wrote:

Is it deliberate that we shift a longer mantissa right, but don't shift a shorter mantissa left? I would have thought it made sense to do both, so that a round-trip conversion from one float type to another and back leaves a quiet NaN unchanged as often as possible. (All the time if the intermediate type is longer, and at least some of the time if it's shorter.)

https://github.com/llvm/llvm-project/pull/209984


More information about the libc-commits mailing list