[flang] Implement IPARITY, PARITY, and FINDLOC reductions

Define APIs for, and implement, these three more recently-introduced
standard reduction transformational intrinsic functions to the runtime.

Differential Revision: https://reviews.llvm.org/D100863
This commit is contained in:
peter klausler 2021-04-20 09:19:21 -07:00
parent f2da1f68d8
commit 8d672c0b3e
5 changed files with 956 additions and 460 deletions

File diff suppressed because it is too large Load diff

View file

@ -24,8 +24,8 @@ namespace Fortran::runtime {
extern "C" {
// Reductions that are known to return scalars have per-type entry
// points. These cover the casse that either have no DIM=
// argument, or have an argument rank of 1. Pass 0 for no DIM=
// points. These cover the cases that either have no DIM=
// argument or have an argument rank of 1. Pass 0 for no DIM=
// or the value of the DIM= argument so that it may be checked.
// The data type in the descriptor is checked against the expected
// return type.
@ -144,20 +144,42 @@ void RTNAME(CppProductComplex16)(std::complex<long double> &,
void RTNAME(ProductDim)(Descriptor &result, const Descriptor &array, int dim,
const char *source, int line, const Descriptor *mask = nullptr);
// MAXLOC and MINLOC
// IPARITY()
std::int8_t RTNAME(IParity1)(const Descriptor &, const char *source, int line,
int dim = 0, const Descriptor *mask = nullptr);
std::int16_t RTNAME(IParity2)(const Descriptor &, const char *source, int line,
int dim = 0, const Descriptor *mask = nullptr);
std::int32_t RTNAME(IParity4)(const Descriptor &, const char *source, int line,
int dim = 0, const Descriptor *mask = nullptr);
std::int64_t RTNAME(IParity8)(const Descriptor &, const char *source, int line,
int dim = 0, const Descriptor *mask = nullptr);
#ifdef __SIZEOF_INT128__
common::int128_t RTNAME(IParity16)(const Descriptor &, const char *source,
int line, int dim = 0, const Descriptor *mask = nullptr);
#endif
void RTNAME(IParityDim)(Descriptor &result, const Descriptor &array, int dim,
const char *source, int line, const Descriptor *mask = nullptr);
// FINDLOC, MAXLOC, & MINLOC
// These return allocated arrays in the supplied descriptor.
// The default value for KIND= should be the default INTEGER in effect at
// compilation time.
void RTNAME(Maxloc)(Descriptor &, const Descriptor &, int kind,
void RTNAME(Findloc)(Descriptor &, const Descriptor &x,
const Descriptor &target, int kind, const char *source, int line,
const Descriptor *mask = nullptr, bool back = false);
void RTNAME(FindlocDim)(Descriptor &, const Descriptor &x,
const Descriptor &target, int kind, int dim, const char *source, int line,
const Descriptor *mask = nullptr, bool back = false);
void RTNAME(Maxloc)(Descriptor &, const Descriptor &x, int kind,
const char *source, int line, const Descriptor *mask = nullptr,
bool back = false);
void RTNAME(MaxlocDim)(Descriptor &, const Descriptor &, int kind, int dim,
void RTNAME(MaxlocDim)(Descriptor &, const Descriptor &x, int kind, int dim,
const char *source, int line, const Descriptor *mask = nullptr,
bool back = false);
void RTNAME(Minloc)(Descriptor &, const Descriptor &, int kind,
void RTNAME(Minloc)(Descriptor &, const Descriptor &x, int kind,
const char *source, int line, const Descriptor *mask = nullptr,
bool back = false);
void RTNAME(MinlocDim)(Descriptor &, const Descriptor &, int kind, int dim,
void RTNAME(MinlocDim)(Descriptor &, const Descriptor &x, int kind, int dim,
const char *source, int line, const Descriptor *mask = nullptr,
bool back = false);
@ -221,7 +243,7 @@ void RTNAME(MaxvalDim)(Descriptor &, const Descriptor &, int dim,
void RTNAME(MinvalDim)(Descriptor &, const Descriptor &, int dim,
const char *source, int line, const Descriptor *mask = nullptr);
// ALL, ANY, & COUNT logical reductions
// ALL, ANY, COUNT, & PARITY logical reductions
bool RTNAME(All)(const Descriptor &, const char *source, int line, int dim = 0);
void RTNAME(AllDim)(Descriptor &result, const Descriptor &, int dim,
const char *source, int line);
@ -232,6 +254,10 @@ std::int64_t RTNAME(Count)(
const Descriptor &, const char *source, int line, int dim = 0);
void RTNAME(CountDim)(Descriptor &result, const Descriptor &, int dim, int kind,
const char *source, int line);
bool RTNAME(Parity)(
const Descriptor &, const char *source, int line, int dim = 0);
void RTNAME(ParityDim)(Descriptor &result, const Descriptor &, int dim,
const char *source, int line);
} // extern "C"
} // namespace Fortran::runtime

View file

@ -24,6 +24,10 @@ public:
Terminator(const Terminator &) = default;
explicit Terminator(const char *sourceFileName, int sourceLine = 0)
: sourceFileName_{sourceFileName}, sourceLine_{sourceLine} {}
const char *sourceFileName() const { return sourceFileName_; }
int sourceLine() const { return sourceLine_; }
void SetLocation(const char *sourceFileName = nullptr, int sourceLine = 0) {
sourceFileName_ = sourceFileName;
sourceLine_ = sourceLine;

View file

@ -13,6 +13,7 @@
#include "descriptor.h"
#include "memory.h"
#include "terminator.h"
#include "flang/Common/long-double.h"
#include <functional>
#include <map>
#include <type_traits>
@ -101,5 +102,83 @@ inline bool SetInteger(INT &x, int kind, std::int64_t value) {
}
}
// Maps a runtime INTEGER kind value to the appropriate instantiation of
// a function object template and calls it with the supplied arguments.
template <template <int KIND> class FUNC, typename RESULT, typename... A>
inline RESULT ApplyIntegerKind(int kind, Terminator &terminator, A &&...x) {
switch (kind) {
case 1:
return FUNC<1>{}(std::forward<A>(x)...);
case 2:
return FUNC<2>{}(std::forward<A>(x)...);
case 4:
return FUNC<4>{}(std::forward<A>(x)...);
case 8:
return FUNC<8>{}(std::forward<A>(x)...);
#ifdef __SIZEOF_INT128__
case 16:
return FUNC<16>{}(std::forward<A>(x)...);
#endif
default:
terminator.Crash("unsupported INTEGER(KIND=%d)", kind);
}
}
template <template <int KIND> class FUNC, typename RESULT, typename... A>
inline RESULT ApplyFloatingPointKind(
int kind, Terminator &terminator, A &&...x) {
switch (kind) {
#if 0 // TODO: REAL/COMPLEX (2 & 3)
case 2:
return FUNC<2>{}(std::forward<A>(x)...);
case 3:
return FUNC<3>{}(std::forward<A>(x)...);
#endif
case 4:
return FUNC<4>{}(std::forward<A>(x)...);
case 8:
return FUNC<8>{}(std::forward<A>(x)...);
#if LONG_DOUBLE == 80
case 10:
return FUNC<10>{}(std::forward<A>(x)...);
#elif LONG_DOUBLE == 128
case 16:
return FUNC<16>{}(std::forward<A>(x)...);
#endif
default:
terminator.Crash("unsupported REAL/COMPLEX(KIND=%d)", kind);
}
}
template <template <int KIND> class FUNC, typename RESULT, typename... A>
inline RESULT ApplyCharacterKind(int kind, Terminator &terminator, A &&...x) {
switch (kind) {
case 1:
return FUNC<1>{}(std::forward<A>(x)...);
case 2:
return FUNC<2>{}(std::forward<A>(x)...);
case 4:
return FUNC<4>{}(std::forward<A>(x)...);
default:
terminator.Crash("unsupported CHARACTER(KIND=%d)", kind);
}
}
template <template <int KIND> class FUNC, typename RESULT, typename... A>
inline RESULT ApplyLogicalKind(int kind, Terminator &terminator, A &&...x) {
switch (kind) {
case 1:
return FUNC<1>{}(std::forward<A>(x)...);
case 2:
return FUNC<2>{}(std::forward<A>(x)...);
case 4:
return FUNC<4>{}(std::forward<A>(x)...);
case 8:
return FUNC<8>{}(std::forward<A>(x)...);
default:
terminator.Crash("unsupported LOGICAL(KIND=%d)", kind);
}
}
} // namespace Fortran::runtime
#endif // FORTRAN_RUNTIME_TOOLS_H_

View file

@ -138,8 +138,8 @@ TEST(Reductions, Character) {
std::vector<int> shape{2, 3};
auto array{MakeArray<TypeCategory::Character, 1>(shape,
std::vector<std::string>{"abc", "def", "ghi", "jkl", "mno", "abc"}, 3)};
StaticDescriptor<1> statDesc;
Descriptor &res{statDesc.descriptor()};
StaticDescriptor<1> statDesc[2];
Descriptor &res{statDesc[0].descriptor()};
RTNAME(MaxvalCharacter)(res, *array, __FILE__, __LINE__);
EXPECT_EQ(res.rank(), 0);
EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Character, 1}.raw()));
@ -202,6 +202,30 @@ TEST(Reductions, Character) {
EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 2);
EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 3);
res.Destroy();
static const char targetChar[]{"abc"};
Descriptor &target{statDesc[1].descriptor()};
target.Establish(1, std::strlen(targetChar),
const_cast<void *>(static_cast<const void *>(&targetChar)), 0, nullptr,
CFI_attribute_pointer);
RTNAME(Findloc)
(res, *array, target, /*KIND=*/4, __FILE__, __LINE__, nullptr,
/*BACK=*/false);
EXPECT_EQ(res.rank(), 1);
EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 4}.raw()));
EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(res.GetDimension(0).Extent(), 2);
EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 1);
EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 1);
res.Destroy();
RTNAME(Findloc)
(res, *array, target, /*KIND=*/4, __FILE__, __LINE__, nullptr, /*BACK=*/true);
EXPECT_EQ(res.rank(), 1);
EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 4}.raw()));
EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(res.GetDimension(0).Extent(), 2);
EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 2);
EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 3);
res.Destroy();
}
TEST(Reductions, Logical) {
@ -211,9 +235,10 @@ TEST(Reductions, Logical) {
ASSERT_EQ(array->ElementBytes(), std::size_t{4});
EXPECT_EQ(RTNAME(All)(*array, __FILE__, __LINE__), false);
EXPECT_EQ(RTNAME(Any)(*array, __FILE__, __LINE__), true);
EXPECT_EQ(RTNAME(Parity)(*array, __FILE__, __LINE__), false);
EXPECT_EQ(RTNAME(Count)(*array, __FILE__, __LINE__), 2);
StaticDescriptor<2> statDesc;
Descriptor &res{statDesc.descriptor()};
StaticDescriptor<2> statDesc[2];
Descriptor &res{statDesc[0].descriptor()};
RTNAME(AllDim)(res, *array, /*DIM=*/1, __FILE__, __LINE__);
EXPECT_EQ(res.rank(), 1);
EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Logical, 4}.raw()));
@ -246,6 +271,22 @@ TEST(Reductions, Logical) {
EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 1);
EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 1);
res.Destroy();
RTNAME(ParityDim)(res, *array, /*DIM=*/1, __FILE__, __LINE__);
EXPECT_EQ(res.rank(), 1);
EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Logical, 4}.raw()));
EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(res.GetDimension(0).Extent(), 2);
EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 0);
EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 0);
res.Destroy();
RTNAME(ParityDim)(res, *array, /*DIM=*/2, __FILE__, __LINE__);
EXPECT_EQ(res.rank(), 1);
EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Logical, 4}.raw()));
EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(res.GetDimension(0).Extent(), 2);
EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 1);
EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 1);
res.Destroy();
RTNAME(CountDim)(res, *array, /*DIM=*/1, /*KIND=*/4, __FILE__, __LINE__);
EXPECT_EQ(res.rank(), 1);
EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 4}.raw()));
@ -262,4 +303,123 @@ TEST(Reductions, Logical) {
EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int64_t>(0), 1);
EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int64_t>(1), 1);
res.Destroy();
bool boolValue{false};
Descriptor &target{statDesc[1].descriptor()};
target.Establish(TypeCategory::Logical, 1, static_cast<void *>(&boolValue), 0,
nullptr, CFI_attribute_pointer);
RTNAME(Findloc)
(res, *array, target, /*KIND=*/4, __FILE__, __LINE__, nullptr,
/*BACK=*/false);
EXPECT_EQ(res.rank(), 1);
EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 4}.raw()));
EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(res.GetDimension(0).Extent(), 2);
EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 1);
EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 1);
res.Destroy();
boolValue = true;
RTNAME(Findloc)
(res, *array, target, /*KIND=*/4, __FILE__, __LINE__, nullptr, /*BACK=*/true);
EXPECT_EQ(res.rank(), 1);
EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 4}.raw()));
EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(res.GetDimension(0).Extent(), 2);
EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 2);
EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 2);
res.Destroy();
}
TEST(Reductions, FindlocNumeric) {
std::vector<int> shape{2, 3};
auto realArray{MakeArray<TypeCategory::Real, 8>(shape,
std::vector<double>{0.0, -0.0, 1.0, 3.14,
std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::infinity()})};
ASSERT_EQ(realArray->ElementBytes(), sizeof(double));
StaticDescriptor<2> statDesc[2];
Descriptor &res{statDesc[0].descriptor()};
// Find the first zero
Descriptor &target{statDesc[1].descriptor()};
double value{0.0};
target.Establish(TypeCategory::Real, 8, static_cast<void *>(&value), 0,
nullptr, CFI_attribute_pointer);
RTNAME(Findloc)
(res, *realArray, target, 8, __FILE__, __LINE__, nullptr, /*BACK=*/false);
EXPECT_EQ(res.rank(), 1);
EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 8}.raw()));
EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(res.GetDimension(0).UpperBound(), 2);
EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(0), 1);
EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(1), 1);
res.Destroy();
// Find last zero (even though it's negative)
RTNAME(Findloc)
(res, *realArray, target, 8, __FILE__, __LINE__, nullptr, /*BACK=*/true);
EXPECT_EQ(res.rank(), 1);
EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 8}.raw()));
EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(res.GetDimension(0).UpperBound(), 2);
EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(0), 2);
EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(1), 1);
res.Destroy();
// Find the +Inf
value = std::numeric_limits<double>::infinity();
RTNAME(Findloc)
(res, *realArray, target, 8, __FILE__, __LINE__, nullptr, /*BACK=*/false);
EXPECT_EQ(res.rank(), 1);
EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 8}.raw()));
EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(res.GetDimension(0).UpperBound(), 2);
EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(0), 2);
EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(1), 3);
res.Destroy();
// Ensure that we can't find a NaN
value = std::numeric_limits<double>::quiet_NaN();
RTNAME(Findloc)
(res, *realArray, target, 8, __FILE__, __LINE__, nullptr, /*BACK=*/false);
EXPECT_EQ(res.rank(), 1);
EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 8}.raw()));
EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(res.GetDimension(0).UpperBound(), 2);
EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(0), 0);
EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(1), 0);
res.Destroy();
// Find a value of a distinct type
int intValue{1};
target.Establish(TypeCategory::Integer, 4, static_cast<void *>(&intValue), 0,
nullptr, CFI_attribute_pointer);
RTNAME(Findloc)
(res, *realArray, target, 8, __FILE__, __LINE__, nullptr, /*BACK=*/false);
EXPECT_EQ(res.rank(), 1);
EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 8}.raw()));
EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(res.GetDimension(0).UpperBound(), 2);
EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(0), 1);
EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(1), 2);
res.Destroy();
// Partial reductions
value = 1.0;
target.Establish(TypeCategory::Real, 8, static_cast<void *>(&value), 0,
nullptr, CFI_attribute_pointer);
RTNAME(FindlocDim)
(res, *realArray, target, 8, /*DIM=*/1, __FILE__, __LINE__, nullptr,
/*BACK=*/false);
EXPECT_EQ(res.rank(), 1);
EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 8}.raw()));
EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(res.GetDimension(0).UpperBound(), 3);
EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(0), 0);
EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(1), 1);
EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(2), 0);
res.Destroy();
RTNAME(FindlocDim)
(res, *realArray, target, 8, /*DIM=*/2, __FILE__, __LINE__, nullptr,
/*BACK=*/true);
EXPECT_EQ(res.rank(), 1);
EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 8}.raw()));
EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(res.GetDimension(0).UpperBound(), 2);
EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(0), 2);
EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(1), 0);
res.Destroy();
}