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