xref: /llvm-project/flang/lib/Evaluate/intrinsics-library.cpp (revision 71d4f343f52756ca086d02151662e68633a0db52)
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