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