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
19using 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.
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();
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 adendums
103 auto getAdendum = [](unsigned i) { return i % 2; };
104 // create descriptors with varying ranks
105 auto getRank = [](unsigned i) { return max(x: i % 8, y: 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
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 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(x: i - dim, y: 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

source code of flang/unittests/Runtime/TemporaryStack.cpp