File: | build/source/flang/runtime/reduction-templates.h |
Warning: | line 111, column 26 The left operand of '-' is a garbage value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===-- runtime/product.cpp -----------------------------------------------===// | |||
2 | // | |||
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |||
4 | // See https://llvm.org/LICENSE.txt for license information. | |||
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |||
6 | // | |||
7 | //===----------------------------------------------------------------------===// | |||
8 | ||||
9 | // Implements PRODUCT for all required operand types and shapes. | |||
10 | ||||
11 | #include "reduction-templates.h" | |||
12 | #include "flang/Runtime/float128.h" | |||
13 | #include "flang/Runtime/reduction.h" | |||
14 | #include <cfloat> | |||
15 | #include <cinttypes> | |||
16 | #include <complex> | |||
17 | ||||
18 | namespace Fortran::runtime { | |||
19 | template <typename INTERMEDIATE> class NonComplexProductAccumulator { | |||
20 | public: | |||
21 | explicit NonComplexProductAccumulator(const Descriptor &array) | |||
22 | : array_{array} {} | |||
23 | void Reinitialize() { product_ = 1; } | |||
24 | template <typename A> void GetResult(A *p, int /*zeroBasedDim*/ = -1) const { | |||
25 | *p = static_cast<A>(product_); | |||
26 | } | |||
27 | template <typename A> bool AccumulateAt(const SubscriptValue at[]) { | |||
28 | product_ *= *array_.Element<A>(at); | |||
29 | return product_ != 0; | |||
30 | } | |||
31 | ||||
32 | private: | |||
33 | const Descriptor &array_; | |||
34 | INTERMEDIATE product_{1}; | |||
35 | }; | |||
36 | ||||
37 | template <typename PART> class ComplexProductAccumulator { | |||
38 | public: | |||
39 | explicit ComplexProductAccumulator(const Descriptor &array) : array_{array} {} | |||
40 | void Reinitialize() { product_ = std::complex<PART>{1, 0}; } | |||
41 | template <typename A> void GetResult(A *p, int /*zeroBasedDim*/ = -1) const { | |||
42 | using ResultPart = typename A::value_type; | |||
43 | *p = {static_cast<ResultPart>(product_.real()), | |||
44 | static_cast<ResultPart>(product_.imag())}; | |||
45 | } | |||
46 | template <typename A> bool AccumulateAt(const SubscriptValue at[]) { | |||
47 | product_ *= *array_.Element<A>(at); | |||
48 | return true; | |||
49 | } | |||
50 | ||||
51 | private: | |||
52 | const Descriptor &array_; | |||
53 | std::complex<PART> product_{1, 0}; | |||
54 | }; | |||
55 | ||||
56 | extern "C" { | |||
57 | CppTypeFor<TypeCategory::Integer, 1> RTNAME(ProductInteger1)_FortranAProductInteger1( | |||
58 | const Descriptor &x, const char *source, int line, int dim, | |||
59 | const Descriptor *mask) { | |||
60 | return GetTotalReduction<TypeCategory::Integer, 1>(x, source, line, dim, mask, | |||
61 | NonComplexProductAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x}, | |||
62 | "PRODUCT"); | |||
63 | } | |||
64 | CppTypeFor<TypeCategory::Integer, 2> RTNAME(ProductInteger2)_FortranAProductInteger2( | |||
65 | const Descriptor &x, const char *source, int line, int dim, | |||
66 | const Descriptor *mask) { | |||
67 | return GetTotalReduction<TypeCategory::Integer, 2>(x, source, line, dim, mask, | |||
68 | NonComplexProductAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x}, | |||
69 | "PRODUCT"); | |||
70 | } | |||
71 | CppTypeFor<TypeCategory::Integer, 4> RTNAME(ProductInteger4)_FortranAProductInteger4( | |||
72 | const Descriptor &x, const char *source, int line, int dim, | |||
73 | const Descriptor *mask) { | |||
74 | return GetTotalReduction<TypeCategory::Integer, 4>(x, source, line, dim, mask, | |||
75 | NonComplexProductAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x}, | |||
76 | "PRODUCT"); | |||
77 | } | |||
78 | CppTypeFor<TypeCategory::Integer, 8> RTNAME(ProductInteger8)_FortranAProductInteger8( | |||
79 | const Descriptor &x, const char *source, int line, int dim, | |||
80 | const Descriptor *mask) { | |||
81 | return GetTotalReduction<TypeCategory::Integer, 8>(x, source, line, dim, mask, | |||
82 | NonComplexProductAccumulator<CppTypeFor<TypeCategory::Integer, 8>>{x}, | |||
83 | "PRODUCT"); | |||
84 | } | |||
85 | #ifdef __SIZEOF_INT128__16 | |||
86 | CppTypeFor<TypeCategory::Integer, 16> RTNAME(ProductInteger16)_FortranAProductInteger16( | |||
87 | const Descriptor &x, const char *source, int line, int dim, | |||
88 | const Descriptor *mask) { | |||
89 | return GetTotalReduction<TypeCategory::Integer, 16>(x, source, line, dim, | |||
90 | mask, | |||
91 | NonComplexProductAccumulator<CppTypeFor<TypeCategory::Integer, 16>>{x}, | |||
92 | "PRODUCT"); | |||
93 | } | |||
94 | #endif | |||
95 | ||||
96 | // TODO: real/complex(2 & 3) | |||
97 | CppTypeFor<TypeCategory::Real, 4> RTNAME(ProductReal4)_FortranAProductReal4(const Descriptor &x, | |||
98 | const char *source, int line, int dim, const Descriptor *mask) { | |||
99 | return GetTotalReduction<TypeCategory::Real, 4>(x, source, line, dim, mask, | |||
100 | NonComplexProductAccumulator<CppTypeFor<TypeCategory::Real, 8>>{x}, | |||
101 | "PRODUCT"); | |||
102 | } | |||
103 | CppTypeFor<TypeCategory::Real, 8> RTNAME(ProductReal8)_FortranAProductReal8(const Descriptor &x, | |||
104 | const char *source, int line, int dim, const Descriptor *mask) { | |||
105 | return GetTotalReduction<TypeCategory::Real, 8>(x, source, line, dim, mask, | |||
106 | NonComplexProductAccumulator<CppTypeFor<TypeCategory::Real, 8>>{x}, | |||
107 | "PRODUCT"); | |||
108 | } | |||
109 | #if LDBL_MANT_DIG64 == 64 | |||
110 | CppTypeFor<TypeCategory::Real, 10> RTNAME(ProductReal10)_FortranAProductReal10(const Descriptor &x, | |||
111 | const char *source, int line, int dim, const Descriptor *mask) { | |||
112 | return GetTotalReduction<TypeCategory::Real, 10>(x, source, line, dim, mask, | |||
113 | NonComplexProductAccumulator<CppTypeFor<TypeCategory::Real, 10>>{x}, | |||
114 | "PRODUCT"); | |||
115 | } | |||
116 | #elif LDBL_MANT_DIG64 == 113 | |||
117 | CppTypeFor<TypeCategory::Real, 16> RTNAME(ProductReal16)_FortranAProductReal16(const Descriptor &x, | |||
118 | const char *source, int line, int dim, const Descriptor *mask) { | |||
119 | return GetTotalReduction<TypeCategory::Real, 16>(x, source, line, dim, mask, | |||
120 | NonComplexProductAccumulator<CppTypeFor<TypeCategory::Real, 16>>{x}, | |||
121 | "PRODUCT"); | |||
122 | } | |||
123 | #endif | |||
124 | ||||
125 | void RTNAME(CppProductComplex4)_FortranACppProductComplex4(CppTypeFor<TypeCategory::Complex, 4> &result, | |||
126 | const Descriptor &x, const char *source, int line, int dim, | |||
127 | const Descriptor *mask) { | |||
128 | result = GetTotalReduction<TypeCategory::Complex, 4>(x, source, line, dim, | |||
129 | mask, ComplexProductAccumulator<CppTypeFor<TypeCategory::Real, 8>>{x}, | |||
130 | "PRODUCT"); | |||
131 | } | |||
132 | void RTNAME(CppProductComplex8)_FortranACppProductComplex8(CppTypeFor<TypeCategory::Complex, 8> &result, | |||
133 | const Descriptor &x, const char *source, int line, int dim, | |||
134 | const Descriptor *mask) { | |||
135 | result = GetTotalReduction<TypeCategory::Complex, 8>(x, source, line, dim, | |||
136 | mask, ComplexProductAccumulator<CppTypeFor<TypeCategory::Real, 8>>{x}, | |||
137 | "PRODUCT"); | |||
138 | } | |||
139 | #if LDBL_MANT_DIG64 == 64 | |||
140 | void RTNAME(CppProductComplex10)_FortranACppProductComplex10(CppTypeFor<TypeCategory::Complex, 10> &result, | |||
141 | const Descriptor &x, const char *source, int line, int dim, | |||
142 | const Descriptor *mask) { | |||
143 | result = GetTotalReduction<TypeCategory::Complex, 10>(x, source, line, dim, | |||
144 | mask, ComplexProductAccumulator<CppTypeFor<TypeCategory::Real, 10>>{x}, | |||
145 | "PRODUCT"); | |||
146 | } | |||
147 | #elif LDBL_MANT_DIG64 == 113 | |||
148 | void RTNAME(CppProductComplex16)_FortranACppProductComplex16(CppTypeFor<TypeCategory::Complex, 16> &result, | |||
149 | const Descriptor &x, const char *source, int line, int dim, | |||
150 | const Descriptor *mask) { | |||
151 | result = GetTotalReduction<TypeCategory::Complex, 16>(x, source, line, dim, | |||
152 | mask, ComplexProductAccumulator<CppTypeFor<TypeCategory::Real, 16>>{x}, | |||
153 | "PRODUCT"); | |||
154 | } | |||
155 | #endif | |||
156 | ||||
157 | void RTNAME(ProductDim)_FortranAProductDim(Descriptor &result, const Descriptor &x, int dim, | |||
158 | const char *source, int line, const Descriptor *mask) { | |||
159 | TypedPartialNumericReduction<NonComplexProductAccumulator, | |||
| ||||
160 | NonComplexProductAccumulator, ComplexProductAccumulator>( | |||
161 | result, x, dim, source, line, mask, "PRODUCT"); | |||
162 | } | |||
163 | } // extern "C" | |||
164 | } // namespace Fortran::runtime |
1 | //===-- runtime/reduction-templates.h -------------------------------------===// | ||||
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 | // Generic function templates used by various reduction transformation | ||||
10 | // intrinsic functions (SUM, PRODUCT, &c.) | ||||
11 | // | ||||
12 | // * Partial reductions (i.e., those with DIM= arguments that are not | ||||
13 | // required to be 1 by the rank of the argument) return arrays that | ||||
14 | // are dynamically allocated in a caller-supplied descriptor. | ||||
15 | // * Total reductions (i.e., no DIM= argument) with FINDLOC, MAXLOC, & MINLOC | ||||
16 | // return integer vectors of some kind, not scalars; a caller-supplied | ||||
17 | // descriptor is used | ||||
18 | // * Character-valued reductions (MAXVAL & MINVAL) return arbitrary | ||||
19 | // length results, dynamically allocated in a caller-supplied descriptor | ||||
20 | |||||
21 | #ifndef FORTRAN_RUNTIME_REDUCTION_TEMPLATES_H_ | ||||
22 | #define FORTRAN_RUNTIME_REDUCTION_TEMPLATES_H_ | ||||
23 | |||||
24 | #include "terminator.h" | ||||
25 | #include "tools.h" | ||||
26 | #include "flang/Runtime/cpp-type.h" | ||||
27 | #include "flang/Runtime/descriptor.h" | ||||
28 | |||||
29 | namespace Fortran::runtime { | ||||
30 | |||||
31 | // Reductions are implemented with *accumulators*, which are instances of | ||||
32 | // classes that incrementally build up the result (or an element thereof) during | ||||
33 | // a traversal of the unmasked elements of an array. Each accumulator class | ||||
34 | // supports a constructor (which captures a reference to the array), an | ||||
35 | // AccumulateAt() member function that applies supplied subscripts to the | ||||
36 | // array and does something with a scalar element, and a GetResult() | ||||
37 | // member function that copies a final result into its destination. | ||||
38 | |||||
39 | // Total reduction of the array argument to a scalar (or to a vector in the | ||||
40 | // cases of FINDLOC, MAXLOC, & MINLOC). These are the cases without DIM= or | ||||
41 | // cases where the argument has rank 1 and DIM=, if present, must be 1. | ||||
42 | template <typename TYPE, typename ACCUMULATOR> | ||||
43 | inline void DoTotalReduction(const Descriptor &x, int dim, | ||||
44 | const Descriptor *mask, ACCUMULATOR &accumulator, const char *intrinsic, | ||||
45 | Terminator &terminator) { | ||||
46 | if (dim < 0 || dim > 1) { | ||||
47 | terminator.Crash("%s: bad DIM=%d for ARRAY argument with rank %d", | ||||
48 | intrinsic, dim, x.rank()); | ||||
49 | } | ||||
50 | SubscriptValue xAt[maxRank]; | ||||
51 | x.GetLowerBounds(xAt); | ||||
52 | if (mask) { | ||||
53 | CheckConformability(x, *mask, terminator, intrinsic, "ARRAY", "MASK"); | ||||
54 | SubscriptValue maskAt[maxRank]; | ||||
55 | mask->GetLowerBounds(maskAt); | ||||
56 | if (mask->rank() > 0) { | ||||
57 | for (auto elements{x.Elements()}; elements--; | ||||
58 | x.IncrementSubscripts(xAt), mask->IncrementSubscripts(maskAt)) { | ||||
59 | if (IsLogicalElementTrue(*mask, maskAt)) { | ||||
60 | if (!accumulator.template AccumulateAt<TYPE>(xAt)) | ||||
61 | break; | ||||
62 | } | ||||
63 | } | ||||
64 | return; | ||||
65 | } else if (!IsLogicalElementTrue(*mask, maskAt)) { | ||||
66 | // scalar MASK=.FALSE.: return identity value | ||||
67 | return; | ||||
68 | } | ||||
69 | } | ||||
70 | // No MASK=, or scalar MASK=.TRUE. | ||||
71 | for (auto elements{x.Elements()}; elements--; x.IncrementSubscripts(xAt)) { | ||||
72 | if (!accumulator.template AccumulateAt<TYPE>(xAt)) { | ||||
73 | break; // cut short, result is known | ||||
74 | } | ||||
75 | } | ||||
76 | } | ||||
77 | |||||
78 | template <TypeCategory CAT, int KIND, typename ACCUMULATOR> | ||||
79 | inline CppTypeFor<CAT, KIND> GetTotalReduction(const Descriptor &x, | ||||
80 | const char *source, int line, int dim, const Descriptor *mask, | ||||
81 | ACCUMULATOR &&accumulator, const char *intrinsic) { | ||||
82 | Terminator terminator{source, line}; | ||||
83 | RUNTIME_CHECK(terminator, TypeCode(CAT, KIND) == x.type())if (TypeCode(CAT, KIND) == x.type()) ; else (terminator).CheckFailed ("TypeCode(CAT, KIND) == x.type()", "flang/runtime/reduction-templates.h" , 83); | ||||
84 | using CppType = CppTypeFor<CAT, KIND>; | ||||
85 | DoTotalReduction<CppType>(x, dim, mask, accumulator, intrinsic, terminator); | ||||
86 | CppType result; | ||||
87 | #ifdef _MSC_VER // work around MSVC spurious error | ||||
88 | accumulator.GetResult(&result); | ||||
89 | #else | ||||
90 | accumulator.template GetResult(&result); | ||||
91 | #endif | ||||
92 | return result; | ||||
93 | } | ||||
94 | |||||
95 | // For reductions on a dimension, e.g. SUM(array,DIM=2) where the shape | ||||
96 | // of the array is [2,3,5], the shape of the result is [2,5] and | ||||
97 | // result(j,k) = SUM(array(j,:,k)), possibly modified if the array has | ||||
98 | // lower bounds other than one. This utility subroutine creates an | ||||
99 | // array of subscripts [j,_,k] for result subscripts [j,k] so that the | ||||
100 | // elements of array(j,:,k) can be reduced. | ||||
101 | inline void GetExpandedSubscripts(SubscriptValue at[], | ||||
102 | const Descriptor &descriptor, int zeroBasedDim, | ||||
103 | const SubscriptValue from[]) { | ||||
104 | descriptor.GetLowerBounds(at); | ||||
105 | int rank{descriptor.rank()}; | ||||
106 | int j{0}; | ||||
107 | for (; j < zeroBasedDim; ++j) { | ||||
108 | at[j] += from[j] - 1 /*lower bound*/; | ||||
109 | } | ||||
110 | for (++j; j < rank; ++j) { | ||||
111 | at[j] += from[j - 1] - 1; | ||||
| |||||
112 | } | ||||
113 | } | ||||
114 | |||||
115 | template <typename TYPE, typename ACCUMULATOR> | ||||
116 | inline void ReduceDimToScalar(const Descriptor &x, int zeroBasedDim, | ||||
117 | SubscriptValue subscripts[], TYPE *result, ACCUMULATOR &accumulator) { | ||||
118 | SubscriptValue xAt[maxRank]; | ||||
119 | GetExpandedSubscripts(xAt, x, zeroBasedDim, subscripts); | ||||
120 | const auto &dim{x.GetDimension(zeroBasedDim)}; | ||||
121 | SubscriptValue at{dim.LowerBound()}; | ||||
122 | for (auto n{dim.Extent()}; n-- > 0; ++at) { | ||||
123 | xAt[zeroBasedDim] = at; | ||||
124 | if (!accumulator.template AccumulateAt<TYPE>(xAt)) { | ||||
125 | break; | ||||
126 | } | ||||
127 | } | ||||
128 | #ifdef _MSC_VER // work around MSVC spurious error | ||||
129 | accumulator.GetResult(result, zeroBasedDim); | ||||
130 | #else | ||||
131 | accumulator.template GetResult(result, zeroBasedDim); | ||||
132 | #endif | ||||
133 | } | ||||
134 | |||||
135 | template <typename TYPE, typename ACCUMULATOR> | ||||
136 | inline void ReduceDimMaskToScalar(const Descriptor &x, int zeroBasedDim, | ||||
137 | SubscriptValue subscripts[], const Descriptor &mask, TYPE *result, | ||||
138 | ACCUMULATOR &accumulator) { | ||||
139 | SubscriptValue xAt[maxRank], maskAt[maxRank]; | ||||
140 | GetExpandedSubscripts(xAt, x, zeroBasedDim, subscripts); | ||||
141 | GetExpandedSubscripts(maskAt, mask, zeroBasedDim, subscripts); | ||||
142 | const auto &xDim{x.GetDimension(zeroBasedDim)}; | ||||
143 | SubscriptValue xPos{xDim.LowerBound()}; | ||||
144 | const auto &maskDim{mask.GetDimension(zeroBasedDim)}; | ||||
145 | SubscriptValue maskPos{maskDim.LowerBound()}; | ||||
146 | for (auto n{x.GetDimension(zeroBasedDim).Extent()}; n-- > 0; | ||||
147 | ++xPos, ++maskPos) { | ||||
148 | maskAt[zeroBasedDim] = maskPos; | ||||
149 | if (IsLogicalElementTrue(mask, maskAt)) { | ||||
150 | xAt[zeroBasedDim] = xPos; | ||||
151 | if (!accumulator.template AccumulateAt<TYPE>(xAt)) { | ||||
152 | break; | ||||
153 | } | ||||
154 | } | ||||
155 | } | ||||
156 | #ifdef _MSC_VER // work around MSVC spurious error | ||||
157 | accumulator.GetResult(result, zeroBasedDim); | ||||
158 | #else | ||||
159 | accumulator.template GetResult(result, zeroBasedDim); | ||||
160 | #endif | ||||
161 | } | ||||
162 | |||||
163 | // Utility: establishes & allocates the result array for a partial | ||||
164 | // reduction (i.e., one with DIM=). | ||||
165 | static void CreatePartialReductionResult(Descriptor &result, | ||||
166 | const Descriptor &x, std::size_t resultElementSize, int dim, | ||||
167 | Terminator &terminator, const char *intrinsic, TypeCode typeCode) { | ||||
168 | int xRank{x.rank()}; | ||||
169 | if (dim < 1 || dim > xRank) { | ||||
170 | terminator.Crash( | ||||
171 | "%s: bad DIM=%d for ARRAY with rank %d", intrinsic, dim, xRank); | ||||
172 | } | ||||
173 | int zeroBasedDim{dim - 1}; | ||||
174 | SubscriptValue resultExtent[maxRank]; | ||||
175 | for (int j{0}; j < zeroBasedDim; ++j) { | ||||
176 | resultExtent[j] = x.GetDimension(j).Extent(); | ||||
177 | } | ||||
178 | for (int j{zeroBasedDim + 1}; j < xRank; ++j) { | ||||
179 | resultExtent[j - 1] = x.GetDimension(j).Extent(); | ||||
180 | } | ||||
181 | result.Establish(typeCode, resultElementSize, nullptr, xRank - 1, | ||||
182 | resultExtent, CFI_attribute_allocatable2); | ||||
183 | for (int j{0}; j + 1 < xRank; ++j) { | ||||
184 | result.GetDimension(j).SetBounds(1, resultExtent[j]); | ||||
185 | } | ||||
186 | if (int stat{result.Allocate()}) { | ||||
187 | terminator.Crash( | ||||
188 | "%s: could not allocate memory for result; STAT=%d", intrinsic, stat); | ||||
189 | } | ||||
190 | } | ||||
191 | |||||
192 | // Partial reductions with DIM= | ||||
193 | |||||
194 | template <typename ACCUMULATOR, TypeCategory CAT, int KIND> | ||||
195 | inline void PartialReduction(Descriptor &result, const Descriptor &x, | ||||
196 | std::size_t resultElementSize, int dim, const Descriptor *mask, | ||||
197 | Terminator &terminator, const char *intrinsic, ACCUMULATOR &accumulator) { | ||||
198 | CreatePartialReductionResult(result, x, resultElementSize, dim, terminator, | ||||
199 | intrinsic, TypeCode{CAT, KIND}); | ||||
200 | SubscriptValue at[maxRank]; | ||||
201 | result.GetLowerBounds(at); | ||||
202 | INTERNAL_CHECK(result.rank() == 0 || at[0] == 1)if (result.rank() == 0 || at[0] == 1) ; else Terminator{"flang/runtime/reduction-templates.h" , 202}.CheckFailed("result.rank() == 0 || at[0] == 1"); | ||||
203 | using CppType = CppTypeFor<CAT, KIND>; | ||||
204 | if (mask) { | ||||
205 | CheckConformability(x, *mask, terminator, intrinsic, "ARRAY", "MASK"); | ||||
206 | SubscriptValue maskAt[maxRank]; // contents unused | ||||
207 | if (mask->rank() > 0) { | ||||
208 | for (auto n{result.Elements()}; n-- > 0; result.IncrementSubscripts(at)) { | ||||
209 | accumulator.Reinitialize(); | ||||
210 | ReduceDimMaskToScalar<CppType, ACCUMULATOR>( | ||||
211 | x, dim - 1, at, *mask, result.Element<CppType>(at), accumulator); | ||||
212 | } | ||||
213 | return; | ||||
214 | } else if (!IsLogicalElementTrue(*mask, maskAt)) { | ||||
215 | // scalar MASK=.FALSE. | ||||
216 | accumulator.Reinitialize(); | ||||
217 | for (auto n{result.Elements()}; n-- > 0; result.IncrementSubscripts(at)) { | ||||
218 | accumulator.GetResult(result.Element<CppType>(at)); | ||||
219 | } | ||||
220 | return; | ||||
221 | } | ||||
222 | } | ||||
223 | // No MASK= or scalar MASK=.TRUE. | ||||
224 | for (auto n{result.Elements()}; n-- > 0; result.IncrementSubscripts(at)) { | ||||
225 | accumulator.Reinitialize(); | ||||
226 | ReduceDimToScalar<CppType, ACCUMULATOR>( | ||||
227 | x, dim - 1, at, result.Element<CppType>(at), accumulator); | ||||
228 | } | ||||
229 | } | ||||
230 | |||||
231 | template <template <typename> class ACCUM> | ||||
232 | struct PartialIntegerReductionHelper { | ||||
233 | template <int KIND> struct Functor { | ||||
234 | static constexpr int Intermediate{ | ||||
235 | std::max(KIND, 4)}; // use at least "int" for intermediate results | ||||
236 | void operator()(Descriptor &result, const Descriptor &x, int dim, | ||||
237 | const Descriptor *mask, Terminator &terminator, | ||||
238 | const char *intrinsic) const { | ||||
239 | using Accumulator = | ||||
240 | ACCUM<CppTypeFor<TypeCategory::Integer, Intermediate>>; | ||||
241 | Accumulator accumulator{x}; | ||||
242 | // Element size of the destination descriptor is the same | ||||
243 | // as the element size of the source. | ||||
244 | PartialReduction<Accumulator, TypeCategory::Integer, KIND>(result, x, | ||||
245 | x.ElementBytes(), dim, mask, terminator, intrinsic, accumulator); | ||||
246 | } | ||||
247 | }; | ||||
248 | }; | ||||
249 | |||||
250 | template <template <typename> class INTEGER_ACCUM> | ||||
251 | inline void PartialIntegerReduction(Descriptor &result, const Descriptor &x, | ||||
252 | int dim, int kind, const Descriptor *mask, const char *intrinsic, | ||||
253 | Terminator &terminator) { | ||||
254 | ApplyIntegerKind< | ||||
255 | PartialIntegerReductionHelper<INTEGER_ACCUM>::template Functor, void>( | ||||
256 | kind, terminator, result, x, dim, mask, terminator, intrinsic); | ||||
257 | } | ||||
258 | |||||
259 | template <TypeCategory CAT, template <typename> class ACCUM> | ||||
260 | struct PartialFloatingReductionHelper { | ||||
261 | template <int KIND> struct Functor { | ||||
262 | static constexpr int Intermediate{ | ||||
263 | std::max(KIND, 8)}; // use at least "double" for intermediate results | ||||
264 | void operator()(Descriptor &result, const Descriptor &x, int dim, | ||||
265 | const Descriptor *mask, Terminator &terminator, | ||||
266 | const char *intrinsic) const { | ||||
267 | using Accumulator = ACCUM<CppTypeFor<TypeCategory::Real, Intermediate>>; | ||||
268 | Accumulator accumulator{x}; | ||||
269 | // Element size of the destination descriptor is the same | ||||
270 | // as the element size of the source. | ||||
271 | PartialReduction<Accumulator, CAT, KIND>(result, x, x.ElementBytes(), dim, | ||||
272 | mask, terminator, intrinsic, accumulator); | ||||
273 | } | ||||
274 | }; | ||||
275 | }; | ||||
276 | |||||
277 | template <template <typename> class INTEGER_ACCUM, | ||||
278 | template <typename> class REAL_ACCUM, | ||||
279 | template <typename> class COMPLEX_ACCUM> | ||||
280 | inline void TypedPartialNumericReduction(Descriptor &result, | ||||
281 | const Descriptor &x, int dim, const char *source, int line, | ||||
282 | const Descriptor *mask, const char *intrinsic) { | ||||
283 | Terminator terminator{source, line}; | ||||
284 | auto catKind{x.type().GetCategoryAndKind()}; | ||||
285 | RUNTIME_CHECK(terminator, catKind.has_value())if (catKind.has_value()) ; else (terminator).CheckFailed("catKind.has_value()" , "flang/runtime/reduction-templates.h", 285); | ||||
286 | switch (catKind->first) { | ||||
287 | case TypeCategory::Integer: | ||||
288 | PartialIntegerReduction<INTEGER_ACCUM>( | ||||
289 | result, x, dim, catKind->second, mask, intrinsic, terminator); | ||||
290 | break; | ||||
291 | case TypeCategory::Real: | ||||
292 | ApplyFloatingPointKind<PartialFloatingReductionHelper<TypeCategory::Real, | ||||
| |||||
293 | REAL_ACCUM>::template Functor, | ||||
294 | void>(catKind->second, terminator, result, x, dim, mask, terminator, | ||||
295 | intrinsic); | ||||
296 | break; | ||||
297 | case TypeCategory::Complex: | ||||
298 | ApplyFloatingPointKind<PartialFloatingReductionHelper<TypeCategory::Complex, | ||||
299 | COMPLEX_ACCUM>::template Functor, | ||||
300 | void>(catKind->second, terminator, result, x, dim, mask, terminator, | ||||
301 | intrinsic); | ||||
302 | break; | ||||
303 | default: | ||||
304 | terminator.Crash("%s: bad type code %d", intrinsic, x.type().raw()); | ||||
305 | } | ||||
306 | } | ||||
307 | |||||
308 | template <typename ACCUMULATOR> struct LocationResultHelper { | ||||
309 | template <int KIND> struct Functor { | ||||
310 | void operator()(ACCUMULATOR &accumulator, const Descriptor &result) const { | ||||
311 | accumulator.GetResult( | ||||
312 | result.OffsetElement<CppTypeFor<TypeCategory::Integer, KIND>>()); | ||||
313 | } | ||||
314 | }; | ||||
315 | }; | ||||
316 | |||||
317 | template <typename ACCUMULATOR> struct PartialLocationHelper { | ||||
318 | template <int KIND> struct Functor { | ||||
319 | void operator()(Descriptor &result, const Descriptor &x, int dim, | ||||
320 | const Descriptor *mask, Terminator &terminator, const char *intrinsic, | ||||
321 | ACCUMULATOR &accumulator) const { | ||||
322 | // Element size of the destination descriptor is the size | ||||
323 | // of {TypeCategory::Integer, KIND}. | ||||
324 | PartialReduction<ACCUMULATOR, TypeCategory::Integer, KIND>(result, x, | ||||
325 | Descriptor::BytesFor(TypeCategory::Integer, KIND), dim, mask, | ||||
326 | terminator, intrinsic, accumulator); | ||||
327 | } | ||||
328 | }; | ||||
329 | }; | ||||
330 | |||||
331 | } // namespace Fortran::runtime | ||||
332 | #endif // FORTRAN_RUNTIME_REDUCTION_TEMPLATES_H_ |
1 | //===-- runtime/tools.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_RUNTIME_TOOLS_H_ |
10 | #define FORTRAN_RUNTIME_TOOLS_H_ |
11 | |
12 | #include "terminator.h" |
13 | #include "flang/Runtime/cpp-type.h" |
14 | #include "flang/Runtime/descriptor.h" |
15 | #include "flang/Runtime/memory.h" |
16 | #include <cstring> |
17 | #include <functional> |
18 | #include <map> |
19 | #include <type_traits> |
20 | |
21 | namespace Fortran::runtime { |
22 | |
23 | class Terminator; |
24 | |
25 | std::size_t TrimTrailingSpaces(const char *, std::size_t); |
26 | |
27 | OwningPtr<char> SaveDefaultCharacter( |
28 | const char *, std::size_t, const Terminator &); |
29 | |
30 | // For validating and recognizing default CHARACTER values in a |
31 | // case-insensitive manner. Returns the zero-based index into the |
32 | // null-terminated array of upper-case possibilities when the value is valid, |
33 | // or -1 when it has no match. |
34 | int IdentifyValue( |
35 | const char *value, std::size_t length, const char *possibilities[]); |
36 | |
37 | // Truncates or pads as necessary |
38 | void ToFortranDefaultCharacter( |
39 | char *to, std::size_t toLength, const char *from); |
40 | |
41 | // Utility for dealing with elemental LOGICAL arguments |
42 | inline bool IsLogicalElementTrue( |
43 | const Descriptor &logical, const SubscriptValue at[]) { |
44 | // A LOGICAL value is false if and only if all of its bytes are zero. |
45 | const char *p{logical.Element<char>(at)}; |
46 | for (std::size_t j{logical.ElementBytes()}; j-- > 0; ++p) { |
47 | if (*p) { |
48 | return true; |
49 | } |
50 | } |
51 | return false; |
52 | } |
53 | |
54 | // Check array conformability; a scalar 'x' conforms. Crashes on error. |
55 | void CheckConformability(const Descriptor &to, const Descriptor &x, |
56 | Terminator &, const char *funcName, const char *toName, |
57 | const char *fromName); |
58 | |
59 | // Helper to store integer value in result[at]. |
60 | template <int KIND> struct StoreIntegerAt { |
61 | void operator()(const Fortran::runtime::Descriptor &result, std::size_t at, |
62 | std::int64_t value) const { |
63 | *result.ZeroBasedIndexedElement<Fortran::runtime::CppTypeFor< |
64 | Fortran::common::TypeCategory::Integer, KIND>>(at) = value; |
65 | } |
66 | }; |
67 | |
68 | // Validate a KIND= argument |
69 | void CheckIntegerKind(Terminator &, int kind, const char *intrinsic); |
70 | |
71 | template <typename TO, typename FROM> |
72 | inline void PutContiguousConverted(TO *to, FROM *from, std::size_t count) { |
73 | while (count-- > 0) { |
74 | *to++ = *from++; |
75 | } |
76 | } |
77 | |
78 | static inline std::int64_t GetInt64( |
79 | const char *p, std::size_t bytes, Terminator &terminator) { |
80 | switch (bytes) { |
81 | case 1: |
82 | return *reinterpret_cast<const CppTypeFor<TypeCategory::Integer, 1> *>(p); |
83 | case 2: |
84 | return *reinterpret_cast<const CppTypeFor<TypeCategory::Integer, 2> *>(p); |
85 | case 4: |
86 | return *reinterpret_cast<const CppTypeFor<TypeCategory::Integer, 4> *>(p); |
87 | case 8: |
88 | return *reinterpret_cast<const CppTypeFor<TypeCategory::Integer, 8> *>(p); |
89 | default: |
90 | terminator.Crash("GetInt64: no case for %zd bytes", bytes); |
91 | } |
92 | } |
93 | |
94 | template <typename INT> |
95 | inline bool SetInteger(INT &x, int kind, std::int64_t value) { |
96 | switch (kind) { |
97 | case 1: |
98 | reinterpret_cast<CppTypeFor<TypeCategory::Integer, 1> &>(x) = value; |
99 | return value == reinterpret_cast<CppTypeFor<TypeCategory::Integer, 1> &>(x); |
100 | case 2: |
101 | reinterpret_cast<CppTypeFor<TypeCategory::Integer, 2> &>(x) = value; |
102 | return value == reinterpret_cast<CppTypeFor<TypeCategory::Integer, 2> &>(x); |
103 | case 4: |
104 | reinterpret_cast<CppTypeFor<TypeCategory::Integer, 4> &>(x) = value; |
105 | return value == reinterpret_cast<CppTypeFor<TypeCategory::Integer, 4> &>(x); |
106 | case 8: |
107 | reinterpret_cast<CppTypeFor<TypeCategory::Integer, 8> &>(x) = value; |
108 | return value == reinterpret_cast<CppTypeFor<TypeCategory::Integer, 8> &>(x); |
109 | default: |
110 | return false; |
111 | } |
112 | } |
113 | |
114 | // Maps intrinsic runtime type category and kind values to the appropriate |
115 | // instantiation of a function object template and calls it with the supplied |
116 | // arguments. |
117 | template <template <TypeCategory, int> class FUNC, typename RESULT, |
118 | typename... A> |
119 | inline RESULT ApplyType( |
120 | TypeCategory cat, int kind, Terminator &terminator, A &&...x) { |
121 | switch (cat) { |
122 | case TypeCategory::Integer: |
123 | switch (kind) { |
124 | case 1: |
125 | return FUNC<TypeCategory::Integer, 1>{}(std::forward<A>(x)...); |
126 | case 2: |
127 | return FUNC<TypeCategory::Integer, 2>{}(std::forward<A>(x)...); |
128 | case 4: |
129 | return FUNC<TypeCategory::Integer, 4>{}(std::forward<A>(x)...); |
130 | case 8: |
131 | return FUNC<TypeCategory::Integer, 8>{}(std::forward<A>(x)...); |
132 | #ifdef __SIZEOF_INT128__16 |
133 | case 16: |
134 | return FUNC<TypeCategory::Integer, 16>{}(std::forward<A>(x)...); |
135 | #endif |
136 | default: |
137 | terminator.Crash("not yet implemented: INTEGER(KIND=%d)", kind); |
138 | } |
139 | case TypeCategory::Real: |
140 | switch (kind) { |
141 | #if 0 // TODO: REAL(2 & 3) |
142 | case 2: |
143 | return FUNC<TypeCategory::Real, 2>{}(std::forward<A>(x)...); |
144 | case 3: |
145 | return FUNC<TypeCategory::Real, 3>{}(std::forward<A>(x)...); |
146 | #endif |
147 | case 4: |
148 | return FUNC<TypeCategory::Real, 4>{}(std::forward<A>(x)...); |
149 | case 8: |
150 | return FUNC<TypeCategory::Real, 8>{}(std::forward<A>(x)...); |
151 | case 10: |
152 | if constexpr (HasCppTypeFor<TypeCategory::Real, 10>) { |
153 | return FUNC<TypeCategory::Real, 10>{}(std::forward<A>(x)...); |
154 | } |
155 | break; |
156 | case 16: |
157 | if constexpr (HasCppTypeFor<TypeCategory::Real, 16>) { |
158 | return FUNC<TypeCategory::Real, 16>{}(std::forward<A>(x)...); |
159 | } |
160 | break; |
161 | } |
162 | terminator.Crash("not yet implemented: REAL(KIND=%d)", kind); |
163 | case TypeCategory::Complex: |
164 | switch (kind) { |
165 | #if 0 // TODO: COMPLEX(2 & 3) |
166 | case 2: |
167 | return FUNC<TypeCategory::Complex, 2>{}(std::forward<A>(x)...); |
168 | case 3: |
169 | return FUNC<TypeCategory::Complex, 3>{}(std::forward<A>(x)...); |
170 | #endif |
171 | case 4: |
172 | return FUNC<TypeCategory::Complex, 4>{}(std::forward<A>(x)...); |
173 | case 8: |
174 | return FUNC<TypeCategory::Complex, 8>{}(std::forward<A>(x)...); |
175 | case 10: |
176 | if constexpr (HasCppTypeFor<TypeCategory::Real, 10>) { |
177 | return FUNC<TypeCategory::Complex, 10>{}(std::forward<A>(x)...); |
178 | } |
179 | break; |
180 | case 16: |
181 | if constexpr (HasCppTypeFor<TypeCategory::Real, 16>) { |
182 | return FUNC<TypeCategory::Complex, 16>{}(std::forward<A>(x)...); |
183 | } |
184 | break; |
185 | } |
186 | terminator.Crash("not yet implemented: COMPLEX(KIND=%d)", kind); |
187 | case TypeCategory::Character: |
188 | switch (kind) { |
189 | case 1: |
190 | return FUNC<TypeCategory::Character, 1>{}(std::forward<A>(x)...); |
191 | case 2: |
192 | return FUNC<TypeCategory::Character, 2>{}(std::forward<A>(x)...); |
193 | case 4: |
194 | return FUNC<TypeCategory::Character, 4>{}(std::forward<A>(x)...); |
195 | default: |
196 | terminator.Crash("not yet implemented: CHARACTER(KIND=%d)", kind); |
197 | } |
198 | case TypeCategory::Logical: |
199 | switch (kind) { |
200 | case 1: |
201 | return FUNC<TypeCategory::Logical, 1>{}(std::forward<A>(x)...); |
202 | case 2: |
203 | return FUNC<TypeCategory::Logical, 2>{}(std::forward<A>(x)...); |
204 | case 4: |
205 | return FUNC<TypeCategory::Logical, 4>{}(std::forward<A>(x)...); |
206 | case 8: |
207 | return FUNC<TypeCategory::Logical, 8>{}(std::forward<A>(x)...); |
208 | default: |
209 | terminator.Crash("not yet implemented: LOGICAL(KIND=%d)", kind); |
210 | } |
211 | default: |
212 | terminator.Crash( |
213 | "not yet implemented: type category(%d)", static_cast<int>(cat)); |
214 | } |
215 | } |
216 | |
217 | // Maps a runtime INTEGER kind value to the appropriate instantiation of |
218 | // a function object template and calls it with the supplied arguments. |
219 | template <template <int KIND> class FUNC, typename RESULT, typename... A> |
220 | inline RESULT ApplyIntegerKind(int kind, Terminator &terminator, A &&...x) { |
221 | switch (kind) { |
222 | case 1: |
223 | return FUNC<1>{}(std::forward<A>(x)...); |
224 | case 2: |
225 | return FUNC<2>{}(std::forward<A>(x)...); |
226 | case 4: |
227 | return FUNC<4>{}(std::forward<A>(x)...); |
228 | case 8: |
229 | return FUNC<8>{}(std::forward<A>(x)...); |
230 | #ifdef __SIZEOF_INT128__16 |
231 | case 16: |
232 | return FUNC<16>{}(std::forward<A>(x)...); |
233 | #endif |
234 | default: |
235 | terminator.Crash("not yet implemented: INTEGER(KIND=%d)", kind); |
236 | } |
237 | } |
238 | |
239 | template <template <int KIND> class FUNC, typename RESULT, typename... A> |
240 | inline RESULT ApplyFloatingPointKind( |
241 | int kind, Terminator &terminator, A &&...x) { |
242 | switch (kind) { |
243 | #if 0 // TODO: REAL/COMPLEX (2 & 3) |
244 | case 2: |
245 | return FUNC<2>{}(std::forward<A>(x)...); |
246 | case 3: |
247 | return FUNC<3>{}(std::forward<A>(x)...); |
248 | #endif |
249 | case 4: |
250 | return FUNC<4>{}(std::forward<A>(x)...); |
251 | case 8: |
252 | return FUNC<8>{}(std::forward<A>(x)...); |
253 | case 10: |
254 | if constexpr (HasCppTypeFor<TypeCategory::Real, 10>) { |
255 | return FUNC<10>{}(std::forward<A>(x)...); |
256 | } |
257 | break; |
258 | case 16: |
259 | if constexpr (HasCppTypeFor<TypeCategory::Real, 16>) { |
260 | return FUNC<16>{}(std::forward<A>(x)...); |
261 | } |
262 | break; |
263 | } |
264 | terminator.Crash("not yet implemented: REAL/COMPLEX(KIND=%d)", kind); |
265 | } |
266 | |
267 | template <template <int KIND> class FUNC, typename RESULT, typename... A> |
268 | inline RESULT ApplyCharacterKind(int kind, Terminator &terminator, A &&...x) { |
269 | switch (kind) { |
270 | case 1: |
271 | return FUNC<1>{}(std::forward<A>(x)...); |
272 | case 2: |
273 | return FUNC<2>{}(std::forward<A>(x)...); |
274 | case 4: |
275 | return FUNC<4>{}(std::forward<A>(x)...); |
276 | default: |
277 | terminator.Crash("not yet implemented: CHARACTER(KIND=%d)", kind); |
278 | } |
279 | } |
280 | |
281 | template <template <int KIND> class FUNC, typename RESULT, typename... A> |
282 | inline RESULT ApplyLogicalKind(int kind, Terminator &terminator, A &&...x) { |
283 | switch (kind) { |
284 | case 1: |
285 | return FUNC<1>{}(std::forward<A>(x)...); |
286 | case 2: |
287 | return FUNC<2>{}(std::forward<A>(x)...); |
288 | case 4: |
289 | return FUNC<4>{}(std::forward<A>(x)...); |
290 | case 8: |
291 | return FUNC<8>{}(std::forward<A>(x)...); |
292 | default: |
293 | terminator.Crash("not yet implemented: LOGICAL(KIND=%d)", kind); |
294 | } |
295 | } |
296 | |
297 | // Calculate result type of (X op Y) for *, //, DOT_PRODUCT, &c. |
298 | std::optional<std::pair<TypeCategory, int>> inline constexpr GetResultType( |
299 | TypeCategory xCat, int xKind, TypeCategory yCat, int yKind) { |
300 | int maxKind{std::max(xKind, yKind)}; |
301 | switch (xCat) { |
302 | case TypeCategory::Integer: |
303 | switch (yCat) { |
304 | case TypeCategory::Integer: |
305 | return std::make_pair(TypeCategory::Integer, maxKind); |
306 | case TypeCategory::Real: |
307 | case TypeCategory::Complex: |
308 | return std::make_pair(yCat, yKind); |
309 | default: |
310 | break; |
311 | } |
312 | break; |
313 | case TypeCategory::Real: |
314 | switch (yCat) { |
315 | case TypeCategory::Integer: |
316 | return std::make_pair(TypeCategory::Real, xKind); |
317 | case TypeCategory::Real: |
318 | case TypeCategory::Complex: |
319 | return std::make_pair(yCat, maxKind); |
320 | default: |
321 | break; |
322 | } |
323 | break; |
324 | case TypeCategory::Complex: |
325 | switch (yCat) { |
326 | case TypeCategory::Integer: |
327 | return std::make_pair(TypeCategory::Complex, xKind); |
328 | case TypeCategory::Real: |
329 | case TypeCategory::Complex: |
330 | return std::make_pair(TypeCategory::Complex, maxKind); |
331 | default: |
332 | break; |
333 | } |
334 | break; |
335 | case TypeCategory::Character: |
336 | if (yCat == TypeCategory::Character) { |
337 | return std::make_pair(TypeCategory::Character, maxKind); |
338 | } else { |
339 | return std::nullopt; |
340 | } |
341 | case TypeCategory::Logical: |
342 | if (yCat == TypeCategory::Logical) { |
343 | return std::make_pair(TypeCategory::Logical, maxKind); |
344 | } else { |
345 | return std::nullopt; |
346 | } |
347 | default: |
348 | break; |
349 | } |
350 | return std::nullopt; |
351 | } |
352 | |
353 | // Accumulate floating-point results in (at least) double precision |
354 | template <TypeCategory CAT, int KIND> |
355 | using AccumulationType = CppTypeFor<CAT, |
356 | CAT == TypeCategory::Real || CAT == TypeCategory::Complex |
357 | ? std::max(KIND, static_cast<int>(sizeof(double))) |
358 | : KIND>; |
359 | |
360 | // memchr() for any character type |
361 | template <typename CHAR> |
362 | static inline const CHAR *FindCharacter( |
363 | const CHAR *data, CHAR ch, std::size_t chars) { |
364 | const CHAR *end{data + chars}; |
365 | for (const CHAR *p{data}; p < end; ++p) { |
366 | if (*p == ch) { |
367 | return p; |
368 | } |
369 | } |
370 | return nullptr; |
371 | } |
372 | |
373 | template <> |
374 | inline const char *FindCharacter(const char *data, char ch, std::size_t chars) { |
375 | return reinterpret_cast<const char *>( |
376 | std::memchr(data, static_cast<int>(ch), chars)); |
377 | } |
378 | |
379 | } // namespace Fortran::runtime |
380 | #endif // FORTRAN_RUNTIME_TOOLS_H_ |
1 | //===-- include/flang/Runtime/descriptor.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_RUNTIME_DESCRIPTOR_H_ |
10 | #define FORTRAN_RUNTIME_DESCRIPTOR_H_ |
11 | |
12 | // Defines data structures used during execution of a Fortran program |
13 | // to implement nontrivial dummy arguments, pointers, allocatables, |
14 | // function results, and the special behaviors of instances of derived types. |
15 | // This header file includes and extends the published language |
16 | // interoperability header that is required by the Fortran 2018 standard |
17 | // as a subset of definitions suitable for exposure to user C/C++ code. |
18 | // User C code is welcome to depend on that ISO_Fortran_binding.h file, |
19 | // but should never reference this internal header. |
20 | |
21 | #include "flang/ISO_Fortran_binding.h" |
22 | #include "flang/Runtime/memory.h" |
23 | #include "flang/Runtime/type-code.h" |
24 | #include <algorithm> |
25 | #include <cassert> |
26 | #include <cinttypes> |
27 | #include <cstddef> |
28 | #include <cstdio> |
29 | #include <cstring> |
30 | |
31 | namespace Fortran::runtime::typeInfo { |
32 | using TypeParameterValue = std::int64_t; |
33 | class DerivedType; |
34 | } // namespace Fortran::runtime::typeInfo |
35 | |
36 | namespace Fortran::runtime { |
37 | |
38 | using SubscriptValue = ISO::CFI_index_t; |
39 | |
40 | static constexpr int maxRank{CFI_MAX_RANK15}; |
41 | |
42 | // A C++ view of the sole interoperable standard descriptor (ISO::CFI_cdesc_t) |
43 | // and its type and per-dimension information. |
44 | |
45 | class Dimension { |
46 | public: |
47 | SubscriptValue LowerBound() const { return raw_.lower_bound; } |
48 | SubscriptValue Extent() const { return raw_.extent; } |
49 | SubscriptValue UpperBound() const { return LowerBound() + Extent() - 1; } |
50 | SubscriptValue ByteStride() const { return raw_.sm; } |
51 | |
52 | Dimension &SetBounds(SubscriptValue lower, SubscriptValue upper) { |
53 | if (upper >= lower) { |
54 | raw_.lower_bound = lower; |
55 | raw_.extent = upper - lower + 1; |
56 | } else { |
57 | raw_.lower_bound = 1; |
58 | raw_.extent = 0; |
59 | } |
60 | return *this; |
61 | } |
62 | // Do not use this API to cause the LB of an empty dimension |
63 | // to be anything other than 1. Use SetBounds() instead if you can. |
64 | Dimension &SetLowerBound(SubscriptValue lower) { |
65 | raw_.lower_bound = lower; |
66 | return *this; |
67 | } |
68 | Dimension &SetUpperBound(SubscriptValue upper) { |
69 | auto lower{raw_.lower_bound}; |
70 | raw_.extent = upper >= lower ? upper - lower + 1 : 0; |
71 | return *this; |
72 | } |
73 | Dimension &SetExtent(SubscriptValue extent) { |
74 | raw_.extent = extent; |
75 | return *this; |
76 | } |
77 | Dimension &SetByteStride(SubscriptValue bytes) { |
78 | raw_.sm = bytes; |
79 | return *this; |
80 | } |
81 | |
82 | private: |
83 | ISO::CFI_dim_t raw_; |
84 | }; |
85 | |
86 | // The storage for this object follows the last used dim[] entry in a |
87 | // Descriptor (CFI_cdesc_t) generic descriptor. Space matters here, since |
88 | // descriptors serve as POINTER and ALLOCATABLE components of derived type |
89 | // instances. The presence of this structure is implied by the flag |
90 | // CFI_cdesc_t.f18Addendum, and the number of elements in the len_[] |
91 | // array is determined by derivedType_->LenParameters(). |
92 | class DescriptorAddendum { |
93 | public: |
94 | explicit DescriptorAddendum(const typeInfo::DerivedType *dt = nullptr) |
95 | : derivedType_{dt} {} |
96 | DescriptorAddendum &operator=(const DescriptorAddendum &); |
97 | |
98 | const typeInfo::DerivedType *derivedType() const { return derivedType_; } |
99 | DescriptorAddendum &set_derivedType(const typeInfo::DerivedType *dt) { |
100 | derivedType_ = dt; |
101 | return *this; |
102 | } |
103 | |
104 | std::size_t LenParameters() const; |
105 | |
106 | typeInfo::TypeParameterValue LenParameterValue(int which) const { |
107 | return len_[which]; |
108 | } |
109 | static constexpr std::size_t SizeInBytes(int lenParameters) { |
110 | // TODO: Don't waste that last word if lenParameters == 0 |
111 | return sizeof(DescriptorAddendum) + |
112 | std::max(lenParameters - 1, 0) * sizeof(typeInfo::TypeParameterValue); |
113 | } |
114 | std::size_t SizeInBytes() const; |
115 | |
116 | void SetLenParameterValue(int which, typeInfo::TypeParameterValue x) { |
117 | len_[which] = x; |
118 | } |
119 | |
120 | void Dump(FILE * = stdoutstdout) const; |
121 | |
122 | private: |
123 | const typeInfo::DerivedType *derivedType_; |
124 | typeInfo::TypeParameterValue len_[1]; // must be the last component |
125 | // The LEN type parameter values can also include captured values of |
126 | // specification expressions that were used for bounds and for LEN type |
127 | // parameters of components. The values have been truncated to the LEN |
128 | // type parameter's type, if shorter than 64 bits, then sign-extended. |
129 | }; |
130 | |
131 | // A C++ view of a standard descriptor object. |
132 | class Descriptor { |
133 | public: |
134 | // Be advised: this class type is not suitable for use when allocating |
135 | // a descriptor -- it is a dynamic view of the common descriptor format. |
136 | // If used in a simple declaration of a local variable or dynamic allocation, |
137 | // the size is going to be correct only by accident, since the true size of |
138 | // a descriptor depends on the number of its dimensions and the presence and |
139 | // size of an addendum, which depends on the type of the data. |
140 | // Use the class template StaticDescriptor (below) to declare a descriptor |
141 | // whose type and rank are fixed and known at compilation time. Use the |
142 | // Create() static member functions otherwise to dynamically allocate a |
143 | // descriptor. |
144 | |
145 | Descriptor(const Descriptor &); |
146 | Descriptor &operator=(const Descriptor &); |
147 | |
148 | // Returns the number of bytes occupied by an element of the given |
149 | // category and kind including any alignment padding required |
150 | // between adjacent elements. |
151 | static std::size_t BytesFor(TypeCategory category, int kind); |
152 | |
153 | void Establish(TypeCode t, std::size_t elementBytes, void *p = nullptr, |
154 | int rank = maxRank, const SubscriptValue *extent = nullptr, |
155 | ISO::CFI_attribute_t attribute = CFI_attribute_other0, |
156 | bool addendum = false); |
157 | void Establish(TypeCategory, int kind, void *p = nullptr, int rank = maxRank, |
158 | const SubscriptValue *extent = nullptr, |
159 | ISO::CFI_attribute_t attribute = CFI_attribute_other0, |
160 | bool addendum = false); |
161 | void Establish(int characterKind, std::size_t characters, void *p = nullptr, |
162 | int rank = maxRank, const SubscriptValue *extent = nullptr, |
163 | ISO::CFI_attribute_t attribute = CFI_attribute_other0, |
164 | bool addendum = false); |
165 | void Establish(const typeInfo::DerivedType &dt, void *p = nullptr, |
166 | int rank = maxRank, const SubscriptValue *extent = nullptr, |
167 | ISO::CFI_attribute_t attribute = CFI_attribute_other0); |
168 | |
169 | static OwningPtr<Descriptor> Create(TypeCode t, std::size_t elementBytes, |
170 | void *p = nullptr, int rank = maxRank, |
171 | const SubscriptValue *extent = nullptr, |
172 | ISO::CFI_attribute_t attribute = CFI_attribute_other0, |
173 | int derivedTypeLenParameters = 0); |
174 | static OwningPtr<Descriptor> Create(TypeCategory, int kind, void *p = nullptr, |
175 | int rank = maxRank, const SubscriptValue *extent = nullptr, |
176 | ISO::CFI_attribute_t attribute = CFI_attribute_other0); |
177 | static OwningPtr<Descriptor> Create(int characterKind, |
178 | SubscriptValue characters, void *p = nullptr, int rank = maxRank, |
179 | const SubscriptValue *extent = nullptr, |
180 | ISO::CFI_attribute_t attribute = CFI_attribute_other0); |
181 | static OwningPtr<Descriptor> Create(const typeInfo::DerivedType &dt, |
182 | void *p = nullptr, int rank = maxRank, |
183 | const SubscriptValue *extent = nullptr, |
184 | ISO::CFI_attribute_t attribute = CFI_attribute_other0); |
185 | |
186 | ISO::CFI_cdesc_t &raw() { return raw_; } |
187 | const ISO::CFI_cdesc_t &raw() const { return raw_; } |
188 | std::size_t ElementBytes() const { return raw_.elem_len; } |
189 | int rank() const { return raw_.rank; } |
190 | TypeCode type() const { return TypeCode{raw_.type}; } |
191 | |
192 | Descriptor &set_base_addr(void *p) { |
193 | raw_.base_addr = p; |
194 | return *this; |
195 | } |
196 | |
197 | bool IsPointer() const { return raw_.attribute == CFI_attribute_pointer1; } |
198 | bool IsAllocatable() const { |
199 | return raw_.attribute == CFI_attribute_allocatable2; |
200 | } |
201 | bool IsAllocated() const { return raw_.base_addr != nullptr; } |
202 | |
203 | Dimension &GetDimension(int dim) { |
204 | return *reinterpret_cast<Dimension *>(&raw_.dim[dim]); |
205 | } |
206 | const Dimension &GetDimension(int dim) const { |
207 | return *reinterpret_cast<const Dimension *>(&raw_.dim[dim]); |
208 | } |
209 | |
210 | std::size_t SubscriptByteOffset( |
211 | int dim, SubscriptValue subscriptValue) const { |
212 | const Dimension &dimension{GetDimension(dim)}; |
213 | return (subscriptValue - dimension.LowerBound()) * dimension.ByteStride(); |
214 | } |
215 | |
216 | std::size_t SubscriptsToByteOffset(const SubscriptValue subscript[]) const { |
217 | std::size_t offset{0}; |
218 | for (int j{0}; j < raw_.rank; ++j) { |
219 | offset += SubscriptByteOffset(j, subscript[j]); |
220 | } |
221 | return offset; |
222 | } |
223 | |
224 | template <typename A = char> A *OffsetElement(std::size_t offset = 0) const { |
225 | return reinterpret_cast<A *>( |
226 | reinterpret_cast<char *>(raw_.base_addr) + offset); |
227 | } |
228 | |
229 | template <typename A> A *Element(const SubscriptValue subscript[]) const { |
230 | return OffsetElement<A>(SubscriptsToByteOffset(subscript)); |
231 | } |
232 | |
233 | template <typename A> A *ZeroBasedIndexedElement(std::size_t n) const { |
234 | SubscriptValue at[maxRank]; |
235 | if (SubscriptsForZeroBasedElementNumber(at, n)) { |
236 | return Element<A>(at); |
237 | } |
238 | return nullptr; |
239 | } |
240 | |
241 | int GetLowerBounds(SubscriptValue subscript[]) const { |
242 | for (int j{0}; j < raw_.rank; ++j) { |
243 | subscript[j] = GetDimension(j).LowerBound(); |
244 | } |
245 | return raw_.rank; |
246 | } |
247 | |
248 | int GetShape(SubscriptValue subscript[]) const { |
249 | for (int j{0}; j < raw_.rank; ++j) { |
250 | subscript[j] = GetDimension(j).Extent(); |
251 | } |
252 | return raw_.rank; |
253 | } |
254 | |
255 | // When the passed subscript vector contains the last (or first) |
256 | // subscripts of the array, these wrap the subscripts around to |
257 | // their first (or last) values and return false. |
258 | bool IncrementSubscripts( |
259 | SubscriptValue subscript[], const int *permutation = nullptr) const { |
260 | for (int j{0}; j < raw_.rank; ++j) { |
261 | int k{permutation ? permutation[j] : j}; |
262 | const Dimension &dim{GetDimension(k)}; |
263 | if (subscript[k]++ < dim.UpperBound()) { |
264 | return true; |
265 | } |
266 | subscript[k] = dim.LowerBound(); |
267 | } |
268 | return false; |
269 | } |
270 | |
271 | bool DecrementSubscripts( |
272 | SubscriptValue[], const int *permutation = nullptr) const; |
273 | |
274 | // False when out of range. |
275 | bool SubscriptsForZeroBasedElementNumber(SubscriptValue subscript[], |
276 | std::size_t elementNumber, const int *permutation = nullptr) const { |
277 | if (raw_.rank == 0) { |
278 | return elementNumber == 0; |
279 | } |
280 | std::size_t dimCoefficient[maxRank]; |
281 | int k0{permutation ? permutation[0] : 0}; |
282 | dimCoefficient[0] = 1; |
283 | auto coefficient{static_cast<std::size_t>(GetDimension(k0).Extent())}; |
284 | for (int j{1}; j < raw_.rank; ++j) { |
285 | int k{permutation ? permutation[j] : j}; |
286 | const Dimension &dim{GetDimension(k)}; |
287 | dimCoefficient[j] = coefficient; |
288 | coefficient *= dim.Extent(); |
289 | } |
290 | if (elementNumber >= coefficient) { |
291 | return false; // out of range |
292 | } |
293 | for (int j{raw_.rank - 1}; j > 0; --j) { |
294 | int k{permutation ? permutation[j] : j}; |
295 | const Dimension &dim{GetDimension(k)}; |
296 | std::size_t quotient{elementNumber / dimCoefficient[j]}; |
297 | subscript[k] = quotient + dim.LowerBound(); |
298 | elementNumber -= quotient * dimCoefficient[j]; |
299 | } |
300 | subscript[k0] = elementNumber + GetDimension(k0).LowerBound(); |
301 | return true; |
302 | } |
303 | |
304 | std::size_t ZeroBasedElementNumber( |
305 | const SubscriptValue *, const int *permutation = nullptr) const; |
306 | |
307 | DescriptorAddendum *Addendum() { |
308 | if (raw_.f18Addendum != 0) { |
309 | return reinterpret_cast<DescriptorAddendum *>(&GetDimension(rank())); |
310 | } else { |
311 | return nullptr; |
312 | } |
313 | } |
314 | const DescriptorAddendum *Addendum() const { |
315 | if (raw_.f18Addendum != 0) { |
316 | return reinterpret_cast<const DescriptorAddendum *>( |
317 | &GetDimension(rank())); |
318 | } else { |
319 | return nullptr; |
320 | } |
321 | } |
322 | |
323 | // Returns size in bytes of the descriptor (not the data) |
324 | static constexpr std::size_t SizeInBytes( |
325 | int rank, bool addendum = false, int lengthTypeParameters = 0) { |
326 | std::size_t bytes{sizeof(Descriptor) - sizeof(Dimension)}; |
327 | bytes += rank * sizeof(Dimension); |
328 | if (addendum || lengthTypeParameters > 0) { |
329 | bytes += DescriptorAddendum::SizeInBytes(lengthTypeParameters); |
330 | } |
331 | return bytes; |
332 | } |
333 | |
334 | std::size_t SizeInBytes() const; |
335 | |
336 | std::size_t Elements() const; |
337 | |
338 | // Allocate() assumes Elements() and ElementBytes() work; |
339 | // define the extents of the dimensions and the element length |
340 | // before calling. It (re)computes the byte strides after |
341 | // allocation. Does not allocate automatic components or |
342 | // perform default component initialization. |
343 | int Allocate(); |
344 | |
345 | // Deallocates storage; does not call FINAL subroutines or |
346 | // deallocate allocatable/automatic components. |
347 | int Deallocate(); |
348 | |
349 | // Deallocates storage, including allocatable and automatic |
350 | // components. Optionally invokes FINAL subroutines. |
351 | int Destroy(bool finalize = false, bool destroyPointers = false); |
352 | |
353 | bool IsContiguous(int leadingDimensions = maxRank) const { |
354 | auto bytes{static_cast<SubscriptValue>(ElementBytes())}; |
355 | if (leadingDimensions > raw_.rank) { |
356 | leadingDimensions = raw_.rank; |
357 | } |
358 | for (int j{0}; j < leadingDimensions; ++j) { |
359 | const Dimension &dim{GetDimension(j)}; |
360 | if (bytes != dim.ByteStride()) { |
361 | return false; |
362 | } |
363 | bytes *= dim.Extent(); |
364 | } |
365 | return true; |
366 | } |
367 | |
368 | // Establishes a pointer to a section or element. |
369 | bool EstablishPointerSection(const Descriptor &source, |
370 | const SubscriptValue *lower = nullptr, |
371 | const SubscriptValue *upper = nullptr, |
372 | const SubscriptValue *stride = nullptr); |
373 | |
374 | void Check() const; |
375 | |
376 | void Dump(FILE * = stdoutstdout) const; |
377 | |
378 | private: |
379 | ISO::CFI_cdesc_t raw_; |
380 | }; |
381 | static_assert(sizeof(Descriptor) == sizeof(ISO::CFI_cdesc_t)); |
382 | |
383 | // Properly configured instances of StaticDescriptor will occupy the |
384 | // exact amount of storage required for the descriptor, its dimensional |
385 | // information, and possible addendum. To build such a static descriptor, |
386 | // declare an instance of StaticDescriptor<>, extract a reference to its |
387 | // descriptor via the descriptor() accessor, and then built a Descriptor |
388 | // therein via descriptor.Establish(), e.g.: |
389 | // StaticDescriptor<R,A,LP> statDesc; |
390 | // Descriptor &descriptor{statDesc.descriptor()}; |
391 | // descriptor.Establish( ... ); |
392 | template <int MAX_RANK = maxRank, bool ADDENDUM = false, int MAX_LEN_PARMS = 0> |
393 | class alignas(Descriptor) StaticDescriptor { |
394 | public: |
395 | static constexpr int maxRank{MAX_RANK}; |
396 | static constexpr int maxLengthTypeParameters{MAX_LEN_PARMS}; |
397 | static constexpr bool hasAddendum{ADDENDUM || MAX_LEN_PARMS > 0}; |
398 | static constexpr std::size_t byteSize{ |
399 | Descriptor::SizeInBytes(maxRank, hasAddendum, maxLengthTypeParameters)}; |
400 | |
401 | Descriptor &descriptor() { return *reinterpret_cast<Descriptor *>(storage_); } |
402 | const Descriptor &descriptor() const { |
403 | return *reinterpret_cast<const Descriptor *>(storage_); |
404 | } |
405 | |
406 | void Check() { |
407 | assert(descriptor().rank() <= maxRank)(static_cast <bool> (descriptor().rank() <= maxRank) ? void (0) : __assert_fail ("descriptor().rank() <= maxRank" , "flang/include/flang/Runtime/descriptor.h", 407, __extension__ __PRETTY_FUNCTION__)); |
408 | assert(descriptor().SizeInBytes() <= byteSize)(static_cast <bool> (descriptor().SizeInBytes() <= byteSize ) ? void (0) : __assert_fail ("descriptor().SizeInBytes() <= byteSize" , "flang/include/flang/Runtime/descriptor.h", 408, __extension__ __PRETTY_FUNCTION__)); |
409 | if (DescriptorAddendum * addendum{descriptor().Addendum()}) { |
410 | assert(hasAddendum)(static_cast <bool> (hasAddendum) ? void (0) : __assert_fail ("hasAddendum", "flang/include/flang/Runtime/descriptor.h", 410 , __extension__ __PRETTY_FUNCTION__)); |
411 | assert(addendum->LenParameters() <= maxLengthTypeParameters)(static_cast <bool> (addendum->LenParameters() <= maxLengthTypeParameters) ? void (0) : __assert_fail ("addendum->LenParameters() <= maxLengthTypeParameters" , "flang/include/flang/Runtime/descriptor.h", 411, __extension__ __PRETTY_FUNCTION__)); |
412 | } else { |
413 | assert(!hasAddendum)(static_cast <bool> (!hasAddendum) ? void (0) : __assert_fail ("!hasAddendum", "flang/include/flang/Runtime/descriptor.h", 413, __extension__ __PRETTY_FUNCTION__)); |
414 | assert(maxLengthTypeParameters == 0)(static_cast <bool> (maxLengthTypeParameters == 0) ? void (0) : __assert_fail ("maxLengthTypeParameters == 0", "flang/include/flang/Runtime/descriptor.h" , 414, __extension__ __PRETTY_FUNCTION__)); |
415 | } |
416 | descriptor().Check(); |
417 | } |
418 | |
419 | private: |
420 | char storage_[byteSize]{}; |
421 | }; |
422 | } // namespace Fortran::runtime |
423 | #endif // FORTRAN_RUNTIME_DESCRIPTOR_H_ |