266 lines · cpp
1//===-- unittests/Runtime/TemporaryStack.cpp --------------------*- C++ -*-===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "gtest/gtest.h"10#include "flang-rt/runtime/descriptor.h"11#include "flang-rt/runtime/tools.h"12#include "flang-rt/runtime/type-code.h"13#include "flang/Common/ISO_Fortran_binding_wrapper.h"14#include "flang/Runtime/allocatable.h"15#include "flang/Runtime/cpp-type.h"16#include "flang/Runtime/temporary-stack.h"17#include <vector>18 19using namespace Fortran::runtime;20 21// true if two descriptors are otherwise identical, except for different data22// pointers. The pointed-to elements are bit for bit identical.23static void descriptorAlmostEqual(24 const Descriptor &lhs, const Descriptor &rhs) {25 const Fortran::ISO::CFI_cdesc_t &lhsRaw = lhs.raw();26 const Fortran::ISO::CFI_cdesc_t &rhsRaw = rhs.raw();27 28 ASSERT_EQ(lhs.ElementBytes() == rhs.ElementBytes(), true);29 ASSERT_EQ(lhsRaw.version == rhsRaw.version, true);30 ASSERT_EQ(lhs.rank() == rhs.rank(), true);31 ASSERT_EQ(lhs.type() == rhs.type(), true);32 ASSERT_EQ(lhsRaw.attribute == rhsRaw.attribute, true);33 34 ASSERT_EQ(memcmp(lhsRaw.dim, rhsRaw.dim, lhs.rank()) == 0, true);35 const std::size_t bytes = lhs.Elements() * lhs.ElementBytes();36 ASSERT_EQ(memcmp(lhsRaw.base_addr, rhsRaw.base_addr, bytes) == 0, true);37 38 const DescriptorAddendum *lhsAdd = lhs.Addendum();39 const DescriptorAddendum *rhsAdd = rhs.Addendum();40 if (lhsAdd) {41 ASSERT_NE(rhsAdd, nullptr);42 ASSERT_EQ(lhsAdd->SizeInBytes() == rhsAdd->SizeInBytes(), true);43 ASSERT_EQ(memcmp(lhsAdd, rhsAdd, lhsAdd->SizeInBytes()) == 0, true);44 } else {45 ASSERT_EQ(rhsAdd, nullptr);46 }47}48 49TEST(TemporaryStack, ValueStackBasic) {50 const TypeCode code{CFI_type_int32_t};51 constexpr size_t elementBytes = 4;52 constexpr size_t rank = 2;53 void *const descriptorPtr = reinterpret_cast<void *>(0xdeadbeef);54 const SubscriptValue extent[rank]{42, 24};55 56 StaticDescriptor<rank> testDescriptorStorage[3];57 Descriptor &inputDesc{testDescriptorStorage[0].descriptor()};58 Descriptor &outputDesc{testDescriptorStorage[1].descriptor()};59 Descriptor &outputDesc2{testDescriptorStorage[2].descriptor()};60 inputDesc.Establish(code, elementBytes, descriptorPtr, rank, extent);61 62 inputDesc.Allocate(kNoAsyncObject);63 ASSERT_EQ(inputDesc.IsAllocated(), true);64 uint32_t *inputData = static_cast<uint32_t *>(inputDesc.raw().base_addr);65 for (std::size_t i = 0; i < inputDesc.Elements(); ++i) {66 inputData[i] = i;67 }68 69 void *storage = RTNAME(CreateValueStack)(__FILE__, __LINE__);70 ASSERT_NE(storage, nullptr);71 72 RTNAME(PushValue)(storage, inputDesc);73 74 RTNAME(ValueAt)(storage, 0, outputDesc);75 descriptorAlmostEqual(inputDesc, outputDesc);76 77 RTNAME(PopValue)(storage, outputDesc2);78 descriptorAlmostEqual(inputDesc, outputDesc2);79 80 RTNAME(DestroyValueStack)(storage);81}82 83static unsigned max(unsigned x, unsigned y) {84 if (x > y) {85 return x;86 }87 return y;88}89 90TEST(TemporaryStack, ValueStackMultiSize) {91 constexpr unsigned numToTest = 42;92 const TypeCode code{CFI_type_int32_t};93 constexpr size_t elementBytes = 4;94 SubscriptValue extent[CFI_MAX_RANK];95 96 std::vector<OwningPtr<Descriptor>> inputDescriptors;97 inputDescriptors.reserve(numToTest);98 99 void *storage = RTNAME(CreateValueStack)(__FILE__, __LINE__);100 ASSERT_NE(storage, nullptr);101 102 // create descriptors with and without adendums103 auto getAdendum = [](unsigned i) { return i % 2; };104 // create descriptors with varying ranks105 auto getRank = [](unsigned i) { return max(i % 8, 1); };106 107 // push descriptors of varying sizes and contents108 for (unsigned i = 0; i < numToTest; ++i) {109 const bool adendum = getAdendum(i);110 const size_t rank = getRank(i);111 for (unsigned dim = 0; dim < rank; ++dim) {112 extent[dim] = ((i + dim) % 8) + 1;113 }114 115 const OwningPtr<Descriptor> &desc =116 inputDescriptors.emplace_back(Descriptor::Create(code, elementBytes,117 nullptr, rank, extent, CFI_attribute_allocatable, adendum));118 119 // Descriptor::Establish doesn't initialise the extents if baseaddr is null120 for (unsigned dim = 0; dim < rank; ++dim) {121 Fortran::ISO::CFI_dim_t &boxDims = desc->raw().dim[dim];122 boxDims.lower_bound = 1;123 boxDims.extent = extent[dim];124 boxDims.sm = elementBytes;125 }126 desc->Allocate(kNoAsyncObject);127 128 // fill the array with some data to test129 for (uint32_t i = 0; i < desc->Elements(); ++i) {130 uint32_t *data = static_cast<uint32_t *>(desc->raw().base_addr);131 ASSERT_NE(data, nullptr);132 data[i] = i;133 }134 135 RTNAME(PushValue)(storage, *desc.get());136 }137 138 const TypeCode boolCode{CFI_type_Bool};139 // peek and test each descriptor140 for (unsigned i = 0; i < numToTest; ++i) {141 const OwningPtr<Descriptor> &input = inputDescriptors[i];142 const bool adendum = getAdendum(i);143 const size_t rank = getRank(i);144 145 // buffer to return the descriptor into146 OwningPtr<Descriptor> out = Descriptor::Create(147 boolCode, 1, nullptr, rank, extent, CFI_attribute_other, adendum);148 149 (void)input;150 RTNAME(ValueAt)(storage, i, *out.get());151 descriptorAlmostEqual(*input, *out);152 }153 154 // pop and test each descriptor155 for (unsigned i = numToTest; i > 0; --i) {156 const OwningPtr<Descriptor> &input = inputDescriptors[i - 1];157 const bool adendum = getAdendum(i - 1);158 const size_t rank = getRank(i - 1);159 160 // buffer to return the descriptor into161 OwningPtr<Descriptor> out = Descriptor::Create(162 boolCode, 1, nullptr, rank, extent, CFI_attribute_other, adendum);163 164 RTNAME(PopValue)(storage, *out.get());165 descriptorAlmostEqual(*input, *out);166 }167 168 RTNAME(DestroyValueStack)(storage);169}170 171TEST(TemporaryStack, DescriptorStackBasic) {172 const TypeCode code{CFI_type_Bool};173 constexpr size_t elementBytes = 4;174 constexpr size_t rank = 2;175 void *const descriptorPtr = reinterpret_cast<void *>(0xdeadbeef);176 const SubscriptValue extent[rank]{42, 24};177 178 StaticDescriptor<rank> testDescriptorStorage[3];179 Descriptor &inputDesc{testDescriptorStorage[0].descriptor()};180 Descriptor &outputDesc{testDescriptorStorage[1].descriptor()};181 Descriptor &outputDesc2{testDescriptorStorage[2].descriptor()};182 inputDesc.Establish(code, elementBytes, descriptorPtr, rank, extent);183 184 void *storage = RTNAME(CreateDescriptorStack)(__FILE__, __LINE__);185 ASSERT_NE(storage, nullptr);186 187 RTNAME(PushDescriptor)(storage, inputDesc);188 189 RTNAME(DescriptorAt)(storage, 0, outputDesc);190 ASSERT_EQ(191 memcmp(&inputDesc, &outputDesc, testDescriptorStorage[0].byteSize), 0);192 193 RTNAME(PopDescriptor)(storage, outputDesc2);194 ASSERT_EQ(195 memcmp(&inputDesc, &outputDesc2, testDescriptorStorage[0].byteSize), 0);196 197 RTNAME(DestroyDescriptorStack)(storage);198}199 200TEST(TemporaryStack, DescriptorStackMultiSize) {201 constexpr unsigned numToTest = 42;202 const TypeCode code{CFI_type_Bool};203 constexpr size_t elementBytes = 4;204 const uintptr_t ptrBase = 0xdeadbeef;205 SubscriptValue extent[CFI_MAX_RANK];206 207 std::vector<OwningPtr<Descriptor>> inputDescriptors;208 inputDescriptors.reserve(numToTest);209 210 void *storage = RTNAME(CreateDescriptorStack)(__FILE__, __LINE__);211 ASSERT_NE(storage, nullptr);212 213 // create descriptors with and without adendums214 auto getAdendum = [](unsigned i) { return i % 2; };215 // create descriptors with varying ranks216 auto getRank = [](unsigned i) { return max(i % CFI_MAX_RANK, 1); };217 218 // push descriptors of varying sizes and contents219 for (unsigned i = 0; i < numToTest; ++i) {220 const bool adendum = getAdendum(i);221 const size_t rank = getRank(i);222 for (unsigned dim = 0; dim < rank; ++dim) {223 extent[dim] = max(i - dim, 1);224 }225 226 // varying pointers227 void *const ptr = reinterpret_cast<void *>(ptrBase + i * elementBytes);228 229 const OwningPtr<Descriptor> &desc =230 inputDescriptors.emplace_back(Descriptor::Create(code, elementBytes,231 ptr, rank, extent, CFI_attribute_other, adendum));232 RTNAME(PushDescriptor)(storage, *desc.get());233 }234 235 const TypeCode intCode{CFI_type_int8_t};236 // peek and test each descriptor237 for (unsigned i = 0; i < numToTest; ++i) {238 const OwningPtr<Descriptor> &input = inputDescriptors[i];239 const bool adendum = getAdendum(i);240 const size_t rank = getRank(i);241 242 // buffer to return the descriptor into243 OwningPtr<Descriptor> out = Descriptor::Create(244 intCode, 1, nullptr, rank, extent, CFI_attribute_other, adendum);245 246 RTNAME(DescriptorAt)(storage, i, *out.get());247 ASSERT_EQ(memcmp(input.get(), out.get(), input->SizeInBytes()), 0);248 }249 250 // pop and test each descriptor251 for (unsigned i = numToTest; i > 0; --i) {252 const OwningPtr<Descriptor> &input = inputDescriptors[i - 1];253 const bool adendum = getAdendum(i - 1);254 const size_t rank = getRank(i - 1);255 256 // buffer to return the descriptor into257 OwningPtr<Descriptor> out = Descriptor::Create(258 intCode, 1, nullptr, rank, extent, CFI_attribute_other, adendum);259 260 RTNAME(PopDescriptor)(storage, *out.get());261 ASSERT_EQ(memcmp(input.get(), out.get(), input->SizeInBytes()), 0);262 }263 264 RTNAME(DestroyDescriptorStack)(storage);265}266