[libcxx-commits] [libcxx] 5aacf93 - [libc++] Use _Complex for multiplication and division of complex floating point types (#83575)

via libcxx-commits libcxx-commits at lists.llvm.org
Fri Jul 5 02:26:02 PDT 2024


Author: Nikolas Klauser
Date: 2024-07-05T11:25:59+02:00
New Revision: 5aacf93a8968b1ae83382ed0ce6de8279b0cd753

URL: https://github.com/llvm/llvm-project/commit/5aacf93a8968b1ae83382ed0ce6de8279b0cd753
DIFF: https://github.com/llvm/llvm-project/commit/5aacf93a8968b1ae83382ed0ce6de8279b0cd753.diff

LOG: [libc++] Use _Complex for multiplication and division of complex floating point types (#83575)

This significantly simplifies the implementation and improves the
codegen. The only downside is that the accuracy can be marginally worse,
but that is up to the compiler to decide with this change, which means
it can be controlled by compiler flags.

Differential Revision: https://reviews.llvm.org/D155312

Added: 
    libcxx/src/support/win32/compiler_rt_shims.cpp
    libcxx/test/support/floating_pointer_helpers.h

Modified: 
    libcxx/include/cmath
    libcxx/include/complex
    libcxx/src/CMakeLists.txt
    libcxx/test/std/numerics/complex.number/complex.member.ops/divide_equal_complex.pass.cpp
    libcxx/test/std/numerics/complex.number/complex.ops/complex_divide_complex.pass.cpp
    libcxx/test/std/numerics/complex.number/complex.ops/complex_times_complex.pass.cpp
    libcxx/test/std/numerics/complex.number/complex.ops/scalar_divide_complex.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/cmath b/libcxx/include/cmath
index 6d3e26aa36a3d..7a87e35c84603 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -610,161 +610,6 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool __constexpr_isfinite(_A1 __lcpp_x)
   return __builtin_isfinite(__lcpp_x);
 }
 
-_LIBCPP_CONSTEXPR inline _LIBCPP_HIDE_FROM_ABI float __constexpr_copysign(float __x, float __y) _NOEXCEPT {
-  return __builtin_copysignf(__x, __y);
-}
-
-_LIBCPP_CONSTEXPR inline _LIBCPP_HIDE_FROM_ABI double __constexpr_copysign(double __x, double __y) _NOEXCEPT {
-  return __builtin_copysign(__x, __y);
-}
-
-_LIBCPP_CONSTEXPR inline _LIBCPP_HIDE_FROM_ABI long double
-__constexpr_copysign(long double __x, long double __y) _NOEXCEPT {
-  return __builtin_copysignl(__x, __y);
-}
-
-template <class _A1,
-          class _A2,
-          __enable_if_t<std::is_arithmetic<_A1>::value && std::is_arithmetic<_A2>::value, int> = 0>
-_LIBCPP_CONSTEXPR inline _LIBCPP_HIDE_FROM_ABI typename __promote<_A1, _A2>::type
-__constexpr_copysign(_A1 __x, _A2 __y) _NOEXCEPT {
-  typedef typename std::__promote<_A1, _A2>::type __result_type;
-  static_assert(!(std::_IsSame<_A1, __result_type>::value && std::_IsSame<_A2, __result_type>::value), "");
-  return __builtin_copysign((__result_type)__x, (__result_type)__y);
-}
-
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR float __constexpr_fabs(float __x) _NOEXCEPT {
-  return __builtin_fabsf(__x);
-}
-
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR double __constexpr_fabs(double __x) _NOEXCEPT {
-  return __builtin_fabs(__x);
-}
-
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR long double __constexpr_fabs(long double __x) _NOEXCEPT {
-  return __builtin_fabsl(__x);
-}
-
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR double __constexpr_fabs(_Tp __x) _NOEXCEPT {
-  return __builtin_fabs(static_cast<double>(__x));
-}
-
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 float __constexpr_fmax(float __x, float __y) _NOEXCEPT {
-#if !__has_constexpr_builtin(__builtin_fmaxf)
-  if (__libcpp_is_constant_evaluated()) {
-    if (std::__constexpr_isnan(__x))
-      return __y;
-    if (std::__constexpr_isnan(__y))
-      return __x;
-    return __x < __y ? __y : __x;
-  }
-#endif
-  return __builtin_fmaxf(__x, __y);
-}
-
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 double __constexpr_fmax(double __x, double __y) _NOEXCEPT {
-#if !__has_constexpr_builtin(__builtin_fmax)
-  if (__libcpp_is_constant_evaluated()) {
-    if (std::__constexpr_isnan(__x))
-      return __y;
-    if (std::__constexpr_isnan(__y))
-      return __x;
-    return __x < __y ? __y : __x;
-  }
-#endif
-  return __builtin_fmax(__x, __y);
-}
-
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 long double
-__constexpr_fmax(long double __x, long double __y) _NOEXCEPT {
-#if !__has_constexpr_builtin(__builtin_fmaxl)
-  if (__libcpp_is_constant_evaluated()) {
-    if (std::__constexpr_isnan(__x))
-      return __y;
-    if (std::__constexpr_isnan(__y))
-      return __x;
-    return __x < __y ? __y : __x;
-  }
-#endif
-  return __builtin_fmaxl(__x, __y);
-}
-
-template <class _Tp, class _Up, __enable_if_t<is_arithmetic<_Tp>::value && is_arithmetic<_Up>::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 typename __promote<_Tp, _Up>::type
-__constexpr_fmax(_Tp __x, _Up __y) _NOEXCEPT {
-  using __result_type = typename __promote<_Tp, _Up>::type;
-  return std::__constexpr_fmax(static_cast<__result_type>(__x), static_cast<__result_type>(__y));
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp __constexpr_logb(_Tp __x) {
-#if !__has_constexpr_builtin(__builtin_logb)
-  if (__libcpp_is_constant_evaluated()) {
-    if (__x == _Tp(0)) {
-      // raise FE_DIVBYZERO
-      return -numeric_limits<_Tp>::infinity();
-    }
-
-    if (std::__constexpr_isinf(__x))
-      return numeric_limits<_Tp>::infinity();
-
-    if (std::__constexpr_isnan(__x))
-      return numeric_limits<_Tp>::quiet_NaN();
-
-    __x                      = std::__constexpr_fabs(__x);
-    unsigned long long __exp = 0;
-    while (__x >= numeric_limits<_Tp>::radix) {
-      __x /= numeric_limits<_Tp>::radix;
-      __exp += 1;
-    }
-    return _Tp(__exp);
-  }
-#endif // !__has_constexpr_builtin(__builtin_logb)
-  return __builtin_logb(__x);
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp __constexpr_scalbn(_Tp __x, int __exp) {
-#if !__has_constexpr_builtin(__builtin_scalbln)
-  if (__libcpp_is_constant_evaluated()) {
-    if (__x == _Tp(0))
-      return __x;
-
-    if (std::__constexpr_isinf(__x))
-      return __x;
-
-    if (__exp == _Tp(0))
-      return __x;
-
-    if (std::__constexpr_isnan(__x))
-      return numeric_limits<_Tp>::quiet_NaN();
-
-    _Tp __mult(1);
-    if (__exp > 0) {
-      __mult = numeric_limits<_Tp>::radix;
-      --__exp;
-    } else {
-      ++__exp;
-      __exp = -__exp;
-      __mult /= numeric_limits<_Tp>::radix;
-    }
-
-    while (__exp > 0) {
-      if (!(__exp & 1)) {
-        __mult *= __mult;
-        __exp >>= 1;
-      } else {
-        __x *= __mult;
-        --__exp;
-      }
-    }
-    return __x;
-  }
-#endif // !__has_constexpr_builtin(__builtin_scalbln)
-  return __builtin_scalbn(__x, __exp);
-}
-
 #if _LIBCPP_STD_VER >= 20
 template <typename _Fp>
 _LIBCPP_HIDE_FROM_ABI constexpr _Fp __lerp(_Fp __a, _Fp __b, _Fp __t) noexcept {

diff  --git a/libcxx/include/complex b/libcxx/include/complex
index a81f968143c4c..69a9fcce6f7ef 100644
--- a/libcxx/include/complex
+++ b/libcxx/include/complex
@@ -281,10 +281,19 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class _Tp>
 class _LIBCPP_TEMPLATE_VIS complex;
 
-template <class _Tp>
+template <class _Tp, __enable_if_t<is_floating_point<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 complex<_Tp>
 operator*(const complex<_Tp>& __z, const complex<_Tp>& __w);
-template <class _Tp>
+
+template <class _Tp, __enable_if_t<!is_floating_point<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 complex<_Tp>
+operator*(const complex<_Tp>& __z, const complex<_Tp>& __w);
+
+template <class _Tp, __enable_if_t<is_floating_point<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 complex<_Tp>
+operator/(const complex<_Tp>& __x, const complex<_Tp>& __y);
+
+template <class _Tp, __enable_if_t<!is_floating_point<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 complex<_Tp>
 operator/(const complex<_Tp>& __x, const complex<_Tp>& __y);
 
@@ -384,6 +393,23 @@ class _LIBCPP_TEMPLATE_VIS complex<double>;
 template <>
 class _LIBCPP_TEMPLATE_VIS complex<long double>;
 
+struct __from_builtin_tag {};
+
+template <class _Tp>
+using __complex_t =
+    __conditional_t<is_same<_Tp, float>::value,
+                    _Complex float,
+                    __conditional_t<is_same<_Tp, double>::value, _Complex double, _Complex long double> >;
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __complex_t<_Tp> __make_complex(_Tp __re, _Tp __im) {
+#if __has_builtin(__builtin_complex)
+  return __builtin_complex(__re, __im);
+#else
+  return __complex_t<_Tp>{__re, __im};
+#endif
+}
+
 template <>
 class _LIBCPP_TEMPLATE_VIS complex<float> {
   float __re_;
@@ -393,6 +419,10 @@ public:
   typedef float value_type;
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR complex(float __re = 0.0f, float __im = 0.0f) : __re_(__re), __im_(__im) {}
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR complex(__from_builtin_tag, _Complex float __v)
+      : __re_(__real__ __v), __im_(__imag__ __v) {}
+
   _LIBCPP_HIDE_FROM_ABI explicit _LIBCPP_CONSTEXPR complex(const complex<double>& __c);
   _LIBCPP_HIDE_FROM_ABI explicit _LIBCPP_CONSTEXPR complex(const complex<long double>& __c);
 
@@ -402,6 +432,12 @@ public:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void real(value_type __re) { __re_ = __re; }
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void imag(value_type __im) { __im_ = __im; }
 
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Complex float __builtin() const { return std::__make_complex(__re_, __im_); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __builtin(_Complex float __f) {
+    __re_ = __real__ __f;
+    __im_ = __imag__ __f;
+  }
+
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 complex& operator=(float __re) {
     __re_ = __re;
     __im_ = value_type();
@@ -479,6 +515,10 @@ public:
   typedef double value_type;
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR complex(double __re = 0.0, double __im = 0.0) : __re_(__re), __im_(__im) {}
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR complex(__from_builtin_tag, _Complex double __v)
+      : __re_(__real__ __v), __im_(__imag__ __v) {}
+
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR complex(const complex<float>& __c);
   _LIBCPP_HIDE_FROM_ABI explicit _LIBCPP_CONSTEXPR complex(const complex<long double>& __c);
 
@@ -488,6 +528,15 @@ public:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void real(value_type __re) { __re_ = __re; }
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void imag(value_type __im) { __im_ = __im; }
 
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Complex double __builtin() const {
+    return std::__make_complex(__re_, __im_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __builtin(_Complex double __f) {
+    __re_ = __real__ __f;
+    __im_ = __imag__ __f;
+  }
+
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 complex& operator=(double __re) {
     __re_ = __re;
     __im_ = value_type();
@@ -566,6 +615,10 @@ public:
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR complex(long double __re = 0.0L, long double __im = 0.0L)
       : __re_(__re), __im_(__im) {}
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR complex(__from_builtin_tag, _Complex long double __v)
+      : __re_(__real__ __v), __im_(__imag__ __v) {}
+
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR complex(const complex<float>& __c);
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR complex(const complex<double>& __c);
 
@@ -575,6 +628,15 @@ public:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void real(value_type __re) { __re_ = __re; }
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void imag(value_type __im) { __im_ = __im; }
 
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Complex long double __builtin() const {
+    return std::__make_complex(__re_, __im_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __builtin(_Complex long double __f) {
+    __re_ = __real__ __f;
+    __im_ = __imag__ __f;
+  }
+
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 complex& operator=(long double __re) {
     __re_ = __re;
     __im_ = value_type();
@@ -709,7 +771,13 @@ operator-(const _Tp& __x, const complex<_Tp>& __y) {
   return __t;
 }
 
-template <class _Tp>
+template <class _Tp, __enable_if_t<is_floating_point<_Tp>::value, int> >
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 complex<_Tp>
+operator*(const complex<_Tp>& __lhs, const complex<_Tp>& __rhs) {
+  return complex<_Tp>(__from_builtin_tag(), __lhs.__builtin() * __rhs.__builtin());
+}
+
+template <class _Tp, __enable_if_t<!is_floating_point<_Tp>::value, int> >
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 complex<_Tp>
 operator*(const complex<_Tp>& __z, const complex<_Tp>& __w) {
   _Tp __a = __z.real();
@@ -717,78 +785,7 @@ operator*(const complex<_Tp>& __z, const complex<_Tp>& __w) {
   _Tp __c = __w.real();
   _Tp __d = __w.imag();
 
-  // Avoid floating point operations that are invalid during constant evaluation
-  if (__libcpp_is_constant_evaluated()) {
-    bool __z_zero = __a == _Tp(0) && __b == _Tp(0);
-    bool __w_zero = __c == _Tp(0) && __d == _Tp(0);
-    bool __z_inf  = std::__constexpr_isinf(__a) || std::__constexpr_isinf(__b);
-    bool __w_inf  = std::__constexpr_isinf(__c) || std::__constexpr_isinf(__d);
-    bool __z_nan =
-        !__z_inf && ((std::__constexpr_isnan(__a) && std::__constexpr_isnan(__b)) ||
-                     (std::__constexpr_isnan(__a) && __b == _Tp(0)) || (__a == _Tp(0) && std::__constexpr_isnan(__b)));
-    bool __w_nan =
-        !__w_inf && ((std::__constexpr_isnan(__c) && std::__constexpr_isnan(__d)) ||
-                     (std::__constexpr_isnan(__c) && __d == _Tp(0)) || (__c == _Tp(0) && std::__constexpr_isnan(__d)));
-    if (__z_nan || __w_nan) {
-      return complex<_Tp>(_Tp(numeric_limits<_Tp>::quiet_NaN()), _Tp(0));
-    }
-    if (__z_inf || __w_inf) {
-      if (__z_zero || __w_zero) {
-        return complex<_Tp>(_Tp(numeric_limits<_Tp>::quiet_NaN()), _Tp(0));
-      }
-      return complex<_Tp>(_Tp(numeric_limits<_Tp>::infinity()), _Tp(numeric_limits<_Tp>::infinity()));
-    }
-    bool __z_nonzero_nan = !__z_inf && !__z_nan && (std::__constexpr_isnan(__a) || std::__constexpr_isnan(__b));
-    bool __w_nonzero_nan = !__w_inf && !__w_nan && (std::__constexpr_isnan(__c) || std::__constexpr_isnan(__d));
-    if (__z_nonzero_nan || __w_nonzero_nan) {
-      return complex<_Tp>(_Tp(numeric_limits<_Tp>::quiet_NaN()), _Tp(0));
-    }
-  }
-
-  _Tp __ac = __a * __c;
-  _Tp __bd = __b * __d;
-  _Tp __ad = __a * __d;
-  _Tp __bc = __b * __c;
-  _Tp __x  = __ac - __bd;
-  _Tp __y  = __ad + __bc;
-  if (std::__constexpr_isnan(__x) && std::__constexpr_isnan(__y)) {
-    bool __recalc = false;
-    if (std::__constexpr_isinf(__a) || std::__constexpr_isinf(__b)) {
-      __a = std::__constexpr_copysign(std::__constexpr_isinf(__a) ? _Tp(1) : _Tp(0), __a);
-      __b = std::__constexpr_copysign(std::__constexpr_isinf(__b) ? _Tp(1) : _Tp(0), __b);
-      if (std::__constexpr_isnan(__c))
-        __c = std::__constexpr_copysign(_Tp(0), __c);
-      if (std::__constexpr_isnan(__d))
-        __d = std::__constexpr_copysign(_Tp(0), __d);
-      __recalc = true;
-    }
-    if (std::__constexpr_isinf(__c) || std::__constexpr_isinf(__d)) {
-      __c = std::__constexpr_copysign(std::__constexpr_isinf(__c) ? _Tp(1) : _Tp(0), __c);
-      __d = std::__constexpr_copysign(std::__constexpr_isinf(__d) ? _Tp(1) : _Tp(0), __d);
-      if (std::__constexpr_isnan(__a))
-        __a = std::__constexpr_copysign(_Tp(0), __a);
-      if (std::__constexpr_isnan(__b))
-        __b = std::__constexpr_copysign(_Tp(0), __b);
-      __recalc = true;
-    }
-    if (!__recalc && (std::__constexpr_isinf(__ac) || std::__constexpr_isinf(__bd) || std::__constexpr_isinf(__ad) ||
-                      std::__constexpr_isinf(__bc))) {
-      if (std::__constexpr_isnan(__a))
-        __a = std::__constexpr_copysign(_Tp(0), __a);
-      if (std::__constexpr_isnan(__b))
-        __b = std::__constexpr_copysign(_Tp(0), __b);
-      if (std::__constexpr_isnan(__c))
-        __c = std::__constexpr_copysign(_Tp(0), __c);
-      if (std::__constexpr_isnan(__d))
-        __d = std::__constexpr_copysign(_Tp(0), __d);
-      __recalc = true;
-    }
-    if (__recalc) {
-      __x = _Tp(INFINITY) * (__a * __c - __b * __d);
-      __y = _Tp(INFINITY) * (__a * __d + __b * __c);
-    }
-  }
-  return complex<_Tp>(__x, __y);
+  return complex<_Tp>((__a * __c) - (__b * __d), (__a * __d) + (__b * __c));
 }
 
 template <class _Tp>
@@ -807,80 +804,22 @@ operator*(const _Tp& __x, const complex<_Tp>& __y) {
   return __t;
 }
 
-template <class _Tp>
+template <class _Tp, __enable_if_t<is_floating_point<_Tp>::value, int> >
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 complex<_Tp>
+operator/(const complex<_Tp>& __lhs, const complex<_Tp>& __rhs) {
+  return complex<_Tp>(__from_builtin_tag(), __lhs.__builtin() / __rhs.__builtin());
+}
+
+template <class _Tp, __enable_if_t<!is_floating_point<_Tp>::value, int> >
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 complex<_Tp>
 operator/(const complex<_Tp>& __z, const complex<_Tp>& __w) {
-  int __ilogbw = 0;
-  _Tp __a      = __z.real();
-  _Tp __b      = __z.imag();
-  _Tp __c      = __w.real();
-  _Tp __d      = __w.imag();
-  _Tp __logbw  = std::__constexpr_logb(std::__constexpr_fmax(std::__constexpr_fabs(__c), std::__constexpr_fabs(__d)));
-  if (std::__constexpr_isfinite(__logbw)) {
-    __ilogbw = static_cast<int>(__logbw);
-    __c      = std::__constexpr_scalbn(__c, -__ilogbw);
-    __d      = std::__constexpr_scalbn(__d, -__ilogbw);
-  }
-
-  // Avoid floating point operations that are invalid during constant evaluation
-  if (__libcpp_is_constant_evaluated()) {
-    bool __z_zero = __a == _Tp(0) && __b == _Tp(0);
-    bool __w_zero = __c == _Tp(0) && __d == _Tp(0);
-    bool __z_inf  = std::__constexpr_isinf(__a) || std::__constexpr_isinf(__b);
-    bool __w_inf  = std::__constexpr_isinf(__c) || std::__constexpr_isinf(__d);
-    bool __z_nan =
-        !__z_inf && ((std::__constexpr_isnan(__a) && std::__constexpr_isnan(__b)) ||
-                     (std::__constexpr_isnan(__a) && __b == _Tp(0)) || (__a == _Tp(0) && std::__constexpr_isnan(__b)));
-    bool __w_nan =
-        !__w_inf && ((std::__constexpr_isnan(__c) && std::__constexpr_isnan(__d)) ||
-                     (std::__constexpr_isnan(__c) && __d == _Tp(0)) || (__c == _Tp(0) && std::__constexpr_isnan(__d)));
-    if ((__z_nan || __w_nan) || (__z_inf && __w_inf)) {
-      return complex<_Tp>(_Tp(numeric_limits<_Tp>::quiet_NaN()), _Tp(0));
-    }
-    bool __z_nonzero_nan = !__z_inf && !__z_nan && (std::__constexpr_isnan(__a) || std::__constexpr_isnan(__b));
-    bool __w_nonzero_nan = !__w_inf && !__w_nan && (std::__constexpr_isnan(__c) || std::__constexpr_isnan(__d));
-    if (__z_nonzero_nan || __w_nonzero_nan) {
-      if (__w_zero) {
-        return complex<_Tp>(_Tp(numeric_limits<_Tp>::infinity()), _Tp(numeric_limits<_Tp>::infinity()));
-      }
-      return complex<_Tp>(_Tp(numeric_limits<_Tp>::quiet_NaN()), _Tp(0));
-    }
-    if (__w_inf) {
-      return complex<_Tp>(_Tp(0), _Tp(0));
-    }
-    if (__z_inf) {
-      return complex<_Tp>(_Tp(numeric_limits<_Tp>::infinity()), _Tp(numeric_limits<_Tp>::infinity()));
-    }
-    if (__w_zero) {
-      if (__z_zero) {
-        return complex<_Tp>(_Tp(numeric_limits<_Tp>::quiet_NaN()), _Tp(0));
-      }
-      return complex<_Tp>(_Tp(numeric_limits<_Tp>::infinity()), _Tp(numeric_limits<_Tp>::infinity()));
-    }
-  }
+  _Tp __a = __z.real();
+  _Tp __b = __z.imag();
+  _Tp __c = __w.real();
+  _Tp __d = __w.imag();
 
   _Tp __denom = __c * __c + __d * __d;
-  _Tp __x     = std::__constexpr_scalbn((__a * __c + __b * __d) / __denom, -__ilogbw);
-  _Tp __y     = std::__constexpr_scalbn((__b * __c - __a * __d) / __denom, -__ilogbw);
-  if (std::__constexpr_isnan(__x) && std::__constexpr_isnan(__y)) {
-    if ((__denom == _Tp(0)) && (!std::__constexpr_isnan(__a) || !std::__constexpr_isnan(__b))) {
-      __x = std::__constexpr_copysign(_Tp(INFINITY), __c) * __a;
-      __y = std::__constexpr_copysign(_Tp(INFINITY), __c) * __b;
-    } else if ((std::__constexpr_isinf(__a) || std::__constexpr_isinf(__b)) && std::__constexpr_isfinite(__c) &&
-               std::__constexpr_isfinite(__d)) {
-      __a = std::__constexpr_copysign(std::__constexpr_isinf(__a) ? _Tp(1) : _Tp(0), __a);
-      __b = std::__constexpr_copysign(std::__constexpr_isinf(__b) ? _Tp(1) : _Tp(0), __b);
-      __x = _Tp(INFINITY) * (__a * __c + __b * __d);
-      __y = _Tp(INFINITY) * (__b * __c - __a * __d);
-    } else if (std::__constexpr_isinf(__logbw) && __logbw > _Tp(0) && std::__constexpr_isfinite(__a) &&
-               std::__constexpr_isfinite(__b)) {
-      __c = std::__constexpr_copysign(std::__constexpr_isinf(__c) ? _Tp(1) : _Tp(0), __c);
-      __d = std::__constexpr_copysign(std::__constexpr_isinf(__d) ? _Tp(1) : _Tp(0), __d);
-      __x = _Tp(0) * (__a * __c + __b * __d);
-      __y = _Tp(0) * (__b * __c - __a * __d);
-    }
-  }
-  return complex<_Tp>(__x, __y);
+  return complex<_Tp>((__a * __c + __b * __d) / __denom, (__b * __c - __a * __d) / __denom);
 }
 
 template <class _Tp>

diff  --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 8ba6f00bf9dad..b34f9e614ae0b 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -99,6 +99,7 @@ endif()
 
 if(WIN32)
   list(APPEND LIBCXX_SOURCES
+    support/win32/compiler_rt_shims.cpp
     support/win32/locale_win32.cpp
     support/win32/support.cpp
     )

diff  --git a/libcxx/src/support/win32/compiler_rt_shims.cpp b/libcxx/src/support/win32/compiler_rt_shims.cpp
new file mode 100644
index 0000000000000..ab263224906ed
--- /dev/null
+++ b/libcxx/src/support/win32/compiler_rt_shims.cpp
@@ -0,0 +1,110 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+//
+// This file reimplements builtins that are normally provided by compiler-rt, which is
+// not provided on Windows. This should go away once compiler-rt is shipped on Windows.
+//
+
+#include <cmath>
+#include <complex>
+
+template <class T>
+static std::__complex_t<T> mul_impl(T a, T b, T c, T d) {
+  T __ac = a * c;
+  T __bd = b * d;
+  T __ad = a * d;
+  T __bc = b * c;
+  T __x  = __ac - __bd;
+  T __y  = __ad + __bc;
+  if (std::isnan(__x) && std::isnan(__y)) {
+    bool recalc = false;
+    if (std::isinf(a) || std::isinf(b)) {
+      a = std::copysign(std::isinf(a) ? T(1) : T(0), a);
+      b = std::copysign(std::isinf(b) ? T(1) : T(0), b);
+      if (std::isnan(c))
+        c = std::copysign(T(0), c);
+      if (std::isnan(d))
+        d = std::copysign(T(0), d);
+      recalc = true;
+    }
+    if (std::isinf(c) || std::isinf(d)) {
+      c = std::copysign(std::isinf(c) ? T(1) : T(0), c);
+      d = std::copysign(std::isinf(d) ? T(1) : T(0), d);
+      if (std::isnan(a))
+        a = std::copysign(T(0), a);
+      if (std::isnan(b))
+        b = std::copysign(T(0), b);
+      recalc = true;
+    }
+    if (!recalc && (std::isinf(__ac) || std::isinf(__bd) || std::isinf(__ad) || std::isinf(__bc))) {
+      if (std::isnan(a))
+        a = std::copysign(T(0), a);
+      if (std::isnan(b))
+        b = std::copysign(T(0), b);
+      if (std::isnan(c))
+        c = std::copysign(T(0), c);
+      if (std::isnan(d))
+        d = std::copysign(T(0), d);
+      recalc = true;
+    }
+    if (recalc) {
+      __x = T(INFINITY) * (a * c - b * d);
+      __y = T(INFINITY) * (a * d + b * c);
+    }
+  }
+  return {__x, __y};
+}
+
+extern "C" _LIBCPP_EXPORTED_FROM_ABI _Complex double __muldc3(double a, double b, double c, double d) {
+  return mul_impl(a, b, c, d);
+}
+
+extern "C" _LIBCPP_EXPORTED_FROM_ABI _Complex float __mulsc3(float a, float b, float c, float d) {
+  return mul_impl(a, b, c, d);
+}
+
+template <class T>
+std::__complex_t<T> div_impl(T a, T b, T c, T d) {
+  int ilogbw = 0;
+  T __logbw  = std::logb(std::fmax(std::fabs(c), std::fabs(d)));
+  if (std::isfinite(__logbw)) {
+    ilogbw = static_cast<int>(__logbw);
+    c      = std::scalbn(c, -ilogbw);
+    d      = std::scalbn(d, -ilogbw);
+  }
+
+  T denom = c * c + d * d;
+  T x     = std::scalbn((a * c + b * d) / denom, -ilogbw);
+  T y     = std::scalbn((b * c - a * d) / denom, -ilogbw);
+  if (std::isnan(x) && std::isnan(y)) {
+    if ((denom == T(0)) && (!std::isnan(a) || !std::isnan(b))) {
+      x = std::copysign(T(INFINITY), c) * a;
+      y = std::copysign(T(INFINITY), c) * b;
+    } else if ((std::isinf(a) || std::isinf(b)) && std::isfinite(c) && std::isfinite(d)) {
+      a = std::copysign(std::isinf(a) ? T(1) : T(0), a);
+      b = std::copysign(std::isinf(b) ? T(1) : T(0), b);
+      x = T(INFINITY) * (a * c + b * d);
+      y = T(INFINITY) * (b * c - a * d);
+    } else if (std::isinf(__logbw) && __logbw > T(0) && std::isfinite(a) && std::isfinite(b)) {
+      c = std::copysign(std::isinf(c) ? T(1) : T(0), c);
+      d = std::copysign(std::isinf(d) ? T(1) : T(0), d);
+      x = T(0) * (a * c + b * d);
+      y = T(0) * (b * c - a * d);
+    }
+  }
+  return {x, y};
+}
+
+extern "C" _LIBCPP_EXPORTED_FROM_ABI _Complex double __divdc3(double a, double b, double c, double d) {
+  return div_impl(a, b, c, d);
+}
+
+extern "C" _LIBCPP_EXPORTED_FROM_ABI _Complex float __divsc3(float a, float b, float c, float d) {
+  return div_impl(a, b, c, d);
+}

diff  --git a/libcxx/test/std/numerics/complex.number/complex.member.ops/divide_equal_complex.pass.cpp b/libcxx/test/std/numerics/complex.number/complex.member.ops/divide_equal_complex.pass.cpp
index af2561e4cb9e9..61e0d01f2ae4d 100644
--- a/libcxx/test/std/numerics/complex.number/complex.member.ops/divide_equal_complex.pass.cpp
+++ b/libcxx/test/std/numerics/complex.number/complex.member.ops/divide_equal_complex.pass.cpp
@@ -10,9 +10,10 @@
 
 // complex& operator/=(const complex& rhs); // constexpr in C++20
 
-#include <complex>
 #include <cassert>
+#include <complex>
 
+#include "floating_pointer_helpers.h"
 #include "test_macros.h"
 
 template <class T>
@@ -22,28 +23,28 @@ test()
 {
     std::complex<T> c(-4, 7.5);
     const std::complex<T> c2(1.5, 2.5);
-    assert(c.real() == -4);
-    assert(c.imag() == 7.5);
+    assert(is_close(c.real(), T(-4)));
+    assert(is_close(c.imag(), T(7.5)));
     c /= c2;
-    assert(c.real() == 1.5);
-    assert(c.imag() == 2.5);
+    assert(is_close(c.real(), T(1.5)));
+    assert(is_close(c.imag(), T(2.5)));
     c /= c2;
-    assert(c.real() == 1);
-    assert(c.imag() == 0);
+    assert(is_close(c.real(), T(1)));
+    assert(is_close(c.imag(), T(0)));
 
     std::complex<T> c3;
 
     c3 = c;
     std::complex<int> ic (1,1);
     c3 /= ic;
-    assert(c3.real() == 0.5);
-    assert(c3.imag() == -0.5);
+    assert(is_close(c3.real(), T(0.5)));
+    assert(is_close(c3.imag(), T(-0.5)));
 
     c3 = c;
     std::complex<float> fc (1,1);
     c3 /= fc;
-    assert(c3.real() == 0.5);
-    assert(c3.imag() == -0.5);
+    assert(is_close(c3.real(), T(0.5)));
+    assert(is_close(c3.imag(), T(-0.5)));
     return true;
 }
 

diff  --git a/libcxx/test/std/numerics/complex.number/complex.ops/complex_divide_complex.pass.cpp b/libcxx/test/std/numerics/complex.number/complex.ops/complex_divide_complex.pass.cpp
index 7119710a95557..d12dfd994b0ae 100644
--- a/libcxx/test/std/numerics/complex.number/complex.ops/complex_divide_complex.pass.cpp
+++ b/libcxx/test/std/numerics/complex.number/complex.ops/complex_divide_complex.pass.cpp
@@ -12,11 +12,12 @@
 //   complex<T>
 //   operator/(const complex<T>& lhs, const complex<T>& rhs); // constexpr in C++20
 
-// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=5000000
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=2000000
 
-#include <complex>
 #include <cassert>
+#include <complex>
 
+#include "floating_pointer_helpers.h"
 #include "test_macros.h"
 #include "../cases.h"
 
@@ -27,7 +28,9 @@ test()
 {
     const std::complex<T> lhs(-4.0, 7.5);
     const std::complex<T> rhs(1.5, 2.5);
-    assert(lhs / rhs == std::complex<T>(1.5, 2.5));
+    const std::complex<T> c = lhs / rhs;
+    assert(is_close(c.real(), T(1.5)));
+    assert(is_close(c.imag(), T(2.5)));
     return true;
 }
 

diff  --git a/libcxx/test/std/numerics/complex.number/complex.ops/complex_times_complex.pass.cpp b/libcxx/test/std/numerics/complex.number/complex.ops/complex_times_complex.pass.cpp
index 38ec71e3a00a5..817d6cdf492d3 100644
--- a/libcxx/test/std/numerics/complex.number/complex.ops/complex_times_complex.pass.cpp
+++ b/libcxx/test/std/numerics/complex.number/complex.ops/complex_times_complex.pass.cpp
@@ -12,7 +12,7 @@
 //   complex<T>
 //   operator*(const complex<T>& lhs, const complex<T>& rhs); // constexpr in C++20
 
-// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=5000000
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=2000000
 
 #include <complex>
 #include <cassert>

diff  --git a/libcxx/test/std/numerics/complex.number/complex.ops/scalar_divide_complex.pass.cpp b/libcxx/test/std/numerics/complex.number/complex.ops/scalar_divide_complex.pass.cpp
index 50981216cc5f0..f32b560c1e864 100644
--- a/libcxx/test/std/numerics/complex.number/complex.ops/scalar_divide_complex.pass.cpp
+++ b/libcxx/test/std/numerics/complex.number/complex.ops/scalar_divide_complex.pass.cpp
@@ -24,7 +24,11 @@ test()
 {
     const T lhs(-8.5);
     const std::complex<T> rhs(1.5, 2.5);
-    assert(lhs / rhs == std::complex<T>(-1.5, 2.5));
+    const std::complex<T> c = lhs / rhs;
+    assert(c.real() >= T(-1.500000000000001));
+    assert(c.real() <= T(-1.499999999999999));
+    assert(c.imag() >= T(2.499999999999999));
+    assert(c.imag() <= T(2.500000000000001));
     return true;
 }
 

diff  --git a/libcxx/test/support/floating_pointer_helpers.h b/libcxx/test/support/floating_pointer_helpers.h
new file mode 100644
index 0000000000000..5943425bab018
--- /dev/null
+++ b/libcxx/test/support/floating_pointer_helpers.h
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_SUPPORT_FLOATING_POINT_HELPERS_H
+#define TEST_SUPPORT_FLOATING_POINT_HELPERS_H
+
+#include <limits>
+
+#include "test_macros.h"
+
+template <class T>
+TEST_CONSTEXPR_CXX20 bool is_close(T v, T comp) {
+  return v <= comp + std::numeric_limits<T>::epsilon() && v >= comp - std::numeric_limits<T>::epsilon();
+}
+
+#endif // TEST_SUPPORT_FLOATING_POINT_HELPERS_H


        


More information about the libcxx-commits mailing list