Skip to content

Commit 127349f

Browse files
authored
[libc][math] Add floating-point cast independent of compiler runtime (#105152)
Fixes build and tests with compiler-rt on x86.
1 parent df0864e commit 127349f

40 files changed

+620
-230
lines changed

libc/cmake/modules/CheckCompilerFeatures.cmake

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ set(
1010
"builtin_round"
1111
"builtin_roundeven"
1212
"float16"
13+
"float16_conversion"
1314
"float128"
1415
"fixed_point"
1516
)
@@ -61,15 +62,21 @@ foreach(feature IN LISTS ALL_COMPILER_FEATURES)
6162
set(link_options "")
6263
if(${feature} STREQUAL "fixed_point")
6364
list(APPEND compile_options "-ffixed-point")
64-
elseif(${feature} MATCHES "^builtin_")
65+
elseif(${feature} MATCHES "^builtin_" OR
66+
${feature} STREQUAL "float16_conversion")
6567
set(compile_options ${LIBC_COMPILE_OPTIONS_DEFAULT})
6668
set(link_options -nostdlib)
67-
# The compiler might handle calls to rounding builtins by generating calls
68-
# to the respective libc math functions, in which case we cannot use these
69+
# The compiler might handle calls to math builtins by generating calls to
70+
# the respective libc math functions, in which case we cannot use these
6971
# builtins in our implementations of these functions. We check that this is
7072
# not the case by trying to link an executable, since linking would fail due
7173
# to unresolved references with -nostdlib if calls to libc functions were
7274
# generated.
75+
#
76+
# We also had issues with soft-float float16 conversion functions using both
77+
# compiler-rt and libgcc, so we also check whether we can convert from and
78+
# to float16 without calls to compiler runtime functions by trying to link
79+
# an executable with -nostdlib.
7380
set(CMAKE_TRY_COMPILE_TARGET_TYPE EXECUTABLE)
7481
endif()
7582

@@ -97,6 +104,8 @@ foreach(feature IN LISTS ALL_COMPILER_FEATURES)
97104
list(APPEND AVAILABLE_COMPILER_FEATURES ${feature})
98105
if(${feature} STREQUAL "float16")
99106
set(LIBC_TYPES_HAS_FLOAT16 TRUE)
107+
elseif(${feature} STREQUAL "float16_conversion")
108+
add_compile_definitions(__LIBC_USE_FLOAT16_CONVERSION)
100109
elseif(${feature} STREQUAL "float128")
101110
set(LIBC_TYPES_HAS_FLOAT128 TRUE)
102111
elseif(${feature} STREQUAL "fixed_point")
@@ -115,6 +124,10 @@ foreach(feature IN LISTS ALL_COMPILER_FEATURES)
115124
endif()
116125
endforeach()
117126

127+
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
128+
set(compile_options ${LIBC_COMPILE_OPTIONS_DEFAULT})
129+
set(link_options "")
130+
118131
message(STATUS "Compiler features available: ${AVAILABLE_COMPILER_FEATURES}")
119132

120133
### Compiler Feature Detection ###
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#include "include/llvm-libc-macros/float16-macros.h"
2+
#include "include/llvm-libc-types/float128.h"
3+
4+
#ifndef LIBC_TYPES_HAS_FLOAT16
5+
#error unsupported
6+
#endif
7+
8+
_Float16 cvt_from_float(float x) { return static_cast<_Float16>(x); }
9+
10+
_Float16 cvt_from_double(double x) { return static_cast<_Float16>(x); }
11+
12+
_Float16 cvt_from_long_double(long double x) {
13+
return static_cast<_Float16>(x);
14+
}
15+
16+
#ifdef LIBC_TYPES_HAS_FLOAT128
17+
_Float16 cvt_from_float128(float128 x) { return static_cast<_Float16>(x); }
18+
#endif
19+
20+
float cvt_to_float(_Float16 x) { return x; }
21+
22+
double cvt_to_double(_Float16 x) { return x; }
23+
24+
long double cvt_to_long_double(_Float16 x) { return x; }
25+
26+
#ifdef LIBC_TYPES_HAS_FLOAT128
27+
float128 cvt_to_float128(_Float16 x) { return x; }
28+
#endif
29+
30+
extern "C" void _start() {}

libc/src/__support/FPUtil/CMakeLists.txt

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,14 @@ add_header_library(
9292
HDRS
9393
except_value_utils.h
9494
DEPENDS
95+
.cast
9596
.fp_bits
9697
.fenv_impl
9798
.rounding_mode
9899
libc.src.__support.CPP.optional
99100
libc.src.__support.macros.optimization
101+
libc.src.__support.macros.properties.cpu_features
102+
libc.src.__support.macros.properties.types
100103
)
101104

102105

@@ -175,9 +178,13 @@ add_header_library(
175178
.fenv_impl
176179
.fp_bits
177180
.multiply_add
181+
.rounding_mode
182+
libc.hdr.errno_macros
183+
libc.hdr.fenv_macros
178184
libc.src.__support.CPP.type_traits
179185
libc.src.__support.big_int
180186
libc.src.__support.macros.optimization
187+
libc.src.__support.macros.properties.types
181188
)
182189

183190
add_header_library(
@@ -217,18 +224,32 @@ add_header_library(
217224
HDRS
218225
ManipulationFunctions.h
219226
DEPENDS
227+
.cast
228+
.dyadic_float
220229
.fenv_impl
221230
.fp_bits
222-
.dyadic_float
223231
.nearest_integer_operations
224232
.normal_float
225233
libc.hdr.math_macros
234+
libc.src.errno.errno
235+
libc.src.__support.common
226236
libc.src.__support.CPP.bit
227237
libc.src.__support.CPP.limits
228238
libc.src.__support.CPP.type_traits
229-
libc.src.__support.common
230239
libc.src.__support.macros.optimization
231-
libc.src.errno.errno
240+
)
241+
242+
add_header_library(
243+
cast
244+
HDRS
245+
cast.h
246+
DEPENDS
247+
.dyadic_float
248+
.fp_bits
249+
libc.hdr.fenv_macros
250+
libc.src.__support.CPP.algorithm
251+
libc.src.__support.CPP.type_traits
252+
libc.src.__support.macros.properties.types
232253
)
233254

234255
add_subdirectory(generic)

libc/src/__support/FPUtil/ManipulationFunctions.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "FPBits.h"
1313
#include "NearestIntegerOperations.h"
1414
#include "NormalFloat.h"
15+
#include "cast.h"
1516
#include "dyadic_float.h"
1617
#include "rounding_mode.h"
1718

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

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

208210
FPBits<U> to_bits(to);
209211
if (to_bits.is_nan())
210-
return static_cast<T>(to);
212+
return cast<T>(to);
211213

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

218220
using StorageType = typename FPBits<T>::StorageType;
219221
if (from != T(0)) {
220-
if ((static_cast<U>(from) < to) == (from > T(0))) {
222+
if ((cast<U>(from) < to) == (from > T(0))) {
221223
from_bits = FPBits<T>(StorageType(from_bits.uintval() + 1));
222224
} else {
223225
from_bits = FPBits<T>(StorageType(from_bits.uintval() - 1));

libc/src/__support/FPUtil/cast.h

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//===-- Conversion between floating-point types -----------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC___SUPPORT_FPUTIL_CAST_H
10+
#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_CAST_H
11+
12+
#include "FPBits.h"
13+
#include "dyadic_float.h"
14+
#include "hdr/fenv_macros.h"
15+
#include "src/__support/CPP/algorithm.h"
16+
#include "src/__support/CPP/type_traits.h"
17+
#include "src/__support/macros/properties/types.h"
18+
19+
namespace LIBC_NAMESPACE::fputil {
20+
21+
template <typename OutType, typename InType>
22+
LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&
23+
cpp::is_floating_point_v<InType>,
24+
OutType>
25+
cast(InType x) {
26+
#if defined(LIBC_TYPES_HAS_FLOAT16) && !defined(__LIBC_USE_FLOAT16_CONVERSION)
27+
if constexpr (cpp::is_same_v<OutType, float16> ||
28+
cpp::is_same_v<InType, float16>) {
29+
using InFPBits = FPBits<InType>;
30+
using InStorageType = typename InFPBits::StorageType;
31+
using OutFPBits = FPBits<OutType>;
32+
using OutStorageType = typename OutFPBits::StorageType;
33+
34+
InFPBits x_bits(x);
35+
36+
if (x_bits.is_nan()) {
37+
if (x_bits.is_signaling_nan()) {
38+
raise_except_if_required(FE_INVALID);
39+
return OutFPBits::quiet_nan().get_val();
40+
}
41+
42+
InStorageType x_mant = x_bits.get_mantissa();
43+
if (InFPBits::FRACTION_LEN > OutFPBits::FRACTION_LEN)
44+
x_mant >>= InFPBits::FRACTION_LEN - OutFPBits::FRACTION_LEN;
45+
return OutFPBits::quiet_nan(x_bits.sign(),
46+
static_cast<OutStorageType>(x_mant))
47+
.get_val();
48+
}
49+
50+
if (x_bits.is_inf())
51+
return OutFPBits::inf(x_bits.sign()).get_val();
52+
53+
constexpr size_t MAX_FRACTION_LEN =
54+
cpp::max(OutFPBits::FRACTION_LEN, InFPBits::FRACTION_LEN);
55+
DyadicFloat<cpp::bit_ceil(MAX_FRACTION_LEN)> xd(x);
56+
return xd.template as<OutType, /*ShouldSignalExceptions=*/true>();
57+
}
58+
#endif
59+
60+
return static_cast<OutType>(x);
61+
}
62+
63+
} // namespace LIBC_NAMESPACE::fputil
64+
65+
#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_CAST_H

0 commit comments

Comments
 (0)