forked from OSchip/llvm-project
378 lines
14 KiB
C++
378 lines
14 KiB
C++
//===-- runtime/reduction.cpp ---------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Implements ALL, ANY, COUNT, IALL, IANY, IPARITY, & PARITY for all required
|
|
// operand types and shapes.
|
|
//
|
|
// DOT_PRODUCT, FINDLOC, MATMUL, SUM, and PRODUCT are in their own eponymous
|
|
// source files.
|
|
// NORM2, MAXLOC, MINLOC, MAXVAL, and MINVAL are in extrema.cpp.
|
|
//
|
|
// TODO: IALL, IANY
|
|
|
|
#include "reduction.h"
|
|
#include "reduction-templates.h"
|
|
#include <cinttypes>
|
|
|
|
namespace Fortran::runtime {
|
|
|
|
// IALL, IANY, IPARITY
|
|
|
|
template <typename INTERMEDIATE> class IntegerAndAccumulator {
|
|
public:
|
|
explicit IntegerAndAccumulator(const Descriptor &array) : array_{array} {}
|
|
void Reinitialize() { and_ = ~INTERMEDIATE{0}; }
|
|
template <typename A> void GetResult(A *p, int /*zeroBasedDim*/ = -1) const {
|
|
*p = static_cast<A>(and_);
|
|
}
|
|
template <typename A> bool AccumulateAt(const SubscriptValue at[]) {
|
|
and_ &= *array_.Element<A>(at);
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
const Descriptor &array_;
|
|
INTERMEDIATE and_{~INTERMEDIATE{0}};
|
|
};
|
|
|
|
template <typename INTERMEDIATE> class IntegerOrAccumulator {
|
|
public:
|
|
explicit IntegerOrAccumulator(const Descriptor &array) : array_{array} {}
|
|
void Reinitialize() { or_ = 0; }
|
|
template <typename A> void GetResult(A *p, int /*zeroBasedDim*/ = -1) const {
|
|
*p = static_cast<A>(or_);
|
|
}
|
|
template <typename A> bool AccumulateAt(const SubscriptValue at[]) {
|
|
or_ |= *array_.Element<A>(at);
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
const Descriptor &array_;
|
|
INTERMEDIATE or_{0};
|
|
};
|
|
|
|
template <typename INTERMEDIATE> class IntegerXorAccumulator {
|
|
public:
|
|
explicit IntegerXorAccumulator(const Descriptor &array) : array_{array} {}
|
|
void Reinitialize() { xor_ = 0; }
|
|
template <typename A> void GetResult(A *p, int /*zeroBasedDim*/ = -1) const {
|
|
*p = static_cast<A>(xor_);
|
|
}
|
|
template <typename A> bool AccumulateAt(const SubscriptValue at[]) {
|
|
xor_ ^= *array_.Element<A>(at);
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
const Descriptor &array_;
|
|
INTERMEDIATE xor_{0};
|
|
};
|
|
|
|
extern "C" {
|
|
CppTypeFor<TypeCategory::Integer, 1> RTNAME(IAll1)(const Descriptor &x,
|
|
const char *source, int line, int dim, const Descriptor *mask) {
|
|
return GetTotalReduction<TypeCategory::Integer, 1>(x, source, line, dim, mask,
|
|
IntegerAndAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x}, "IALL");
|
|
}
|
|
CppTypeFor<TypeCategory::Integer, 2> RTNAME(IAll2)(const Descriptor &x,
|
|
const char *source, int line, int dim, const Descriptor *mask) {
|
|
return GetTotalReduction<TypeCategory::Integer, 2>(x, source, line, dim, mask,
|
|
IntegerAndAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x}, "IALL");
|
|
}
|
|
CppTypeFor<TypeCategory::Integer, 4> RTNAME(IAll4)(const Descriptor &x,
|
|
const char *source, int line, int dim, const Descriptor *mask) {
|
|
return GetTotalReduction<TypeCategory::Integer, 4>(x, source, line, dim, mask,
|
|
IntegerAndAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x}, "IALL");
|
|
}
|
|
CppTypeFor<TypeCategory::Integer, 8> RTNAME(IAll8)(const Descriptor &x,
|
|
const char *source, int line, int dim, const Descriptor *mask) {
|
|
return GetTotalReduction<TypeCategory::Integer, 8>(x, source, line, dim, mask,
|
|
IntegerAndAccumulator<CppTypeFor<TypeCategory::Integer, 8>>{x}, "IALL");
|
|
}
|
|
#ifdef __SIZEOF_INT128__
|
|
CppTypeFor<TypeCategory::Integer, 16> RTNAME(IAll16)(const Descriptor &x,
|
|
const char *source, int line, int dim, const Descriptor *mask) {
|
|
return GetTotalReduction<TypeCategory::Integer, 16>(x, source, line, dim,
|
|
mask, IntegerAndAccumulator<CppTypeFor<TypeCategory::Integer, 16>>{x},
|
|
"IALL");
|
|
}
|
|
#endif
|
|
void RTNAME(IAllDim)(Descriptor &result, const Descriptor &x, int dim,
|
|
const char *source, int line, const Descriptor *mask) {
|
|
Terminator terminator{source, line};
|
|
auto catKind{x.type().GetCategoryAndKind()};
|
|
RUNTIME_CHECK(terminator,
|
|
catKind.has_value() && catKind->first == TypeCategory::Integer);
|
|
PartialIntegerReduction<IntegerAndAccumulator>(
|
|
result, x, dim, catKind->second, mask, "IALL", terminator);
|
|
}
|
|
|
|
CppTypeFor<TypeCategory::Integer, 1> RTNAME(IAny1)(const Descriptor &x,
|
|
const char *source, int line, int dim, const Descriptor *mask) {
|
|
return GetTotalReduction<TypeCategory::Integer, 1>(x, source, line, dim, mask,
|
|
IntegerOrAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x}, "IANY");
|
|
}
|
|
CppTypeFor<TypeCategory::Integer, 2> RTNAME(IAny2)(const Descriptor &x,
|
|
const char *source, int line, int dim, const Descriptor *mask) {
|
|
return GetTotalReduction<TypeCategory::Integer, 2>(x, source, line, dim, mask,
|
|
IntegerOrAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x}, "IANY");
|
|
}
|
|
CppTypeFor<TypeCategory::Integer, 4> RTNAME(IAny4)(const Descriptor &x,
|
|
const char *source, int line, int dim, const Descriptor *mask) {
|
|
return GetTotalReduction<TypeCategory::Integer, 4>(x, source, line, dim, mask,
|
|
IntegerOrAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x}, "IANY");
|
|
}
|
|
CppTypeFor<TypeCategory::Integer, 8> RTNAME(IAny8)(const Descriptor &x,
|
|
const char *source, int line, int dim, const Descriptor *mask) {
|
|
return GetTotalReduction<TypeCategory::Integer, 8>(x, source, line, dim, mask,
|
|
IntegerOrAccumulator<CppTypeFor<TypeCategory::Integer, 8>>{x}, "IANY");
|
|
}
|
|
#ifdef __SIZEOF_INT128__
|
|
CppTypeFor<TypeCategory::Integer, 16> RTNAME(IAny16)(const Descriptor &x,
|
|
const char *source, int line, int dim, const Descriptor *mask) {
|
|
return GetTotalReduction<TypeCategory::Integer, 16>(x, source, line, dim,
|
|
mask, IntegerOrAccumulator<CppTypeFor<TypeCategory::Integer, 16>>{x},
|
|
"IANY");
|
|
}
|
|
#endif
|
|
void RTNAME(IAnyDim)(Descriptor &result, const Descriptor &x, int dim,
|
|
const char *source, int line, const Descriptor *mask) {
|
|
Terminator terminator{source, line};
|
|
auto catKind{x.type().GetCategoryAndKind()};
|
|
RUNTIME_CHECK(terminator,
|
|
catKind.has_value() && catKind->first == TypeCategory::Integer);
|
|
PartialIntegerReduction<IntegerOrAccumulator>(
|
|
result, x, dim, catKind->second, mask, "IANY", terminator);
|
|
}
|
|
|
|
CppTypeFor<TypeCategory::Integer, 1> RTNAME(IParity1)(const Descriptor &x,
|
|
const char *source, int line, int dim, const Descriptor *mask) {
|
|
return GetTotalReduction<TypeCategory::Integer, 1>(x, source, line, dim, mask,
|
|
IntegerXorAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x},
|
|
"IPARITY");
|
|
}
|
|
CppTypeFor<TypeCategory::Integer, 2> RTNAME(IParity2)(const Descriptor &x,
|
|
const char *source, int line, int dim, const Descriptor *mask) {
|
|
return GetTotalReduction<TypeCategory::Integer, 2>(x, source, line, dim, mask,
|
|
IntegerXorAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x},
|
|
"IPARITY");
|
|
}
|
|
CppTypeFor<TypeCategory::Integer, 4> RTNAME(IParity4)(const Descriptor &x,
|
|
const char *source, int line, int dim, const Descriptor *mask) {
|
|
return GetTotalReduction<TypeCategory::Integer, 4>(x, source, line, dim, mask,
|
|
IntegerXorAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x},
|
|
"IPARITY");
|
|
}
|
|
CppTypeFor<TypeCategory::Integer, 8> RTNAME(IParity8)(const Descriptor &x,
|
|
const char *source, int line, int dim, const Descriptor *mask) {
|
|
return GetTotalReduction<TypeCategory::Integer, 8>(x, source, line, dim, mask,
|
|
IntegerXorAccumulator<CppTypeFor<TypeCategory::Integer, 8>>{x},
|
|
"IPARITY");
|
|
}
|
|
#ifdef __SIZEOF_INT128__
|
|
CppTypeFor<TypeCategory::Integer, 16> RTNAME(IParity16)(const Descriptor &x,
|
|
const char *source, int line, int dim, const Descriptor *mask) {
|
|
return GetTotalReduction<TypeCategory::Integer, 16>(x, source, line, dim,
|
|
mask, IntegerXorAccumulator<CppTypeFor<TypeCategory::Integer, 16>>{x},
|
|
"IPARITY");
|
|
}
|
|
#endif
|
|
void RTNAME(IParityDim)(Descriptor &result, const Descriptor &x, int dim,
|
|
const char *source, int line, const Descriptor *mask) {
|
|
Terminator terminator{source, line};
|
|
auto catKind{x.type().GetCategoryAndKind()};
|
|
RUNTIME_CHECK(terminator,
|
|
catKind.has_value() && catKind->first == TypeCategory::Integer);
|
|
PartialIntegerReduction<IntegerXorAccumulator>(
|
|
result, x, dim, catKind->second, mask, "IPARITY", terminator);
|
|
}
|
|
}
|
|
|
|
// ALL, ANY, COUNT, & PARITY
|
|
|
|
enum class LogicalReduction { All, Any, Parity };
|
|
|
|
template <LogicalReduction REDUCTION> class LogicalAccumulator {
|
|
public:
|
|
using Type = bool;
|
|
explicit LogicalAccumulator(const Descriptor &array) : array_{array} {}
|
|
void Reinitialize() { result_ = REDUCTION == LogicalReduction::All; }
|
|
bool Result() const { return result_; }
|
|
bool Accumulate(bool x) {
|
|
if constexpr (REDUCTION == LogicalReduction::Parity) {
|
|
result_ = result_ != x;
|
|
} else if (x != (REDUCTION == LogicalReduction::All)) {
|
|
result_ = x;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
template <typename IGNORED = void>
|
|
bool AccumulateAt(const SubscriptValue at[]) {
|
|
return Accumulate(IsLogicalElementTrue(array_, at));
|
|
}
|
|
|
|
private:
|
|
const Descriptor &array_;
|
|
bool result_{REDUCTION == LogicalReduction::All};
|
|
};
|
|
|
|
template <typename ACCUMULATOR>
|
|
inline auto GetTotalLogicalReduction(const Descriptor &x, const char *source,
|
|
int line, int dim, ACCUMULATOR &&accumulator, const char *intrinsic) ->
|
|
typename ACCUMULATOR::Type {
|
|
Terminator terminator{source, line};
|
|
if (dim < 0 || dim > 1) {
|
|
terminator.Crash("%s: bad DIM=%d", intrinsic, dim);
|
|
}
|
|
SubscriptValue xAt[maxRank];
|
|
x.GetLowerBounds(xAt);
|
|
for (auto elements{x.Elements()}; elements--; x.IncrementSubscripts(xAt)) {
|
|
if (!accumulator.AccumulateAt(xAt)) {
|
|
break; // cut short, result is known
|
|
}
|
|
}
|
|
return accumulator.Result();
|
|
}
|
|
|
|
template <typename ACCUMULATOR>
|
|
inline auto ReduceLogicalDimToScalar(const Descriptor &x, int zeroBasedDim,
|
|
SubscriptValue subscripts[]) -> typename ACCUMULATOR::Type {
|
|
ACCUMULATOR accumulator{x};
|
|
SubscriptValue xAt[maxRank];
|
|
GetExpandedSubscripts(xAt, x, zeroBasedDim, subscripts);
|
|
const auto &dim{x.GetDimension(zeroBasedDim)};
|
|
SubscriptValue at{dim.LowerBound()};
|
|
for (auto n{dim.Extent()}; n-- > 0; ++at) {
|
|
xAt[zeroBasedDim] = at;
|
|
if (!accumulator.AccumulateAt(xAt)) {
|
|
break;
|
|
}
|
|
}
|
|
return accumulator.Result();
|
|
}
|
|
|
|
template <LogicalReduction REDUCTION> struct LogicalReduceHelper {
|
|
template <int KIND> struct Functor {
|
|
void operator()(Descriptor &result, const Descriptor &x, int dim,
|
|
Terminator &terminator, const char *intrinsic) const {
|
|
// Standard requires result to have same LOGICAL kind as argument.
|
|
CreatePartialReductionResult(
|
|
result, x, dim, terminator, intrinsic, x.type());
|
|
SubscriptValue at[maxRank];
|
|
result.GetLowerBounds(at);
|
|
INTERNAL_CHECK(at[0] == 1);
|
|
using CppType = CppTypeFor<TypeCategory::Logical, KIND>;
|
|
for (auto n{result.Elements()}; n-- > 0; result.IncrementSubscripts(at)) {
|
|
*result.Element<CppType>(at) =
|
|
ReduceLogicalDimToScalar<LogicalAccumulator<REDUCTION>>(
|
|
x, dim - 1, at);
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
template <LogicalReduction REDUCTION>
|
|
inline void DoReduceLogicalDimension(Descriptor &result, const Descriptor &x,
|
|
int dim, Terminator &terminator, const char *intrinsic) {
|
|
auto catKind{x.type().GetCategoryAndKind()};
|
|
RUNTIME_CHECK(terminator, catKind && catKind->first == TypeCategory::Logical);
|
|
ApplyLogicalKind<LogicalReduceHelper<REDUCTION>::template Functor, void>(
|
|
catKind->second, terminator, result, x, dim, terminator, intrinsic);
|
|
}
|
|
|
|
// COUNT
|
|
|
|
class CountAccumulator {
|
|
public:
|
|
using Type = std::int64_t;
|
|
explicit CountAccumulator(const Descriptor &array) : array_{array} {}
|
|
void Reinitialize() { result_ = 0; }
|
|
Type Result() const { return result_; }
|
|
template <typename IGNORED = void>
|
|
bool AccumulateAt(const SubscriptValue at[]) {
|
|
if (IsLogicalElementTrue(array_, at)) {
|
|
++result_;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
const Descriptor &array_;
|
|
Type result_{0};
|
|
};
|
|
|
|
template <int KIND> struct CountDimension {
|
|
void operator()(Descriptor &result, const Descriptor &x, int dim,
|
|
Terminator &terminator) const {
|
|
CreatePartialReductionResult(result, x, dim, terminator, "COUNT",
|
|
TypeCode{TypeCategory::Integer, KIND});
|
|
SubscriptValue at[maxRank];
|
|
result.GetLowerBounds(at);
|
|
INTERNAL_CHECK(at[0] == 1);
|
|
using CppType = CppTypeFor<TypeCategory::Integer, KIND>;
|
|
for (auto n{result.Elements()}; n-- > 0; result.IncrementSubscripts(at)) {
|
|
*result.Element<CppType>(at) =
|
|
ReduceLogicalDimToScalar<CountAccumulator>(x, dim - 1, at);
|
|
}
|
|
}
|
|
};
|
|
|
|
extern "C" {
|
|
|
|
bool RTNAME(All)(const Descriptor &x, const char *source, int line, int dim) {
|
|
return GetTotalLogicalReduction(x, source, line, dim,
|
|
LogicalAccumulator<LogicalReduction::All>{x}, "ALL");
|
|
}
|
|
void RTNAME(AllDim)(Descriptor &result, const Descriptor &x, int dim,
|
|
const char *source, int line) {
|
|
Terminator terminator{source, line};
|
|
DoReduceLogicalDimension<LogicalReduction::All>(
|
|
result, x, dim, terminator, "ALL");
|
|
}
|
|
|
|
bool RTNAME(Any)(const Descriptor &x, const char *source, int line, int dim) {
|
|
return GetTotalLogicalReduction(x, source, line, dim,
|
|
LogicalAccumulator<LogicalReduction::Any>{x}, "ANY");
|
|
}
|
|
void RTNAME(AnyDim)(Descriptor &result, const Descriptor &x, int dim,
|
|
const char *source, int line) {
|
|
Terminator terminator{source, line};
|
|
DoReduceLogicalDimension<LogicalReduction::Any>(
|
|
result, x, dim, terminator, "ANY");
|
|
}
|
|
|
|
std::int64_t RTNAME(Count)(
|
|
const Descriptor &x, const char *source, int line, int dim) {
|
|
return GetTotalLogicalReduction(
|
|
x, source, line, dim, CountAccumulator{x}, "COUNT");
|
|
}
|
|
|
|
void RTNAME(CountDim)(Descriptor &result, const Descriptor &x, int dim,
|
|
int kind, const char *source, int line) {
|
|
Terminator terminator{source, line};
|
|
ApplyIntegerKind<CountDimension, void>(
|
|
kind, terminator, result, x, dim, terminator);
|
|
}
|
|
|
|
bool RTNAME(Parity)(
|
|
const Descriptor &x, const char *source, int line, int dim) {
|
|
return GetTotalLogicalReduction(x, source, line, dim,
|
|
LogicalAccumulator<LogicalReduction::Parity>{x}, "PARITY");
|
|
}
|
|
void RTNAME(ParityDim)(Descriptor &result, const Descriptor &x, int dim,
|
|
const char *source, int line) {
|
|
Terminator terminator{source, line};
|
|
DoReduceLogicalDimension<LogicalReduction::Parity>(
|
|
result, x, dim, terminator, "PARITY");
|
|
}
|
|
|
|
} // extern "C"
|
|
} // namespace Fortran::runtime
|