Skip to content

[libc][math] Add floating-point cast independent of compiler runtime #105152

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions libc/cmake/modules/CheckCompilerFeatures.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ set(
"builtin_round"
"builtin_roundeven"
"float16"
"float16_conversion"
"float128"
"fixed_point"
)
Expand Down Expand Up @@ -61,15 +62,21 @@ foreach(feature IN LISTS ALL_COMPILER_FEATURES)
set(link_options "")
if(${feature} STREQUAL "fixed_point")
list(APPEND compile_options "-ffixed-point")
elseif(${feature} MATCHES "^builtin_")
elseif(${feature} MATCHES "^builtin_" OR
${feature} STREQUAL "float16_conversion")
set(compile_options ${LIBC_COMPILE_OPTIONS_DEFAULT})
set(link_options -nostdlib)
# The compiler might handle calls to rounding builtins by generating calls
# to the respective libc math functions, in which case we cannot use these
# The compiler might handle calls to math builtins by generating calls to
# the respective libc math functions, in which case we cannot use these
# builtins in our implementations of these functions. We check that this is
# not the case by trying to link an executable, since linking would fail due
# to unresolved references with -nostdlib if calls to libc functions were
# generated.
#
# We also had issues with soft-float float16 conversion functions using both
# compiler-rt and libgcc, so we also check whether we can convert from and
# to float16 without calls to compiler runtime functions by trying to link
# an executable with -nostdlib.
set(CMAKE_TRY_COMPILE_TARGET_TYPE EXECUTABLE)
endif()

Expand Down Expand Up @@ -97,6 +104,8 @@ foreach(feature IN LISTS ALL_COMPILER_FEATURES)
list(APPEND AVAILABLE_COMPILER_FEATURES ${feature})
if(${feature} STREQUAL "float16")
set(LIBC_TYPES_HAS_FLOAT16 TRUE)
elseif(${feature} STREQUAL "float16_conversion")
add_compile_definitions(__LIBC_USE_FLOAT16_CONVERSION)
elseif(${feature} STREQUAL "float128")
set(LIBC_TYPES_HAS_FLOAT128 TRUE)
elseif(${feature} STREQUAL "fixed_point")
Expand All @@ -115,6 +124,10 @@ foreach(feature IN LISTS ALL_COMPILER_FEATURES)
endif()
endforeach()

set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
set(compile_options ${LIBC_COMPILE_OPTIONS_DEFAULT})
set(link_options "")

message(STATUS "Compiler features available: ${AVAILABLE_COMPILER_FEATURES}")

### Compiler Feature Detection ###
Expand Down
30 changes: 30 additions & 0 deletions libc/cmake/modules/compiler_features/check_float16_conversion.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include "include/llvm-libc-macros/float16-macros.h"
#include "include/llvm-libc-types/float128.h"

#ifndef LIBC_TYPES_HAS_FLOAT16
#error unsupported
#endif

_Float16 cvt_from_float(float x) { return static_cast<_Float16>(x); }

_Float16 cvt_from_double(double x) { return static_cast<_Float16>(x); }

_Float16 cvt_from_long_double(long double x) {
return static_cast<_Float16>(x);
}

#ifdef LIBC_TYPES_HAS_FLOAT128
_Float16 cvt_from_float128(float128 x) { return static_cast<_Float16>(x); }
#endif

float cvt_to_float(_Float16 x) { return x; }

double cvt_to_double(_Float16 x) { return x; }

long double cvt_to_long_double(_Float16 x) { return x; }

#ifdef LIBC_TYPES_HAS_FLOAT128
float128 cvt_to_float128(_Float16 x) { return x; }
#endif

extern "C" void _start() {}
27 changes: 24 additions & 3 deletions libc/src/__support/FPUtil/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,14 @@ add_header_library(
HDRS
except_value_utils.h
DEPENDS
.cast
.fp_bits
.fenv_impl
.rounding_mode
libc.src.__support.CPP.optional
libc.src.__support.macros.optimization
libc.src.__support.macros.properties.cpu_features
libc.src.__support.macros.properties.types
)


Expand Down Expand Up @@ -175,9 +178,13 @@ add_header_library(
.fenv_impl
.fp_bits
.multiply_add
.rounding_mode
libc.hdr.errno_macros
libc.hdr.fenv_macros
libc.src.__support.CPP.type_traits
libc.src.__support.big_int
libc.src.__support.macros.optimization
libc.src.__support.macros.properties.types
)

add_header_library(
Expand Down Expand Up @@ -217,18 +224,32 @@ add_header_library(
HDRS
ManipulationFunctions.h
DEPENDS
.cast
.dyadic_float
.fenv_impl
.fp_bits
.dyadic_float
.nearest_integer_operations
.normal_float
libc.hdr.math_macros
libc.src.errno.errno
libc.src.__support.common
libc.src.__support.CPP.bit
libc.src.__support.CPP.limits
libc.src.__support.CPP.type_traits
libc.src.__support.common
libc.src.__support.macros.optimization
libc.src.errno.errno
)

add_header_library(
cast
HDRS
cast.h
DEPENDS
.dyadic_float
.fp_bits
libc.hdr.fenv_macros
libc.src.__support.CPP.algorithm
libc.src.__support.CPP.type_traits
libc.src.__support.macros.properties.types
)

add_subdirectory(generic)
12 changes: 7 additions & 5 deletions libc/src/__support/FPUtil/ManipulationFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "FPBits.h"
#include "NearestIntegerOperations.h"
#include "NormalFloat.h"
#include "cast.h"
#include "dyadic_float.h"
#include "rounding_mode.h"

Expand Down Expand Up @@ -192,7 +193,8 @@ ldexp(T x, U exp) {
// For all other values, NormalFloat to T conversion handles it the right way.
DyadicFloat<FPBits<T>::STORAGE_LEN> normal(bits.get_val());
normal.exponent += static_cast<int>(exp);
return static_cast<T>(normal);
// TODO: Add tests for exceptions.
return normal.template as<T, /*ShouldRaiseExceptions=*/true>();
}

template <typename T, typename U,
Expand All @@ -207,17 +209,17 @@ LIBC_INLINE T nextafter(T from, U to) {

FPBits<U> to_bits(to);
if (to_bits.is_nan())
return static_cast<T>(to);
return cast<T>(to);

// NOTE: This would work only if `U` has a greater or equal precision than
// `T`. Otherwise `from` could loose its precision and the following statement
// could incorrectly evaluate to `true`.
if (static_cast<U>(from) == to)
return static_cast<T>(to);
if (cast<U>(from) == to)
return cast<T>(to);

using StorageType = typename FPBits<T>::StorageType;
if (from != T(0)) {
if ((static_cast<U>(from) < to) == (from > T(0))) {
if ((cast<U>(from) < to) == (from > T(0))) {
from_bits = FPBits<T>(StorageType(from_bits.uintval() + 1));
} else {
from_bits = FPBits<T>(StorageType(from_bits.uintval() - 1));
Expand Down
65 changes: 65 additions & 0 deletions libc/src/__support/FPUtil/cast.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//===-- Conversion between floating-point types -----------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC___SUPPORT_FPUTIL_CAST_H
#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_CAST_H

#include "FPBits.h"
#include "dyadic_float.h"
#include "hdr/fenv_macros.h"
#include "src/__support/CPP/algorithm.h"
#include "src/__support/CPP/type_traits.h"
#include "src/__support/macros/properties/types.h"

namespace LIBC_NAMESPACE::fputil {

template <typename OutType, typename InType>
LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&
cpp::is_floating_point_v<InType>,
OutType>
cast(InType x) {
#if defined(LIBC_TYPES_HAS_FLOAT16) && !defined(__LIBC_USE_FLOAT16_CONVERSION)
if constexpr (cpp::is_same_v<OutType, float16> ||
cpp::is_same_v<InType, float16>) {
using InFPBits = FPBits<InType>;
using InStorageType = typename InFPBits::StorageType;
using OutFPBits = FPBits<OutType>;
using OutStorageType = typename OutFPBits::StorageType;

InFPBits x_bits(x);

if (x_bits.is_nan()) {
if (x_bits.is_signaling_nan()) {
raise_except_if_required(FE_INVALID);
return OutFPBits::quiet_nan().get_val();
}

InStorageType x_mant = x_bits.get_mantissa();
if (InFPBits::FRACTION_LEN > OutFPBits::FRACTION_LEN)
x_mant >>= InFPBits::FRACTION_LEN - OutFPBits::FRACTION_LEN;
return OutFPBits::quiet_nan(x_bits.sign(),
static_cast<OutStorageType>(x_mant))
.get_val();
}

if (x_bits.is_inf())
return OutFPBits::inf(x_bits.sign()).get_val();

constexpr size_t MAX_FRACTION_LEN =
cpp::max(OutFPBits::FRACTION_LEN, InFPBits::FRACTION_LEN);
DyadicFloat<cpp::bit_ceil(MAX_FRACTION_LEN)> xd(x);
return xd.template as<OutType, /*ShouldSignalExceptions=*/true>();
}
#endif

return static_cast<OutType>(x);
}

} // namespace LIBC_NAMESPACE::fputil

#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_CAST_H
Loading
Loading