Skip to content
Open
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
1 change: 1 addition & 0 deletions libcxx/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,7 @@ config_define(${LIBCXX_ENABLE_UNICODE} _LIBCPP_HAS_UNICODE)
config_define(${LIBCXX_ENABLE_WIDE_CHARACTERS} _LIBCPP_HAS_WIDE_CHARACTERS)
config_define(${LIBCXX_ENABLE_TIME_ZONE_DATABASE} _LIBCPP_HAS_TIME_ZONE_DATABASE)
config_define(${LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS} _LIBCPP_HAS_VENDOR_AVAILABILITY_ANNOTATIONS)
config_define_if(C_SUPPORTS_C99_COMPLEX _LIBCPP_HAS_C99_COMPLEX)

# TODO: Remove in LLVM 21. We're leaving an error to make this fail explicitly.
if (LIBCXX_ENABLE_ASSERTIONS)
Expand Down
25 changes: 25 additions & 0 deletions libcxx/cmake/config-ix.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,31 @@ endif()

check_symbol_exists(__PICOLIBC__ "string.h" PICOLIBC)

# Check for the existence of <complex.h> complex math functions.
# This is necessary even though libcxx uses the builtin versions
# of these functions, because if the builtin cannot be used, a reference
# to the library function is emitted. Using compiler builtins for these
# functions requires corresponding C99 library functions to be present.

cmake_push_check_state()
list(APPEND CMAKE_REQUIRED_LIBRARIES m)
check_c_source_compiles("
#include <complex.h>
typedef __complex__ float float_type;
typedef __complex__ double double_type;
typedef __complex__ long double ld_type;
volatile float_type tmpf;
volatile double_type tmpd;
volatile ld_type tmpld;
int main(void) {
tmpf = cexpf(tmpf);
tmpd = cexp(tmpd);
tmpld = cexpl(tmpld);
return 0;
}
" C_SUPPORTS_C99_COMPLEX)
cmake_pop_check_state()

Comment on lines +106 to +130
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We generally try to avoid these kinds of shenanigans. Why is this required?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is required when libm is missing these functions. If they are missing, we can fall back to the old implementation, which is not using builtin_cexp functions. At least one build configuration encounters this issue: Android 5.0 x86.

# Check libraries
if(WIN32 AND NOT MINGW)
# TODO(compnerd) do we want to support an emulation layer that allows for the
Expand Down
1 change: 1 addition & 0 deletions libcxx/include/__config_site.in
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#cmakedefine01 _LIBCPP_HAS_WIDE_CHARACTERS
#cmakedefine01 _LIBCPP_HAS_TIME_ZONE_DATABASE
#cmakedefine01 _LIBCPP_INSTRUMENTED_WITH_ASAN
#cmakedefine01 _LIBCPP_HAS_C99_COMPLEX

// PSTL backends
#cmakedefine _LIBCPP_PSTL_BACKEND_SERIAL
Expand Down
15 changes: 15 additions & 0 deletions libcxx/include/complex
Original file line number Diff line number Diff line change
Expand Up @@ -1074,6 +1074,16 @@ _LIBCPP_HIDE_FROM_ABI complex<_Tp> sqrt(const complex<_Tp>& __x) {

// exp

# if _LIBCPP_HAS_C99_COMPLEX
_LIBCPP_HIDE_FROM_ABI inline _Complex float __cexp(_Complex float __v) { return __builtin_cexpf(__v); }
_LIBCPP_HIDE_FROM_ABI inline _Complex double __cexp(_Complex double __v) { return __builtin_cexp(__v); }
_LIBCPP_HIDE_FROM_ABI inline _Complex long double __cexp(_Complex long double __v) { return __builtin_cexpl(__v); }

template <class _Tp>
_LIBCPP_HIDE_FROM_ABI complex<_Tp> exp(const complex<_Tp>& __x) {
return complex<_Tp>(__from_builtin_tag(), std::__cexp(__x.__builtin()));
}
# else
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI complex<_Tp> exp(const complex<_Tp>& __x) {
_Tp __i = __x.imag();
Expand All @@ -1091,8 +1101,13 @@ _LIBCPP_HIDE_FROM_ABI complex<_Tp> exp(const complex<_Tp>& __x) {
}
}
_Tp __e = std::exp(__x.real());
if (std::isinf(__e)) {
_Tp __e2 = std::exp(__x.real() * _Tp(0.5));
return complex<_Tp>(__e2 * std::cos(__i) * __e2, __e2 * std::sin(__i) * __e2);
}
return complex<_Tp>(__e * std::cos(__i), __e * std::sin(__i));
}
# endif

// pow

Expand Down
40 changes: 40 additions & 0 deletions libcxx/test/libcxx/numerics/complex.number/exp.pass.cpp
Original file line number Diff line number Diff line change
@@ -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
//
//===----------------------------------------------------------------------===//

// <complex>

// template<class T>
// complex<T>
// exp(const complex<T>& x);

#include <complex>
#include <cassert>
#include <cmath>

#include "test_macros.h"

template <class T>
void test_overflow_case() {
typedef std::complex<T> C;

// In this case, the overflow of exp(real_part) is compensated when
// sin(imag_part) is close to zero, resulting in a finite imaginary part.
C z(T(90.0238094), T(5.900613e-39));
C result = std::exp(z);

assert(std::isinf(result.real()));
assert(result.real() > 0);

assert(std::isfinite(result.imag()));
assert(std::abs(result.imag() - T(7.3746)) < T(1.0));
}

int main(int, char**) {
test_overflow_case<float>();
return 0;
}
Loading