From 09d7b890e035c8ace45642f27edc0ff5768311eb Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Wed, 25 Feb 2026 11:46:51 -0700 Subject: [PATCH] [libc++] Add a thread-safe version of std::lgamma in the dylib (#153631) Libc++ currently redeclares ::lgamma_r on platforms that provide it. This causes issues when building with modules, and redeclaring functions provided by another library (here the C library) is bad hygiene. Instead, use an asm declaration to call the right function without having to redeclare it. --- libcxx/include/__math/gamma.h | 26 ++++++++++++++++++ .../include/__random/binomial_distribution.h | 27 +++---------------- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/libcxx/include/__math/gamma.h b/libcxx/include/__math/gamma.h index 693e111a84e9..6c82cbf0cae9 100644 --- a/libcxx/include/__math/gamma.h +++ b/libcxx/include/__math/gamma.h @@ -55,6 +55,32 @@ inline _LIBCPP_HIDE_FROM_ABI double tgamma(_A1 __x) _NOEXCEPT { return __builtin_tgamma((double)__x); } +// __lgamma_r +// +// POSIX systems provide a function named lgamma_r which is a reentrant version of lgamma. Use that +// whenever possible. However, we avoid re-declaring the actual function since different platforms +// declare it differently in the first place: instead use `asm` to get the compiler to call the right +// function. + +#if defined(_LIBCPP_MSVCRT_LIKE) // reentrant version is not available on Windows + +inline _LIBCPP_HIDE_FROM_ABI double __lgamma_r(double __d) _NOEXCEPT { return __builtin_lgamma(__d); } + +#else + +# if defined(_LIBCPP_OBJECT_FORMAT_MACHO) +double __lgamma_r_shim(double, int*) _NOEXCEPT __asm__("_lgamma_r"); +# else +double __lgamma_r_shim(double, int*) _NOEXCEPT __asm__("lgamma_r"); +# endif + +inline _LIBCPP_HIDE_FROM_ABI double __lgamma_r(double __d) _NOEXCEPT { + int __sign; + return __math::__lgamma_r_shim(__d, &__sign); +} + +#endif + } // namespace __math _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__random/binomial_distribution.h b/libcxx/include/__random/binomial_distribution.h index 6d07ff3c7df0..76996abacb3a 100644 --- a/libcxx/include/__random/binomial_distribution.h +++ b/libcxx/include/__random/binomial_distribution.h @@ -10,6 +10,7 @@ #define _LIBCPP___RANDOM_BINOMIAL_DISTRIBUTION_H #include <__config> +#include <__math/gamma.h> #include <__random/is_valid.h> #include <__random/uniform_real_distribution.h> #include @@ -97,33 +98,13 @@ public: } }; -// Some libc declares the math functions to be `noexcept`. -#if _LIBCPP_GLIBC_PREREQ(2, 8) || _LIBCPP_LIBC_LLVM_LIBC -# define _LIBCPP_LGAMMA_R_NOEXCEPT _NOEXCEPT -#else -# define _LIBCPP_LGAMMA_R_NOEXCEPT -#endif - -#if !defined(_LIBCPP_MSVCRT_LIKE) -extern "C" double lgamma_r(double, int*) _LIBCPP_LGAMMA_R_NOEXCEPT; -#endif - -inline _LIBCPP_HIDE_FROM_ABI double __libcpp_lgamma(double __d) { -#if defined(_LIBCPP_MSVCRT_LIKE) - return lgamma(__d); -#else - int __sign; - return lgamma_r(__d, &__sign); -#endif -} - template binomial_distribution<_IntType>::param_type::param_type(result_type __t, double __p) : __t_(__t), __p_(__p) { if (0 < __p_ && __p_ < 1) { __r0_ = static_cast((__t_ + 1) * __p_); - __pr_ = std::exp( - std::__libcpp_lgamma(__t_ + 1.) - std::__libcpp_lgamma(__r0_ + 1.) - std::__libcpp_lgamma(__t_ - __r0_ + 1.) + - __r0_ * std::log(__p_) + (__t_ - __r0_) * std::log(1 - __p_)); + __pr_ = + std::exp(__math::__lgamma_r(__t_ + 1.) - __math::__lgamma_r(__r0_ + 1.) - + __math::__lgamma_r(__t_ - __r0_ + 1.) + __r0_ * std::log(__p_) + (__t_ - __r0_) * std::log(1 - __p_)); __odds_ratio_ = __p_ / (1 - __p_); } }