1 //===-- lib/Evaluate/intrinsics-library.cpp -------------------------------===// 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 // This file defines host runtime functions that can be used for folding 10 // intrinsic functions. 11 // The default HostIntrinsicProceduresLibrary is built with <cmath> and 12 // <complex> functions that are guaranteed to exist from the C++ standard. 13 14 #include "intrinsics-library-templates.h" 15 #include <cmath> 16 #include <complex> 17 18 namespace Fortran::evaluate { 19 20 // Note: argument passing is ignored in equivalence 21 bool HostIntrinsicProceduresLibrary::HasEquivalentProcedure( 22 const IntrinsicProcedureRuntimeDescription &sym) const { 23 const auto rteProcRange{procedures_.equal_range(sym.name)}; 24 const size_t nargs{sym.argumentsType.size()}; 25 for (auto iter{rteProcRange.first}; iter != rteProcRange.second; ++iter) { 26 if (nargs == iter->second.argumentsType.size() && 27 sym.returnType == iter->second.returnType && 28 (sym.isElemental || iter->second.isElemental)) { 29 bool match{true}; 30 int pos{0}; 31 for (const auto &type : sym.argumentsType) { 32 if (type != iter->second.argumentsType[pos++]) { 33 match = false; 34 break; 35 } 36 } 37 if (match) { 38 return true; 39 } 40 } 41 } 42 return false; 43 } 44 45 // Map numerical intrinsic to <cmath>/<complex> functions 46 47 // Define which host runtime functions will be used for folding 48 49 template <typename HostT> 50 static void AddLibmRealHostProcedures( 51 HostIntrinsicProceduresLibrary &hostIntrinsicLibrary) { 52 using F = FuncPointer<HostT, HostT>; 53 using F2 = FuncPointer<HostT, HostT, HostT>; 54 HostRuntimeIntrinsicProcedure libmSymbols[]{ 55 {"acos", F{std::acos}, true}, 56 {"acosh", F{std::acosh}, true}, 57 {"asin", F{std::asin}, true}, 58 {"asinh", F{std::asinh}, true}, 59 {"atan", F{std::atan}, true}, 60 {"atan", F2{std::atan2}, true}, 61 {"atanh", F{std::atanh}, true}, 62 {"cos", F{std::cos}, true}, 63 {"cosh", F{std::cosh}, true}, 64 {"erf", F{std::erf}, true}, 65 {"erfc", F{std::erfc}, true}, 66 {"exp", F{std::exp}, true}, 67 {"gamma", F{std::tgamma}, true}, 68 {"hypot", F2{std::hypot}, true}, 69 {"log", F{std::log}, true}, 70 {"log10", F{std::log10}, true}, 71 {"log_gamma", F{std::lgamma}, true}, 72 {"mod", F2{std::fmod}, true}, 73 {"pow", F2{std::pow}, true}, 74 {"sin", F{std::sin}, true}, 75 {"sinh", F{std::sinh}, true}, 76 {"sqrt", F{std::sqrt}, true}, 77 {"tan", F{std::tan}, true}, 78 {"tanh", F{std::tanh}, true}, 79 }; 80 // Note: cmath does not have modulo and erfc_scaled equivalent 81 82 // Note regarding lack of bessel function support: 83 // C++17 defined standard Bessel math functions std::cyl_bessel_j 84 // and std::cyl_neumann that can be used for Fortran j and y 85 // bessel functions. However, they are not yet implemented in 86 // clang libc++ (ok in GNU libstdc++). C maths functions j0... 87 // are not C standard but a GNU extension so they are not used 88 // to avoid introducing incompatibilities. 89 // Use libpgmath to get bessel function folding support. 90 // TODO: Add Bessel functions when possible. 91 92 for (auto sym : libmSymbols) { 93 if (!hostIntrinsicLibrary.HasEquivalentProcedure(sym)) { 94 hostIntrinsicLibrary.AddProcedure(std::move(sym)); 95 } 96 } 97 } 98 99 template <typename HostT> 100 static void AddLibmComplexHostProcedures( 101 HostIntrinsicProceduresLibrary &hostIntrinsicLibrary) { 102 using F = FuncPointer<std::complex<HostT>, const std::complex<HostT> &>; 103 using F2 = FuncPointer<std::complex<HostT>, const std::complex<HostT> &, 104 const std::complex<HostT> &>; 105 using F2a = FuncPointer<std::complex<HostT>, const HostT &, 106 const std::complex<HostT> &>; 107 using F2b = FuncPointer<std::complex<HostT>, const std::complex<HostT> &, 108 const HostT &>; 109 HostRuntimeIntrinsicProcedure libmSymbols[]{ 110 {"abs", FuncPointer<HostT, const std::complex<HostT> &>{std::abs}, true}, 111 {"acos", F{std::acos}, true}, 112 {"acosh", F{std::acosh}, true}, 113 {"asin", F{std::asin}, true}, 114 {"asinh", F{std::asinh}, true}, 115 {"atan", F{std::atan}, true}, 116 {"atanh", F{std::atanh}, true}, 117 {"cos", F{std::cos}, true}, 118 {"cosh", F{std::cosh}, true}, 119 {"exp", F{std::exp}, true}, 120 {"log", F{std::log}, true}, 121 {"pow", F2{std::pow}, true}, 122 {"pow", F2a{std::pow}, true}, 123 {"pow", F2b{std::pow}, true}, 124 {"sin", F{std::sin}, true}, 125 {"sinh", F{std::sinh}, true}, 126 {"sqrt", F{std::sqrt}, true}, 127 {"tan", F{std::tan}, true}, 128 {"tanh", F{std::tanh}, true}, 129 }; 130 131 for (auto sym : libmSymbols) { 132 if (!hostIntrinsicLibrary.HasEquivalentProcedure(sym)) { 133 hostIntrinsicLibrary.AddProcedure(std::move(sym)); 134 } 135 } 136 } 137 138 void InitHostIntrinsicLibraryWithLibm(HostIntrinsicProceduresLibrary &lib) { 139 if constexpr (host::FortranTypeExists<float>()) { 140 AddLibmRealHostProcedures<float>(lib); 141 } 142 if constexpr (host::FortranTypeExists<double>()) { 143 AddLibmRealHostProcedures<double>(lib); 144 } 145 if constexpr (host::FortranTypeExists<long double>()) { 146 AddLibmRealHostProcedures<long double>(lib); 147 } 148 149 if constexpr (host::FortranTypeExists<std::complex<float>>()) { 150 AddLibmComplexHostProcedures<float>(lib); 151 } 152 if constexpr (host::FortranTypeExists<std::complex<double>>()) { 153 AddLibmComplexHostProcedures<double>(lib); 154 } 155 if constexpr (host::FortranTypeExists<std::complex<long double>>()) { 156 AddLibmComplexHostProcedures<long double>(lib); 157 } 158 } 159 160 #if LINK_WITH_LIBPGMATH 161 namespace pgmath { 162 // Define mapping between numerical intrinsics and libpgmath symbols 163 // namespace is used to have shorter names on repeated patterns. 164 // A class would be better to hold all these defs, but GCC does not 165 // support specialization of template variables inside class even 166 // if it is C++14 standard compliant here because there are only full 167 // specializations. 168 169 // List of intrinsics that have libpgmath implementations that can be used for 170 // constant folding The tag names must match the name used inside libpgmath name 171 // so that the macro below work. 172 enum class I { 173 acos, 174 acosh, 175 asin, 176 asinh, 177 atan, 178 atan2, 179 atanh, 180 bessel_j0, 181 bessel_j1, 182 bessel_jn, 183 bessel_y0, 184 bessel_y1, 185 bessel_yn, 186 cos, 187 cosh, 188 erf, 189 erfc, 190 erfc_scaled, 191 exp, 192 gamma, 193 hypot, 194 log, 195 log10, 196 log_gamma, 197 mod, 198 pow, 199 sin, 200 sinh, 201 sqrt, 202 tan, 203 tanh 204 }; 205 206 // Library versions: P for Precise, R for Relaxed, F for Fast 207 enum class L { F, R, P }; 208 209 struct NoSuchRuntimeSymbol {}; 210 template <L, I, typename> constexpr auto Sym{NoSuchRuntimeSymbol{}}; 211 212 // Macros to declare fast/relaxed/precise libpgmath variants. 213 #define DECLARE_PGMATH_FAST_REAL(func) \ 214 extern "C" float __fs_##func##_1(float); \ 215 extern "C" double __fd_##func##_1(double); \ 216 template <> constexpr auto Sym<L::F, I::func, float>{__fs_##func##_1}; \ 217 template <> constexpr auto Sym<L::F, I::func, double>{__fd_##func##_1}; 218 219 #define DECLARE_PGMATH_FAST_COMPLEX(func) \ 220 extern "C" float _Complex __fc_##func##_1(float _Complex); \ 221 extern "C" double _Complex __fz_##func##_1(double _Complex); \ 222 template <> \ 223 constexpr auto Sym<L::F, I::func, std::complex<float>>{__fc_##func##_1}; \ 224 template <> \ 225 constexpr auto Sym<L::F, I::func, std::complex<double>>{__fz_##func##_1}; 226 227 #define DECLARE_PGMATH_FAST_ALL_FP(func) \ 228 DECLARE_PGMATH_FAST_REAL(func) \ 229 DECLARE_PGMATH_FAST_COMPLEX(func) 230 231 #define DECLARE_PGMATH_PRECISE_REAL(func) \ 232 extern "C" float __ps_##func##_1(float); \ 233 extern "C" double __pd_##func##_1(double); \ 234 template <> constexpr auto Sym<L::P, I::func, float>{__ps_##func##_1}; \ 235 template <> constexpr auto Sym<L::P, I::func, double>{__pd_##func##_1}; 236 237 #define DECLARE_PGMATH_PRECISE_COMPLEX(func) \ 238 extern "C" float _Complex __pc_##func##_1(float _Complex); \ 239 extern "C" double _Complex __pz_##func##_1(double _Complex); \ 240 template <> \ 241 constexpr auto Sym<L::P, I::func, std::complex<float>>{__pc_##func##_1}; \ 242 template <> \ 243 constexpr auto Sym<L::P, I::func, std::complex<double>>{__pz_##func##_1}; 244 245 #define DECLARE_PGMATH_PRECISE_ALL_FP(func) \ 246 DECLARE_PGMATH_PRECISE_REAL(func) \ 247 DECLARE_PGMATH_PRECISE_COMPLEX(func) 248 249 #define DECLARE_PGMATH_RELAXED_REAL(func) \ 250 extern "C" float __rs_##func##_1(float); \ 251 extern "C" double __rd_##func##_1(double); \ 252 template <> constexpr auto Sym<L::R, I::func, float>{__rs_##func##_1}; \ 253 template <> constexpr auto Sym<L::R, I::func, double>{__rd_##func##_1}; 254 255 #define DECLARE_PGMATH_RELAXED_COMPLEX(func) \ 256 extern "C" float _Complex __rc_##func##_1(float _Complex); \ 257 extern "C" double _Complex __rz_##func##_1(double _Complex); \ 258 template <> \ 259 constexpr auto Sym<L::R, I::func, std::complex<float>>{__rc_##func##_1}; \ 260 template <> \ 261 constexpr auto Sym<L::R, I::func, std::complex<double>>{__rz_##func##_1}; 262 263 #define DECLARE_PGMATH_RELAXED_ALL_FP(func) \ 264 DECLARE_PGMATH_RELAXED_REAL(func) \ 265 DECLARE_PGMATH_RELAXED_COMPLEX(func) 266 267 #define DECLARE_PGMATH_REAL(func) \ 268 DECLARE_PGMATH_FAST_REAL(func) \ 269 DECLARE_PGMATH_PRECISE_REAL(func) \ 270 DECLARE_PGMATH_RELAXED_REAL(func) 271 272 #define DECLARE_PGMATH_COMPLEX(func) \ 273 DECLARE_PGMATH_FAST_COMPLEX(func) \ 274 DECLARE_PGMATH_PRECISE_COMPLEX(func) \ 275 DECLARE_PGMATH_RELAXED_COMPLEX(func) 276 277 #define DECLARE_PGMATH_ALL(func) \ 278 DECLARE_PGMATH_REAL(func) \ 279 DECLARE_PGMATH_COMPLEX(func) 280 281 // Macros to declare fast/relaxed/precise libpgmath variants with two arguments. 282 #define DECLARE_PGMATH_FAST_REAL2(func) \ 283 extern "C" float __fs_##func##_1(float, float); \ 284 extern "C" double __fd_##func##_1(double, double); \ 285 template <> constexpr auto Sym<L::F, I::func, float>{__fs_##func##_1}; \ 286 template <> constexpr auto Sym<L::F, I::func, double>{__fd_##func##_1}; 287 288 #define DECLARE_PGMATH_FAST_COMPLEX2(func) \ 289 extern "C" float _Complex __fc_##func##_1(float _Complex, float _Complex); \ 290 extern "C" double _Complex __fz_##func##_1( \ 291 double _Complex, double _Complex); \ 292 template <> \ 293 constexpr auto Sym<L::F, I::func, std::complex<float>>{__fc_##func##_1}; \ 294 template <> \ 295 constexpr auto Sym<L::F, I::func, std::complex<double>>{__fz_##func##_1}; 296 297 #define DECLARE_PGMATH_FAST_ALL_FP2(func) \ 298 DECLARE_PGMATH_FAST_REAL2(func) \ 299 DECLARE_PGMATH_FAST_COMPLEX2(func) 300 301 #define DECLARE_PGMATH_PRECISE_REAL2(func) \ 302 extern "C" float __ps_##func##_1(float, float); \ 303 extern "C" double __pd_##func##_1(double, double); \ 304 template <> constexpr auto Sym<L::P, I::func, float>{__ps_##func##_1}; \ 305 template <> constexpr auto Sym<L::P, I::func, double>{__pd_##func##_1}; 306 307 #define DECLARE_PGMATH_PRECISE_COMPLEX2(func) \ 308 extern "C" float _Complex __pc_##func##_1(float _Complex, float _Complex); \ 309 extern "C" double _Complex __pz_##func##_1( \ 310 double _Complex, double _Complex); \ 311 template <> \ 312 constexpr auto Sym<L::P, I::func, std::complex<float>>{__pc_##func##_1}; \ 313 template <> \ 314 constexpr auto Sym<L::P, I::func, std::complex<double>>{__pz_##func##_1}; 315 316 #define DECLARE_PGMATH_PRECISE_ALL_FP2(func) \ 317 DECLARE_PGMATH_PRECISE_REAL2(func) \ 318 DECLARE_PGMATH_PRECISE_COMPLEX2(func) 319 320 #define DECLARE_PGMATH_RELAXED_REAL2(func) \ 321 extern "C" float __rs_##func##_1(float, float); \ 322 extern "C" double __rd_##func##_1(double, double); \ 323 template <> constexpr auto Sym<L::R, I::func, float>{__rs_##func##_1}; \ 324 template <> constexpr auto Sym<L::R, I::func, double>{__rd_##func##_1}; 325 326 #define DECLARE_PGMATH_RELAXED_COMPLEX2(func) \ 327 extern "C" float _Complex __rc_##func##_1(float _Complex, float _Complex); \ 328 extern "C" double _Complex __rz_##func##_1( \ 329 double _Complex, double _Complex); \ 330 template <> \ 331 constexpr auto Sym<L::R, I::func, std::complex<float>>{__rc_##func##_1}; \ 332 template <> \ 333 constexpr auto Sym<L::R, I::func, std::complex<double>>{__rz_##func##_1}; 334 335 #define DECLARE_PGMATH_RELAXED_ALL_FP2(func) \ 336 DECLARE_PGMATH_RELAXED_REAL2(func) \ 337 DECLARE_PGMATH_RELAXED_COMPLEX2(func) 338 339 #define DECLARE_PGMATH_REAL2(func) \ 340 DECLARE_PGMATH_FAST_REAL2(func) \ 341 DECLARE_PGMATH_PRECISE_REAL2(func) \ 342 DECLARE_PGMATH_RELAXED_REAL2(func) 343 344 #define DECLARE_PGMATH_COMPLEX2(func) \ 345 DECLARE_PGMATH_FAST_COMPLEX2(func) \ 346 DECLARE_PGMATH_PRECISE_COMPLEX2(func) \ 347 DECLARE_PGMATH_RELAXED_COMPLEX2(func) 348 349 #define DECLARE_PGMATH_ALL2(func) \ 350 DECLARE_PGMATH_REAL2(func) \ 351 DECLARE_PGMATH_COMPLEX2(func) 352 353 // Marcos to declare __mth_i libpgmath variants 354 #define DECLARE_PGMATH_MTH_VERSION_REAL(func) \ 355 extern "C" float __mth_i_##func(float); \ 356 extern "C" double __mth_i_d##func(double); \ 357 template <> constexpr auto Sym<L::F, I::func, float>{__mth_i_##func}; \ 358 template <> constexpr auto Sym<L::F, I::func, double>{__mth_i_d##func}; \ 359 template <> constexpr auto Sym<L::P, I::func, float>{__mth_i_##func}; \ 360 template <> constexpr auto Sym<L::P, I::func, double>{__mth_i_d##func}; \ 361 template <> constexpr auto Sym<L::R, I::func, float>{__mth_i_##func}; \ 362 template <> constexpr auto Sym<L::R, I::func, double>{__mth_i_d##func}; 363 364 // Actual libpgmath declarations 365 DECLARE_PGMATH_ALL(acos) 366 DECLARE_PGMATH_MTH_VERSION_REAL(acosh) 367 DECLARE_PGMATH_ALL(asin) 368 DECLARE_PGMATH_MTH_VERSION_REAL(asinh) 369 DECLARE_PGMATH_ALL(atan) 370 DECLARE_PGMATH_REAL2(atan2) 371 DECLARE_PGMATH_MTH_VERSION_REAL(atanh) 372 DECLARE_PGMATH_MTH_VERSION_REAL(bessel_j0) 373 DECLARE_PGMATH_MTH_VERSION_REAL(bessel_j1) 374 DECLARE_PGMATH_MTH_VERSION_REAL(bessel_y0) 375 DECLARE_PGMATH_MTH_VERSION_REAL(bessel_y1) 376 // bessel_jn and bessel_yn takes an int as first arg 377 extern "C" float __mth_i_bessel_jn(int, float); 378 extern "C" double __mth_i_dbessel_jn(int, double); 379 template <> constexpr auto Sym<L::F, I::bessel_jn, float>{__mth_i_bessel_jn}; 380 template <> constexpr auto Sym<L::F, I::bessel_jn, double>{__mth_i_dbessel_jn}; 381 template <> constexpr auto Sym<L::P, I::bessel_jn, float>{__mth_i_bessel_jn}; 382 template <> constexpr auto Sym<L::P, I::bessel_jn, double>{__mth_i_dbessel_jn}; 383 template <> constexpr auto Sym<L::R, I::bessel_jn, float>{__mth_i_bessel_jn}; 384 template <> constexpr auto Sym<L::R, I::bessel_jn, double>{__mth_i_dbessel_jn}; 385 extern "C" float __mth_i_bessel_yn(int, float); 386 extern "C" double __mth_i_dbessel_yn(int, double); 387 template <> constexpr auto Sym<L::F, I::bessel_yn, float>{__mth_i_bessel_yn}; 388 template <> constexpr auto Sym<L::F, I::bessel_yn, double>{__mth_i_dbessel_yn}; 389 template <> constexpr auto Sym<L::P, I::bessel_yn, float>{__mth_i_bessel_yn}; 390 template <> constexpr auto Sym<L::P, I::bessel_yn, double>{__mth_i_dbessel_yn}; 391 template <> constexpr auto Sym<L::R, I::bessel_yn, float>{__mth_i_bessel_yn}; 392 template <> constexpr auto Sym<L::R, I::bessel_yn, double>{__mth_i_dbessel_yn}; 393 DECLARE_PGMATH_ALL(cos) 394 DECLARE_PGMATH_ALL(cosh) 395 DECLARE_PGMATH_MTH_VERSION_REAL(erf) 396 DECLARE_PGMATH_MTH_VERSION_REAL(erfc) 397 DECLARE_PGMATH_MTH_VERSION_REAL(erfc_scaled) 398 DECLARE_PGMATH_ALL(exp) 399 DECLARE_PGMATH_MTH_VERSION_REAL(gamma) 400 extern "C" float __mth_i_hypot(float, float); 401 extern "C" double __mth_i_dhypot(double, double); 402 template <> constexpr auto Sym<L::F, I::hypot, float>{__mth_i_hypot}; 403 template <> constexpr auto Sym<L::F, I::hypot, double>{__mth_i_dhypot}; 404 template <> constexpr auto Sym<L::P, I::hypot, float>{__mth_i_hypot}; 405 template <> constexpr auto Sym<L::P, I::hypot, double>{__mth_i_dhypot}; 406 template <> constexpr auto Sym<L::R, I::hypot, float>{__mth_i_hypot}; 407 template <> constexpr auto Sym<L::R, I::hypot, double>{__mth_i_dhypot}; 408 DECLARE_PGMATH_ALL(log) 409 DECLARE_PGMATH_REAL(log10) 410 DECLARE_PGMATH_MTH_VERSION_REAL(log_gamma) 411 // no function for modulo in libpgmath 412 extern "C" float __fs_mod_1(float, float); 413 extern "C" double __fd_mod_1(double, double); 414 template <> constexpr auto Sym<L::F, I::mod, float>{__fs_mod_1}; 415 template <> constexpr auto Sym<L::F, I::mod, double>{__fd_mod_1}; 416 template <> constexpr auto Sym<L::P, I::mod, float>{__fs_mod_1}; 417 template <> constexpr auto Sym<L::P, I::mod, double>{__fd_mod_1}; 418 template <> constexpr auto Sym<L::R, I::mod, float>{__fs_mod_1}; 419 template <> constexpr auto Sym<L::R, I::mod, double>{__fd_mod_1}; 420 DECLARE_PGMATH_ALL2(pow) 421 DECLARE_PGMATH_ALL(sin) 422 DECLARE_PGMATH_ALL(sinh) 423 DECLARE_PGMATH_MTH_VERSION_REAL(sqrt) 424 DECLARE_PGMATH_COMPLEX(sqrt) // real versions are __mth_i... 425 DECLARE_PGMATH_ALL(tan) 426 DECLARE_PGMATH_ALL(tanh) 427 428 // Fill the function map used for folding with libpgmath symbols 429 template <L Lib, typename HostT> 430 static void AddLibpgmathRealHostProcedures( 431 HostIntrinsicProceduresLibrary &hostIntrinsicLibrary) { 432 static_assert(std::is_same_v<HostT, float> || std::is_same_v<HostT, double>); 433 HostRuntimeIntrinsicProcedure pgmathSymbols[]{ 434 {"acos", Sym<Lib, I::acos, HostT>, true}, 435 {"acosh", Sym<Lib, I::acosh, HostT>, true}, 436 {"asin", Sym<Lib, I::asin, HostT>, true}, 437 {"asinh", Sym<Lib, I::asinh, HostT>, true}, 438 {"atan", Sym<Lib, I::atan, HostT>, true}, 439 {"atan", Sym<Lib, I::atan2, HostT>, 440 true}, // atan is also the generic name for atan2 441 {"atanh", Sym<Lib, I::atanh, HostT>, true}, 442 {"bessel_j0", Sym<Lib, I::bessel_j0, HostT>, true}, 443 {"bessel_j1", Sym<Lib, I::bessel_j1, HostT>, true}, 444 {"bessel_jn", Sym<Lib, I::bessel_jn, HostT>, true}, 445 {"bessel_y0", Sym<Lib, I::bessel_y0, HostT>, true}, 446 {"bessel_y1", Sym<Lib, I::bessel_y1, HostT>, true}, 447 {"bessel_yn", Sym<Lib, I::bessel_yn, HostT>, true}, 448 {"cos", Sym<Lib, I::cos, HostT>, true}, 449 {"cosh", Sym<Lib, I::cosh, HostT>, true}, 450 {"erf", Sym<Lib, I::erf, HostT>, true}, 451 {"erfc", Sym<Lib, I::erfc, HostT>, true}, 452 {"erfc_scaled", Sym<Lib, I::erfc_scaled, HostT>, true}, 453 {"exp", Sym<Lib, I::exp, HostT>, true}, 454 {"gamma", Sym<Lib, I::gamma, HostT>, true}, 455 {"hypot", Sym<Lib, I::hypot, HostT>, true}, 456 {"log", Sym<Lib, I::log, HostT>, true}, 457 {"log10", Sym<Lib, I::log10, HostT>, true}, 458 {"log_gamma", Sym<Lib, I::log_gamma, HostT>, true}, 459 {"mod", Sym<Lib, I::mod, HostT>, true}, 460 {"pow", Sym<Lib, I::pow, HostT>, true}, 461 {"sin", Sym<Lib, I::sin, HostT>, true}, 462 {"sinh", Sym<Lib, I::sinh, HostT>, true}, 463 {"sqrt", Sym<Lib, I::sqrt, HostT>, true}, 464 {"tan", Sym<Lib, I::tan, HostT>, true}, 465 {"tanh", Sym<Lib, I::tanh, HostT>, true}, 466 }; 467 468 for (auto sym : pgmathSymbols) { 469 hostIntrinsicLibrary.AddProcedure(std::move(sym)); 470 } 471 } 472 473 // Note: std::complex and _complex are layout compatible but are not guaranteed 474 // to be linkage compatible. For instance, on i386, float _Complex is returned 475 // by a pair of register but std::complex<float> is returned by structure 476 // address. To fix the issue, wrapper around C _Complex functions are defined 477 // below. 478 template <FuncPointer<float _Complex, float _Complex> func> 479 static std::complex<float> ComplexCFuncWrapper(std::complex<float> &arg) { 480 float _Complex res{func(*reinterpret_cast<float _Complex *>(&arg))}; 481 return *reinterpret_cast<std::complex<float> *>(&res); 482 } 483 484 template <FuncPointer<double _Complex, double _Complex> func> 485 static std::complex<double> ComplexCFuncWrapper(std::complex<double> &arg) { 486 double _Complex res{func(*reinterpret_cast<double _Complex *>(&arg))}; 487 return *reinterpret_cast<std::complex<double> *>(&res); 488 } 489 490 template <FuncPointer<float _Complex, float _Complex, float _Complex> func> 491 static std::complex<float> ComplexCFuncWrapper( 492 std::complex<float> &arg1, std::complex<float> &arg2) { 493 float _Complex res{func(*reinterpret_cast<float _Complex *>(&arg1), 494 *reinterpret_cast<float _Complex *>(&arg2))}; 495 return *reinterpret_cast<std::complex<float> *>(&res); 496 } 497 498 template <FuncPointer<double _Complex, double _Complex, double _Complex> func> 499 static std::complex<double> ComplexCFuncWrapper( 500 std::complex<double> &arg1, std::complex<double> &arg2) { 501 double _Complex res{func(*reinterpret_cast<double _Complex *>(&arg1), 502 *reinterpret_cast<double _Complex *>(&arg2))}; 503 return *reinterpret_cast<std::complex<double> *>(&res); 504 } 505 506 template <L Lib, typename HostT> 507 static void AddLibpgmathComplexHostProcedures( 508 HostIntrinsicProceduresLibrary &hostIntrinsicLibrary) { 509 static_assert(std::is_same_v<HostT, float> || std::is_same_v<HostT, double>); 510 using CHostT = std::complex<HostT>; 511 // cmath is used to complement pgmath when symbols are not available 512 using CmathF = FuncPointer<CHostT, const CHostT &>; 513 HostRuntimeIntrinsicProcedure pgmathSymbols[]{ 514 {"abs", FuncPointer<HostT, const CHostT &>{std::abs}, true}, 515 {"acos", ComplexCFuncWrapper<Sym<Lib, I::acos, CHostT>>, true}, 516 {"acosh", CmathF{std::acosh}, true}, 517 {"asin", ComplexCFuncWrapper<Sym<Lib, I::asin, CHostT>>, true}, 518 {"asinh", CmathF{std::asinh}, true}, 519 {"atan", ComplexCFuncWrapper<Sym<Lib, I::atan, CHostT>>, true}, 520 {"atanh", CmathF{std::atanh}, true}, 521 {"cos", ComplexCFuncWrapper<Sym<Lib, I::cos, CHostT>>, true}, 522 {"cosh", ComplexCFuncWrapper<Sym<Lib, I::cosh, CHostT>>, true}, 523 {"exp", ComplexCFuncWrapper<Sym<Lib, I::exp, CHostT>>, true}, 524 {"log", ComplexCFuncWrapper<Sym<Lib, I::log, CHostT>>, true}, 525 {"pow", ComplexCFuncWrapper<Sym<Lib, I::pow, CHostT>>, true}, 526 {"sin", ComplexCFuncWrapper<Sym<Lib, I::sin, CHostT>>, true}, 527 {"sinh", ComplexCFuncWrapper<Sym<Lib, I::sinh, CHostT>>, true}, 528 {"sqrt", ComplexCFuncWrapper<Sym<Lib, I::sqrt, CHostT>>, true}, 529 {"tan", ComplexCFuncWrapper<Sym<Lib, I::tan, CHostT>>, true}, 530 {"tanh", ComplexCFuncWrapper<Sym<Lib, I::tanh, CHostT>>, true}, 531 }; 532 533 for (auto sym : pgmathSymbols) { 534 hostIntrinsicLibrary.AddProcedure(std::move(sym)); 535 } 536 } 537 538 template <L Lib> 539 static void InitHostIntrinsicLibraryWithLibpgmath( 540 HostIntrinsicProceduresLibrary &lib) { 541 if constexpr (host::FortranTypeExists<float>()) { 542 AddLibpgmathRealHostProcedures<Lib, float>(lib); 543 } 544 if constexpr (host::FortranTypeExists<double>()) { 545 AddLibpgmathRealHostProcedures<Lib, double>(lib); 546 } 547 if constexpr (host::FortranTypeExists<std::complex<float>>()) { 548 AddLibpgmathComplexHostProcedures<Lib, float>(lib); 549 } 550 if constexpr (host::FortranTypeExists<std::complex<double>>()) { 551 AddLibpgmathComplexHostProcedures<Lib, double>(lib); 552 } 553 // No long double functions in libpgmath 554 if constexpr (host::FortranTypeExists<long double>()) { 555 AddLibmRealHostProcedures<long double>(lib); 556 } 557 if constexpr (host::FortranTypeExists<std::complex<long double>>()) { 558 AddLibmComplexHostProcedures<long double>(lib); 559 } 560 } 561 } // namespace pgmath 562 #endif // LINK_WITH_LIBPGMATH 563 564 // Define which host runtime functions will be used for folding 565 HostIntrinsicProceduresLibrary::HostIntrinsicProceduresLibrary() { 566 // TODO: When command line options regarding targeted numerical library is 567 // available, this needs to be revisited to take it into account. So far, 568 // default to libpgmath if F18 is built with it. 569 #if LINK_WITH_LIBPGMATH 570 // This looks and is stupid for now (until TODO above), but it is needed 571 // to silence clang warnings on unused symbols if all declared pgmath 572 // symbols are not used somewhere. 573 if (true) { 574 pgmath::InitHostIntrinsicLibraryWithLibpgmath<pgmath::L::P>(*this); 575 } else if (false) { 576 pgmath::InitHostIntrinsicLibraryWithLibpgmath<pgmath::L::F>(*this); 577 } else { 578 pgmath::InitHostIntrinsicLibraryWithLibpgmath<pgmath::L::R>(*this); 579 } 580 #else 581 InitHostIntrinsicLibraryWithLibm(*this); 582 #endif 583 } 584 } // namespace Fortran::evaluate 585