FLANG

intrinsics-library-templates.h
1 //===-- lib/Evaluate/intrinsics-library-templates.h -------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #ifndef FORTRAN_EVALUATE_INTRINSICS_LIBRARY_TEMPLATES_H_
10 #define FORTRAN_EVALUATE_INTRINSICS_LIBRARY_TEMPLATES_H_
11
12 // This header defines the actual implementation of the templatized member
13 // function of the structures defined in intrinsics-library.h. It should only be
14 // included if these member functions are used, else intrinsics-library.h is
15 // sufficient. This is to avoid circular dependencies. The below implementation
16 // cannot be defined in .cpp file because it would be too cumbersome to decide
17 // which version should be instantiated in a generic way.
18
19 #include "host.h"
20 #include "flang/Common/template.h"
21 #include "flang/Evaluate/intrinsics-library.h"
22 #include "flang/Evaluate/type.h"
23
24 #include <tuple>
25 #include <type_traits>
26
27 namespace Fortran::evaluate {
28
29 // Define meaningful types for the runtime
30 using RuntimeTypes = evaluate::AllIntrinsicTypes;
31
32 template <typename T, typename... TT> struct IndexInTupleHelper {};
33 template <typename T, typename... TT>
34 struct IndexInTupleHelper<T, std::tuple<TT...>> {
35 static constexpr TypeCode value{common::TypeIndex<T, TT...>};
36 };
37
38 static_assert(
39 std::tuple_size_v<RuntimeTypes> < std::numeric_limits<TypeCode>::max(),
40 "TypeCode is too small");
41 template <typename T>
42 inline constexpr TypeCode typeCodeOf{
43 IndexInTupleHelper<T, RuntimeTypes>::value};
44
45 template <TypeCode n>
46 using RuntimeTypeOf = typename std::tuple_element_t<n, RuntimeTypes>;
47
48 template <typename TA, PassBy Pass>
49 using HostArgType = std::conditional_t<Pass == PassBy::Ref,
50 std::add_lvalue_reference_t<std::add_const_t<host::HostType<TA>>>,
51 host::HostType<TA>>;
52
53 template <typename TR, typename... ArgInfo>
54 using HostFuncPointer = FuncPointer<host::HostType<TR>,
55 HostArgType<typename ArgInfo::Type, ArgInfo::pass>...>;
56
57 // Software Subnormal Flushing helper.
58 template <typename T> struct Flusher {
59 // Only flush floating-points. Forward other scalars untouched.
60 static constexpr inline const Scalar<T> &FlushSubnormals(const Scalar<T> &x) {
61 return x;
62 }
63 };
64 template <int Kind> struct Flusher<Type<TypeCategory::Real, Kind>> {
65 using T = Type<TypeCategory::Real, Kind>;
66 static constexpr inline Scalar<T> FlushSubnormals(const Scalar<T> &x) {
67 return x.FlushSubnormalToZero();
68 }
69 };
70 template <int Kind> struct Flusher<Type<TypeCategory::Complex, Kind>> {
71 using T = Type<TypeCategory::Complex, Kind>;
72 static constexpr inline Scalar<T> FlushSubnormals(const Scalar<T> &x) {
73 return x.FlushSubnormalToZero();
74 }
75 };
76
77 // Callable factory
78 template <typename TR, typename... ArgInfo> struct CallableHostWrapper {
79 static Scalar<TR> scalarCallable(FoldingContext &context,
80 HostFuncPointer<TR, ArgInfo...> func,
81 const Scalar<typename ArgInfo::Type> &... x) {
82 if constexpr (host::HostTypeExists<TR, typename ArgInfo::Type...>()) {
83 host::HostFloatingPointEnvironment hostFPE;
84 hostFPE.SetUpHostFloatingPointEnvironment(context);
85 host::HostType<TR> hostResult{};
86 Scalar<TR> result{};
87 if (context.flushSubnormalsToZero() &&
88 !hostFPE.hasSubnormalFlushingHardwareControl()) {
89 hostResult = func(host::CastFortranToHost<typename ArgInfo::Type>(
90 Flusher<typename ArgInfo::Type>::FlushSubnormals(x))...);
91 result = Flusher<TR>::FlushSubnormals(
92 host::CastHostToFortran<TR>(hostResult));
93 } else {
94 hostResult =
95 func(host::CastFortranToHost<typename ArgInfo::Type>(x)...);
96 result = host::CastHostToFortran<TR>(hostResult);
97 }
98 if (!hostFPE.hardwareFlagsAreReliable()) {
99 CheckFloatingPointIssues(hostFPE, result);
100 }
101 hostFPE.CheckAndRestoreFloatingPointEnvironment(context);
102 return result;
103 } else {
104 common::die("Internal error: Host does not supports this function type."
105 "This should not have been called for folding");
106 }
107 }
108 static constexpr inline auto MakeScalarCallable() { return &scalarCallable; }
109
110 static void CheckFloatingPointIssues(
111 host::HostFloatingPointEnvironment &hostFPE, const Scalar<TR> &x) {
112 if constexpr (TR::category == TypeCategory::Complex ||
113 TR::category == TypeCategory::Real) {
114 if (x.IsNotANumber()) {
115 hostFPE.SetFlag(RealFlag::InvalidArgument);
116 } else if (x.IsInfinite()) {
117 hostFPE.SetFlag(RealFlag::Overflow);
118 }
119 }
120 }
121 };
122
123 template <typename TR, typename... TA>
124 inline GenericFunctionPointer ToGenericFunctionPointer(
125 FuncPointer<TR, TA...> f) {
126 return reinterpret_cast<GenericFunctionPointer>(f);
127 }
128
129 template <typename TR, typename... TA>
130 inline FuncPointer<TR, TA...> FromGenericFunctionPointer(
131 GenericFunctionPointer g) {
132 return reinterpret_cast<FuncPointer<TR, TA...>>(g);
133 }
134
135 template <typename TR, typename... ArgInfo>
136 IntrinsicProcedureRuntimeDescription::IntrinsicProcedureRuntimeDescription(
137 const Signature<TR, ArgInfo...> &signature, bool isElemental)
138 : name{signature.name}, returnType{typeCodeOf<TR>},
139 argumentsType{typeCodeOf<typename ArgInfo::Type>...},
140 argumentsPassedBy{ArgInfo::pass...}, isElemental{isElemental},
141 callable{ToGenericFunctionPointer(
142 CallableHostWrapper<TR, ArgInfo...>::MakeScalarCallable())} {}
143
144 template <typename HostTA> static constexpr inline PassBy PassByMethod() {
145 if constexpr (std::is_pointer_v<std::decay_t<HostTA>> ||
146 std::is_lvalue_reference_v<HostTA>) {
147 return PassBy::Ref;
148 }
149 return PassBy::Val;
150 }
151
152 template <typename HostTA>
153 using ArgInfoFromHostType =
154 ArgumentInfo<host::FortranType<std::remove_pointer_t<std::decay_t<HostTA>>>,
155 PassByMethod<HostTA>()>;
156
157 template <typename HostTR, typename... HostTA>
158 using SignatureFromHostFuncPointer =
159 Signature<host::FortranType<HostTR>, ArgInfoFromHostType<HostTA>...>;
160
161 template <typename HostTR, typename... HostTA>
162 HostRuntimeIntrinsicProcedure::HostRuntimeIntrinsicProcedure(
163 const std::string &name, FuncPointer<HostTR, HostTA...> func,
164 bool isElemental)
165 : IntrinsicProcedureRuntimeDescription(
166 SignatureFromHostFuncPointer<HostTR, HostTA...>{name}, isElemental),
167 handle{ToGenericFunctionPointer(func)} {}
168
169 template <template <typename> typename ConstantContainer, typename TR,
170 typename... TA>
171 std::optional<HostProcedureWrapper<ConstantContainer, TR, TA...>>
172 HostIntrinsicProceduresLibrary::GetHostProcedureWrapper(
173 const std::string &name) const {
174 if constexpr (host::HostTypeExists<TR, TA...>()) {
175 auto rteProcRange{procedures_.equal_range(name)};
176 const TypeCode resTypeCode{typeCodeOf<TR>};
177 const std::vector<TypeCode> argTypes{typeCodeOf<TA>...};
178 const size_t nargs{argTypes.size()};
179 for (auto iter{rteProcRange.first}; iter != rteProcRange.second; ++iter) {
180 if (nargs == iter->second.argumentsType.size() &&
181 resTypeCode == iter->second.returnType &&
182 (!std::is_same_v<ConstantContainer<TR>, Scalar<TR>> ||
183 iter->second.isElemental)) {
184 bool match{true};
185 int pos{0};
186 for (auto const &type : argTypes) {
187 if (type != iter->second.argumentsType[pos++]) {
188 match = false;
189 break;
190 }
191 }
192 if (match) {
193 return {HostProcedureWrapper<ConstantContainer, TR, TA...>{
194 [=](FoldingContext &context,
195 const ConstantContainer<TA> &... args) {
196 auto callable{FromGenericFunctionPointer<ConstantContainer<TR>,
197 FoldingContext &, GenericFunctionPointer,
198 const ConstantContainer<TA> &...>(iter->second.callable)};
199 return callable(context, iter->second.handle, args...);
200 }}};
201 }
202 }
203 }
204 }
205 return std::nullopt;
206 }
207
208 } // namespace Fortran::evaluate
209 #endif // FORTRAN_EVALUATE_INTRINSICS_LIBRARY_TEMPLATES_H_
Fortran::evaluate::host::HostFloatingPointEnvironment
Definition: host.h:32
Fortran::evaluate::Type
Definition: type.h:48
Fortran::evaluate::CallableHostWrapper
Definition: intrinsics-library-templates.h:78
Fortran::evaluate::Flusher
Definition: intrinsics-library-templates.h:58
Fortran::evaluate::FoldingContext
Definition: common.h:209
Fortran::evaluate::IndexInTupleHelper
Definition: intrinsics-library-templates.h:32