164ab3302SCarolineConcatto //===-- lib/Evaluate/intrinsics-library.cpp -------------------------------===// 264ab3302SCarolineConcatto // 364ab3302SCarolineConcatto // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 464ab3302SCarolineConcatto // See https://llvm.org/LICENSE.txt for license information. 564ab3302SCarolineConcatto // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 664ab3302SCarolineConcatto // 764ab3302SCarolineConcatto //===----------------------------------------------------------------------===// 864ab3302SCarolineConcatto 964ab3302SCarolineConcatto // This file defines host runtime functions that can be used for folding 1064ab3302SCarolineConcatto // intrinsic functions. 1194d9a4fdSJean Perier // The default host runtime folders are built with <cmath> and 1264ab3302SCarolineConcatto // <complex> functions that are guaranteed to exist from the C++ standard. 1364ab3302SCarolineConcatto 1494d9a4fdSJean Perier #include "flang/Evaluate/intrinsics-library.h" 1594d9a4fdSJean Perier #include "fold-implementation.h" 1694d9a4fdSJean Perier #include "host.h" 1738b9dd7aSPeter Klausler #include "flang/Common/erfc-scaled.h" 18*71d4f343SKelvin Li #include "flang/Common/idioms.h" 1994d9a4fdSJean Perier #include "flang/Common/static-multimap-view.h" 2094d9a4fdSJean Perier #include "flang/Evaluate/expression.h" 214daa33f6SPeter Klausler #include <cfloat> 2264ab3302SCarolineConcatto #include <cmath> 2364ab3302SCarolineConcatto #include <complex> 2494d9a4fdSJean Perier #include <functional> 25478e0b58SPeter Steinfeld #if HAS_QUADMATHLIB 26478e0b58SPeter Steinfeld #include "quadmath.h" 27478e0b58SPeter Steinfeld #endif 28fc51c7f0SSlava Zakharin #include "flang/Common/float128.h" 29fc51c7f0SSlava Zakharin #include "flang/Common/float80.h" 3094d9a4fdSJean Perier #include <type_traits> 3164ab3302SCarolineConcatto 3264ab3302SCarolineConcatto namespace Fortran::evaluate { 3364ab3302SCarolineConcatto 3494d9a4fdSJean Perier // Define a vector like class that can hold an arbitrary number of 3594d9a4fdSJean Perier // Dynamic type and be built at compile time. This is like a 3694d9a4fdSJean Perier // std::vector<DynamicType>, but constexpr only. 3794d9a4fdSJean Perier template <typename... FortranType> struct TypeVectorStorage { 3894d9a4fdSJean Perier static constexpr DynamicType values[]{FortranType{}.GetType()...}; 3994d9a4fdSJean Perier static constexpr const DynamicType *start{&values[0]}; 4094d9a4fdSJean Perier static constexpr const DynamicType *end{start + sizeof...(FortranType)}; 4194d9a4fdSJean Perier }; 4294d9a4fdSJean Perier template <> struct TypeVectorStorage<> { 4394d9a4fdSJean Perier static constexpr const DynamicType *start{nullptr}, *end{nullptr}; 4494d9a4fdSJean Perier }; 4594d9a4fdSJean Perier struct TypeVector { 4694d9a4fdSJean Perier template <typename... FortranType> static constexpr TypeVector Create() { 4794d9a4fdSJean Perier using storage = TypeVectorStorage<FortranType...>; 4894d9a4fdSJean Perier return TypeVector{storage::start, storage::end, sizeof...(FortranType)}; 4964ab3302SCarolineConcatto } 5094d9a4fdSJean Perier constexpr size_t size() const { return size_; }; 5194d9a4fdSJean Perier using const_iterator = const DynamicType *; 5294d9a4fdSJean Perier constexpr const_iterator begin() const { return startPtr; } 5394d9a4fdSJean Perier constexpr const_iterator end() const { return endPtr; } 5494d9a4fdSJean Perier const DynamicType &operator[](size_t i) const { return *(startPtr + i); } 5594d9a4fdSJean Perier 5694d9a4fdSJean Perier const DynamicType *startPtr{nullptr}; 5794d9a4fdSJean Perier const DynamicType *endPtr{nullptr}; 5894d9a4fdSJean Perier const size_t size_; 5994d9a4fdSJean Perier }; 6094d9a4fdSJean Perier inline bool operator==( 6194d9a4fdSJean Perier const TypeVector &lhs, const std::vector<DynamicType> &rhs) { 6294d9a4fdSJean Perier if (lhs.size() != rhs.size()) { 6364ab3302SCarolineConcatto return false; 6464ab3302SCarolineConcatto } 6594d9a4fdSJean Perier for (size_t i{0}; i < lhs.size(); ++i) { 6694d9a4fdSJean Perier if (lhs[i] != rhs[i]) { 6794d9a4fdSJean Perier return false; 6894d9a4fdSJean Perier } 6994d9a4fdSJean Perier } 7094d9a4fdSJean Perier return true; 7194d9a4fdSJean Perier } 7294d9a4fdSJean Perier 7394d9a4fdSJean Perier // HostRuntimeFunction holds a pointer to a Folder function that can fold 7494d9a4fdSJean Perier // a Fortran scalar intrinsic using host runtime functions (e.g libm). 7594d9a4fdSJean Perier // The folder take care of all conversions between Fortran types and the related 7694d9a4fdSJean Perier // host types as well as setting and cleaning-up the floating point environment. 7794d9a4fdSJean Perier // HostRuntimeFunction are intended to be built at compile time (members are all 7894d9a4fdSJean Perier // constexpr constructible) so that they can be stored in a compile time static 7994d9a4fdSJean Perier // map. 8094d9a4fdSJean Perier struct HostRuntimeFunction { 8194d9a4fdSJean Perier using Folder = Expr<SomeType> (*)( 8294d9a4fdSJean Perier FoldingContext &, std::vector<Expr<SomeType>> &&); 8394d9a4fdSJean Perier using Key = std::string_view; 8494d9a4fdSJean Perier // Needed for implicit compare with keys. 8594d9a4fdSJean Perier constexpr operator Key() const { return key; } 8694d9a4fdSJean Perier // Name of the related Fortran intrinsic. 8794d9a4fdSJean Perier Key key; 8894d9a4fdSJean Perier // DynamicType of the Expr<SomeType> returns by folder. 8994d9a4fdSJean Perier DynamicType resultType; 9094d9a4fdSJean Perier // DynamicTypes expected for the Expr<SomeType> arguments of the folder. 9194d9a4fdSJean Perier // The folder will crash if provided arguments of different types. 9294d9a4fdSJean Perier TypeVector argumentTypes; 9394d9a4fdSJean Perier // Folder to be called to fold the intrinsic with host runtime. The provided 9494d9a4fdSJean Perier // Expr<SomeType> arguments must wrap scalar constants of the type described 9594d9a4fdSJean Perier // in argumentTypes, otherwise folder will crash. Any floating point issue 9694d9a4fdSJean Perier // raised while executing the host runtime will be reported in FoldingContext 9794d9a4fdSJean Perier // messages. 9894d9a4fdSJean Perier Folder folder; 9994d9a4fdSJean Perier }; 10094d9a4fdSJean Perier 10194d9a4fdSJean Perier // Translate a host function type signature (template arguments) into a 10294d9a4fdSJean Perier // constexpr data representation based on Fortran DynamicType that can be 10394d9a4fdSJean Perier // stored. 10494d9a4fdSJean Perier template <typename TR, typename... TA> using FuncPointer = TR (*)(TA...); 10594d9a4fdSJean Perier template <typename T> struct FuncTypeAnalyzer {}; 10694d9a4fdSJean Perier template <typename HostTR, typename... HostTA> 10794d9a4fdSJean Perier struct FuncTypeAnalyzer<FuncPointer<HostTR, HostTA...>> { 10894d9a4fdSJean Perier static constexpr DynamicType result{host::FortranType<HostTR>{}.GetType()}; 10994d9a4fdSJean Perier static constexpr TypeVector arguments{ 11094d9a4fdSJean Perier TypeVector::Create<host::FortranType<HostTA>...>()}; 11194d9a4fdSJean Perier }; 11294d9a4fdSJean Perier 11394d9a4fdSJean Perier // Define helpers to deal with host floating environment. 11494d9a4fdSJean Perier template <typename TR> 11594d9a4fdSJean Perier static void CheckFloatingPointIssues( 11694d9a4fdSJean Perier host::HostFloatingPointEnvironment &hostFPE, const Scalar<TR> &x) { 11794d9a4fdSJean Perier if constexpr (TR::category == TypeCategory::Complex || 11894d9a4fdSJean Perier TR::category == TypeCategory::Real) { 11994d9a4fdSJean Perier if (x.IsNotANumber()) { 12094d9a4fdSJean Perier hostFPE.SetFlag(RealFlag::InvalidArgument); 12194d9a4fdSJean Perier } else if (x.IsInfinite()) { 12294d9a4fdSJean Perier hostFPE.SetFlag(RealFlag::Overflow); 12394d9a4fdSJean Perier } 12494d9a4fdSJean Perier } 12594d9a4fdSJean Perier } 12694d9a4fdSJean Perier // Software Subnormal Flushing helper. 12794d9a4fdSJean Perier // Only flush floating-points. Forward other scalars untouched. 12894d9a4fdSJean Perier // Software flushing is only performed if hardware flushing is not available 12994d9a4fdSJean Perier // because it may not result in the same behavior as hardware flushing. 13094d9a4fdSJean Perier // Some runtime implementations are "working around" subnormal flushing to 13194d9a4fdSJean Perier // return results that they deem better than returning the result they would 13294d9a4fdSJean Perier // with a null argument. An example is logf that should return -inf if arguments 13394d9a4fdSJean Perier // are flushed to zero, but some implementations return -1.03972076416015625e2_4 13494d9a4fdSJean Perier // for all subnormal values instead. It is impossible to reproduce this with the 13594d9a4fdSJean Perier // simple software flushing below. 13694d9a4fdSJean Perier template <typename T> 13794d9a4fdSJean Perier static constexpr inline const Scalar<T> FlushSubnormals(Scalar<T> &&x) { 13894d9a4fdSJean Perier if constexpr (T::category == TypeCategory::Real || 13994d9a4fdSJean Perier T::category == TypeCategory::Complex) { 14094d9a4fdSJean Perier return x.FlushSubnormalToZero(); 14194d9a4fdSJean Perier } 14294d9a4fdSJean Perier return x; 14394d9a4fdSJean Perier } 14494d9a4fdSJean Perier 14594d9a4fdSJean Perier // This is the kernel called by all HostRuntimeFunction folders, it convert the 14694d9a4fdSJean Perier // Fortran Expr<SomeType> to the host runtime function argument types, calls 14794d9a4fdSJean Perier // the runtime function, and wrap back the result into an Expr<SomeType>. 14894d9a4fdSJean Perier // It deals with host floating point environment set-up and clean-up. 14994d9a4fdSJean Perier template <typename FuncType, typename TR, typename... TA, size_t... I> 15094d9a4fdSJean Perier static Expr<SomeType> ApplyHostFunctionHelper(FuncType func, 15194d9a4fdSJean Perier FoldingContext &context, std::vector<Expr<SomeType>> &&args, 15294d9a4fdSJean Perier std::index_sequence<I...>) { 15394d9a4fdSJean Perier host::HostFloatingPointEnvironment hostFPE; 15494d9a4fdSJean Perier hostFPE.SetUpHostFloatingPointEnvironment(context); 15594d9a4fdSJean Perier host::HostType<TR> hostResult{}; 15694d9a4fdSJean Perier Scalar<TR> result{}; 15794d9a4fdSJean Perier std::tuple<Scalar<TA>...> scalarArgs{ 15894d9a4fdSJean Perier GetScalarConstantValue<TA>(args[I]).value()...}; 15923c2bedfSPeter Klausler if (context.targetCharacteristics().areSubnormalsFlushedToZero() && 16094d9a4fdSJean Perier !hostFPE.hasSubnormalFlushingHardwareControl()) { 16194d9a4fdSJean Perier hostResult = func(host::CastFortranToHost<TA>( 16294d9a4fdSJean Perier FlushSubnormals<TA>(std::move(std::get<I>(scalarArgs))))...); 16394d9a4fdSJean Perier result = FlushSubnormals<TR>(host::CastHostToFortran<TR>(hostResult)); 16494d9a4fdSJean Perier } else { 16594d9a4fdSJean Perier hostResult = func(host::CastFortranToHost<TA>(std::get<I>(scalarArgs))...); 16694d9a4fdSJean Perier result = host::CastHostToFortran<TR>(hostResult); 16794d9a4fdSJean Perier } 16894d9a4fdSJean Perier if (!hostFPE.hardwareFlagsAreReliable()) { 16994d9a4fdSJean Perier CheckFloatingPointIssues<TR>(hostFPE, result); 17094d9a4fdSJean Perier } 17194d9a4fdSJean Perier hostFPE.CheckAndRestoreFloatingPointEnvironment(context); 17294d9a4fdSJean Perier return AsGenericExpr(Constant<TR>(std::move(result))); 17394d9a4fdSJean Perier } 17494d9a4fdSJean Perier template <typename HostTR, typename... HostTA> 17594d9a4fdSJean Perier Expr<SomeType> ApplyHostFunction(FuncPointer<HostTR, HostTA...> func, 17694d9a4fdSJean Perier FoldingContext &context, std::vector<Expr<SomeType>> &&args) { 17794d9a4fdSJean Perier return ApplyHostFunctionHelper<decltype(func), host::FortranType<HostTR>, 17894d9a4fdSJean Perier host::FortranType<HostTA>...>( 17994d9a4fdSJean Perier func, context, std::move(args), std::index_sequence_for<HostTA...>{}); 18094d9a4fdSJean Perier } 18194d9a4fdSJean Perier 18294d9a4fdSJean Perier // FolderFactory builds a HostRuntimeFunction for the host runtime function 18394d9a4fdSJean Perier // passed as a template argument. 18494d9a4fdSJean Perier // Its static member function "fold" is the resulting folder. It captures the 18594d9a4fdSJean Perier // host runtime function pointer and pass it to the host runtime function folder 18694d9a4fdSJean Perier // kernel. 18794d9a4fdSJean Perier template <typename HostFuncType, HostFuncType func> class FolderFactory { 18894d9a4fdSJean Perier public: 18994d9a4fdSJean Perier static constexpr HostRuntimeFunction Create(const std::string_view &name) { 19094d9a4fdSJean Perier return HostRuntimeFunction{name, FuncTypeAnalyzer<HostFuncType>::result, 19194d9a4fdSJean Perier FuncTypeAnalyzer<HostFuncType>::arguments, &Fold}; 19294d9a4fdSJean Perier } 19394d9a4fdSJean Perier 19494d9a4fdSJean Perier private: 19594d9a4fdSJean Perier static Expr<SomeType> Fold( 19694d9a4fdSJean Perier FoldingContext &context, std::vector<Expr<SomeType>> &&args) { 19794d9a4fdSJean Perier return ApplyHostFunction(func, context, std::move(args)); 19894d9a4fdSJean Perier } 19994d9a4fdSJean Perier }; 20094d9a4fdSJean Perier 20194d9a4fdSJean Perier // Define host runtime libraries that can be used for folding and 20294d9a4fdSJean Perier // fill their description if they are available. 2036c81b4a0SJean Perier enum class LibraryVersion { 2046c81b4a0SJean Perier Libm, 2056c81b4a0SJean Perier LibmExtensions, 2066c81b4a0SJean Perier PgmathFast, 2076c81b4a0SJean Perier PgmathRelaxed, 2086c81b4a0SJean Perier PgmathPrecise 2096c81b4a0SJean Perier }; 21094d9a4fdSJean Perier template <typename HostT, LibraryVersion> struct HostRuntimeLibrary { 21194d9a4fdSJean Perier // When specialized, this class holds a static constexpr table containing 21294d9a4fdSJean Perier // all the HostRuntimeLibrary for functions of library LibraryVersion 21394d9a4fdSJean Perier // that returns a value of type HostT. 21494d9a4fdSJean Perier }; 21594d9a4fdSJean Perier 21694d9a4fdSJean Perier using HostRuntimeMap = common::StaticMultimapView<HostRuntimeFunction>; 21764ab3302SCarolineConcatto 21864ab3302SCarolineConcatto // Map numerical intrinsic to <cmath>/<complex> functions 219d393ce3bSPeter Klausler // (Note: ABS() is folded in fold-real.cpp.) 22064ab3302SCarolineConcatto template <typename HostT> 22194d9a4fdSJean Perier struct HostRuntimeLibrary<HostT, LibraryVersion::Libm> { 22264ab3302SCarolineConcatto using F = FuncPointer<HostT, HostT>; 22364ab3302SCarolineConcatto using F2 = FuncPointer<HostT, HostT, HostT>; 22494d9a4fdSJean Perier static constexpr HostRuntimeFunction table[]{ 22594d9a4fdSJean Perier FolderFactory<F, F{std::acos}>::Create("acos"), 22694d9a4fdSJean Perier FolderFactory<F, F{std::acosh}>::Create("acosh"), 22794d9a4fdSJean Perier FolderFactory<F, F{std::asin}>::Create("asin"), 22894d9a4fdSJean Perier FolderFactory<F, F{std::asinh}>::Create("asinh"), 22994d9a4fdSJean Perier FolderFactory<F, F{std::atan}>::Create("atan"), 23094d9a4fdSJean Perier FolderFactory<F2, F2{std::atan2}>::Create("atan2"), 23194d9a4fdSJean Perier FolderFactory<F, F{std::atanh}>::Create("atanh"), 23294d9a4fdSJean Perier FolderFactory<F, F{std::cos}>::Create("cos"), 23394d9a4fdSJean Perier FolderFactory<F, F{std::cosh}>::Create("cosh"), 23494d9a4fdSJean Perier FolderFactory<F, F{std::erf}>::Create("erf"), 23594d9a4fdSJean Perier FolderFactory<F, F{std::erfc}>::Create("erfc"), 23638b9dd7aSPeter Klausler FolderFactory<F, F{common::ErfcScaled}>::Create("erfc_scaled"), 23794d9a4fdSJean Perier FolderFactory<F, F{std::exp}>::Create("exp"), 23894d9a4fdSJean Perier FolderFactory<F, F{std::tgamma}>::Create("gamma"), 23994d9a4fdSJean Perier FolderFactory<F, F{std::log}>::Create("log"), 24094d9a4fdSJean Perier FolderFactory<F, F{std::log10}>::Create("log10"), 24194d9a4fdSJean Perier FolderFactory<F, F{std::lgamma}>::Create("log_gamma"), 24294d9a4fdSJean Perier FolderFactory<F2, F2{std::pow}>::Create("pow"), 24394d9a4fdSJean Perier FolderFactory<F, F{std::sin}>::Create("sin"), 24494d9a4fdSJean Perier FolderFactory<F, F{std::sinh}>::Create("sinh"), 24594d9a4fdSJean Perier FolderFactory<F, F{std::tan}>::Create("tan"), 24694d9a4fdSJean Perier FolderFactory<F, F{std::tanh}>::Create("tanh"), 24764ab3302SCarolineConcatto }; 24864ab3302SCarolineConcatto // Note: cmath does not have modulo and erfc_scaled equivalent 24964ab3302SCarolineConcatto 25064ab3302SCarolineConcatto // Note regarding lack of bessel function support: 25164ab3302SCarolineConcatto // C++17 defined standard Bessel math functions std::cyl_bessel_j 25264ab3302SCarolineConcatto // and std::cyl_neumann that can be used for Fortran j and y 25364ab3302SCarolineConcatto // bessel functions. However, they are not yet implemented in 25464ab3302SCarolineConcatto // clang libc++ (ok in GNU libstdc++). C maths functions j0... 25564ab3302SCarolineConcatto // are not C standard but a GNU extension so they are not used 25664ab3302SCarolineConcatto // to avoid introducing incompatibilities. 25764ab3302SCarolineConcatto // Use libpgmath to get bessel function folding support. 25864ab3302SCarolineConcatto // TODO: Add Bessel functions when possible. 25994d9a4fdSJean Perier static constexpr HostRuntimeMap map{table}; 26094d9a4fdSJean Perier static_assert(map.Verify(), "map must be sorted"); 26194d9a4fdSJean Perier }; 2624a10b4c0SjeanPerier 2634a10b4c0SjeanPerier // Helpers to map complex std::pow whose resolution in F2{std::pow} is 2644a10b4c0SjeanPerier // ambiguous as of clang++ 20. 2654a10b4c0SjeanPerier template <typename HostT> 2664a10b4c0SjeanPerier static std::complex<HostT> StdPowF2( 2674a10b4c0SjeanPerier const std::complex<HostT> &x, const std::complex<HostT> &y) { 2684a10b4c0SjeanPerier return std::pow(x, y); 2694a10b4c0SjeanPerier } 2704a10b4c0SjeanPerier template <typename HostT> 2714a10b4c0SjeanPerier static std::complex<HostT> StdPowF2A( 2724a10b4c0SjeanPerier const HostT &x, const std::complex<HostT> &y) { 2734a10b4c0SjeanPerier return std::pow(x, y); 2744a10b4c0SjeanPerier } 2754a10b4c0SjeanPerier template <typename HostT> 2764a10b4c0SjeanPerier static std::complex<HostT> StdPowF2B( 2774a10b4c0SjeanPerier const std::complex<HostT> &x, const HostT &y) { 2784a10b4c0SjeanPerier return std::pow(x, y); 2794a10b4c0SjeanPerier } 2804a10b4c0SjeanPerier 281*71d4f343SKelvin Li #ifdef _AIX 282*71d4f343SKelvin Li #ifdef __clang_major__ 283*71d4f343SKelvin Li #pragma clang diagnostic ignored "-Wc99-extensions" 284*71d4f343SKelvin Li #endif 285*71d4f343SKelvin Li 286*71d4f343SKelvin Li extern "C" { 287*71d4f343SKelvin Li float _Complex cacosf(float _Complex); 288*71d4f343SKelvin Li double _Complex cacos(double _Complex); 289*71d4f343SKelvin Li float _Complex csqrtf(float _Complex); 290*71d4f343SKelvin Li double _Complex csqrt(double _Complex); 291*71d4f343SKelvin Li } 292*71d4f343SKelvin Li 293*71d4f343SKelvin Li enum CRI { Real, Imag }; 294*71d4f343SKelvin Li template <typename TR, typename TA> static TR &reIm(TA &x, CRI n) { 295*71d4f343SKelvin Li return reinterpret_cast<TR(&)[2]>(x)[n]; 296*71d4f343SKelvin Li } 297*71d4f343SKelvin Li template <typename TR, typename T> static TR CppToC(const std::complex<T> &x) { 298*71d4f343SKelvin Li TR r; 299*71d4f343SKelvin Li reIm<T, TR>(r, CRI::Real) = x.real(); 300*71d4f343SKelvin Li reIm<T, TR>(r, CRI::Imag) = x.imag(); 301*71d4f343SKelvin Li return r; 302*71d4f343SKelvin Li } 303*71d4f343SKelvin Li template <typename T, typename TA> static std::complex<T> CToCpp(const TA &x) { 304*71d4f343SKelvin Li TA &z{const_cast<TA &>(x)}; 305*71d4f343SKelvin Li return std::complex<T>(reIm<T, TA>(z, CRI::Real), reIm<T, TA>(z, CRI::Imag)); 306*71d4f343SKelvin Li } 307*71d4f343SKelvin Li #endif 308*71d4f343SKelvin Li 309*71d4f343SKelvin Li template <typename HostT> 310*71d4f343SKelvin Li static std::complex<HostT> CSqrt(const std::complex<HostT> &x) { 311*71d4f343SKelvin Li std::complex<HostT> res; 312*71d4f343SKelvin Li #ifdef _AIX 313*71d4f343SKelvin Li // On AIX, the implementation of csqrt[f] and std::sqrt is different, 314*71d4f343SKelvin Li // use csqrt[f] in folding. 315*71d4f343SKelvin Li if constexpr (std::is_same_v<HostT, float>) { 316*71d4f343SKelvin Li float _Complex r{csqrtf(CppToC<float _Complex, float>(x))}; 317*71d4f343SKelvin Li res = CToCpp<float, float _Complex>(r); 318*71d4f343SKelvin Li } else if constexpr (std::is_same_v<HostT, double>) { 319*71d4f343SKelvin Li double _Complex r{csqrt(CppToC<double _Complex, double>(x))}; 320*71d4f343SKelvin Li res = CToCpp<double, double _Complex>(r); 321*71d4f343SKelvin Li } else { 322*71d4f343SKelvin Li DIE("bad complex component type"); 323*71d4f343SKelvin Li } 324*71d4f343SKelvin Li #else 325*71d4f343SKelvin Li res = std::sqrt(x); 326*71d4f343SKelvin Li #endif 327*71d4f343SKelvin Li return res; 328*71d4f343SKelvin Li } 329*71d4f343SKelvin Li 330*71d4f343SKelvin Li template <typename HostT> 331*71d4f343SKelvin Li static std::complex<HostT> CAcos(const std::complex<HostT> &x) { 332*71d4f343SKelvin Li std::complex<HostT> res; 333*71d4f343SKelvin Li #ifdef _AIX 334*71d4f343SKelvin Li // On AIX, the implementation of cacos[f] and std::acos is different, 335*71d4f343SKelvin Li // use cacos[f] in folding. 336*71d4f343SKelvin Li if constexpr (std::is_same_v<HostT, float>) { 337*71d4f343SKelvin Li float _Complex r{cacosf(CppToC<float _Complex, float>(x))}; 338*71d4f343SKelvin Li res = CToCpp<float, float _Complex>(r); 339*71d4f343SKelvin Li } else if constexpr (std::is_same_v<HostT, double>) { 340*71d4f343SKelvin Li double _Complex r{cacos(CppToC<double _Complex, double>(x))}; 341*71d4f343SKelvin Li res = CToCpp<double, double _Complex>(r); 342*71d4f343SKelvin Li } else { 343*71d4f343SKelvin Li DIE("bad complex component type"); 344*71d4f343SKelvin Li } 345*71d4f343SKelvin Li #else 346*71d4f343SKelvin Li res = std::acos(x); 347*71d4f343SKelvin Li #endif 348*71d4f343SKelvin Li return res; 349*71d4f343SKelvin Li } 350*71d4f343SKelvin Li 35164ab3302SCarolineConcatto template <typename HostT> 35294d9a4fdSJean Perier struct HostRuntimeLibrary<std::complex<HostT>, LibraryVersion::Libm> { 35364ab3302SCarolineConcatto using F = FuncPointer<std::complex<HostT>, const std::complex<HostT> &>; 35464ab3302SCarolineConcatto using F2 = FuncPointer<std::complex<HostT>, const std::complex<HostT> &, 35564ab3302SCarolineConcatto const std::complex<HostT> &>; 35694d9a4fdSJean Perier using F2A = FuncPointer<std::complex<HostT>, const HostT &, 35764ab3302SCarolineConcatto const std::complex<HostT> &>; 35894d9a4fdSJean Perier using F2B = FuncPointer<std::complex<HostT>, const std::complex<HostT> &, 35964ab3302SCarolineConcatto const HostT &>; 36094d9a4fdSJean Perier static constexpr HostRuntimeFunction table[]{ 361*71d4f343SKelvin Li FolderFactory<F, F{CAcos}>::Create("acos"), 36294d9a4fdSJean Perier FolderFactory<F, F{std::acosh}>::Create("acosh"), 36394d9a4fdSJean Perier FolderFactory<F, F{std::asin}>::Create("asin"), 36494d9a4fdSJean Perier FolderFactory<F, F{std::asinh}>::Create("asinh"), 36594d9a4fdSJean Perier FolderFactory<F, F{std::atan}>::Create("atan"), 36694d9a4fdSJean Perier FolderFactory<F, F{std::atanh}>::Create("atanh"), 36794d9a4fdSJean Perier FolderFactory<F, F{std::cos}>::Create("cos"), 36894d9a4fdSJean Perier FolderFactory<F, F{std::cosh}>::Create("cosh"), 36994d9a4fdSJean Perier FolderFactory<F, F{std::exp}>::Create("exp"), 37094d9a4fdSJean Perier FolderFactory<F, F{std::log}>::Create("log"), 3714a10b4c0SjeanPerier FolderFactory<F2, F2{StdPowF2}>::Create("pow"), 3724a10b4c0SjeanPerier FolderFactory<F2A, F2A{StdPowF2A}>::Create("pow"), 3734a10b4c0SjeanPerier FolderFactory<F2B, F2B{StdPowF2B}>::Create("pow"), 37494d9a4fdSJean Perier FolderFactory<F, F{std::sin}>::Create("sin"), 37594d9a4fdSJean Perier FolderFactory<F, F{std::sinh}>::Create("sinh"), 376*71d4f343SKelvin Li FolderFactory<F, F{CSqrt}>::Create("sqrt"), 37794d9a4fdSJean Perier FolderFactory<F, F{std::tan}>::Create("tan"), 37894d9a4fdSJean Perier FolderFactory<F, F{std::tanh}>::Create("tanh"), 37994d9a4fdSJean Perier }; 38094d9a4fdSJean Perier static constexpr HostRuntimeMap map{table}; 38194d9a4fdSJean Perier static_assert(map.Verify(), "map must be sorted"); 38264ab3302SCarolineConcatto }; 3836c81b4a0SJean Perier // Note regarding cmath: 3846c81b4a0SJean Perier // - cmath does not have modulo and erfc_scaled equivalent 3856c81b4a0SJean Perier // - C++17 defined standard Bessel math functions std::cyl_bessel_j 3866c81b4a0SJean Perier // and std::cyl_neumann that can be used for Fortran j and y 3876c81b4a0SJean Perier // bessel functions. However, they are not yet implemented in 3886c81b4a0SJean Perier // clang libc++ (ok in GNU libstdc++). Instead, the Posix libm 3896c81b4a0SJean Perier // extensions are used when available below. 3906c81b4a0SJean Perier 3916c81b4a0SJean Perier #if _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600 3926c81b4a0SJean Perier /// Define libm extensions 3936c81b4a0SJean Perier /// Bessel functions are defined in POSIX.1-2001. 3946c81b4a0SJean Perier 3953149c934SLeandro Lupori // Remove float bessel functions for AIX and Darwin as they are not supported 3963149c934SLeandro Lupori #if !defined(_AIX) && !defined(__APPLE__) 3976c81b4a0SJean Perier template <> struct HostRuntimeLibrary<float, LibraryVersion::LibmExtensions> { 3986c81b4a0SJean Perier using F = FuncPointer<float, float>; 3996c81b4a0SJean Perier using FN = FuncPointer<float, int, float>; 4006c81b4a0SJean Perier static constexpr HostRuntimeFunction table[]{ 4016c81b4a0SJean Perier FolderFactory<F, F{::j0f}>::Create("bessel_j0"), 4026c81b4a0SJean Perier FolderFactory<F, F{::j1f}>::Create("bessel_j1"), 4036c81b4a0SJean Perier FolderFactory<FN, FN{::jnf}>::Create("bessel_jn"), 4046c81b4a0SJean Perier FolderFactory<F, F{::y0f}>::Create("bessel_y0"), 4056c81b4a0SJean Perier FolderFactory<F, F{::y1f}>::Create("bessel_y1"), 4066c81b4a0SJean Perier FolderFactory<FN, FN{::ynf}>::Create("bessel_yn"), 4076c81b4a0SJean Perier }; 4086c81b4a0SJean Perier static constexpr HostRuntimeMap map{table}; 4096c81b4a0SJean Perier static_assert(map.Verify(), "map must be sorted"); 4106c81b4a0SJean Perier }; 4110c08418dSKelvin Li #endif 4126c81b4a0SJean Perier 413478e0b58SPeter Steinfeld #if HAS_QUADMATHLIB 414478e0b58SPeter Steinfeld template <> struct HostRuntimeLibrary<__float128, LibraryVersion::Libm> { 415478e0b58SPeter Steinfeld using F = FuncPointer<__float128, __float128>; 416478e0b58SPeter Steinfeld using F2 = FuncPointer<__float128, __float128, __float128>; 4174cb1d914SjeanPerier using FN = FuncPointer<__float128, int, __float128>; 418478e0b58SPeter Steinfeld static constexpr HostRuntimeFunction table[]{ 419478e0b58SPeter Steinfeld FolderFactory<F, F{::acosq}>::Create("acos"), 420478e0b58SPeter Steinfeld FolderFactory<F, F{::acoshq}>::Create("acosh"), 421478e0b58SPeter Steinfeld FolderFactory<F, F{::asinq}>::Create("asin"), 422478e0b58SPeter Steinfeld FolderFactory<F, F{::asinhq}>::Create("asinh"), 423478e0b58SPeter Steinfeld FolderFactory<F, F{::atanq}>::Create("atan"), 424478e0b58SPeter Steinfeld FolderFactory<F2, F2{::atan2q}>::Create("atan2"), 425478e0b58SPeter Steinfeld FolderFactory<F, F{::atanhq}>::Create("atanh"), 4264cb1d914SjeanPerier FolderFactory<F, F{::j0q}>::Create("bessel_j0"), 4274cb1d914SjeanPerier FolderFactory<F, F{::j1q}>::Create("bessel_j1"), 4284cb1d914SjeanPerier FolderFactory<FN, FN{::jnq}>::Create("bessel_jn"), 4294cb1d914SjeanPerier FolderFactory<F, F{::y0q}>::Create("bessel_y0"), 4304cb1d914SjeanPerier FolderFactory<F, F{::y1q}>::Create("bessel_y1"), 4314cb1d914SjeanPerier FolderFactory<FN, FN{::ynq}>::Create("bessel_yn"), 432478e0b58SPeter Steinfeld FolderFactory<F, F{::cosq}>::Create("cos"), 433478e0b58SPeter Steinfeld FolderFactory<F, F{::coshq}>::Create("cosh"), 434478e0b58SPeter Steinfeld FolderFactory<F, F{::erfq}>::Create("erf"), 435478e0b58SPeter Steinfeld FolderFactory<F, F{::erfcq}>::Create("erfc"), 436478e0b58SPeter Steinfeld FolderFactory<F, F{::expq}>::Create("exp"), 437478e0b58SPeter Steinfeld FolderFactory<F, F{::tgammaq}>::Create("gamma"), 438478e0b58SPeter Steinfeld FolderFactory<F, F{::logq}>::Create("log"), 439478e0b58SPeter Steinfeld FolderFactory<F, F{::log10q}>::Create("log10"), 440478e0b58SPeter Steinfeld FolderFactory<F, F{::lgammaq}>::Create("log_gamma"), 441478e0b58SPeter Steinfeld FolderFactory<F2, F2{::powq}>::Create("pow"), 442478e0b58SPeter Steinfeld FolderFactory<F, F{::sinq}>::Create("sin"), 443478e0b58SPeter Steinfeld FolderFactory<F, F{::sinhq}>::Create("sinh"), 444478e0b58SPeter Steinfeld FolderFactory<F, F{::tanq}>::Create("tan"), 445478e0b58SPeter Steinfeld FolderFactory<F, F{::tanhq}>::Create("tanh"), 446478e0b58SPeter Steinfeld }; 447478e0b58SPeter Steinfeld static constexpr HostRuntimeMap map{table}; 448478e0b58SPeter Steinfeld static_assert(map.Verify(), "map must be sorted"); 449478e0b58SPeter Steinfeld }; 450478e0b58SPeter Steinfeld template <> struct HostRuntimeLibrary<__complex128, LibraryVersion::Libm> { 451478e0b58SPeter Steinfeld using F = FuncPointer<__complex128, __complex128>; 452478e0b58SPeter Steinfeld using F2 = FuncPointer<__complex128, __complex128, __complex128>; 453478e0b58SPeter Steinfeld static constexpr HostRuntimeFunction table[]{ 454478e0b58SPeter Steinfeld FolderFactory<F, F{::cacosq}>::Create("acos"), 455478e0b58SPeter Steinfeld FolderFactory<F, F{::cacoshq}>::Create("acosh"), 456478e0b58SPeter Steinfeld FolderFactory<F, F{::casinq}>::Create("asin"), 457478e0b58SPeter Steinfeld FolderFactory<F, F{::casinhq}>::Create("asinh"), 458478e0b58SPeter Steinfeld FolderFactory<F, F{::catanq}>::Create("atan"), 459478e0b58SPeter Steinfeld FolderFactory<F, F{::catanhq}>::Create("atanh"), 460478e0b58SPeter Steinfeld FolderFactory<F, F{::ccosq}>::Create("cos"), 461478e0b58SPeter Steinfeld FolderFactory<F, F{::ccoshq}>::Create("cosh"), 462478e0b58SPeter Steinfeld FolderFactory<F, F{::cexpq}>::Create("exp"), 463478e0b58SPeter Steinfeld FolderFactory<F, F{::clogq}>::Create("log"), 464478e0b58SPeter Steinfeld FolderFactory<F2, F2{::cpowq}>::Create("pow"), 465478e0b58SPeter Steinfeld FolderFactory<F, F{::csinq}>::Create("sin"), 466478e0b58SPeter Steinfeld FolderFactory<F, F{::csinhq}>::Create("sinh"), 467478e0b58SPeter Steinfeld FolderFactory<F, F{::csqrtq}>::Create("sqrt"), 468478e0b58SPeter Steinfeld FolderFactory<F, F{::ctanq}>::Create("tan"), 469478e0b58SPeter Steinfeld FolderFactory<F, F{::ctanhq}>::Create("tanh"), 470478e0b58SPeter Steinfeld }; 471478e0b58SPeter Steinfeld static constexpr HostRuntimeMap map{table}; 472478e0b58SPeter Steinfeld static_assert(map.Verify(), "map must be sorted"); 473478e0b58SPeter Steinfeld }; 474478e0b58SPeter Steinfeld #endif 475478e0b58SPeter Steinfeld 4766c81b4a0SJean Perier template <> struct HostRuntimeLibrary<double, LibraryVersion::LibmExtensions> { 4776c81b4a0SJean Perier using F = FuncPointer<double, double>; 4786c81b4a0SJean Perier using FN = FuncPointer<double, int, double>; 4796c81b4a0SJean Perier static constexpr HostRuntimeFunction table[]{ 4806c81b4a0SJean Perier FolderFactory<F, F{::j0}>::Create("bessel_j0"), 4816c81b4a0SJean Perier FolderFactory<F, F{::j1}>::Create("bessel_j1"), 4826c81b4a0SJean Perier FolderFactory<FN, FN{::jn}>::Create("bessel_jn"), 4836c81b4a0SJean Perier FolderFactory<F, F{::y0}>::Create("bessel_y0"), 4846c81b4a0SJean Perier FolderFactory<F, F{::y1}>::Create("bessel_y1"), 4856c81b4a0SJean Perier FolderFactory<FN, FN{::yn}>::Create("bessel_yn"), 4866c81b4a0SJean Perier }; 4876c81b4a0SJean Perier static constexpr HostRuntimeMap map{table}; 4886c81b4a0SJean Perier static_assert(map.Verify(), "map must be sorted"); 4896c81b4a0SJean Perier }; 4906c81b4a0SJean Perier 49191b5bef3SDavid Truby #if defined(__GLIBC__) && (HAS_FLOAT80 || HAS_LDBL128) 4926c81b4a0SJean Perier template <> 4936c81b4a0SJean Perier struct HostRuntimeLibrary<long double, LibraryVersion::LibmExtensions> { 4946c81b4a0SJean Perier using F = FuncPointer<long double, long double>; 4956c81b4a0SJean Perier using FN = FuncPointer<long double, int, long double>; 4966c81b4a0SJean Perier static constexpr HostRuntimeFunction table[]{ 4976c81b4a0SJean Perier FolderFactory<F, F{::j0l}>::Create("bessel_j0"), 4986c81b4a0SJean Perier FolderFactory<F, F{::j1l}>::Create("bessel_j1"), 4996c81b4a0SJean Perier FolderFactory<FN, FN{::jnl}>::Create("bessel_jn"), 5006c81b4a0SJean Perier FolderFactory<F, F{::y0l}>::Create("bessel_y0"), 5016c81b4a0SJean Perier FolderFactory<F, F{::y1l}>::Create("bessel_y1"), 5026c81b4a0SJean Perier FolderFactory<FN, FN{::ynl}>::Create("bessel_yn"), 5036c81b4a0SJean Perier }; 5046c81b4a0SJean Perier static constexpr HostRuntimeMap map{table}; 5056c81b4a0SJean Perier static_assert(map.Verify(), "map must be sorted"); 5066c81b4a0SJean Perier }; 507fc51c7f0SSlava Zakharin #endif // HAS_FLOAT80 || HAS_LDBL128 508478e0b58SPeter Steinfeld #endif //_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600 50964ab3302SCarolineConcatto 51094d9a4fdSJean Perier /// Define pgmath description 51164ab3302SCarolineConcatto #if LINK_WITH_LIBPGMATH 512eb305631SJean Perier // Only use libpgmath for folding if it is available. 513eb305631SJean Perier // First declare all libpgmaths functions 51494d9a4fdSJean Perier #define PGMATH_LINKING 515eb305631SJean Perier #define PGMATH_DECLARE 516830c0b90SPeter Klausler #include "flang/Evaluate/pgmath.h.inc" 51764ab3302SCarolineConcatto 51894d9a4fdSJean Perier #define REAL_FOLDER(name, func) \ 51994d9a4fdSJean Perier FolderFactory<decltype(&func), &func>::Create(#name) 52094d9a4fdSJean Perier template <> struct HostRuntimeLibrary<float, LibraryVersion::PgmathFast> { 52194d9a4fdSJean Perier static constexpr HostRuntimeFunction table[]{ 522eb305631SJean Perier #define PGMATH_FAST 52394d9a4fdSJean Perier #define PGMATH_USE_S(name, func) REAL_FOLDER(name, func), 524830c0b90SPeter Klausler #include "flang/Evaluate/pgmath.h.inc" 52564ab3302SCarolineConcatto }; 52694d9a4fdSJean Perier static constexpr HostRuntimeMap map{table}; 52794d9a4fdSJean Perier static_assert(map.Verify(), "map must be sorted"); 528eb305631SJean Perier }; 52994d9a4fdSJean Perier template <> struct HostRuntimeLibrary<double, LibraryVersion::PgmathFast> { 53094d9a4fdSJean Perier static constexpr HostRuntimeFunction table[]{ 531eb305631SJean Perier #define PGMATH_FAST 53294d9a4fdSJean Perier #define PGMATH_USE_D(name, func) REAL_FOLDER(name, func), 533830c0b90SPeter Klausler #include "flang/Evaluate/pgmath.h.inc" 534eb305631SJean Perier }; 53594d9a4fdSJean Perier static constexpr HostRuntimeMap map{table}; 53694d9a4fdSJean Perier static_assert(map.Verify(), "map must be sorted"); 53794d9a4fdSJean Perier }; 53894d9a4fdSJean Perier template <> struct HostRuntimeLibrary<float, LibraryVersion::PgmathRelaxed> { 53994d9a4fdSJean Perier static constexpr HostRuntimeFunction table[]{ 540eb305631SJean Perier #define PGMATH_RELAXED 54194d9a4fdSJean Perier #define PGMATH_USE_S(name, func) REAL_FOLDER(name, func), 542830c0b90SPeter Klausler #include "flang/Evaluate/pgmath.h.inc" 543eb305631SJean Perier }; 54494d9a4fdSJean Perier static constexpr HostRuntimeMap map{table}; 54594d9a4fdSJean Perier static_assert(map.Verify(), "map must be sorted"); 546eb305631SJean Perier }; 54794d9a4fdSJean Perier template <> struct HostRuntimeLibrary<double, LibraryVersion::PgmathRelaxed> { 54894d9a4fdSJean Perier static constexpr HostRuntimeFunction table[]{ 549eb305631SJean Perier #define PGMATH_RELAXED 55094d9a4fdSJean Perier #define PGMATH_USE_D(name, func) REAL_FOLDER(name, func), 551830c0b90SPeter Klausler #include "flang/Evaluate/pgmath.h.inc" 552eb305631SJean Perier }; 55394d9a4fdSJean Perier static constexpr HostRuntimeMap map{table}; 55494d9a4fdSJean Perier static_assert(map.Verify(), "map must be sorted"); 55594d9a4fdSJean Perier }; 55694d9a4fdSJean Perier template <> struct HostRuntimeLibrary<float, LibraryVersion::PgmathPrecise> { 55794d9a4fdSJean Perier static constexpr HostRuntimeFunction table[]{ 558eb305631SJean Perier #define PGMATH_PRECISE 55994d9a4fdSJean Perier #define PGMATH_USE_S(name, func) REAL_FOLDER(name, func), 560830c0b90SPeter Klausler #include "flang/Evaluate/pgmath.h.inc" 561eb305631SJean Perier }; 56294d9a4fdSJean Perier static constexpr HostRuntimeMap map{table}; 56394d9a4fdSJean Perier static_assert(map.Verify(), "map must be sorted"); 564eb305631SJean Perier }; 56594d9a4fdSJean Perier template <> struct HostRuntimeLibrary<double, LibraryVersion::PgmathPrecise> { 56694d9a4fdSJean Perier static constexpr HostRuntimeFunction table[]{ 567eb305631SJean Perier #define PGMATH_PRECISE 56894d9a4fdSJean Perier #define PGMATH_USE_D(name, func) REAL_FOLDER(name, func), 569830c0b90SPeter Klausler #include "flang/Evaluate/pgmath.h.inc" 570eb305631SJean Perier }; 57194d9a4fdSJean Perier static constexpr HostRuntimeMap map{table}; 57294d9a4fdSJean Perier static_assert(map.Verify(), "map must be sorted"); 57394d9a4fdSJean Perier }; 57494d9a4fdSJean Perier 57594d9a4fdSJean Perier // TODO: double _Complex/float _Complex have been removed from llvm flang 57694d9a4fdSJean Perier // pgmath.h.inc because they caused warnings, they need to be added back 57794d9a4fdSJean Perier // so that the complex pgmath versions can be used when requested. 57894d9a4fdSJean Perier 57994d9a4fdSJean Perier #endif /* LINK_WITH_LIBPGMATH */ 58094d9a4fdSJean Perier 58194d9a4fdSJean Perier // Helper to check if a HostRuntimeLibrary specialization exists 58294d9a4fdSJean Perier template <typename T, typename = void> struct IsAvailable : std::false_type {}; 58394d9a4fdSJean Perier template <typename T> 58494d9a4fdSJean Perier struct IsAvailable<T, decltype((void)T::table, void())> : std::true_type {}; 58594d9a4fdSJean Perier // Define helpers to find host runtime library map according to desired version 58694d9a4fdSJean Perier // and type. 58794d9a4fdSJean Perier template <typename HostT, LibraryVersion version> 58894d9a4fdSJean Perier static const HostRuntimeMap *GetHostRuntimeMapHelper( 58994d9a4fdSJean Perier [[maybe_unused]] DynamicType resultType) { 59094d9a4fdSJean Perier // A library must only be instantiated if LibraryVersion is 59194d9a4fdSJean Perier // available on the host and if HostT maps to a Fortran type. 59294d9a4fdSJean Perier // For instance, whenever long double and double are both 64-bits, double 59394d9a4fdSJean Perier // is mapped to Fortran 64bits real type, and long double will be left 59494d9a4fdSJean Perier // unmapped. 59594d9a4fdSJean Perier if constexpr (host::FortranTypeExists<HostT>()) { 59694d9a4fdSJean Perier using Lib = HostRuntimeLibrary<HostT, version>; 59794d9a4fdSJean Perier if constexpr (IsAvailable<Lib>::value) { 59894d9a4fdSJean Perier if (host::FortranType<HostT>{}.GetType() == resultType) { 59994d9a4fdSJean Perier return &Lib::map; 600eb305631SJean Perier } 601eb305631SJean Perier } 60294d9a4fdSJean Perier } 60394d9a4fdSJean Perier return nullptr; 60494d9a4fdSJean Perier } 60594d9a4fdSJean Perier template <LibraryVersion version> 60694d9a4fdSJean Perier static const HostRuntimeMap *GetHostRuntimeMapVersion(DynamicType resultType) { 60794d9a4fdSJean Perier if (resultType.category() == TypeCategory::Real) { 60894d9a4fdSJean Perier if (const auto *map{GetHostRuntimeMapHelper<float, version>(resultType)}) { 60994d9a4fdSJean Perier return map; 61094d9a4fdSJean Perier } 61194d9a4fdSJean Perier if (const auto *map{GetHostRuntimeMapHelper<double, version>(resultType)}) { 61294d9a4fdSJean Perier return map; 61394d9a4fdSJean Perier } 61494d9a4fdSJean Perier if (const auto *map{ 61594d9a4fdSJean Perier GetHostRuntimeMapHelper<long double, version>(resultType)}) { 61694d9a4fdSJean Perier return map; 61794d9a4fdSJean Perier } 618478e0b58SPeter Steinfeld #if HAS_QUADMATHLIB 619478e0b58SPeter Steinfeld if (const auto *map{ 620478e0b58SPeter Steinfeld GetHostRuntimeMapHelper<__float128, version>(resultType)}) { 621478e0b58SPeter Steinfeld return map; 622478e0b58SPeter Steinfeld } 623478e0b58SPeter Steinfeld #endif 62494d9a4fdSJean Perier } 62594d9a4fdSJean Perier if (resultType.category() == TypeCategory::Complex) { 62694d9a4fdSJean Perier if (const auto *map{GetHostRuntimeMapHelper<std::complex<float>, version>( 62794d9a4fdSJean Perier resultType)}) { 62894d9a4fdSJean Perier return map; 62994d9a4fdSJean Perier } 63094d9a4fdSJean Perier if (const auto *map{GetHostRuntimeMapHelper<std::complex<double>, version>( 63194d9a4fdSJean Perier resultType)}) { 63294d9a4fdSJean Perier return map; 63394d9a4fdSJean Perier } 63494d9a4fdSJean Perier if (const auto *map{ 63594d9a4fdSJean Perier GetHostRuntimeMapHelper<std::complex<long double>, version>( 63694d9a4fdSJean Perier resultType)}) { 63794d9a4fdSJean Perier return map; 63894d9a4fdSJean Perier } 639478e0b58SPeter Steinfeld #if HAS_QUADMATHLIB 640478e0b58SPeter Steinfeld if (const auto *map{ 641478e0b58SPeter Steinfeld GetHostRuntimeMapHelper<__complex128, version>(resultType)}) { 642478e0b58SPeter Steinfeld return map; 643478e0b58SPeter Steinfeld } 644478e0b58SPeter Steinfeld #endif 64594d9a4fdSJean Perier } 64694d9a4fdSJean Perier return nullptr; 64794d9a4fdSJean Perier } 64894d9a4fdSJean Perier static const HostRuntimeMap *GetHostRuntimeMap( 64994d9a4fdSJean Perier LibraryVersion version, DynamicType resultType) { 65094d9a4fdSJean Perier switch (version) { 65194d9a4fdSJean Perier case LibraryVersion::Libm: 65294d9a4fdSJean Perier return GetHostRuntimeMapVersion<LibraryVersion::Libm>(resultType); 6536c81b4a0SJean Perier case LibraryVersion::LibmExtensions: 6546c81b4a0SJean Perier return GetHostRuntimeMapVersion<LibraryVersion::LibmExtensions>(resultType); 65594d9a4fdSJean Perier case LibraryVersion::PgmathPrecise: 65694d9a4fdSJean Perier return GetHostRuntimeMapVersion<LibraryVersion::PgmathPrecise>(resultType); 65794d9a4fdSJean Perier case LibraryVersion::PgmathRelaxed: 65894d9a4fdSJean Perier return GetHostRuntimeMapVersion<LibraryVersion::PgmathRelaxed>(resultType); 65994d9a4fdSJean Perier case LibraryVersion::PgmathFast: 66094d9a4fdSJean Perier return GetHostRuntimeMapVersion<LibraryVersion::PgmathFast>(resultType); 66194d9a4fdSJean Perier } 66294d9a4fdSJean Perier return nullptr; 66394d9a4fdSJean Perier } 664eb305631SJean Perier 66594d9a4fdSJean Perier static const HostRuntimeFunction *SearchInHostRuntimeMap( 66694d9a4fdSJean Perier const HostRuntimeMap &map, const std::string &name, DynamicType resultType, 66794d9a4fdSJean Perier const std::vector<DynamicType> &argTypes) { 66894d9a4fdSJean Perier auto sameNameRange{map.equal_range(name)}; 66994d9a4fdSJean Perier for (const auto *iter{sameNameRange.first}; iter != sameNameRange.second; 67094d9a4fdSJean Perier ++iter) { 67194d9a4fdSJean Perier if (iter->resultType == resultType && iter->argumentTypes == argTypes) { 67294d9a4fdSJean Perier return &*iter; 67394d9a4fdSJean Perier } 67494d9a4fdSJean Perier } 67594d9a4fdSJean Perier return nullptr; 67664ab3302SCarolineConcatto } 67764ab3302SCarolineConcatto 67894d9a4fdSJean Perier // Search host runtime libraries for an exact type match. 67994d9a4fdSJean Perier static const HostRuntimeFunction *SearchHostRuntime(const std::string &name, 68094d9a4fdSJean Perier DynamicType resultType, const std::vector<DynamicType> &argTypes) { 68164ab3302SCarolineConcatto // TODO: When command line options regarding targeted numerical library is 68264ab3302SCarolineConcatto // available, this needs to be revisited to take it into account. So far, 68364ab3302SCarolineConcatto // default to libpgmath if F18 is built with it. 68464ab3302SCarolineConcatto #if LINK_WITH_LIBPGMATH 68594d9a4fdSJean Perier if (const auto *map{ 68694d9a4fdSJean Perier GetHostRuntimeMap(LibraryVersion::PgmathPrecise, resultType)}) { 68794d9a4fdSJean Perier if (const auto *hostFunction{ 68894d9a4fdSJean Perier SearchInHostRuntimeMap(*map, name, resultType, argTypes)}) { 68994d9a4fdSJean Perier return hostFunction; 69064ab3302SCarolineConcatto } 69194d9a4fdSJean Perier } 69294d9a4fdSJean Perier // Default to libm if functions or types are not available in pgmath. 69364ab3302SCarolineConcatto #endif 69494d9a4fdSJean Perier if (const auto *map{GetHostRuntimeMap(LibraryVersion::Libm, resultType)}) { 69594d9a4fdSJean Perier if (const auto *hostFunction{ 69694d9a4fdSJean Perier SearchInHostRuntimeMap(*map, name, resultType, argTypes)}) { 69794d9a4fdSJean Perier return hostFunction; 69894d9a4fdSJean Perier } 69994d9a4fdSJean Perier } 7006c81b4a0SJean Perier if (const auto *map{ 7016c81b4a0SJean Perier GetHostRuntimeMap(LibraryVersion::LibmExtensions, resultType)}) { 7026c81b4a0SJean Perier if (const auto *hostFunction{ 7036c81b4a0SJean Perier SearchInHostRuntimeMap(*map, name, resultType, argTypes)}) { 7046c81b4a0SJean Perier return hostFunction; 7056c81b4a0SJean Perier } 7066c81b4a0SJean Perier } 70794d9a4fdSJean Perier return nullptr; 70894d9a4fdSJean Perier } 70994d9a4fdSJean Perier 71094d9a4fdSJean Perier // Return a DynamicType that can hold all values of a given type. 71194d9a4fdSJean Perier // This is used to allow 16bit float to be folded with 32bits and 71294d9a4fdSJean Perier // x87 float to be folded with IEEE 128bits. 71394d9a4fdSJean Perier static DynamicType BiggerType(DynamicType type) { 71494d9a4fdSJean Perier if (type.category() == TypeCategory::Real || 71594d9a4fdSJean Perier type.category() == TypeCategory::Complex) { 71694d9a4fdSJean Perier // 16 bits floats to IEEE 32 bits float 71794d9a4fdSJean Perier if (type.kind() == common::RealKindForPrecision(11) || 71894d9a4fdSJean Perier type.kind() == common::RealKindForPrecision(8)) { 71994d9a4fdSJean Perier return {type.category(), common::RealKindForPrecision(24)}; 72094d9a4fdSJean Perier } 72194d9a4fdSJean Perier // x87 float to IEEE 128 bits float 72294d9a4fdSJean Perier if (type.kind() == common::RealKindForPrecision(64)) { 72394d9a4fdSJean Perier return {type.category(), common::RealKindForPrecision(113)}; 72494d9a4fdSJean Perier } 72594d9a4fdSJean Perier } 72694d9a4fdSJean Perier return type; 72794d9a4fdSJean Perier } 72894d9a4fdSJean Perier 72970969df7SPete Steinfeld /// Structure to register intrinsic argument checks that must be performed. 73070969df7SPete Steinfeld using ArgumentVerifierFunc = bool (*)( 73170969df7SPete Steinfeld const std::vector<Expr<SomeType>> &, FoldingContext &); 73270969df7SPete Steinfeld struct ArgumentVerifier { 73370969df7SPete Steinfeld using Key = std::string_view; 73470969df7SPete Steinfeld // Needed for implicit compare with keys. 73570969df7SPete Steinfeld constexpr operator Key() const { return key; } 73670969df7SPete Steinfeld Key key; 73770969df7SPete Steinfeld ArgumentVerifierFunc verifier; 73870969df7SPete Steinfeld }; 73970969df7SPete Steinfeld 74070969df7SPete Steinfeld static constexpr int lastArg{-1}; 74170969df7SPete Steinfeld static constexpr int firstArg{0}; 74270969df7SPete Steinfeld 74370969df7SPete Steinfeld static const Expr<SomeType> &GetArg( 74470969df7SPete Steinfeld int position, const std::vector<Expr<SomeType>> &args) { 74570969df7SPete Steinfeld if (position == lastArg) { 74670969df7SPete Steinfeld CHECK(!args.empty()); 74770969df7SPete Steinfeld return args.back(); 74870969df7SPete Steinfeld } 74970969df7SPete Steinfeld CHECK(position >= 0 && static_cast<std::size_t>(position) < args.size()); 75070969df7SPete Steinfeld return args[position]; 75170969df7SPete Steinfeld } 75270969df7SPete Steinfeld 75370969df7SPete Steinfeld template <typename T> 75470969df7SPete Steinfeld static bool IsInRange(const Expr<T> &expr, int lb, int ub) { 75570969df7SPete Steinfeld if (auto scalar{GetScalarConstantValue<T>(expr)}) { 75670969df7SPete Steinfeld auto lbValue{Scalar<T>::FromInteger(value::Integer<8>{lb}).value}; 75770969df7SPete Steinfeld auto ubValue{Scalar<T>::FromInteger(value::Integer<8>{ub}).value}; 75870969df7SPete Steinfeld return Satisfies(RelationalOperator::LE, lbValue.Compare(*scalar)) && 75970969df7SPete Steinfeld Satisfies(RelationalOperator::LE, scalar->Compare(ubValue)); 76070969df7SPete Steinfeld } 76170969df7SPete Steinfeld return true; 76270969df7SPete Steinfeld } 76370969df7SPete Steinfeld 76470969df7SPete Steinfeld /// Verify that the argument in an intrinsic call belongs to [lb, ub] if is 76570969df7SPete Steinfeld /// real. 76670969df7SPete Steinfeld template <int lb, int ub> 76770969df7SPete Steinfeld static bool VerifyInRangeIfReal( 76870969df7SPete Steinfeld const std::vector<Expr<SomeType>> &args, FoldingContext &context) { 76970969df7SPete Steinfeld if (const auto *someReal{ 77070969df7SPete Steinfeld std::get_if<Expr<SomeReal>>(&GetArg(firstArg, args).u)}) { 77170969df7SPete Steinfeld bool isInRange{ 77270969df7SPete Steinfeld std::visit([&](const auto &x) -> bool { return IsInRange(x, lb, ub); }, 77370969df7SPete Steinfeld someReal->u)}; 77470969df7SPete Steinfeld if (!isInRange) { 77570969df7SPete Steinfeld context.messages().Say( 77670969df7SPete Steinfeld "argument is out of range [%d., %d.]"_warn_en_US, lb, ub); 77770969df7SPete Steinfeld } 77870969df7SPete Steinfeld return isInRange; 77970969df7SPete Steinfeld } 78070969df7SPete Steinfeld return true; 78170969df7SPete Steinfeld } 78270969df7SPete Steinfeld 78370969df7SPete Steinfeld template <int argPosition, const char *argName> 78470969df7SPete Steinfeld static bool VerifyStrictlyPositiveIfReal( 78570969df7SPete Steinfeld const std::vector<Expr<SomeType>> &args, FoldingContext &context) { 78670969df7SPete Steinfeld if (const auto *someReal = 78770969df7SPete Steinfeld std::get_if<Expr<SomeReal>>(&GetArg(argPosition, args).u)) { 78870969df7SPete Steinfeld const bool isStrictlyPositive{std::visit( 78970969df7SPete Steinfeld [&](const auto &x) -> bool { 79070969df7SPete Steinfeld using T = typename std::decay_t<decltype(x)>::Result; 79170969df7SPete Steinfeld auto scalar{GetScalarConstantValue<T>(x)}; 79270969df7SPete Steinfeld return Satisfies( 79370969df7SPete Steinfeld RelationalOperator::LT, Scalar<T>{}.Compare(*scalar)); 79470969df7SPete Steinfeld }, 79570969df7SPete Steinfeld someReal->u)}; 79670969df7SPete Steinfeld if (!isStrictlyPositive) { 79770969df7SPete Steinfeld context.messages().Say( 79870969df7SPete Steinfeld "argument '%s' must be strictly positive"_warn_en_US, argName); 79970969df7SPete Steinfeld } 80070969df7SPete Steinfeld return isStrictlyPositive; 80170969df7SPete Steinfeld } 80270969df7SPete Steinfeld return true; 80370969df7SPete Steinfeld } 80470969df7SPete Steinfeld 80570969df7SPete Steinfeld /// Verify that an intrinsic call argument is not zero if it is real. 80670969df7SPete Steinfeld template <int argPosition, const char *argName> 80770969df7SPete Steinfeld static bool VerifyNotZeroIfReal( 80870969df7SPete Steinfeld const std::vector<Expr<SomeType>> &args, FoldingContext &context) { 80970969df7SPete Steinfeld if (const auto *someReal = 81070969df7SPete Steinfeld std::get_if<Expr<SomeReal>>(&GetArg(argPosition, args).u)) { 81170969df7SPete Steinfeld const bool isNotZero{std::visit( 81270969df7SPete Steinfeld [&](const auto &x) -> bool { 81370969df7SPete Steinfeld using T = typename std::decay_t<decltype(x)>::Result; 81470969df7SPete Steinfeld auto scalar{GetScalarConstantValue<T>(x)}; 81570969df7SPete Steinfeld return !scalar || !scalar->IsZero(); 81670969df7SPete Steinfeld }, 81770969df7SPete Steinfeld someReal->u)}; 81870969df7SPete Steinfeld if (!isNotZero) { 81970969df7SPete Steinfeld context.messages().Say( 82070969df7SPete Steinfeld "argument '%s' must be different from zero"_warn_en_US, argName); 82170969df7SPete Steinfeld } 82270969df7SPete Steinfeld return isNotZero; 82370969df7SPete Steinfeld } 82470969df7SPete Steinfeld return true; 82570969df7SPete Steinfeld } 82670969df7SPete Steinfeld 82770969df7SPete Steinfeld /// Verify that the argument in an intrinsic call is not zero if is complex. 82870969df7SPete Steinfeld static bool VerifyNotZeroIfComplex( 82970969df7SPete Steinfeld const std::vector<Expr<SomeType>> &args, FoldingContext &context) { 83070969df7SPete Steinfeld if (const auto *someComplex = 83170969df7SPete Steinfeld std::get_if<Expr<SomeComplex>>(&GetArg(firstArg, args).u)) { 83270969df7SPete Steinfeld const bool isNotZero{std::visit( 83370969df7SPete Steinfeld [&](const auto &z) -> bool { 83470969df7SPete Steinfeld using T = typename std::decay_t<decltype(z)>::Result; 83570969df7SPete Steinfeld auto scalar{GetScalarConstantValue<T>(z)}; 83670969df7SPete Steinfeld return !scalar || !scalar->IsZero(); 83770969df7SPete Steinfeld }, 83870969df7SPete Steinfeld someComplex->u)}; 83970969df7SPete Steinfeld if (!isNotZero) { 84070969df7SPete Steinfeld context.messages().Say( 84170969df7SPete Steinfeld "complex argument must be different from zero"_warn_en_US); 84270969df7SPete Steinfeld } 84370969df7SPete Steinfeld return isNotZero; 84470969df7SPete Steinfeld } 84570969df7SPete Steinfeld return true; 84670969df7SPete Steinfeld } 84770969df7SPete Steinfeld 84870969df7SPete Steinfeld // Verify that the argument in an intrinsic call is not zero and not a negative 84970969df7SPete Steinfeld // integer. 85070969df7SPete Steinfeld static bool VerifyGammaLikeArgument( 85170969df7SPete Steinfeld const std::vector<Expr<SomeType>> &args, FoldingContext &context) { 85270969df7SPete Steinfeld if (const auto *someReal = 85370969df7SPete Steinfeld std::get_if<Expr<SomeReal>>(&GetArg(firstArg, args).u)) { 85470969df7SPete Steinfeld const bool isValid{std::visit( 85570969df7SPete Steinfeld [&](const auto &x) -> bool { 85670969df7SPete Steinfeld using T = typename std::decay_t<decltype(x)>::Result; 85770969df7SPete Steinfeld auto scalar{GetScalarConstantValue<T>(x)}; 85870969df7SPete Steinfeld if (scalar) { 85970969df7SPete Steinfeld return !scalar->IsZero() && 86070969df7SPete Steinfeld !(scalar->IsNegative() && 86170969df7SPete Steinfeld scalar->ToWholeNumber().value == scalar); 86270969df7SPete Steinfeld } 86370969df7SPete Steinfeld return true; 86470969df7SPete Steinfeld }, 86570969df7SPete Steinfeld someReal->u)}; 86670969df7SPete Steinfeld if (!isValid) { 86770969df7SPete Steinfeld context.messages().Say( 86870969df7SPete Steinfeld "argument must not be a negative integer or zero"_warn_en_US); 86970969df7SPete Steinfeld } 87070969df7SPete Steinfeld return isValid; 87170969df7SPete Steinfeld } 87270969df7SPete Steinfeld return true; 87370969df7SPete Steinfeld } 87470969df7SPete Steinfeld 87570969df7SPete Steinfeld // Verify that two real arguments are not both zero. 87670969df7SPete Steinfeld static bool VerifyAtan2LikeArguments( 87770969df7SPete Steinfeld const std::vector<Expr<SomeType>> &args, FoldingContext &context) { 87870969df7SPete Steinfeld if (const auto *someReal = 87970969df7SPete Steinfeld std::get_if<Expr<SomeReal>>(&GetArg(firstArg, args).u)) { 88070969df7SPete Steinfeld const bool isValid{std::visit( 88170969df7SPete Steinfeld [&](const auto &typedExpr) -> bool { 88270969df7SPete Steinfeld using T = typename std::decay_t<decltype(typedExpr)>::Result; 88370969df7SPete Steinfeld auto x{GetScalarConstantValue<T>(typedExpr)}; 88470969df7SPete Steinfeld auto y{GetScalarConstantValue<T>(GetArg(lastArg, args))}; 88570969df7SPete Steinfeld if (x && y) { 88670969df7SPete Steinfeld return !(x->IsZero() && y->IsZero()); 88770969df7SPete Steinfeld } 88870969df7SPete Steinfeld return true; 88970969df7SPete Steinfeld }, 89070969df7SPete Steinfeld someReal->u)}; 89170969df7SPete Steinfeld if (!isValid) { 89270969df7SPete Steinfeld context.messages().Say( 89370969df7SPete Steinfeld "'x' and 'y' arguments must not be both zero"_warn_en_US); 89470969df7SPete Steinfeld } 89570969df7SPete Steinfeld return isValid; 89670969df7SPete Steinfeld } 89770969df7SPete Steinfeld return true; 89870969df7SPete Steinfeld } 89970969df7SPete Steinfeld 90070969df7SPete Steinfeld template <ArgumentVerifierFunc... F> 90170969df7SPete Steinfeld static bool CombineVerifiers( 90270969df7SPete Steinfeld const std::vector<Expr<SomeType>> &args, FoldingContext &context) { 903bf5a2a99SPete Steinfeld return (... && F(args, context)); 90470969df7SPete Steinfeld } 90570969df7SPete Steinfeld 90670969df7SPete Steinfeld /// Define argument names to be used error messages when the intrinsic have 90770969df7SPete Steinfeld /// several arguments. 90870969df7SPete Steinfeld static constexpr char xName[]{"x"}; 90970969df7SPete Steinfeld static constexpr char pName[]{"p"}; 91070969df7SPete Steinfeld 91170969df7SPete Steinfeld /// Register argument verifiers for all intrinsics folded with runtime. 91270969df7SPete Steinfeld static constexpr ArgumentVerifier intrinsicArgumentVerifiers[]{ 91370969df7SPete Steinfeld {"acos", VerifyInRangeIfReal<-1, 1>}, 91470969df7SPete Steinfeld {"asin", VerifyInRangeIfReal<-1, 1>}, 91570969df7SPete Steinfeld {"atan2", VerifyAtan2LikeArguments}, 91670969df7SPete Steinfeld {"bessel_y0", VerifyStrictlyPositiveIfReal<firstArg, xName>}, 91770969df7SPete Steinfeld {"bessel_y1", VerifyStrictlyPositiveIfReal<firstArg, xName>}, 91870969df7SPete Steinfeld {"bessel_yn", VerifyStrictlyPositiveIfReal<lastArg, xName>}, 91970969df7SPete Steinfeld {"gamma", VerifyGammaLikeArgument}, 92070969df7SPete Steinfeld {"log", 92170969df7SPete Steinfeld CombineVerifiers<VerifyStrictlyPositiveIfReal<firstArg, xName>, 92270969df7SPete Steinfeld VerifyNotZeroIfComplex>}, 92370969df7SPete Steinfeld {"log10", VerifyStrictlyPositiveIfReal<firstArg, xName>}, 92470969df7SPete Steinfeld {"log_gamma", VerifyGammaLikeArgument}, 92570969df7SPete Steinfeld {"mod", VerifyNotZeroIfReal<lastArg, pName>}, 92670969df7SPete Steinfeld }; 92770969df7SPete Steinfeld 92870969df7SPete Steinfeld const ArgumentVerifierFunc *findVerifier(const std::string &intrinsicName) { 92970969df7SPete Steinfeld static constexpr Fortran::common::StaticMultimapView<ArgumentVerifier> 93070969df7SPete Steinfeld verifiers(intrinsicArgumentVerifiers); 93170969df7SPete Steinfeld static_assert(verifiers.Verify(), "map must be sorted"); 93270969df7SPete Steinfeld auto range{verifiers.equal_range(intrinsicName)}; 93370969df7SPete Steinfeld if (range.first != range.second) { 93470969df7SPete Steinfeld return &range.first->verifier; 93570969df7SPete Steinfeld } 93670969df7SPete Steinfeld return nullptr; 93770969df7SPete Steinfeld } 93870969df7SPete Steinfeld 93970969df7SPete Steinfeld /// Ensure argument verifiers, if any, are run before calling the runtime 94070969df7SPete Steinfeld /// wrapper to fold an intrinsic. 94170969df7SPete Steinfeld static HostRuntimeWrapper AddArgumentVerifierIfAny( 94270969df7SPete Steinfeld const std::string &intrinsicName, const HostRuntimeFunction &hostFunction) { 94370969df7SPete Steinfeld if (const auto *verifier{findVerifier(intrinsicName)}) { 94470969df7SPete Steinfeld const HostRuntimeFunction *hostFunctionPtr = &hostFunction; 94570969df7SPete Steinfeld return [hostFunctionPtr, verifier]( 94670969df7SPete Steinfeld FoldingContext &context, std::vector<Expr<SomeType>> &&args) { 94770969df7SPete Steinfeld const bool validArguments{(*verifier)(args, context)}; 94870969df7SPete Steinfeld if (!validArguments) { 94970969df7SPete Steinfeld // Silence fp signal warnings since a more detailed warning about 95070969df7SPete Steinfeld // invalid arguments was already emitted. 95170969df7SPete Steinfeld parser::Messages localBuffer; 95270969df7SPete Steinfeld parser::ContextualMessages localMessages{&localBuffer}; 95370969df7SPete Steinfeld FoldingContext localContext{context, localMessages}; 95470969df7SPete Steinfeld return hostFunctionPtr->folder(localContext, std::move(args)); 95570969df7SPete Steinfeld } 95670969df7SPete Steinfeld return hostFunctionPtr->folder(context, std::move(args)); 95770969df7SPete Steinfeld }; 95870969df7SPete Steinfeld } 95970969df7SPete Steinfeld return hostFunction.folder; 96070969df7SPete Steinfeld } 96170969df7SPete Steinfeld 96294d9a4fdSJean Perier std::optional<HostRuntimeWrapper> GetHostRuntimeWrapper(const std::string &name, 96394d9a4fdSJean Perier DynamicType resultType, const std::vector<DynamicType> &argTypes) { 96494d9a4fdSJean Perier if (const auto *hostFunction{SearchHostRuntime(name, resultType, argTypes)}) { 96570969df7SPete Steinfeld return AddArgumentVerifierIfAny(name, *hostFunction); 96694d9a4fdSJean Perier } 96794d9a4fdSJean Perier // If no exact match, search with "bigger" types and insert type 96894d9a4fdSJean Perier // conversions around the folder. 96994d9a4fdSJean Perier std::vector<evaluate::DynamicType> biggerArgTypes; 97094d9a4fdSJean Perier evaluate::DynamicType biggerResultType{BiggerType(resultType)}; 97194d9a4fdSJean Perier for (auto type : argTypes) { 97294d9a4fdSJean Perier biggerArgTypes.emplace_back(BiggerType(type)); 97394d9a4fdSJean Perier } 97494d9a4fdSJean Perier if (const auto *hostFunction{ 97594d9a4fdSJean Perier SearchHostRuntime(name, biggerResultType, biggerArgTypes)}) { 97670969df7SPete Steinfeld auto hostFolderWithChecks{AddArgumentVerifierIfAny(name, *hostFunction)}; 97770969df7SPete Steinfeld return [hostFunction, resultType, hostFolderWithChecks]( 97894d9a4fdSJean Perier FoldingContext &context, std::vector<Expr<SomeType>> &&args) { 97994d9a4fdSJean Perier auto nArgs{args.size()}; 98094d9a4fdSJean Perier for (size_t i{0}; i < nArgs; ++i) { 98194d9a4fdSJean Perier args[i] = Fold(context, 98294d9a4fdSJean Perier ConvertToType(hostFunction->argumentTypes[i], std::move(args[i])) 98394d9a4fdSJean Perier .value()); 98494d9a4fdSJean Perier } 98594d9a4fdSJean Perier return Fold(context, 98694d9a4fdSJean Perier ConvertToType( 98770969df7SPete Steinfeld resultType, hostFolderWithChecks(context, std::move(args))) 98894d9a4fdSJean Perier .value()); 98994d9a4fdSJean Perier }; 99094d9a4fdSJean Perier } 99194d9a4fdSJean Perier return std::nullopt; 99264ab3302SCarolineConcatto } 9931f879005STim Keith } // namespace Fortran::evaluate 994