xref: /llvm-project/flang/runtime/reduction.cpp (revision fc97d2e68b03bc2979395e84b645e5b3ba35aecd)
1 //===-- runtime/reduction.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 ALL, ANY, COUNT, IALL, IANY, IPARITY, & PARITY for all required
10 // operand types and shapes.
11 //
12 // DOT_PRODUCT, FINDLOC, MATMUL, SUM, and PRODUCT are in their own eponymous
13 // source files.
14 // NORM2, MAXLOC, MINLOC, MAXVAL, and MINVAL are in extrema.cpp.
15 
16 #include "flang/Runtime/reduction.h"
17 #include "reduction-templates.h"
18 #include "flang/Runtime/descriptor.h"
19 #include <cinttypes>
20 
21 namespace Fortran::runtime {
22 
23 // IALL, IANY, IPARITY
24 
25 template <typename INTERMEDIATE> class IntegerAndAccumulator {
26 public:
27   explicit RT_API_ATTRS IntegerAndAccumulator(const Descriptor &array)
28       : array_{array} {}
29   RT_API_ATTRS void Reinitialize() { and_ = ~INTERMEDIATE{0}; }
30   template <typename A>
31   RT_API_ATTRS void GetResult(A *p, int /*zeroBasedDim*/ = -1) const {
32     *p = static_cast<A>(and_);
33   }
34   template <typename A>
35   RT_API_ATTRS bool AccumulateAt(const SubscriptValue at[]) {
36     and_ &= *array_.Element<A>(at);
37     return true;
38   }
39 
40 private:
41   const Descriptor &array_;
42   INTERMEDIATE and_{~INTERMEDIATE{0}};
43 };
44 
45 template <typename INTERMEDIATE> class IntegerOrAccumulator {
46 public:
47   explicit RT_API_ATTRS IntegerOrAccumulator(const Descriptor &array)
48       : array_{array} {}
49   RT_API_ATTRS void Reinitialize() { or_ = 0; }
50   template <typename A>
51   RT_API_ATTRS void GetResult(A *p, int /*zeroBasedDim*/ = -1) const {
52     *p = static_cast<A>(or_);
53   }
54   template <typename A>
55   RT_API_ATTRS bool AccumulateAt(const SubscriptValue at[]) {
56     or_ |= *array_.Element<A>(at);
57     return true;
58   }
59 
60 private:
61   const Descriptor &array_;
62   INTERMEDIATE or_{0};
63 };
64 
65 template <typename INTERMEDIATE> class IntegerXorAccumulator {
66 public:
67   explicit RT_API_ATTRS IntegerXorAccumulator(const Descriptor &array)
68       : array_{array} {}
69   RT_API_ATTRS void Reinitialize() { xor_ = 0; }
70   template <typename A>
71   RT_API_ATTRS void GetResult(A *p, int /*zeroBasedDim*/ = -1) const {
72     *p = static_cast<A>(xor_);
73   }
74   template <typename A>
75   RT_API_ATTRS bool AccumulateAt(const SubscriptValue at[]) {
76     xor_ ^= *array_.Element<A>(at);
77     return true;
78   }
79 
80 private:
81   const Descriptor &array_;
82   INTERMEDIATE xor_{0};
83 };
84 
85 extern "C" {
86 CppTypeFor<TypeCategory::Integer, 1> RTDEF(IAll1)(const Descriptor &x,
87     const char *source, int line, int dim, const Descriptor *mask) {
88   return GetTotalReduction<TypeCategory::Integer, 1>(x, source, line, dim, mask,
89       IntegerAndAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x}, "IALL",
90       /*allowUnsignedForInteger=*/true);
91 }
92 CppTypeFor<TypeCategory::Integer, 2> RTDEF(IAll2)(const Descriptor &x,
93     const char *source, int line, int dim, const Descriptor *mask) {
94   return GetTotalReduction<TypeCategory::Integer, 2>(x, source, line, dim, mask,
95       IntegerAndAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x}, "IALL",
96       /*allowUnsignedForInteger=*/true);
97 }
98 CppTypeFor<TypeCategory::Integer, 4> RTDEF(IAll4)(const Descriptor &x,
99     const char *source, int line, int dim, const Descriptor *mask) {
100   return GetTotalReduction<TypeCategory::Integer, 4>(x, source, line, dim, mask,
101       IntegerAndAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x}, "IALL",
102       /*allowUnsignedForInteger=*/true);
103 }
104 CppTypeFor<TypeCategory::Integer, 8> RTDEF(IAll8)(const Descriptor &x,
105     const char *source, int line, int dim, const Descriptor *mask) {
106   return GetTotalReduction<TypeCategory::Integer, 8>(x, source, line, dim, mask,
107       IntegerAndAccumulator<CppTypeFor<TypeCategory::Integer, 8>>{x}, "IALL",
108       /*allowUnsignedForInteger=*/true);
109 }
110 #ifdef __SIZEOF_INT128__
111 CppTypeFor<TypeCategory::Integer, 16> RTDEF(IAll16)(const Descriptor &x,
112     const char *source, int line, int dim, const Descriptor *mask) {
113   return GetTotalReduction<TypeCategory::Integer, 16>(x, source, line, dim,
114       mask, IntegerAndAccumulator<CppTypeFor<TypeCategory::Integer, 16>>{x},
115       "IALL", /*allowUnsignedForInteger=*/true);
116 }
117 #endif
118 void RTDEF(IAllDim)(Descriptor &result, const Descriptor &x, int dim,
119     const char *source, int line, const Descriptor *mask) {
120   Terminator terminator{source, line};
121   auto catKind{x.type().GetCategoryAndKind()};
122   RUNTIME_CHECK(terminator,
123       catKind.has_value() &&
124           (catKind->first == TypeCategory::Integer ||
125               catKind->first == TypeCategory::Unsigned));
126   PartialIntegerReduction<IntegerAndAccumulator>(
127       result, x, dim, catKind->second, mask, "IALL", terminator);
128 }
129 
130 CppTypeFor<TypeCategory::Integer, 1> RTDEF(IAny1)(const Descriptor &x,
131     const char *source, int line, int dim, const Descriptor *mask) {
132   return GetTotalReduction<TypeCategory::Integer, 1>(x, source, line, dim, mask,
133       IntegerOrAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x}, "IANY",
134       /*allowUnsignedForInteger=*/true);
135 }
136 CppTypeFor<TypeCategory::Integer, 2> RTDEF(IAny2)(const Descriptor &x,
137     const char *source, int line, int dim, const Descriptor *mask) {
138   return GetTotalReduction<TypeCategory::Integer, 2>(x, source, line, dim, mask,
139       IntegerOrAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x}, "IANY",
140       /*allowUnsignedForInteger=*/true);
141 }
142 CppTypeFor<TypeCategory::Integer, 4> RTDEF(IAny4)(const Descriptor &x,
143     const char *source, int line, int dim, const Descriptor *mask) {
144   return GetTotalReduction<TypeCategory::Integer, 4>(x, source, line, dim, mask,
145       IntegerOrAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x}, "IANY",
146       /*allowUnsignedForInteger=*/true);
147 }
148 CppTypeFor<TypeCategory::Integer, 8> RTDEF(IAny8)(const Descriptor &x,
149     const char *source, int line, int dim, const Descriptor *mask) {
150   return GetTotalReduction<TypeCategory::Integer, 8>(x, source, line, dim, mask,
151       IntegerOrAccumulator<CppTypeFor<TypeCategory::Integer, 8>>{x}, "IANY",
152       /*allowUnsignedForInteger=*/true);
153 }
154 #ifdef __SIZEOF_INT128__
155 CppTypeFor<TypeCategory::Integer, 16> RTDEF(IAny16)(const Descriptor &x,
156     const char *source, int line, int dim, const Descriptor *mask) {
157   return GetTotalReduction<TypeCategory::Integer, 16>(x, source, line, dim,
158       mask, IntegerOrAccumulator<CppTypeFor<TypeCategory::Integer, 16>>{x},
159       "IANY", /*allowUnsignedForInteger=*/true);
160 }
161 #endif
162 void RTDEF(IAnyDim)(Descriptor &result, const Descriptor &x, int dim,
163     const char *source, int line, const Descriptor *mask) {
164   Terminator terminator{source, line};
165   auto catKind{x.type().GetCategoryAndKind()};
166   RUNTIME_CHECK(terminator,
167       catKind.has_value() &&
168           (catKind->first == TypeCategory::Integer ||
169               catKind->first == TypeCategory::Unsigned));
170   PartialIntegerReduction<IntegerOrAccumulator>(
171       result, x, dim, catKind->second, mask, "IANY", terminator);
172 }
173 
174 CppTypeFor<TypeCategory::Integer, 1> RTDEF(IParity1)(const Descriptor &x,
175     const char *source, int line, int dim, const Descriptor *mask) {
176   return GetTotalReduction<TypeCategory::Integer, 1>(x, source, line, dim, mask,
177       IntegerXorAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x}, "IPARITY",
178       /*allowUnsignedForInteger=*/true);
179 }
180 CppTypeFor<TypeCategory::Integer, 2> RTDEF(IParity2)(const Descriptor &x,
181     const char *source, int line, int dim, const Descriptor *mask) {
182   return GetTotalReduction<TypeCategory::Integer, 2>(x, source, line, dim, mask,
183       IntegerXorAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x}, "IPARITY",
184       /*allowUnsignedForInteger=*/true);
185 }
186 CppTypeFor<TypeCategory::Integer, 4> RTDEF(IParity4)(const Descriptor &x,
187     const char *source, int line, int dim, const Descriptor *mask) {
188   return GetTotalReduction<TypeCategory::Integer, 4>(x, source, line, dim, mask,
189       IntegerXorAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x}, "IPARITY",
190       /*allowUnsignedForInteger=*/true);
191 }
192 CppTypeFor<TypeCategory::Integer, 8> RTDEF(IParity8)(const Descriptor &x,
193     const char *source, int line, int dim, const Descriptor *mask) {
194   return GetTotalReduction<TypeCategory::Integer, 8>(x, source, line, dim, mask,
195       IntegerXorAccumulator<CppTypeFor<TypeCategory::Integer, 8>>{x}, "IPARITY",
196       /*allowUnsignedForInteger=*/true);
197 }
198 #ifdef __SIZEOF_INT128__
199 CppTypeFor<TypeCategory::Integer, 16> RTDEF(IParity16)(const Descriptor &x,
200     const char *source, int line, int dim, const Descriptor *mask) {
201   return GetTotalReduction<TypeCategory::Integer, 16>(x, source, line, dim,
202       mask, IntegerXorAccumulator<CppTypeFor<TypeCategory::Integer, 16>>{x},
203       "IPARITY", /*allowUnsignedForInteger=*/true);
204 }
205 #endif
206 void RTDEF(IParityDim)(Descriptor &result, const Descriptor &x, int dim,
207     const char *source, int line, const Descriptor *mask) {
208   Terminator terminator{source, line};
209   auto catKind{x.type().GetCategoryAndKind()};
210   RUNTIME_CHECK(terminator,
211       catKind.has_value() &&
212           (catKind->first == TypeCategory::Integer ||
213               catKind->first == TypeCategory::Unsigned));
214   PartialIntegerReduction<IntegerXorAccumulator>(
215       result, x, dim, catKind->second, mask, "IPARITY", terminator);
216 }
217 }
218 
219 // ALL, ANY, COUNT, & PARITY
220 
221 enum class LogicalReduction { All, Any, Parity };
222 
223 template <LogicalReduction REDUCTION> class LogicalAccumulator {
224 public:
225   using Type = bool;
226   explicit LogicalAccumulator(const Descriptor &array) : array_{array} {}
227   void Reinitialize() { result_ = REDUCTION == LogicalReduction::All; }
228   bool Result() const { return result_; }
229   bool Accumulate(bool x) {
230     if constexpr (REDUCTION == LogicalReduction::Parity) {
231       result_ = result_ != x;
232     } else if (x != (REDUCTION == LogicalReduction::All)) {
233       result_ = x;
234       return false;
235     }
236     return true;
237   }
238   template <typename IGNORED = void>
239   bool AccumulateAt(const SubscriptValue at[]) {
240     return Accumulate(IsLogicalElementTrue(array_, at));
241   }
242 
243 private:
244   const Descriptor &array_;
245   bool result_{REDUCTION == LogicalReduction::All};
246 };
247 
248 template <typename ACCUMULATOR>
249 inline auto GetTotalLogicalReduction(const Descriptor &x, const char *source,
250     int line, int dim, ACCUMULATOR &&accumulator, const char *intrinsic) ->
251     typename ACCUMULATOR::Type {
252   Terminator terminator{source, line};
253   if (dim < 0 || dim > 1) {
254     terminator.Crash("%s: bad DIM=%d for ARRAY with rank=1", intrinsic, dim);
255   }
256   SubscriptValue xAt[maxRank];
257   x.GetLowerBounds(xAt);
258   for (auto elements{x.Elements()}; elements--; x.IncrementSubscripts(xAt)) {
259     if (!accumulator.AccumulateAt(xAt)) {
260       break; // cut short, result is known
261     }
262   }
263   return accumulator.Result();
264 }
265 
266 template <typename ACCUMULATOR>
267 inline auto ReduceLogicalDimToScalar(const Descriptor &x, int zeroBasedDim,
268     SubscriptValue subscripts[]) -> typename ACCUMULATOR::Type {
269   ACCUMULATOR accumulator{x};
270   SubscriptValue xAt[maxRank];
271   GetExpandedSubscripts(xAt, x, zeroBasedDim, subscripts);
272   const auto &dim{x.GetDimension(zeroBasedDim)};
273   SubscriptValue at{dim.LowerBound()};
274   for (auto n{dim.Extent()}; n-- > 0; ++at) {
275     xAt[zeroBasedDim] = at;
276     if (!accumulator.AccumulateAt(xAt)) {
277       break;
278     }
279   }
280   return accumulator.Result();
281 }
282 
283 template <LogicalReduction REDUCTION> struct LogicalReduceHelper {
284   template <int KIND> struct Functor {
285     void operator()(Descriptor &result, const Descriptor &x, int dim,
286         Terminator &terminator, const char *intrinsic) const {
287       // Standard requires result to have same LOGICAL kind as argument.
288       CreatePartialReductionResult(
289           result, x, x.ElementBytes(), dim, terminator, intrinsic, x.type());
290       SubscriptValue at[maxRank];
291       result.GetLowerBounds(at);
292       INTERNAL_CHECK(result.rank() == 0 || at[0] == 1);
293       using CppType = CppTypeFor<TypeCategory::Logical, KIND>;
294       for (auto n{result.Elements()}; n-- > 0; result.IncrementSubscripts(at)) {
295         *result.Element<CppType>(at) =
296             ReduceLogicalDimToScalar<LogicalAccumulator<REDUCTION>>(
297                 x, dim - 1, at);
298       }
299     }
300   };
301 };
302 
303 template <LogicalReduction REDUCTION>
304 inline void DoReduceLogicalDimension(Descriptor &result, const Descriptor &x,
305     int dim, Terminator &terminator, const char *intrinsic) {
306   auto catKind{x.type().GetCategoryAndKind()};
307   RUNTIME_CHECK(terminator, catKind && catKind->first == TypeCategory::Logical);
308   ApplyLogicalKind<LogicalReduceHelper<REDUCTION>::template Functor, void>(
309       catKind->second, terminator, result, x, dim, terminator, intrinsic);
310 }
311 
312 // COUNT
313 
314 class CountAccumulator {
315 public:
316   using Type = std::int64_t;
317   explicit CountAccumulator(const Descriptor &array) : array_{array} {}
318   void Reinitialize() { result_ = 0; }
319   Type Result() const { return result_; }
320   template <typename IGNORED = void>
321   bool AccumulateAt(const SubscriptValue at[]) {
322     if (IsLogicalElementTrue(array_, at)) {
323       ++result_;
324     }
325     return true;
326   }
327 
328 private:
329   const Descriptor &array_;
330   Type result_{0};
331 };
332 
333 template <int KIND> struct CountDimension {
334   void operator()(Descriptor &result, const Descriptor &x, int dim,
335       Terminator &terminator) const {
336     // Element size of the descriptor descriptor is the size
337     // of {TypeCategory::Integer, KIND}.
338     CreatePartialReductionResult(result, x,
339         Descriptor::BytesFor(TypeCategory::Integer, KIND), dim, terminator,
340         "COUNT", TypeCode{TypeCategory::Integer, KIND});
341     SubscriptValue at[maxRank];
342     result.GetLowerBounds(at);
343     INTERNAL_CHECK(result.rank() == 0 || at[0] == 1);
344     using CppType = CppTypeFor<TypeCategory::Integer, KIND>;
345     for (auto n{result.Elements()}; n-- > 0; result.IncrementSubscripts(at)) {
346       *result.Element<CppType>(at) =
347           ReduceLogicalDimToScalar<CountAccumulator>(x, dim - 1, at);
348     }
349   }
350 };
351 
352 extern "C" {
353 RT_EXT_API_GROUP_BEGIN
354 
355 bool RTDEF(All)(const Descriptor &x, const char *source, int line, int dim) {
356   return GetTotalLogicalReduction(x, source, line, dim,
357       LogicalAccumulator<LogicalReduction::All>{x}, "ALL");
358 }
359 void RTDEF(AllDim)(Descriptor &result, const Descriptor &x, int dim,
360     const char *source, int line) {
361   Terminator terminator{source, line};
362   DoReduceLogicalDimension<LogicalReduction::All>(
363       result, x, dim, terminator, "ALL");
364 }
365 
366 bool RTDEF(Any)(const Descriptor &x, const char *source, int line, int dim) {
367   return GetTotalLogicalReduction(x, source, line, dim,
368       LogicalAccumulator<LogicalReduction::Any>{x}, "ANY");
369 }
370 void RTDEF(AnyDim)(Descriptor &result, const Descriptor &x, int dim,
371     const char *source, int line) {
372   Terminator terminator{source, line};
373   DoReduceLogicalDimension<LogicalReduction::Any>(
374       result, x, dim, terminator, "ANY");
375 }
376 
377 std::int64_t RTDEF(Count)(
378     const Descriptor &x, const char *source, int line, int dim) {
379   return GetTotalLogicalReduction(
380       x, source, line, dim, CountAccumulator{x}, "COUNT");
381 }
382 
383 void RTDEF(CountDim)(Descriptor &result, const Descriptor &x, int dim, int kind,
384     const char *source, int line) {
385   Terminator terminator{source, line};
386   ApplyIntegerKind<CountDimension, void>(
387       kind, terminator, result, x, dim, terminator);
388 }
389 
390 bool RTDEF(Parity)(const Descriptor &x, const char *source, int line, int dim) {
391   return GetTotalLogicalReduction(x, source, line, dim,
392       LogicalAccumulator<LogicalReduction::Parity>{x}, "PARITY");
393 }
394 void RTDEF(ParityDim)(Descriptor &result, const Descriptor &x, int dim,
395     const char *source, int line) {
396   Terminator terminator{source, line};
397   DoReduceLogicalDimension<LogicalReduction::Parity>(
398       result, x, dim, terminator, "PARITY");
399 }
400 
401 RT_EXT_API_GROUP_END
402 } // extern "C"
403 } // namespace Fortran::runtime
404