File: | build/source/flang/runtime/findloc.cpp |
Warning: | line 109, column 22 Assigned value is garbage or undefined |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===-- runtime/findloc.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 FINDLOC for all required operand types and shapes and result | |||
10 | // integer kinds. | |||
11 | ||||
12 | #include "reduction-templates.h" | |||
13 | #include "flang/Runtime/character.h" | |||
14 | #include "flang/Runtime/reduction.h" | |||
15 | #include <cinttypes> | |||
16 | #include <complex> | |||
17 | ||||
18 | namespace Fortran::runtime { | |||
19 | ||||
20 | template <TypeCategory CAT1, int KIND1, TypeCategory CAT2, int KIND2> | |||
21 | struct Equality { | |||
22 | using Type1 = CppTypeFor<CAT1, KIND1>; | |||
23 | using Type2 = CppTypeFor<CAT2, KIND2>; | |||
24 | bool operator()(const Descriptor &array, const SubscriptValue at[], | |||
25 | const Descriptor &target) const { | |||
26 | return *array.Element<Type1>(at) == *target.OffsetElement<Type2>(); | |||
27 | } | |||
28 | }; | |||
29 | ||||
30 | template <int KIND1, int KIND2> | |||
31 | struct Equality<TypeCategory::Complex, KIND1, TypeCategory::Complex, KIND2> { | |||
32 | using Type1 = CppTypeFor<TypeCategory::Complex, KIND1>; | |||
33 | using Type2 = CppTypeFor<TypeCategory::Complex, KIND2>; | |||
34 | bool operator()(const Descriptor &array, const SubscriptValue at[], | |||
35 | const Descriptor &target) const { | |||
36 | const Type1 &xz{*array.Element<Type1>(at)}; | |||
37 | const Type2 &tz{*target.OffsetElement<Type2>()}; | |||
38 | return xz.real() == tz.real() && xz.imag() == tz.imag(); | |||
39 | } | |||
40 | }; | |||
41 | ||||
42 | template <int KIND1, TypeCategory CAT2, int KIND2> | |||
43 | struct Equality<TypeCategory::Complex, KIND1, CAT2, KIND2> { | |||
44 | using Type1 = CppTypeFor<TypeCategory::Complex, KIND1>; | |||
45 | using Type2 = CppTypeFor<CAT2, KIND2>; | |||
46 | bool operator()(const Descriptor &array, const SubscriptValue at[], | |||
47 | const Descriptor &target) const { | |||
48 | const Type1 &z{*array.Element<Type1>(at)}; | |||
49 | return z.imag() == 0 && z.real() == *target.OffsetElement<Type2>(); | |||
50 | } | |||
51 | }; | |||
52 | ||||
53 | template <TypeCategory CAT1, int KIND1, int KIND2> | |||
54 | struct Equality<CAT1, KIND1, TypeCategory::Complex, KIND2> { | |||
55 | using Type1 = CppTypeFor<CAT1, KIND1>; | |||
56 | using Type2 = CppTypeFor<TypeCategory::Complex, KIND2>; | |||
57 | bool operator()(const Descriptor &array, const SubscriptValue at[], | |||
58 | const Descriptor &target) const { | |||
59 | const Type2 &z{*target.OffsetElement<Type2>()}; | |||
60 | return *array.Element<Type1>(at) == z.real() && z.imag() == 0; | |||
61 | } | |||
62 | }; | |||
63 | ||||
64 | template <int KIND> struct CharacterEquality { | |||
65 | using Type = CppTypeFor<TypeCategory::Character, KIND>; | |||
66 | bool operator()(const Descriptor &array, const SubscriptValue at[], | |||
67 | const Descriptor &target) const { | |||
68 | return CharacterScalarCompare<Type>(array.Element<Type>(at), | |||
69 | target.OffsetElement<Type>(), | |||
70 | array.ElementBytes() / static_cast<unsigned>(KIND), | |||
71 | target.ElementBytes() / static_cast<unsigned>(KIND)) == 0; | |||
72 | } | |||
73 | }; | |||
74 | ||||
75 | struct LogicalEquivalence { | |||
76 | bool operator()(const Descriptor &array, const SubscriptValue at[], | |||
77 | const Descriptor &target) const { | |||
78 | return IsLogicalElementTrue(array, at) == | |||
79 | IsLogicalElementTrue(target, at /*ignored*/); | |||
80 | } | |||
81 | }; | |||
82 | ||||
83 | template <typename EQUALITY> class LocationAccumulator { | |||
84 | public: | |||
85 | LocationAccumulator( | |||
86 | const Descriptor &array, const Descriptor &target, bool back) | |||
87 | : array_{array}, target_{target}, back_{back} { | |||
88 | Reinitialize(); | |||
89 | } | |||
90 | void Reinitialize() { | |||
91 | // per standard: result indices are all zero if no data | |||
92 | for (int j{0}; j < rank_; ++j) { | |||
93 | location_[j] = 0; | |||
94 | } | |||
95 | } | |||
96 | template <typename A> void GetResult(A *p, int zeroBasedDim = -1) { | |||
97 | if (zeroBasedDim >= 0) { | |||
98 | *p = location_[zeroBasedDim] - | |||
99 | array_.GetDimension(zeroBasedDim).LowerBound() + 1; | |||
100 | } else { | |||
101 | for (int j{0}; j < rank_; ++j) { | |||
102 | p[j] = location_[j] - array_.GetDimension(j).LowerBound() + 1; | |||
103 | } | |||
104 | } | |||
105 | } | |||
106 | template <typename IGNORED> bool AccumulateAt(const SubscriptValue at[]) { | |||
107 | if (equality_(array_, at, target_)) { | |||
108 | for (int j{0}; j < rank_; ++j) { | |||
109 | location_[j] = at[j]; | |||
| ||||
110 | } | |||
111 | return back_; | |||
112 | } else { | |||
113 | return true; | |||
114 | } | |||
115 | } | |||
116 | ||||
117 | private: | |||
118 | const Descriptor &array_; | |||
119 | const Descriptor &target_; | |||
120 | const bool back_{false}; | |||
121 | const int rank_{array_.rank()}; | |||
122 | SubscriptValue location_[maxRank]; | |||
123 | const EQUALITY equality_{}; | |||
124 | }; | |||
125 | ||||
126 | template <TypeCategory XCAT, int XKIND, TypeCategory TARGET_CAT> | |||
127 | struct TotalNumericFindlocHelper { | |||
128 | template <int TARGET_KIND> struct Functor { | |||
129 | void operator()(Descriptor &result, const Descriptor &x, | |||
130 | const Descriptor &target, int kind, int dim, const Descriptor *mask, | |||
131 | bool back, Terminator &terminator) const { | |||
132 | using Eq = Equality<XCAT, XKIND, TARGET_CAT, TARGET_KIND>; | |||
133 | using Accumulator = LocationAccumulator<Eq>; | |||
134 | Accumulator accumulator{x, target, back}; | |||
135 | DoTotalReduction<void>(x, dim, mask, accumulator, "FINDLOC", terminator); | |||
136 | ApplyIntegerKind<LocationResultHelper<Accumulator>::template Functor, | |||
137 | void>(kind, terminator, accumulator, result); | |||
138 | } | |||
139 | }; | |||
140 | }; | |||
141 | ||||
142 | template <TypeCategory CAT, | |||
143 | template <TypeCategory XCAT, int XKIND, TypeCategory TARGET_CAT> | |||
144 | class HELPER> | |||
145 | struct NumericFindlocHelper { | |||
146 | template <int KIND> struct Functor { | |||
147 | void operator()(TypeCategory targetCat, int targetKind, Descriptor &result, | |||
148 | const Descriptor &x, const Descriptor &target, int kind, int dim, | |||
149 | const Descriptor *mask, bool back, Terminator &terminator) const { | |||
150 | switch (targetCat) { | |||
151 | case TypeCategory::Integer: | |||
152 | ApplyIntegerKind< | |||
153 | HELPER<CAT, KIND, TypeCategory::Integer>::template Functor, void>( | |||
154 | targetKind, terminator, result, x, target, kind, dim, mask, back, | |||
155 | terminator); | |||
156 | break; | |||
157 | case TypeCategory::Real: | |||
158 | ApplyFloatingPointKind< | |||
159 | HELPER<CAT, KIND, TypeCategory::Real>::template Functor, void>( | |||
160 | targetKind, terminator, result, x, target, kind, dim, mask, back, | |||
161 | terminator); | |||
162 | break; | |||
163 | case TypeCategory::Complex: | |||
164 | ApplyFloatingPointKind< | |||
165 | HELPER<CAT, KIND, TypeCategory::Complex>::template Functor, void>( | |||
166 | targetKind, terminator, result, x, target, kind, dim, mask, back, | |||
167 | terminator); | |||
168 | break; | |||
169 | default: | |||
170 | terminator.Crash( | |||
171 | "FINDLOC: bad target category %d for array category %d", | |||
172 | static_cast<int>(targetCat), static_cast<int>(CAT)); | |||
173 | } | |||
174 | } | |||
175 | }; | |||
176 | }; | |||
177 | ||||
178 | template <int KIND> struct CharacterFindlocHelper { | |||
179 | void operator()(Descriptor &result, const Descriptor &x, | |||
180 | const Descriptor &target, int kind, const Descriptor *mask, bool back, | |||
181 | Terminator &terminator) { | |||
182 | using Accumulator = LocationAccumulator<CharacterEquality<KIND>>; | |||
183 | Accumulator accumulator{x, target, back}; | |||
184 | DoTotalReduction<void>(x, 0, mask, accumulator, "FINDLOC", terminator); | |||
185 | ApplyIntegerKind<LocationResultHelper<Accumulator>::template Functor, void>( | |||
186 | kind, terminator, accumulator, result); | |||
187 | } | |||
188 | }; | |||
189 | ||||
190 | static void LogicalFindlocHelper(Descriptor &result, const Descriptor &x, | |||
191 | const Descriptor &target, int kind, const Descriptor *mask, bool back, | |||
192 | Terminator &terminator) { | |||
193 | using Accumulator = LocationAccumulator<LogicalEquivalence>; | |||
194 | Accumulator accumulator{x, target, back}; | |||
195 | DoTotalReduction<void>(x, 0, mask, accumulator, "FINDLOC", terminator); | |||
196 | ApplyIntegerKind<LocationResultHelper<Accumulator>::template Functor, void>( | |||
197 | kind, terminator, accumulator, result); | |||
198 | } | |||
199 | ||||
200 | extern "C" { | |||
201 | void RTNAME(Findloc)_FortranAFindloc(Descriptor &result, const Descriptor &x, | |||
202 | const Descriptor &target, int kind, const char *source, int line, | |||
203 | const Descriptor *mask, bool back) { | |||
204 | int rank{x.rank()}; | |||
205 | SubscriptValue extent[1]{rank}; | |||
206 | result.Establish(TypeCategory::Integer, kind, nullptr, 1, extent, | |||
207 | CFI_attribute_allocatable2); | |||
208 | result.GetDimension(0).SetBounds(1, extent[0]); | |||
209 | Terminator terminator{source, line}; | |||
210 | if (int stat{result.Allocate()}) { | |||
| ||||
211 | terminator.Crash( | |||
212 | "FINDLOC: could not allocate memory for result; STAT=%d", stat); | |||
213 | } | |||
214 | CheckIntegerKind(terminator, kind, "FINDLOC"); | |||
215 | auto xType{x.type().GetCategoryAndKind()}; | |||
216 | auto targetType{target.type().GetCategoryAndKind()}; | |||
217 | RUNTIME_CHECK(terminator, xType.has_value() && targetType.has_value())if (xType.has_value() && targetType.has_value()) ; else (terminator).CheckFailed("xType.has_value() && targetType.has_value()" , "flang/runtime/findloc.cpp", 217); | |||
218 | switch (xType->first) { | |||
219 | case TypeCategory::Integer: | |||
220 | ApplyIntegerKind<NumericFindlocHelper<TypeCategory::Integer, | |||
221 | TotalNumericFindlocHelper>::template Functor, | |||
222 | void>(xType->second, terminator, targetType->first, targetType->second, | |||
223 | result, x, target, kind, 0, mask, back, terminator); | |||
224 | break; | |||
225 | case TypeCategory::Real: | |||
226 | ApplyFloatingPointKind<NumericFindlocHelper<TypeCategory::Real, | |||
227 | TotalNumericFindlocHelper>::template Functor, | |||
228 | void>(xType->second, terminator, targetType->first, targetType->second, | |||
229 | result, x, target, kind, 0, mask, back, terminator); | |||
230 | break; | |||
231 | case TypeCategory::Complex: | |||
232 | ApplyFloatingPointKind<NumericFindlocHelper<TypeCategory::Complex, | |||
233 | TotalNumericFindlocHelper>::template Functor, | |||
234 | void>(xType->second, terminator, targetType->first, targetType->second, | |||
235 | result, x, target, kind, 0, mask, back, terminator); | |||
236 | break; | |||
237 | case TypeCategory::Character: | |||
238 | RUNTIME_CHECK(terminator,if (targetType->first == TypeCategory::Character && targetType->second == xType->second) ; else (terminator ).CheckFailed("targetType->first == TypeCategory::Character && targetType->second == xType->second" , "flang/runtime/findloc.cpp", 240) | |||
239 | targetType->first == TypeCategory::Character &&if (targetType->first == TypeCategory::Character && targetType->second == xType->second) ; else (terminator ).CheckFailed("targetType->first == TypeCategory::Character && targetType->second == xType->second" , "flang/runtime/findloc.cpp", 240) | |||
240 | targetType->second == xType->second)if (targetType->first == TypeCategory::Character && targetType->second == xType->second) ; else (terminator ).CheckFailed("targetType->first == TypeCategory::Character && targetType->second == xType->second" , "flang/runtime/findloc.cpp", 240); | |||
241 | ApplyCharacterKind<CharacterFindlocHelper, void>(xType->second, terminator, | |||
242 | result, x, target, kind, mask, back, terminator); | |||
243 | break; | |||
244 | case TypeCategory::Logical: | |||
245 | RUNTIME_CHECK(terminator, targetType->first == TypeCategory::Logical)if (targetType->first == TypeCategory::Logical) ; else (terminator ).CheckFailed("targetType->first == TypeCategory::Logical" , "flang/runtime/findloc.cpp", 245); | |||
246 | LogicalFindlocHelper(result, x, target, kind, mask, back, terminator); | |||
247 | break; | |||
248 | default: | |||
249 | terminator.Crash( | |||
250 | "FINDLOC: bad data type code (%d) for array", x.type().raw()); | |||
251 | } | |||
252 | } | |||
253 | } // extern "C" | |||
254 | ||||
255 | // FINDLOC with DIM= | |||
256 | ||||
257 | template <TypeCategory XCAT, int XKIND, TypeCategory TARGET_CAT> | |||
258 | struct PartialNumericFindlocHelper { | |||
259 | template <int TARGET_KIND> struct Functor { | |||
260 | void operator()(Descriptor &result, const Descriptor &x, | |||
261 | const Descriptor &target, int kind, int dim, const Descriptor *mask, | |||
262 | bool back, Terminator &terminator) const { | |||
263 | using Eq = Equality<XCAT, XKIND, TARGET_CAT, TARGET_KIND>; | |||
264 | using Accumulator = LocationAccumulator<Eq>; | |||
265 | Accumulator accumulator{x, target, back}; | |||
266 | ApplyIntegerKind<PartialLocationHelper<Accumulator>::template Functor, | |||
267 | void>(kind, terminator, result, x, dim, mask, terminator, "FINDLOC", | |||
268 | accumulator); | |||
269 | } | |||
270 | }; | |||
271 | }; | |||
272 | ||||
273 | template <int KIND> struct PartialCharacterFindlocHelper { | |||
274 | void operator()(Descriptor &result, const Descriptor &x, | |||
275 | const Descriptor &target, int kind, int dim, const Descriptor *mask, | |||
276 | bool back, Terminator &terminator) { | |||
277 | using Accumulator = LocationAccumulator<CharacterEquality<KIND>>; | |||
278 | Accumulator accumulator{x, target, back}; | |||
279 | ApplyIntegerKind<PartialLocationHelper<Accumulator>::template Functor, | |||
280 | void>(kind, terminator, result, x, dim, mask, terminator, "FINDLOC", | |||
281 | accumulator); | |||
282 | } | |||
283 | }; | |||
284 | ||||
285 | static void PartialLogicalFindlocHelper(Descriptor &result, const Descriptor &x, | |||
286 | const Descriptor &target, int kind, int dim, const Descriptor *mask, | |||
287 | bool back, Terminator &terminator) { | |||
288 | using Accumulator = LocationAccumulator<LogicalEquivalence>; | |||
289 | Accumulator accumulator{x, target, back}; | |||
290 | ApplyIntegerKind<PartialLocationHelper<Accumulator>::template Functor, void>( | |||
291 | kind, terminator, result, x, dim, mask, terminator, "FINDLOC", | |||
292 | accumulator); | |||
293 | } | |||
294 | ||||
295 | extern "C" { | |||
296 | void RTNAME(FindlocDim)_FortranAFindlocDim(Descriptor &result, const Descriptor &x, | |||
297 | const Descriptor &target, int kind, int dim, const char *source, int line, | |||
298 | const Descriptor *mask, bool back) { | |||
299 | Terminator terminator{source, line}; | |||
300 | CheckIntegerKind(terminator, kind, "FINDLOC"); | |||
301 | auto xType{x.type().GetCategoryAndKind()}; | |||
302 | auto targetType{target.type().GetCategoryAndKind()}; | |||
303 | RUNTIME_CHECK(terminator, xType.has_value() && targetType.has_value())if (xType.has_value() && targetType.has_value()) ; else (terminator).CheckFailed("xType.has_value() && targetType.has_value()" , "flang/runtime/findloc.cpp", 303); | |||
304 | switch (xType->first) { | |||
305 | case TypeCategory::Integer: | |||
306 | ApplyIntegerKind<NumericFindlocHelper<TypeCategory::Integer, | |||
307 | PartialNumericFindlocHelper>::template Functor, | |||
308 | void>(xType->second, terminator, targetType->first, targetType->second, | |||
309 | result, x, target, kind, dim, mask, back, terminator); | |||
310 | break; | |||
311 | case TypeCategory::Real: | |||
312 | ApplyFloatingPointKind<NumericFindlocHelper<TypeCategory::Real, | |||
313 | PartialNumericFindlocHelper>::template Functor, | |||
314 | void>(xType->second, terminator, targetType->first, targetType->second, | |||
315 | result, x, target, kind, dim, mask, back, terminator); | |||
316 | break; | |||
317 | case TypeCategory::Complex: | |||
318 | ApplyFloatingPointKind<NumericFindlocHelper<TypeCategory::Complex, | |||
319 | PartialNumericFindlocHelper>::template Functor, | |||
320 | void>(xType->second, terminator, targetType->first, targetType->second, | |||
321 | result, x, target, kind, dim, mask, back, terminator); | |||
322 | break; | |||
323 | case TypeCategory::Character: | |||
324 | RUNTIME_CHECK(terminator,if (targetType->first == TypeCategory::Character && targetType->second == xType->second) ; else (terminator ).CheckFailed("targetType->first == TypeCategory::Character && targetType->second == xType->second" , "flang/runtime/findloc.cpp", 326) | |||
325 | targetType->first == TypeCategory::Character &&if (targetType->first == TypeCategory::Character && targetType->second == xType->second) ; else (terminator ).CheckFailed("targetType->first == TypeCategory::Character && targetType->second == xType->second" , "flang/runtime/findloc.cpp", 326) | |||
326 | targetType->second == xType->second)if (targetType->first == TypeCategory::Character && targetType->second == xType->second) ; else (terminator ).CheckFailed("targetType->first == TypeCategory::Character && targetType->second == xType->second" , "flang/runtime/findloc.cpp", 326); | |||
327 | ApplyCharacterKind<PartialCharacterFindlocHelper, void>(xType->second, | |||
328 | terminator, result, x, target, kind, dim, mask, back, terminator); | |||
329 | break; | |||
330 | case TypeCategory::Logical: | |||
331 | RUNTIME_CHECK(terminator, targetType->first == TypeCategory::Logical)if (targetType->first == TypeCategory::Logical) ; else (terminator ).CheckFailed("targetType->first == TypeCategory::Logical" , "flang/runtime/findloc.cpp", 331); | |||
332 | PartialLogicalFindlocHelper( | |||
333 | result, x, target, kind, dim, mask, back, terminator); | |||
334 | break; | |||
335 | default: | |||
336 | terminator.Crash( | |||
337 | "FINDLOC: bad data type code (%d) for array", x.type().raw()); | |||
338 | } | |||
339 | } | |||
340 | } // extern "C" | |||
341 | } // 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
| ||||||||||||
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 | //===-- 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_ |