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