1 //===--- flang/unittests/Runtime/TemporaryStack.cpp -------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "gtest/gtest.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"
19 using namespace Fortran::runtime
;
21 // true if two descriptors are otherwise identical, except for different data
22 // pointers. The pointed-to elements are bit for bit identical.
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();
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);
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);
38 const DescriptorAddendum
*lhsAdd
= lhs
.Addendum();
39 const DescriptorAddendum
*rhsAdd
= rhs
.Addendum();
41 ASSERT_NE(rhsAdd
, nullptr);
42 ASSERT_EQ(lhsAdd
->SizeInBytes() == rhsAdd
->SizeInBytes(), true);
43 ASSERT_EQ(memcmp(lhsAdd
, rhsAdd
, lhsAdd
->SizeInBytes()) == 0, true);
45 ASSERT_EQ(rhsAdd
, nullptr);
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};
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
);
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
) {
69 void *storage
= RTNAME(CreateValueStack
)(__FILE__
, __LINE__
);
70 ASSERT_NE(storage
, nullptr);
72 RTNAME(PushValue
)(storage
, inputDesc
);
74 RTNAME(ValueAt
)(storage
, 0, outputDesc
);
75 descriptorAlmostEqual(inputDesc
, outputDesc
);
77 RTNAME(PopValue
)(storage
, outputDesc2
);
78 descriptorAlmostEqual(inputDesc
, outputDesc2
);
80 RTNAME(DestroyValueStack
)(storage
);
83 static unsigned max(unsigned x
, unsigned y
) {
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
];
96 std::vector
<OwningPtr
<Descriptor
>> inputDescriptors
;
97 inputDescriptors
.reserve(numToTest
);
99 void *storage
= RTNAME(CreateValueStack
)(__FILE__
, __LINE__
);
100 ASSERT_NE(storage
, nullptr);
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); };
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;
115 const OwningPtr
<Descriptor
> &desc
=
116 inputDescriptors
.emplace_back(Descriptor::Create(code
, elementBytes
,
117 nullptr, rank
, extent
, CFI_attribute_allocatable
, adendum
));
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
;
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);
135 RTNAME(PushValue
)(storage
, *desc
.get());
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
);
145 // buffer to return the descriptor into
146 OwningPtr
<Descriptor
> out
= Descriptor::Create(
147 boolCode
, 1, nullptr, rank
, extent
, CFI_attribute_other
, adendum
);
150 RTNAME(ValueAt
)(storage
, i
, *out
.get());
151 descriptorAlmostEqual(*input
, *out
);
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);
160 // buffer to return the descriptor into
161 OwningPtr
<Descriptor
> out
= Descriptor::Create(
162 boolCode
, 1, nullptr, rank
, extent
, CFI_attribute_other
, adendum
);
164 RTNAME(PopValue
)(storage
, *out
.get());
165 descriptorAlmostEqual(*input
, *out
);
168 RTNAME(DestroyValueStack
)(storage
);
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};
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
);
184 void *storage
= RTNAME(CreateDescriptorStack
)(__FILE__
, __LINE__
);
185 ASSERT_NE(storage
, nullptr);
187 RTNAME(PushDescriptor
)(storage
, inputDesc
);
189 RTNAME(DescriptorAt
)(storage
, 0, outputDesc
);
191 memcmp(&inputDesc
, &outputDesc
, testDescriptorStorage
[0].byteSize
), 0);
193 RTNAME(PopDescriptor
)(storage
, outputDesc2
);
195 memcmp(&inputDesc
, &outputDesc2
, testDescriptorStorage
[0].byteSize
), 0);
197 RTNAME(DestroyDescriptorStack
)(storage
);
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
];
207 std::vector
<OwningPtr
<Descriptor
>> inputDescriptors
;
208 inputDescriptors
.reserve(numToTest
);
210 void *storage
= RTNAME(CreateDescriptorStack
)(__FILE__
, __LINE__
);
211 ASSERT_NE(storage
, nullptr);
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); };
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);
227 void *const ptr
= reinterpret_cast<void *>(ptrBase
+ i
* elementBytes
);
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());
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
);
242 // buffer to return the descriptor into
243 OwningPtr
<Descriptor
> out
= Descriptor::Create(
244 intCode
, 1, nullptr, rank
, extent
, CFI_attribute_other
, adendum
);
246 RTNAME(DescriptorAt
)(storage
, i
, *out
.get());
247 ASSERT_EQ(memcmp(input
.get(), out
.get(), input
->SizeInBytes()), 0);
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);
256 // buffer to return the descriptor into
257 OwningPtr
<Descriptor
> out
= Descriptor::Create(
258 intCode
, 1, nullptr, rank
, extent
, CFI_attribute_other
, adendum
);
260 RTNAME(PopDescriptor
)(storage
, *out
.get());
261 ASSERT_EQ(memcmp(input
.get(), out
.get(), input
->SizeInBytes()), 0);
264 RTNAME(DestroyDescriptorStack
)(storage
);