| //===-- lib/Evaluate/intrinsics-library-templates.h -------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef FORTRAN_EVALUATE_INTRINSICS_LIBRARY_TEMPLATES_H_ |
| #define FORTRAN_EVALUATE_INTRINSICS_LIBRARY_TEMPLATES_H_ |
| |
| // This header defines the actual implementation of the templatized member |
| // function of the structures defined in intrinsics-library.h. It should only be |
| // included if these member functions are used, else intrinsics-library.h is |
| // sufficient. This is to avoid circular dependencies. The below implementation |
| // cannot be defined in .cpp file because it would be too cumbersome to decide |
| // which version should be instantiated in a generic way. |
| |
| #include "host.h" |
| #include "flang/Common/template.h" |
| #include "flang/Evaluate/intrinsics-library.h" |
| #include "flang/Evaluate/type.h" |
| |
| #include <tuple> |
| #include <type_traits> |
| |
| namespace Fortran::evaluate { |
| |
| // Define meaningful types for the runtime |
| using RuntimeTypes = evaluate::AllIntrinsicTypes; |
| |
| template <typename T, typename... TT> struct IndexInTupleHelper {}; |
| template <typename T, typename... TT> |
| struct IndexInTupleHelper<T, std::tuple<TT...>> { |
| static constexpr TypeCode value{common::TypeIndex<T, TT...>}; |
| }; |
| |
| static_assert( |
| std::tuple_size_v<RuntimeTypes> < std::numeric_limits<TypeCode>::max(), |
| "TypeCode is too small"); |
| template <typename T> |
| inline constexpr TypeCode typeCodeOf{ |
| IndexInTupleHelper<T, RuntimeTypes>::value}; |
| |
| template <TypeCode n> |
| using RuntimeTypeOf = typename std::tuple_element_t<n, RuntimeTypes>; |
| |
| template <typename TA, PassBy Pass> |
| using HostArgType = std::conditional_t<Pass == PassBy::Ref, |
| std::add_lvalue_reference_t<std::add_const_t<host::HostType<TA>>>, |
| host::HostType<TA>>; |
| |
| template <typename TR, typename... ArgInfo> |
| using HostFuncPointer = FuncPointer<host::HostType<TR>, |
| HostArgType<typename ArgInfo::Type, ArgInfo::pass>...>; |
| |
| // Software Subnormal Flushing helper. |
| template <typename T> struct Flusher { |
| // Only flush floating-points. Forward other scalars untouched. |
| static constexpr inline const Scalar<T> &FlushSubnormals(const Scalar<T> &x) { |
| return x; |
| } |
| }; |
| template <int Kind> struct Flusher<Type<TypeCategory::Real, Kind>> { |
| using T = Type<TypeCategory::Real, Kind>; |
| static constexpr inline Scalar<T> FlushSubnormals(const Scalar<T> &x) { |
| return x.FlushSubnormalToZero(); |
| } |
| }; |
| template <int Kind> struct Flusher<Type<TypeCategory::Complex, Kind>> { |
| using T = Type<TypeCategory::Complex, Kind>; |
| static constexpr inline Scalar<T> FlushSubnormals(const Scalar<T> &x) { |
| return x.FlushSubnormalToZero(); |
| } |
| }; |
| |
| // Callable factory |
| template <typename TR, typename... ArgInfo> struct CallableHostWrapper { |
| static Scalar<TR> scalarCallable(FoldingContext &context, |
| HostFuncPointer<TR, ArgInfo...> func, |
| const Scalar<typename ArgInfo::Type> &... x) { |
| if constexpr (host::HostTypeExists<TR, typename ArgInfo::Type...>()) { |
| host::HostFloatingPointEnvironment hostFPE; |
| hostFPE.SetUpHostFloatingPointEnvironment(context); |
| host::HostType<TR> hostResult{}; |
| Scalar<TR> result{}; |
| if (context.flushSubnormalsToZero() && |
| !hostFPE.hasSubnormalFlushingHardwareControl()) { |
| hostResult = func(host::CastFortranToHost<typename ArgInfo::Type>( |
| Flusher<typename ArgInfo::Type>::FlushSubnormals(x))...); |
| result = Flusher<TR>::FlushSubnormals( |
| host::CastHostToFortran<TR>(hostResult)); |
| } else { |
| hostResult = |
| func(host::CastFortranToHost<typename ArgInfo::Type>(x)...); |
| result = host::CastHostToFortran<TR>(hostResult); |
| } |
| if (!hostFPE.hardwareFlagsAreReliable()) { |
| CheckFloatingPointIssues(hostFPE, result); |
| } |
| hostFPE.CheckAndRestoreFloatingPointEnvironment(context); |
| return result; |
| } else { |
| common::die("Internal error: Host does not supports this function type." |
| "This should not have been called for folding"); |
| } |
| } |
| static constexpr inline auto MakeScalarCallable() { return &scalarCallable; } |
| |
| static void CheckFloatingPointIssues( |
| host::HostFloatingPointEnvironment &hostFPE, const Scalar<TR> &x) { |
| if constexpr (TR::category == TypeCategory::Complex || |
| TR::category == TypeCategory::Real) { |
| if (x.IsNotANumber()) { |
| hostFPE.SetFlag(RealFlag::InvalidArgument); |
| } else if (x.IsInfinite()) { |
| hostFPE.SetFlag(RealFlag::Overflow); |
| } |
| } |
| } |
| }; |
| |
| template <typename TR, typename... TA> |
| inline GenericFunctionPointer ToGenericFunctionPointer( |
| FuncPointer<TR, TA...> f) { |
| return reinterpret_cast<GenericFunctionPointer>(f); |
| } |
| |
| template <typename TR, typename... TA> |
| inline FuncPointer<TR, TA...> FromGenericFunctionPointer( |
| GenericFunctionPointer g) { |
| return reinterpret_cast<FuncPointer<TR, TA...>>(g); |
| } |
| |
| template <typename TR, typename... ArgInfo> |
| IntrinsicProcedureRuntimeDescription::IntrinsicProcedureRuntimeDescription( |
| const Signature<TR, ArgInfo...> &signature, bool isElemental) |
| : name{signature.name}, returnType{typeCodeOf<TR>}, |
| argumentsType{typeCodeOf<typename ArgInfo::Type>...}, |
| argumentsPassedBy{ArgInfo::pass...}, isElemental{isElemental}, |
| callable{ToGenericFunctionPointer( |
| CallableHostWrapper<TR, ArgInfo...>::MakeScalarCallable())} {} |
| |
| template <typename HostTA> static constexpr inline PassBy PassByMethod() { |
| if constexpr (std::is_pointer_v<std::decay_t<HostTA>> || |
| std::is_lvalue_reference_v<HostTA>) { |
| return PassBy::Ref; |
| } |
| return PassBy::Val; |
| } |
| |
| template <typename HostTA> |
| using ArgInfoFromHostType = |
| ArgumentInfo<host::FortranType<std::remove_pointer_t<std::decay_t<HostTA>>>, |
| PassByMethod<HostTA>()>; |
| |
| template <typename HostTR, typename... HostTA> |
| using SignatureFromHostFuncPointer = |
| Signature<host::FortranType<HostTR>, ArgInfoFromHostType<HostTA>...>; |
| |
| template <typename HostTR, typename... HostTA> |
| HostRuntimeIntrinsicProcedure::HostRuntimeIntrinsicProcedure( |
| const std::string &name, FuncPointer<HostTR, HostTA...> func, |
| bool isElemental) |
| : IntrinsicProcedureRuntimeDescription( |
| SignatureFromHostFuncPointer<HostTR, HostTA...>{name}, isElemental), |
| handle{ToGenericFunctionPointer(func)} {} |
| |
| template <template <typename> typename ConstantContainer, typename TR, |
| typename... TA> |
| std::optional<HostProcedureWrapper<ConstantContainer, TR, TA...>> |
| HostIntrinsicProceduresLibrary::GetHostProcedureWrapper( |
| const std::string &name) const { |
| if constexpr (host::HostTypeExists<TR, TA...>()) { |
| auto rteProcRange{procedures_.equal_range(name)}; |
| const TypeCode resTypeCode{typeCodeOf<TR>}; |
| const std::vector<TypeCode> argTypes{typeCodeOf<TA>...}; |
| const size_t nargs{argTypes.size()}; |
| for (auto iter{rteProcRange.first}; iter != rteProcRange.second; ++iter) { |
| if (nargs == iter->second.argumentsType.size() && |
| resTypeCode == iter->second.returnType && |
| (!std::is_same_v<ConstantContainer<TR>, Scalar<TR>> || |
| iter->second.isElemental)) { |
| bool match{true}; |
| int pos{0}; |
| for (auto const &type : argTypes) { |
| if (type != iter->second.argumentsType[pos++]) { |
| match = false; |
| break; |
| } |
| } |
| if (match) { |
| return {HostProcedureWrapper<ConstantContainer, TR, TA...>{ |
| [=](FoldingContext &context, |
| const ConstantContainer<TA> &... args) { |
| auto callable{FromGenericFunctionPointer<ConstantContainer<TR>, |
| FoldingContext &, GenericFunctionPointer, |
| const ConstantContainer<TA> &...>(iter->second.callable)}; |
| return callable(context, iter->second.handle, args...); |
| }}}; |
| } |
| } |
| } |
| } |
| return std::nullopt; |
| } |
| |
| } // namespace Fortran::evaluate |
| #endif // FORTRAN_EVALUATE_INTRINSICS_LIBRARY_TEMPLATES_H_ |