//===--- flang/unittests/Runtime/TemporaryStack.cpp -------------*- C++ -*-===// // // 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 "gtest/gtest.h" #include "tools.h" #include "flang/ISO_Fortran_binding_wrapper.h" #include "flang/Runtime/allocatable.h" #include "flang/Runtime/cpp-type.h" #include "flang/Runtime/descriptor.h" #include "flang/Runtime/temporary-stack.h" #include "flang/Runtime/type-code.h" #include using namespace Fortran::runtime; // true if two descriptors are otherwise identical, except for different data // pointers. The pointed-to elements are bit for bit identical. static void descriptorAlmostEqual( const Descriptor &lhs, const Descriptor &rhs) { const Fortran::ISO::CFI_cdesc_t &lhsRaw = lhs.raw(); const Fortran::ISO::CFI_cdesc_t &rhsRaw = rhs.raw(); ASSERT_EQ(lhs.ElementBytes() == rhs.ElementBytes(), true); ASSERT_EQ(lhsRaw.version == rhsRaw.version, true); ASSERT_EQ(lhs.rank() == rhs.rank(), true); ASSERT_EQ(lhs.type() == rhs.type(), true); ASSERT_EQ(lhsRaw.attribute == rhsRaw.attribute, true); ASSERT_EQ(memcmp(lhsRaw.dim, rhsRaw.dim, lhs.rank()) == 0, true); const std::size_t bytes = lhs.Elements() * lhs.ElementBytes(); ASSERT_EQ(memcmp(lhsRaw.base_addr, rhsRaw.base_addr, bytes) == 0, true); const DescriptorAddendum *lhsAdd = lhs.Addendum(); const DescriptorAddendum *rhsAdd = rhs.Addendum(); if (lhsAdd) { ASSERT_NE(rhsAdd, nullptr); ASSERT_EQ(lhsAdd->SizeInBytes() == rhsAdd->SizeInBytes(), true); ASSERT_EQ(memcmp(lhsAdd, rhsAdd, lhsAdd->SizeInBytes()) == 0, true); } else { ASSERT_EQ(rhsAdd, nullptr); } } TEST(TemporaryStack, ValueStackBasic) { const TypeCode code{CFI_type_int32_t}; constexpr size_t elementBytes = 4; constexpr size_t rank = 2; void *const descriptorPtr = reinterpret_cast(0xdeadbeef); const SubscriptValue extent[rank]{42, 24}; StaticDescriptor testDescriptorStorage[3]; Descriptor &inputDesc{testDescriptorStorage[0].descriptor()}; Descriptor &outputDesc{testDescriptorStorage[1].descriptor()}; Descriptor &outputDesc2{testDescriptorStorage[2].descriptor()}; inputDesc.Establish(code, elementBytes, descriptorPtr, rank, extent); inputDesc.Allocate(); ASSERT_EQ(inputDesc.IsAllocated(), true); uint32_t *inputData = static_cast(inputDesc.raw().base_addr); for (std::size_t i = 0; i < inputDesc.Elements(); ++i) { inputData[i] = i; } void *storage = RTNAME(CreateValueStack)(__FILE__, __LINE__); ASSERT_NE(storage, nullptr); RTNAME(PushValue)(storage, inputDesc); RTNAME(ValueAt)(storage, 0, outputDesc); descriptorAlmostEqual(inputDesc, outputDesc); RTNAME(PopValue)(storage, outputDesc2); descriptorAlmostEqual(inputDesc, outputDesc2); RTNAME(DestroyValueStack)(storage); } static unsigned max(unsigned x, unsigned y) { if (x > y) { return x; } return y; } TEST(TemporaryStack, ValueStackMultiSize) { constexpr unsigned numToTest = 42; const TypeCode code{CFI_type_int32_t}; constexpr size_t elementBytes = 4; SubscriptValue extent[CFI_MAX_RANK]; std::vector> inputDescriptors; inputDescriptors.reserve(numToTest); void *storage = RTNAME(CreateValueStack)(__FILE__, __LINE__); ASSERT_NE(storage, nullptr); // create descriptors with and without adendums auto getAdendum = [](unsigned i) { return i % 2; }; // create descriptors with varying ranks auto getRank = [](unsigned i) { return max(i % 8, 1); }; // push descriptors of varying sizes and contents for (unsigned i = 0; i < numToTest; ++i) { const bool adendum = getAdendum(i); const size_t rank = getRank(i); for (unsigned dim = 0; dim < rank; ++dim) { extent[dim] = ((i + dim) % 8) + 1; } const OwningPtr &desc = inputDescriptors.emplace_back(Descriptor::Create(code, elementBytes, nullptr, rank, extent, CFI_attribute_allocatable, adendum)); // Descriptor::Establish doesn't initialise the extents if baseaddr is null for (unsigned dim = 0; dim < rank; ++dim) { Fortran::ISO::CFI_dim_t &boxDims = desc->raw().dim[dim]; boxDims.lower_bound = 1; boxDims.extent = extent[dim]; boxDims.sm = elementBytes; } desc->Allocate(); // fill the array with some data to test for (uint32_t i = 0; i < desc->Elements(); ++i) { uint32_t *data = static_cast(desc->raw().base_addr); ASSERT_NE(data, nullptr); data[i] = i; } RTNAME(PushValue)(storage, *desc.get()); } const TypeCode boolCode{CFI_type_Bool}; // peek and test each descriptor for (unsigned i = 0; i < numToTest; ++i) { const OwningPtr &input = inputDescriptors[i]; const bool adendum = getAdendum(i); const size_t rank = getRank(i); // buffer to return the descriptor into OwningPtr out = Descriptor::Create( boolCode, 1, nullptr, rank, extent, CFI_attribute_other, adendum); (void)input; RTNAME(ValueAt)(storage, i, *out.get()); descriptorAlmostEqual(*input, *out); } // pop and test each descriptor for (unsigned i = numToTest; i > 0; --i) { const OwningPtr &input = inputDescriptors[i - 1]; const bool adendum = getAdendum(i - 1); const size_t rank = getRank(i - 1); // buffer to return the descriptor into OwningPtr out = Descriptor::Create( boolCode, 1, nullptr, rank, extent, CFI_attribute_other, adendum); RTNAME(PopValue)(storage, *out.get()); descriptorAlmostEqual(*input, *out); } RTNAME(DestroyValueStack)(storage); } TEST(TemporaryStack, DescriptorStackBasic) { const TypeCode code{CFI_type_Bool}; constexpr size_t elementBytes = 4; constexpr size_t rank = 2; void *const descriptorPtr = reinterpret_cast(0xdeadbeef); const SubscriptValue extent[rank]{42, 24}; StaticDescriptor testDescriptorStorage[3]; Descriptor &inputDesc{testDescriptorStorage[0].descriptor()}; Descriptor &outputDesc{testDescriptorStorage[1].descriptor()}; Descriptor &outputDesc2{testDescriptorStorage[2].descriptor()}; inputDesc.Establish(code, elementBytes, descriptorPtr, rank, extent); void *storage = RTNAME(CreateDescriptorStack)(__FILE__, __LINE__); ASSERT_NE(storage, nullptr); RTNAME(PushDescriptor)(storage, inputDesc); RTNAME(DescriptorAt)(storage, 0, outputDesc); ASSERT_EQ( memcmp(&inputDesc, &outputDesc, testDescriptorStorage[0].byteSize), 0); RTNAME(PopDescriptor)(storage, outputDesc2); ASSERT_EQ( memcmp(&inputDesc, &outputDesc2, testDescriptorStorage[0].byteSize), 0); RTNAME(DestroyDescriptorStack)(storage); } TEST(TemporaryStack, DescriptorStackMultiSize) { constexpr unsigned numToTest = 42; const TypeCode code{CFI_type_Bool}; constexpr size_t elementBytes = 4; const uintptr_t ptrBase = 0xdeadbeef; SubscriptValue extent[CFI_MAX_RANK]; std::vector> inputDescriptors; inputDescriptors.reserve(numToTest); void *storage = RTNAME(CreateDescriptorStack)(__FILE__, __LINE__); ASSERT_NE(storage, nullptr); // create descriptors with and without adendums auto getAdendum = [](unsigned i) { return i % 2; }; // create descriptors with varying ranks auto getRank = [](unsigned i) { return max(i % CFI_MAX_RANK, 1); }; // push descriptors of varying sizes and contents for (unsigned i = 0; i < numToTest; ++i) { const bool adendum = getAdendum(i); const size_t rank = getRank(i); for (unsigned dim = 0; dim < rank; ++dim) { extent[dim] = max(i - dim, 1); } // varying pointers void *const ptr = reinterpret_cast(ptrBase + i * elementBytes); const OwningPtr &desc = inputDescriptors.emplace_back(Descriptor::Create(code, elementBytes, ptr, rank, extent, CFI_attribute_other, adendum)); RTNAME(PushDescriptor)(storage, *desc.get()); } const TypeCode intCode{CFI_type_int8_t}; // peek and test each descriptor for (unsigned i = 0; i < numToTest; ++i) { const OwningPtr &input = inputDescriptors[i]; const bool adendum = getAdendum(i); const size_t rank = getRank(i); // buffer to return the descriptor into OwningPtr out = Descriptor::Create( intCode, 1, nullptr, rank, extent, CFI_attribute_other, adendum); RTNAME(DescriptorAt)(storage, i, *out.get()); ASSERT_EQ(memcmp(input.get(), out.get(), input->SizeInBytes()), 0); } // pop and test each descriptor for (unsigned i = numToTest; i > 0; --i) { const OwningPtr &input = inputDescriptors[i - 1]; const bool adendum = getAdendum(i - 1); const size_t rank = getRank(i - 1); // buffer to return the descriptor into OwningPtr out = Descriptor::Create( intCode, 1, nullptr, rank, extent, CFI_attribute_other, adendum); RTNAME(PopDescriptor)(storage, *out.get()); ASSERT_EQ(memcmp(input.get(), out.get(), input->SizeInBytes()), 0); } RTNAME(DestroyDescriptorStack)(storage); }