xref: /llvm-project/flang/unittests/Runtime/TemporaryStack.cpp (revision ffc67bb3602a6a9a4f886af362e1f2d7c9821570)
1 //===--- flang/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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "gtest/gtest.h"
10 #include "tools.h"
11 #include "flang/ISO_Fortran_binding_wrapper.h"
12 #include "flang/Runtime/allocatable.h"
13 #include "flang/Runtime/cpp-type.h"
14 #include "flang/Runtime/descriptor.h"
15 #include "flang/Runtime/temporary-stack.h"
16 #include "flang/Runtime/type-code.h"
17 #include <vector>
18 
19 using namespace Fortran::runtime;
20 
21 // true if two descriptors are otherwise identical, except for different data
22 // pointers. The pointed-to elements are bit for bit identical.
descriptorAlmostEqual(const Descriptor & lhs,const Descriptor & rhs)23 static 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 
TEST(TemporaryStack,ValueStackBasic)49 TEST(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();
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 
max(unsigned x,unsigned y)83 static unsigned max(unsigned x, unsigned y) {
84   if (x > y) {
85     return x;
86   }
87   return y;
88 }
89 
TEST(TemporaryStack,ValueStackMultiSize)90 TEST(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 adendums
103   auto getAdendum = [](unsigned i) { return i % 2; };
104   // create descriptors with varying ranks
105   auto getRank = [](unsigned i) { return max(i % 8, 1); };
106 
107   // push descriptors of varying sizes and contents
108   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 null
120     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();
127 
128     // fill the array with some data to test
129     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 descriptor
140   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 into
146     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 descriptor
155   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 into
161     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 
TEST(TemporaryStack,DescriptorStackBasic)171 TEST(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 
TEST(TemporaryStack,DescriptorStackMultiSize)200 TEST(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 adendums
214   auto getAdendum = [](unsigned i) { return i % 2; };
215   // create descriptors with varying ranks
216   auto getRank = [](unsigned i) { return max(i % CFI_MAX_RANK, 1); };
217 
218   // push descriptors of varying sizes and contents
219   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 pointers
227     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 descriptor
237   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 into
243     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 descriptor
251   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 into
257     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