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