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