llvm-project/flang/unittests/Runtime/Transformational.cpp
Slava Zakharin d37250c9db [flang][runtime] Fixes for element size calculation.
BytesFor() used to return KIND for the size, which is not always
correct, so I changed it to return the size of the actual CppType
corresponding to the given category and kind.

MinElemLen() used to calculate size incorrectly (e.g. CFI_type_extended_double
was sized 10, whereas it may occupy more bytes on a target), so I changed it
to call BytesFor().

Additional changes were needed to resolve new failures for transformational
intrinsics. These intrinsics used to work for not fully supported
data types (e.g. REAL(3)), but now stopped working because CppType
cannot be computed for those categories/kinds. The solution is to use
known element size from the source argument(s) for establishing
the destination descriptor - the element size is all that is needed
for transformational intrinsics to keep working.

Note that this does not help cases, where runtime still has to
compute the element size, e.g. when it creates descriptors for
components of derived types. If the component has unsupported
data type, BytesFor() will still fail. So these cases require
adding support for the missing types.

New regression unit test in Runtime/Transformational.cpp
demonstrates the case that will start working properly with
this commit.
2022-09-22 10:10:42 -07:00

348 lines
14 KiB
C++

//===-- flang/unittests/Runtime/Transformational.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
//
//===----------------------------------------------------------------------===//
#include "flang/Runtime/transformational.h"
#include "gtest/gtest.h"
#include "tools.h"
#include "flang/Runtime/type-code.h"
using namespace Fortran::runtime;
using Fortran::common::TypeCategory;
TEST(Transformational, Shifts) {
// ARRAY 1 3 5
// 2 4 6
auto array{MakeArray<TypeCategory::Integer, 4>(
std::vector<int>{2, 3}, std::vector<std::int32_t>{1, 2, 3, 4, 5, 6})};
array->GetDimension(0).SetLowerBound(0); // shouldn't matter
array->GetDimension(1).SetLowerBound(-1);
StaticDescriptor<2, true> statDesc;
Descriptor &result{statDesc.descriptor()};
auto shift3{MakeArray<TypeCategory::Integer, 8>(
std::vector<int>{3}, std::vector<std::int64_t>{1, -1, 2})};
RTNAME(Cshift)(result, *array, *shift3, 1, __FILE__, __LINE__);
EXPECT_EQ(result.type(), array->type());
EXPECT_EQ(result.rank(), 2);
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), 2);
EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(1).Extent(), 3);
EXPECT_EQ(result.type(), (TypeCode{TypeCategory::Integer, 4}));
static std::int32_t cshiftExpect1[6]{2, 1, 4, 3, 5, 6};
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(
*result.ZeroBasedIndexedElement<std::int32_t>(j), cshiftExpect1[j]);
}
result.Destroy();
auto shift2{MakeArray<TypeCategory::Integer, 1>(
std::vector<int>{2}, std::vector<std::int8_t>{1, -1})};
shift2->GetDimension(0).SetLowerBound(-1); // shouldn't matter
shift2->GetDimension(1).SetLowerBound(2);
RTNAME(Cshift)(result, *array, *shift2, 2, __FILE__, __LINE__);
EXPECT_EQ(result.type(), array->type());
EXPECT_EQ(result.rank(), 2);
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), 2);
EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(1).Extent(), 3);
EXPECT_EQ(result.type(), (TypeCode{TypeCategory::Integer, 4}));
static std::int32_t cshiftExpect2[6]{3, 6, 5, 2, 1, 4};
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(
*result.ZeroBasedIndexedElement<std::int32_t>(j), cshiftExpect2[j]);
}
result.Destroy();
// VECTOR 1 3 5 2 4 6
auto vector{MakeArray<TypeCategory::Integer, 4>(
std::vector<int>{6}, std::vector<std::int32_t>{1, 2, 3, 4, 5, 6})};
vector->GetDimension(0).SetLowerBound(0);
StaticDescriptor<1, true> vectorDesc;
Descriptor &vectorResult{vectorDesc.descriptor()};
RTNAME(CshiftVector)(vectorResult, *vector, 2, __FILE__, __LINE__);
EXPECT_EQ(vectorResult.type(), array->type());
EXPECT_EQ(vectorResult.rank(), 1);
EXPECT_EQ(vectorResult.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(vectorResult.GetDimension(0).Extent(), 6);
EXPECT_EQ(vectorResult.type(), (TypeCode{TypeCategory::Integer, 4}));
static std::int32_t cshiftExpect3[6]{3, 4, 5, 6, 1, 2};
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(*vectorResult.ZeroBasedIndexedElement<std::int32_t>(j),
cshiftExpect3[j]);
}
vectorResult.Destroy();
RTNAME(CshiftVector)(vectorResult, *vector, -2, __FILE__, __LINE__);
EXPECT_EQ(vectorResult.type(), array->type());
EXPECT_EQ(vectorResult.rank(), 1);
EXPECT_EQ(vectorResult.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(vectorResult.GetDimension(0).Extent(), 6);
EXPECT_EQ(vectorResult.type(), (TypeCode{TypeCategory::Integer, 4}));
static std::int32_t cshiftExpect4[6]{5, 6, 1, 2, 3, 4};
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(*vectorResult.ZeroBasedIndexedElement<std::int32_t>(j),
cshiftExpect4[j]);
}
vectorResult.Destroy();
// VECTOR 1 3 5 2 4 6 WITH non zero lower bound in a negative cshift.
auto vectorWithLowerBounds{MakeArray<TypeCategory::Integer, 4>(
std::vector<int>{6}, std::vector<std::int32_t>{1, 2, 3, 4, 5, 6})};
vectorWithLowerBounds->GetDimension(0).SetLowerBound(2);
RTNAME(CshiftVector)
(vectorResult, *vectorWithLowerBounds, -2, __FILE__, __LINE__);
EXPECT_EQ(vectorResult.type(), array->type());
EXPECT_EQ(vectorResult.rank(), 1);
EXPECT_EQ(vectorResult.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(vectorResult.GetDimension(0).Extent(), 6);
EXPECT_EQ(vectorResult.type(), (TypeCode{TypeCategory::Integer, 4}));
static std::int32_t cshiftExpect5[6]{5, 6, 1, 2, 3, 4};
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(*vectorResult.ZeroBasedIndexedElement<std::int32_t>(j),
cshiftExpect5[j]);
}
vectorResult.Destroy();
auto boundary{MakeArray<TypeCategory::Integer, 4>(
std::vector<int>{3}, std::vector<std::int32_t>{-1, -2, -3})};
boundary->GetDimension(0).SetLowerBound(9); // shouldn't matter
RTNAME(Eoshift)(result, *array, *shift3, &*boundary, 1, __FILE__, __LINE__);
EXPECT_EQ(result.type(), array->type());
EXPECT_EQ(result.rank(), 2);
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), 2);
EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(1).Extent(), 3);
EXPECT_EQ(result.type(), (TypeCode{TypeCategory::Integer, 4}));
static std::int32_t eoshiftExpect1[6]{2, -1, -2, 3, -3, -3};
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(
*result.ZeroBasedIndexedElement<std::int32_t>(j), eoshiftExpect1[j]);
}
result.Destroy();
// VECTOR EOSHIFT
StaticDescriptor<0> boundaryDescriptor;
Descriptor vectorBoundary{boundaryDescriptor.descriptor()};
std::int32_t boundaryValue{343};
vectorBoundary.Establish(TypeCategory::Integer, 4,
const_cast<void *>(reinterpret_cast<const void *>(&boundaryValue)), 0);
RTNAME(EoshiftVector)
(vectorResult, *vector, 2, &vectorBoundary, __FILE__, __LINE__);
EXPECT_EQ(vectorResult.type(), array->type());
EXPECT_EQ(vectorResult.rank(), 1);
EXPECT_EQ(vectorResult.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(vectorResult.GetDimension(0).Extent(), 6);
EXPECT_EQ(vectorResult.type(), (TypeCode{TypeCategory::Integer, 4}));
static std::int32_t eoshiftVectorExpect[6]{3, 4, 5, 6, 343, 343};
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(*vectorResult.ZeroBasedIndexedElement<std::int32_t>(j),
eoshiftVectorExpect[j]);
}
vectorResult.Destroy();
// VECTOR EOSHIFT on input with non zero lower bounds
RTNAME(EoshiftVector)
(vectorResult, *vectorWithLowerBounds, -2, &vectorBoundary, __FILE__,
__LINE__);
EXPECT_EQ(vectorResult.type(), array->type());
EXPECT_EQ(vectorResult.rank(), 1);
EXPECT_EQ(vectorResult.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(vectorResult.GetDimension(0).Extent(), 6);
EXPECT_EQ(vectorResult.type(), (TypeCode{TypeCategory::Integer, 4}));
static std::int32_t eoshiftVectorExpect2[6]{343, 343, 1, 2, 3, 4};
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(*vectorResult.ZeroBasedIndexedElement<std::int32_t>(j),
eoshiftVectorExpect2[j]);
}
vectorResult.Destroy();
}
TEST(Transformational, Pack) {
// ARRAY 1 3 5
// 2 4 6
auto array{MakeArray<TypeCategory::Integer, 4>(
std::vector<int>{2, 3}, std::vector<std::int32_t>{1, 2, 3, 4, 5, 6})};
array->GetDimension(0).SetLowerBound(2); // shouldn't matter
array->GetDimension(1).SetLowerBound(-1);
auto mask{MakeArray<TypeCategory::Logical, 1>(std::vector<int>{2, 3},
std::vector<std::uint8_t>{false, true, true, false, false, true})};
mask->GetDimension(0).SetLowerBound(0); // shouldn't matter
mask->GetDimension(1).SetLowerBound(2);
StaticDescriptor<1, true> statDesc;
Descriptor &result{statDesc.descriptor()};
RTNAME(Pack)(result, *array, *mask, nullptr, __FILE__, __LINE__);
EXPECT_EQ(result.type(), array->type());
EXPECT_EQ(result.rank(), 1);
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), 3);
static std::int32_t packExpect1[3]{2, 3, 6};
for (int j{0}; j < 3; ++j) {
EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), packExpect1[j])
<< " at " << j;
}
result.Destroy();
auto vector{MakeArray<TypeCategory::Integer, 4>(
std::vector<int>{5}, std::vector<std::int32_t>{-1, -2, -3, -4, -5})};
RTNAME(Pack)(result, *array, *mask, &*vector, __FILE__, __LINE__);
EXPECT_EQ(result.type(), array->type());
EXPECT_EQ(result.rank(), 1);
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), 5);
static std::int32_t packExpect2[5]{2, 3, 6, -4, -5};
for (int j{0}; j < 5; ++j) {
EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), packExpect2[j])
<< " at " << j;
}
result.Destroy();
}
TEST(Transformational, Spread) {
auto array{MakeArray<TypeCategory::Integer, 4>(
std::vector<int>{3}, std::vector<std::int32_t>{1, 2, 3})};
array->GetDimension(0).SetLowerBound(2); // shouldn't matter
StaticDescriptor<2, true> statDesc;
Descriptor &result{statDesc.descriptor()};
RTNAME(Spread)(result, *array, 1, 2, __FILE__, __LINE__);
EXPECT_EQ(result.type(), array->type());
EXPECT_EQ(result.rank(), 2);
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), 2);
EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(1).Extent(), 3);
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), 1 + j / 2);
}
result.Destroy();
RTNAME(Spread)(result, *array, 2, 2, __FILE__, __LINE__);
EXPECT_EQ(result.type(), array->type());
EXPECT_EQ(result.rank(), 2);
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), 3);
EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(1).Extent(), 2);
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), 1 + j % 3);
}
result.Destroy();
auto scalar{MakeArray<TypeCategory::Integer, 4>(
std::vector<int>{}, std::vector<std::int32_t>{1})};
RTNAME(Spread)(result, *scalar, 1, 2, __FILE__, __LINE__);
EXPECT_EQ(result.type(), array->type());
EXPECT_EQ(result.rank(), 1);
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), 2);
for (int j{0}; j < 2; ++j) {
EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), 1);
}
result.Destroy();
}
TEST(Transformational, Transpose) {
// ARRAY 1 3 5
// 2 4 6
auto array{MakeArray<TypeCategory::Integer, 4>(
std::vector<int>{2, 3}, std::vector<std::int32_t>{1, 2, 3, 4, 5, 6})};
array->GetDimension(0).SetLowerBound(2); // shouldn't matter
array->GetDimension(1).SetLowerBound(-6);
StaticDescriptor<2, true> statDesc;
Descriptor &result{statDesc.descriptor()};
RTNAME(Transpose)(result, *array, __FILE__, __LINE__);
EXPECT_EQ(result.type(), array->type());
EXPECT_EQ(result.rank(), 2);
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), 3);
EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(1).Extent(), 2);
static std::int32_t expect[6]{1, 3, 5, 2, 4, 6};
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), expect[j]);
}
result.Destroy();
}
TEST(Transformational, Unpack) {
auto vector{MakeArray<TypeCategory::Integer, 4>(
std::vector<int>{4}, std::vector<std::int32_t>{1, 2, 3, 4})};
vector->GetDimension(0).SetLowerBound(2); // shouldn't matter
auto mask{MakeArray<TypeCategory::Logical, 1>(std::vector<int>{2, 3},
std::vector<std::uint8_t>{false, true, true, false, false, true})};
mask->GetDimension(0).SetLowerBound(0); // shouldn't matter
mask->GetDimension(1).SetLowerBound(2);
auto field{MakeArray<TypeCategory::Integer, 4>(std::vector<int>{2, 3},
std::vector<std::int32_t>{-1, -2, -3, -4, -5, -6})};
field->GetDimension(0).SetLowerBound(-1); // shouldn't matter
StaticDescriptor<2, true> statDesc;
Descriptor &result{statDesc.descriptor()};
RTNAME(Unpack)(result, *vector, *mask, *field, __FILE__, __LINE__);
EXPECT_EQ(result.type(), vector->type());
EXPECT_EQ(result.rank(), 2);
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), 2);
EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(1).Extent(), 3);
static std::int32_t expect[6]{-1, 1, 2, -4, -5, 3};
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), expect[j]);
}
result.Destroy();
// Test for scalar value of the "field" argument
auto scalarField{MakeArray<TypeCategory::Integer, 4>(
std::vector<int>{}, std::vector<std::int32_t>{343})};
RTNAME(Unpack)(result, *vector, *mask, *scalarField, __FILE__, __LINE__);
EXPECT_EQ(result.rank(), 2);
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), 2);
EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(1).Extent(), 3);
static std::int32_t scalarExpect[6]{343, 1, 2, 343, 343, 3};
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(
*result.ZeroBasedIndexedElement<std::int32_t>(j), scalarExpect[j]);
}
result.Destroy();
}
#if LDBL_MANT_DIG == 64
// Make sure the destination descriptor is created by the runtime
// with proper element size, when REAL*10 maps to 'long double'.
#define Real10CppType long double
TEST(Transformational, TransposeReal10) {
// ARRAY 1 3 5
// 2 4 6
auto array{MakeArray<TypeCategory::Real, 10>(std::vector<int>{2, 3},
std::vector<Real10CppType>{1.0, 2.0, 3.0, 4.0, 5.0, 6.0},
sizeof(Real10CppType))};
StaticDescriptor<2, true> statDesc;
Descriptor &result{statDesc.descriptor()};
RTNAME(Transpose)(result, *array, __FILE__, __LINE__);
EXPECT_EQ(result.ElementBytes(), sizeof(Real10CppType));
EXPECT_EQ(result.type(), array->type());
EXPECT_EQ(result.rank(), 2);
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), 3);
EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(1).Extent(), 2);
static Real10CppType expect[6]{1.0, 3.0, 5.0, 2.0, 4.0, 6.0};
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(*result.ZeroBasedIndexedElement<Real10CppType>(j), expect[j]);
}
result.Destroy();
}
#endif