1 | //===-- unittests/Evaluate/ISO-Fortran-binding.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 "flang-rt/runtime/descriptor.h" |
10 | #include "flang/Common/ISO_Fortran_binding_wrapper.h" |
11 | #include "flang/Testing/testing.h" |
12 | #include "llvm/Support/raw_ostream.h" |
13 | #include <type_traits> |
14 | |
15 | using namespace Fortran::runtime; |
16 | using namespace Fortran::ISO; |
17 | |
18 | // CFI_CDESC_T test helpers |
19 | template <int rank> class Test_CFI_CDESC_T { |
20 | public: |
21 | Test_CFI_CDESC_T() {} |
22 | ~Test_CFI_CDESC_T() {} |
23 | void Check() { |
24 | // Test CFI_CDESC_T macro defined in section 18.5.4 of F2018 standard |
25 | // CFI_CDESC_T must give storage that is: |
26 | using type = decltype(dvStorage_); |
27 | // unqualified |
28 | MATCH(false, std::is_const<type>::value); |
29 | MATCH(false, std::is_volatile<type>::value); |
30 | // suitable in size |
31 | if (rank > 0) { |
32 | MATCH(sizeof(dvStorage_), Descriptor::SizeInBytes(rank_, false)); |
33 | } else { // C++ implementation over-allocates for rank=0 by 24bytes. |
34 | MATCH(true, sizeof(dvStorage_) >= Descriptor::SizeInBytes(rank_, false)); |
35 | } |
36 | // suitable in alignment |
37 | MATCH(0, |
38 | reinterpret_cast<std::uintptr_t>(&dvStorage_) & |
39 | (alignof(CFI_cdesc_t) - 1)); |
40 | } |
41 | |
42 | private: |
43 | static constexpr int rank_{rank}; |
44 | CFI_CDESC_T(rank) dvStorage_; |
45 | }; |
46 | |
47 | template <int rank> static void TestCdescMacroForAllRanksSmallerThan() { |
48 | static_assert(rank > 0, "rank<0!" ); |
49 | Test_CFI_CDESC_T<rank> obj; |
50 | obj.Check(); |
51 | TestCdescMacroForAllRanksSmallerThan<rank - 1>(); |
52 | } |
53 | |
54 | template <> void TestCdescMacroForAllRanksSmallerThan<0>() { |
55 | Test_CFI_CDESC_T<0> obj; |
56 | obj.Check(); |
57 | } |
58 | |
59 | // CFI_establish test helper |
60 | static void AddNoiseToCdesc(CFI_cdesc_t *dv, CFI_rank_t rank) { |
61 | static const int trap{0}; |
62 | dv->rank = 16; |
63 | // This address is not supposed to be used. Any write attempt should trigger |
64 | // program termination |
65 | dv->base_addr = const_cast<int *>(&trap); |
66 | dv->elem_len = 320; |
67 | dv->type = CFI_type_struct; |
68 | dv->attribute = CFI_attribute_pointer; |
69 | for (int i{0}; i < rank; i++) { |
70 | dv->dim[i].extent = -42; |
71 | dv->dim[i].lower_bound = -42; |
72 | dv->dim[i].sm = -42; |
73 | } |
74 | } |
75 | |
76 | #ifdef VERBOSE |
77 | static void DumpTestWorld(const void *bAddr, CFI_attribute_t attr, |
78 | CFI_type_t ty, std::size_t eLen, CFI_rank_t rank, |
79 | const CFI_index_t *eAddr) { |
80 | llvm::outs() << " base_addr: " ; |
81 | llvm::outs().write_hex(reinterpret_cast<std::intptr_t>(bAddr)) |
82 | << " attribute: " << static_cast<int>(attr) |
83 | << " type: " << static_cast<int>(ty) << " elem_len: " << eLen |
84 | << " rank: " << static_cast<int>(rank) << " extent: " ; |
85 | llvm::outs().write_hex(reinterpret_cast<std::intptr_t>(eAddr)) << '\n'; |
86 | llvm::outs().flush(); |
87 | } |
88 | #endif |
89 | |
90 | static void check_CFI_establish(CFI_cdesc_t *dv, void *base_addr, |
91 | CFI_attribute_t attribute, CFI_type_t type, std::size_t elem_len, |
92 | CFI_rank_t rank, const CFI_index_t extents[]) { |
93 | #ifdef VERBOSE |
94 | DumpTestWorld(base_addr, attribute, type, elem_len, rank, extent); |
95 | #endif |
96 | // CFI_establish reqs from F2018 section 18.5.5 |
97 | int retCode{ |
98 | CFI_establish(dv, base_addr, attribute, type, elem_len, rank, extents)}; |
99 | Descriptor *res{reinterpret_cast<Descriptor *>(dv)}; |
100 | if (retCode == CFI_SUCCESS) { |
101 | res->Check(); |
102 | MATCH((attribute == CFI_attribute_pointer), res->IsPointer()); |
103 | MATCH((attribute == CFI_attribute_allocatable), res->IsAllocatable()); |
104 | MATCH(rank, res->rank()); |
105 | MATCH(reinterpret_cast<std::intptr_t>(dv->base_addr), |
106 | reinterpret_cast<std::intptr_t>(base_addr)); |
107 | MATCH(true, dv->version == CFI_VERSION); |
108 | if (base_addr != nullptr) { |
109 | MATCH(true, res->IsContiguous()); |
110 | for (int i{0}; i < rank; ++i) { |
111 | MATCH(extents[i], res->GetDimension(i).Extent()); |
112 | } |
113 | } |
114 | if (attribute == CFI_attribute_allocatable) { |
115 | MATCH(res->IsAllocated(), false); |
116 | } |
117 | if (attribute == CFI_attribute_pointer) { |
118 | if (base_addr != nullptr) { |
119 | for (int i{0}; i < rank; ++i) { |
120 | MATCH(0, res->GetDimension(i).LowerBound()); |
121 | } |
122 | } |
123 | } |
124 | if (type == CFI_type_struct || type == CFI_type_char || |
125 | type == CFI_type_char16_t || type == CFI_type_char32_t || |
126 | type == CFI_type_other) { |
127 | MATCH(elem_len, res->ElementBytes()); |
128 | } |
129 | } |
130 | // Checking failure/success according to combination of args forbidden by the |
131 | // standard: |
132 | int numErr{0}; |
133 | int expectedRetCode{CFI_SUCCESS}; |
134 | if (base_addr != nullptr && attribute == CFI_attribute_allocatable) { |
135 | ++numErr; |
136 | expectedRetCode = CFI_ERROR_BASE_ADDR_NOT_NULL; |
137 | } |
138 | if (rank > CFI_MAX_RANK) { |
139 | ++numErr; |
140 | expectedRetCode = CFI_INVALID_RANK; |
141 | } |
142 | if (type < 0 || type > CFI_TYPE_LAST) { |
143 | ++numErr; |
144 | expectedRetCode = CFI_INVALID_TYPE; |
145 | } |
146 | |
147 | if ((type == CFI_type_struct || type == CFI_type_char || |
148 | type == CFI_type_char16_t || type == CFI_type_char32_t || |
149 | type == CFI_type_other) && |
150 | elem_len <= 0) { |
151 | ++numErr; |
152 | expectedRetCode = CFI_INVALID_ELEM_LEN; |
153 | } |
154 | if (rank > 0 && base_addr != nullptr && extents == nullptr) { |
155 | ++numErr; |
156 | expectedRetCode = CFI_INVALID_EXTENT; |
157 | } |
158 | if (numErr > 1) { |
159 | MATCH(true, retCode != CFI_SUCCESS); |
160 | } else { |
161 | MATCH(retCode, expectedRetCode); |
162 | } |
163 | } |
164 | |
165 | static void run_CFI_establish_tests() { |
166 | // Testing CFI_establish defined in section 18.5.5 |
167 | CFI_index_t extents[CFI_MAX_RANK]; |
168 | for (int i{0}; i < CFI_MAX_RANK; ++i) { |
169 | extents[i] = i + 66; |
170 | } |
171 | CFI_CDESC_T(CFI_MAX_RANK) dv_storage; |
172 | CFI_cdesc_t *dv{&dv_storage}; |
173 | char base; |
174 | void *dummyAddr{&base}; |
175 | // Define test space |
176 | CFI_attribute_t attrCases[]{ |
177 | CFI_attribute_pointer, CFI_attribute_allocatable, CFI_attribute_other}; |
178 | CFI_type_t typeCases[]{CFI_type_int, CFI_type_struct, CFI_type_double, |
179 | CFI_type_char, CFI_type_char16_t, CFI_type_char32_t, CFI_type_other, |
180 | CFI_TYPE_LAST + 1}; |
181 | CFI_index_t *extentCases[]{extents, nullptr}; |
182 | void *baseAddrCases[]{dummyAddr, nullptr}; |
183 | CFI_rank_t rankCases[]{0, 1, CFI_MAX_RANK, CFI_MAX_RANK + 1}; |
184 | std::size_t lenCases[]{0, 42}; |
185 | |
186 | for (CFI_attribute_t attribute : attrCases) { |
187 | for (void *base_addr : baseAddrCases) { |
188 | for (CFI_index_t *extent : extentCases) { |
189 | for (CFI_rank_t rank : rankCases) { |
190 | for (CFI_type_t type : typeCases) { |
191 | for (size_t elem_len : lenCases) { |
192 | AddNoiseToCdesc(dv, CFI_MAX_RANK); |
193 | check_CFI_establish( |
194 | dv, base_addr, attribute, type, elem_len, rank, extent); |
195 | } |
196 | } |
197 | } |
198 | } |
199 | } |
200 | } |
201 | // If base_addr is null, extents shall be ignored even if rank !=0 |
202 | const int rank3d{3}; |
203 | CFI_CDESC_T(rank3d) dv3darrayStorage; |
204 | CFI_cdesc_t *dv_3darray{&dv3darrayStorage}; |
205 | AddNoiseToCdesc(dv_3darray, rank3d); // => dv_3darray->dim[2].extent = -42 |
206 | check_CFI_establish(dv_3darray, nullptr, CFI_attribute_other, CFI_type_int, 4, |
207 | rank3d, extents); |
208 | MATCH(false, |
209 | dv_3darray->dim[2].extent == 2 + 66); // extents was read |
210 | } |
211 | |
212 | static void check_CFI_address( |
213 | const CFI_cdesc_t *dv, const CFI_index_t subscripts[]) { |
214 | // 18.5.5.2 |
215 | void *addr{CFI_address(dv, subscripts)}; |
216 | const Descriptor *desc{reinterpret_cast<const Descriptor *>(dv)}; |
217 | void *addrCheck{desc->Element<void>(subscripts)}; |
218 | MATCH(true, addr == addrCheck); |
219 | } |
220 | |
221 | // Helper function to set lower bound of descriptor |
222 | static void EstablishLowerBounds(CFI_cdesc_t *dv, CFI_index_t *sub) { |
223 | for (int i{0}; i < dv->rank; ++i) { |
224 | dv->dim[i].lower_bound = sub[i]; |
225 | } |
226 | } |
227 | |
228 | // Helper to get size without making internal compiler functions accessible |
229 | static std::size_t ByteSize(CFI_type_t ty, std::size_t size) { |
230 | CFI_CDESC_T(0) storage; |
231 | CFI_cdesc_t *dv{&storage}; |
232 | int retCode{ |
233 | CFI_establish(dv, nullptr, CFI_attribute_other, ty, size, 0, nullptr)}; |
234 | return retCode == CFI_SUCCESS ? dv->elem_len : 0; |
235 | } |
236 | |
237 | static void run_CFI_address_tests() { |
238 | // Test CFI_address defined in 18.5.5.2 |
239 | // Create test world |
240 | CFI_index_t extents[CFI_MAX_RANK]; |
241 | CFI_CDESC_T(CFI_MAX_RANK) dv_storage; |
242 | CFI_cdesc_t *dv{&dv_storage}; |
243 | char base; |
244 | void *dummyAddr{&base}; |
245 | CFI_attribute_t attrCases[]{ |
246 | CFI_attribute_pointer, CFI_attribute_allocatable, CFI_attribute_other}; |
247 | CFI_type_t validTypeCases[]{ |
248 | CFI_type_int, CFI_type_struct, CFI_type_double, CFI_type_char}; |
249 | CFI_index_t subscripts[CFI_MAX_RANK]; |
250 | CFI_index_t negativeLowerBounds[CFI_MAX_RANK]; |
251 | CFI_index_t zeroLowerBounds[CFI_MAX_RANK]; |
252 | CFI_index_t positiveLowerBounds[CFI_MAX_RANK]; |
253 | CFI_index_t *lowerBoundCases[]{ |
254 | negativeLowerBounds, zeroLowerBounds, positiveLowerBounds}; |
255 | for (int i{0}; i < CFI_MAX_RANK; ++i) { |
256 | negativeLowerBounds[i] = -1; |
257 | zeroLowerBounds[i] = 0; |
258 | positiveLowerBounds[i] = 1; |
259 | extents[i] = i + 2; |
260 | subscripts[i] = i + 1; |
261 | } |
262 | |
263 | // test for scalar |
264 | for (CFI_attribute_t attribute : attrCases) { |
265 | for (CFI_type_t type : validTypeCases) { |
266 | CFI_establish(dv, dummyAddr, attribute, type, 42, 0, nullptr); |
267 | check_CFI_address(dv, nullptr); |
268 | } |
269 | } |
270 | // test for arrays |
271 | CFI_establish(dv, dummyAddr, CFI_attribute_other, CFI_type_int, 0, |
272 | CFI_MAX_RANK, extents); |
273 | for (CFI_index_t *lowerBounds : lowerBoundCases) { |
274 | EstablishLowerBounds(dv, lowerBounds); |
275 | for (CFI_type_t type : validTypeCases) { |
276 | for (bool contiguous : {true, false}) { |
277 | std::size_t size{ByteSize(type, 12)}; |
278 | dv->elem_len = size; |
279 | for (int i{0}; i < dv->rank; ++i) { |
280 | dv->dim[i].sm = size + (contiguous ? 0 : dv->elem_len); |
281 | size = dv->dim[i].sm * dv->dim[i].extent; |
282 | } |
283 | for (CFI_attribute_t attribute : attrCases) { |
284 | dv->attribute = attribute; |
285 | check_CFI_address(dv, subscripts); |
286 | } |
287 | } |
288 | } |
289 | } |
290 | // Test on an assumed size array. |
291 | CFI_establish( |
292 | dv, dummyAddr, CFI_attribute_other, CFI_type_int, 0, 3, extents); |
293 | dv->dim[2].extent = -1; |
294 | check_CFI_address(dv, subscripts); |
295 | } |
296 | |
297 | static void check_CFI_allocate(CFI_cdesc_t *dv, |
298 | const CFI_index_t lower_bounds[], const CFI_index_t upper_bounds[], |
299 | std::size_t elem_len) { |
300 | // 18.5.5.3 |
301 | // Backup descriptor data for future checks |
302 | const CFI_rank_t rank{dv->rank}; |
303 | const std::size_t desc_elem_len{dv->elem_len}; |
304 | const CFI_attribute_t attribute{dv->attribute}; |
305 | const CFI_type_t type{dv->type}; |
306 | const void *base_addr{dv->base_addr}; |
307 | const int version{dv->version}; |
308 | #ifdef VERBOSE |
309 | DumpTestWorld(base_addr, attribute, type, elem_len, rank, nullptr); |
310 | #endif |
311 | int retCode{CFI_allocate(dv, lower_bounds, upper_bounds, elem_len)}; |
312 | Descriptor *desc = reinterpret_cast<Descriptor *>(dv); |
313 | if (retCode == CFI_SUCCESS) { |
314 | // check res properties from 18.5.5.3 par 3 |
315 | MATCH(true, dv->base_addr != nullptr); |
316 | for (int i{0}; i < rank; ++i) { |
317 | MATCH(lower_bounds[i], dv->dim[i].lower_bound); |
318 | MATCH(upper_bounds[i], dv->dim[i].extent + dv->dim[i].lower_bound - 1); |
319 | } |
320 | if (type == CFI_type_char) { |
321 | MATCH(elem_len, dv->elem_len); |
322 | } else { |
323 | MATCH(true, desc_elem_len == dv->elem_len); |
324 | } |
325 | MATCH(true, desc->IsContiguous()); |
326 | } else { |
327 | MATCH(true, base_addr == dv->base_addr); |
328 | } |
329 | |
330 | // Below dv members shall not be altered by CFI_allocate regardless of |
331 | // success/failure |
332 | MATCH(true, attribute == dv->attribute); |
333 | MATCH(true, rank == dv->rank); |
334 | MATCH(true, type == dv->type); |
335 | MATCH(true, version == dv->version); |
336 | |
337 | // Success/failure according to standard |
338 | int numErr{0}; |
339 | int expectedRetCode{CFI_SUCCESS}; |
340 | if (rank > CFI_MAX_RANK) { |
341 | ++numErr; |
342 | expectedRetCode = CFI_INVALID_RANK; |
343 | } |
344 | if (type < 0 || type > CFI_TYPE_LAST) { |
345 | ++numErr; |
346 | expectedRetCode = CFI_INVALID_TYPE; |
347 | } |
348 | if (base_addr != nullptr && attribute == CFI_attribute_allocatable) { |
349 | // This is less restrictive than 18.5.5.3 arg req for which pointers arg |
350 | // shall be unassociated. However, this match ALLOCATE behavior |
351 | // (9.7.3/9.7.4) |
352 | ++numErr; |
353 | expectedRetCode = CFI_ERROR_BASE_ADDR_NOT_NULL; |
354 | } |
355 | if (attribute != CFI_attribute_pointer && |
356 | attribute != CFI_attribute_allocatable) { |
357 | ++numErr; |
358 | expectedRetCode = CFI_INVALID_ATTRIBUTE; |
359 | } |
360 | if (rank > 0 && (lower_bounds == nullptr || upper_bounds == nullptr)) { |
361 | ++numErr; |
362 | expectedRetCode = CFI_INVALID_EXTENT; |
363 | } |
364 | |
365 | // Memory allocation failures are unpredictable in this test. |
366 | if (numErr == 0 && retCode != CFI_SUCCESS) { |
367 | MATCH(true, retCode == CFI_ERROR_MEM_ALLOCATION); |
368 | } else if (numErr > 1) { |
369 | MATCH(true, retCode != CFI_SUCCESS); |
370 | } else { |
371 | MATCH(expectedRetCode, retCode); |
372 | } |
373 | // clean-up |
374 | if (retCode == CFI_SUCCESS) { |
375 | CFI_deallocate(dv); |
376 | } |
377 | } |
378 | |
379 | static void run_CFI_allocate_tests() { |
380 | // 18.5.5.3 |
381 | // create test world |
382 | CFI_CDESC_T(CFI_MAX_RANK) dv_storage; |
383 | CFI_cdesc_t *dv{&dv_storage}; |
384 | char base; |
385 | void *dummyAddr{&base}; |
386 | CFI_attribute_t attrCases[]{ |
387 | CFI_attribute_pointer, CFI_attribute_allocatable, CFI_attribute_other}; |
388 | CFI_type_t typeCases[]{CFI_type_int, CFI_type_struct, CFI_type_double, |
389 | CFI_type_char, CFI_type_other, CFI_TYPE_LAST + 1}; |
390 | void *baseAddrCases[]{dummyAddr, nullptr}; |
391 | CFI_rank_t rankCases[]{0, 1, CFI_MAX_RANK, CFI_MAX_RANK + 1}; |
392 | std::size_t lenCases[]{0, 42}; |
393 | CFI_index_t lb1[CFI_MAX_RANK]; |
394 | CFI_index_t ub1[CFI_MAX_RANK]; |
395 | for (int i{0}; i < CFI_MAX_RANK; ++i) { |
396 | lb1[i] = -1; |
397 | ub1[i] = 0; |
398 | } |
399 | |
400 | check_CFI_establish( |
401 | dv, nullptr, CFI_attribute_other, CFI_type_int, 0, 0, nullptr); |
402 | for (CFI_type_t type : typeCases) { |
403 | std::size_t ty_len{ByteSize(type, 12)}; |
404 | for (CFI_attribute_t attribute : attrCases) { |
405 | for (void *base_addr : baseAddrCases) { |
406 | for (CFI_rank_t rank : rankCases) { |
407 | for (size_t elem_len : lenCases) { |
408 | dv->base_addr = base_addr; |
409 | dv->rank = rank; |
410 | dv->attribute = attribute; |
411 | dv->type = type; |
412 | dv->elem_len = ty_len; |
413 | check_CFI_allocate(dv, lb1, ub1, elem_len); |
414 | } |
415 | } |
416 | } |
417 | } |
418 | } |
419 | } |
420 | |
421 | static void run_CFI_section_tests() { |
422 | // simple tests |
423 | bool testPreConditions{true}; |
424 | constexpr CFI_index_t m{5}, n{6}, o{7}; |
425 | constexpr CFI_rank_t rank{3}; |
426 | long long array[o][n][m]; // Fortran A(m,n,o) |
427 | long long counter{1}; |
428 | |
429 | for (CFI_index_t k{0}; k < o; ++k) { |
430 | for (CFI_index_t j{0}; j < n; ++j) { |
431 | for (CFI_index_t i{0}; i < m; ++i) { |
432 | array[k][j][i] = counter++; // Fortran A(i,j,k) |
433 | } |
434 | } |
435 | } |
436 | CFI_CDESC_T(rank) sourceStorage; |
437 | CFI_cdesc_t *source{&sourceStorage}; |
438 | CFI_index_t extent[rank] = {m, n, o}; |
439 | int retCode{CFI_establish(source, &array, CFI_attribute_other, |
440 | CFI_type_long_long, 0, rank, extent)}; |
441 | testPreConditions &= (retCode == CFI_SUCCESS); |
442 | |
443 | CFI_index_t lb[rank] = {2, 5, 4}; |
444 | CFI_index_t ub[rank] = {4, 5, 6}; |
445 | CFI_index_t strides[rank] = {2, 0, 2}; |
446 | constexpr CFI_rank_t resultRank{rank - 1}; |
447 | |
448 | CFI_CDESC_T(resultRank) resultStorage; |
449 | CFI_cdesc_t *result{&resultStorage}; |
450 | retCode = CFI_establish(result, nullptr, CFI_attribute_other, |
451 | CFI_type_long_long, 0, resultRank, nullptr); |
452 | testPreConditions &= (retCode == CFI_SUCCESS); |
453 | |
454 | if (!testPreConditions) { |
455 | MATCH(true, testPreConditions); |
456 | return; |
457 | } |
458 | |
459 | retCode = CFI_section( |
460 | result, source, lb, ub, strides); // Fortran B = A(2:4:2, 5:5:0, 4:6:2) |
461 | MATCH(true, retCode == CFI_SUCCESS); |
462 | |
463 | const CFI_index_t lbs0{source->dim[0].lower_bound}; |
464 | const CFI_index_t lbs1{source->dim[1].lower_bound}; |
465 | const CFI_index_t lbs2{source->dim[2].lower_bound}; |
466 | |
467 | CFI_index_t resJ{result->dim[1].lower_bound}; |
468 | for (CFI_index_t k{lb[2]}; k <= ub[2]; k += strides[2]) { |
469 | for (CFI_index_t j{lb[1]}; j <= ub[1]; j += strides[1] ? strides[1] : 1) { |
470 | CFI_index_t resI{result->dim[0].lower_bound}; |
471 | for (CFI_index_t i{lb[0]}; i <= ub[0]; i += strides[0]) { |
472 | // check A(i,j,k) == B(resI, resJ) == array[k-1][j-1][i-1] |
473 | const CFI_index_t resSubcripts[]{resI, resJ}; |
474 | const CFI_index_t srcSubcripts[]{i, j, k}; |
475 | MATCH(true, |
476 | CFI_address(source, srcSubcripts) == |
477 | CFI_address(result, resSubcripts)); |
478 | MATCH(true, |
479 | CFI_address(source, srcSubcripts) == |
480 | &array[k - lbs2][j - lbs1][i - lbs0]); |
481 | ++resI; |
482 | } |
483 | } |
484 | ++resJ; |
485 | } |
486 | |
487 | strides[0] = -1; |
488 | lb[0] = 4; |
489 | ub[0] = 2; |
490 | retCode = CFI_section( |
491 | result, source, lb, ub, strides); // Fortran B = A(4:2:-1, 5:5:0, 4:6:2) |
492 | MATCH(true, retCode == CFI_SUCCESS); |
493 | |
494 | resJ = result->dim[1].lower_bound; |
495 | for (CFI_index_t k{lb[2]}; k <= ub[2]; k += strides[2]) { |
496 | for (CFI_index_t j{lb[1]}; j <= ub[1]; j += 1) { |
497 | CFI_index_t resI{result->dim[1].lower_bound + result->dim[0].extent - 1}; |
498 | for (CFI_index_t i{2}; i <= 4; ++i) { |
499 | // check A(i,j,k) == B(resI, resJ) == array[k-1][j-1][i-1] |
500 | const CFI_index_t resSubcripts[]{resI, resJ}; |
501 | const CFI_index_t srcSubcripts[]{i, j, k}; |
502 | MATCH(true, |
503 | CFI_address(source, srcSubcripts) == |
504 | CFI_address(result, resSubcripts)); |
505 | MATCH(true, |
506 | CFI_address(source, srcSubcripts) == |
507 | &array[k - lbs2][j - lbs1][i - lbs0]); |
508 | --resI; |
509 | } |
510 | } |
511 | ++resJ; |
512 | } |
513 | } |
514 | |
515 | static void run_CFI_select_part_tests() { |
516 | constexpr std::size_t name_len{5}; |
517 | typedef struct { |
518 | double distance; |
519 | int stars; |
520 | char name[name_len]; |
521 | } Galaxy; |
522 | |
523 | const CFI_rank_t rank{2}; |
524 | constexpr CFI_index_t universeSize[]{2, 3}; |
525 | Galaxy universe[universeSize[1]][universeSize[0]]; |
526 | |
527 | for (int i{0}; i < universeSize[1]; ++i) { |
528 | for (int j{0}; j < universeSize[0]; ++j) { |
529 | // Initializing Fortran var universe(j,i) |
530 | universe[i][j].distance = j + i * 32; |
531 | universe[i][j].stars = j * 2 + i * 64; |
532 | universe[i][j].name[2] = static_cast<char>(j); |
533 | universe[i][j].name[3] = static_cast<char>(i); |
534 | } |
535 | } |
536 | |
537 | CFI_CDESC_T(rank) resStorage, srcStorage; |
538 | CFI_cdesc_t *result{&resStorage}; |
539 | CFI_cdesc_t *source{&srcStorage}; |
540 | |
541 | bool testPreConditions{true}; |
542 | int retCode{CFI_establish(result, nullptr, CFI_attribute_other, CFI_type_int, |
543 | sizeof(int), rank, nullptr)}; |
544 | testPreConditions &= (retCode == CFI_SUCCESS); |
545 | retCode = CFI_establish(source, &universe, CFI_attribute_other, |
546 | CFI_type_struct, sizeof(Galaxy), rank, universeSize); |
547 | testPreConditions &= (retCode == CFI_SUCCESS); |
548 | if (!testPreConditions) { |
549 | MATCH(true, testPreConditions); |
550 | return; |
551 | } |
552 | |
553 | std::size_t displacement{offsetof(Galaxy, stars)}; |
554 | std::size_t elem_len{0}; // ignored |
555 | retCode = CFI_select_part(result, source, displacement, elem_len); |
556 | MATCH(CFI_SUCCESS, retCode); |
557 | |
558 | bool baseAddrShiftedOk{ |
559 | static_cast<char *>(source->base_addr) + displacement == |
560 | result->base_addr}; |
561 | MATCH(true, baseAddrShiftedOk); |
562 | if (!baseAddrShiftedOk) { |
563 | return; |
564 | } |
565 | |
566 | MATCH(sizeof(int), result->elem_len); |
567 | for (CFI_index_t j{0}; j < universeSize[1]; ++j) { |
568 | for (CFI_index_t i{0}; i < universeSize[0]; ++i) { |
569 | CFI_index_t subscripts[]{ |
570 | result->dim[0].lower_bound + i, result->dim[1].lower_bound + j}; |
571 | MATCH( |
572 | i * 2 + j * 64, *static_cast<int *>(CFI_address(result, subscripts))); |
573 | } |
574 | } |
575 | |
576 | // Test for Fortran character type |
577 | retCode = CFI_establish( |
578 | result, nullptr, CFI_attribute_other, CFI_type_char, 2, rank, nullptr); |
579 | testPreConditions &= (retCode == CFI_SUCCESS); |
580 | if (!testPreConditions) { |
581 | MATCH(true, testPreConditions); |
582 | return; |
583 | } |
584 | |
585 | displacement = offsetof(Galaxy, name) + 2; |
586 | elem_len = 2; // not ignored this time |
587 | retCode = CFI_select_part(result, source, displacement, elem_len); |
588 | MATCH(CFI_SUCCESS, retCode); |
589 | |
590 | baseAddrShiftedOk = static_cast<char *>(source->base_addr) + displacement == |
591 | result->base_addr; |
592 | MATCH(true, baseAddrShiftedOk); |
593 | if (!baseAddrShiftedOk) { |
594 | return; |
595 | } |
596 | |
597 | MATCH(elem_len, result->elem_len); |
598 | for (CFI_index_t j{0}; j < universeSize[1]; ++j) { |
599 | for (CFI_index_t i{0}; i < universeSize[0]; ++i) { |
600 | CFI_index_t subscripts[]{ |
601 | result->dim[0].lower_bound + i, result->dim[1].lower_bound + j}; |
602 | MATCH(static_cast<char>(i), |
603 | static_cast<char *>(CFI_address(result, subscripts))[0]); |
604 | MATCH(static_cast<char>(j), |
605 | static_cast<char *>(CFI_address(result, subscripts))[1]); |
606 | } |
607 | } |
608 | } |
609 | |
610 | static void run_CFI_setpointer_tests() { |
611 | constexpr CFI_rank_t rank{3}; |
612 | CFI_CDESC_T(rank) resStorage, srcStorage; |
613 | CFI_cdesc_t *result{&resStorage}; |
614 | CFI_cdesc_t *source{&srcStorage}; |
615 | CFI_index_t lower_bounds[rank]; |
616 | CFI_index_t extents[rank]; |
617 | for (int i{0}; i < rank; ++i) { |
618 | lower_bounds[i] = i; |
619 | extents[i] = 2; |
620 | } |
621 | |
622 | char target; |
623 | char *dummyBaseAddress{&target}; |
624 | bool testPreConditions{true}; |
625 | CFI_type_t type{CFI_type_int}; |
626 | std::size_t elem_len{ByteSize(type, 42)}; |
627 | int retCode{CFI_establish( |
628 | result, nullptr, CFI_attribute_pointer, type, elem_len, rank, nullptr)}; |
629 | testPreConditions &= (retCode == CFI_SUCCESS); |
630 | retCode = CFI_establish(source, dummyBaseAddress, CFI_attribute_other, type, |
631 | elem_len, rank, extents); |
632 | testPreConditions &= (retCode == CFI_SUCCESS); |
633 | if (!testPreConditions) { |
634 | MATCH(true, testPreConditions); |
635 | return; |
636 | } |
637 | |
638 | retCode = CFI_setpointer(result, source, lower_bounds); |
639 | MATCH(CFI_SUCCESS, retCode); |
640 | |
641 | // The following members must be invariant |
642 | MATCH(rank, result->rank); |
643 | MATCH(elem_len, result->elem_len); |
644 | MATCH(type, result->type); |
645 | // check pointer association |
646 | MATCH(true, result->base_addr == source->base_addr); |
647 | for (int j{0}; j < rank; ++j) { |
648 | MATCH(source->dim[j].extent, result->dim[j].extent); |
649 | MATCH(source->dim[j].sm, result->dim[j].sm); |
650 | MATCH(lower_bounds[j], result->dim[j].lower_bound); |
651 | } |
652 | } |
653 | |
654 | static void run_CFI_is_contiguous_tests() { |
655 | // INTEGER :: A(0:3,0:3) |
656 | constexpr CFI_rank_t rank{2}; |
657 | CFI_index_t extents[rank] = {4, 4}; |
658 | CFI_CDESC_T(rank) dv_storage; |
659 | CFI_cdesc_t *dv{&dv_storage}; |
660 | Descriptor *dvDesc{reinterpret_cast<Descriptor *>(dv)}; |
661 | char base; |
662 | void *base_addr{&base}; |
663 | int retCode{CFI_establish(dv, base_addr, CFI_attribute_other, CFI_type_int, |
664 | /*elem_len=*/0, rank, extents)}; |
665 | MATCH(retCode == CFI_SUCCESS, true); |
666 | |
667 | MATCH(true, CFI_is_contiguous(dv) == 1); |
668 | MATCH(true, dvDesc->IsContiguous()); |
669 | |
670 | CFI_CDESC_T(rank) sectionDescriptorStorage; |
671 | CFI_cdesc_t *section{§ionDescriptorStorage}; |
672 | Descriptor *sectionDesc{reinterpret_cast<Descriptor *>(section)}; |
673 | retCode = CFI_establish(section, base_addr, CFI_attribute_other, CFI_type_int, |
674 | /*elem_len=*/0, rank, extents); |
675 | MATCH(retCode == CFI_SUCCESS, true); |
676 | |
677 | // Test empty section B = A(0:3:2,0:3:-2) is contiguous. |
678 | CFI_index_t lb[rank] = {0, 0}; |
679 | CFI_index_t ub[rank] = {3, 3}; |
680 | CFI_index_t strides[rank] = {2, -2}; |
681 | retCode = CFI_section(section, dv, lb, ub, strides); |
682 | MATCH(true, retCode == CFI_SUCCESS); |
683 | MATCH(true, CFI_is_contiguous(section) == 1); |
684 | MATCH(true, sectionDesc->IsContiguous()); |
685 | |
686 | // Test 1 element section B = A(0:1:2,0:1:2) is contiguous. |
687 | lb[0] = 0; |
688 | lb[1] = 0; |
689 | ub[0] = 1; |
690 | ub[1] = 1; |
691 | strides[0] = 2; |
692 | strides[1] = 2; |
693 | retCode = CFI_section(section, dv, lb, ub, strides); |
694 | MATCH(true, retCode == CFI_SUCCESS); |
695 | MATCH(true, CFI_is_contiguous(section) == 1); |
696 | MATCH(true, sectionDesc->IsContiguous()); |
697 | |
698 | // Test section B = A(0:3:1,0:2:1) is contiguous. |
699 | lb[0] = 0; |
700 | lb[1] = 0; |
701 | ub[0] = 3; |
702 | ub[1] = 2; |
703 | strides[0] = 1; |
704 | strides[1] = 1; |
705 | retCode = CFI_section(section, dv, lb, ub, strides); |
706 | sectionDesc->Dump(); |
707 | MATCH(true, retCode == CFI_SUCCESS); |
708 | MATCH(true, CFI_is_contiguous(section) == 1); |
709 | MATCH(true, sectionDesc->IsContiguous()); |
710 | |
711 | // Test section B = A(0:2:1,0:2:1) is not contiguous. |
712 | lb[0] = 0; |
713 | lb[1] = 0; |
714 | ub[0] = 2; |
715 | ub[1] = 2; |
716 | strides[0] = 1; |
717 | strides[1] = 1; |
718 | retCode = CFI_section(section, dv, lb, ub, strides); |
719 | sectionDesc->Dump(); |
720 | MATCH(true, retCode == CFI_SUCCESS); |
721 | MATCH(true, CFI_is_contiguous(section) == 0); |
722 | MATCH(false, sectionDesc->IsContiguous()); |
723 | |
724 | // Test section B = A(0:3:2,0:3:1) is not contiguous. |
725 | lb[0] = 0; |
726 | lb[1] = 0; |
727 | ub[0] = 3; |
728 | ub[1] = 3; |
729 | strides[0] = 2; |
730 | strides[1] = 1; |
731 | retCode = CFI_section(section, dv, lb, ub, strides); |
732 | MATCH(true, retCode == CFI_SUCCESS); |
733 | MATCH(true, CFI_is_contiguous(section) == 0); |
734 | MATCH(false, sectionDesc->IsContiguous()); |
735 | |
736 | // Test section B = A(0:3:1,0:3:2) is not contiguous. |
737 | lb[0] = 0; |
738 | lb[1] = 0; |
739 | ub[0] = 3; |
740 | ub[1] = 3; |
741 | strides[0] = 1; |
742 | strides[1] = 2; |
743 | retCode = CFI_section(section, dv, lb, ub, strides); |
744 | MATCH(true, retCode == CFI_SUCCESS); |
745 | MATCH(true, CFI_is_contiguous(section) == 0); |
746 | MATCH(false, sectionDesc->IsContiguous()); |
747 | |
748 | // Test section B = A(0:3:1,0:0:2) is contiguous. |
749 | lb[0] = 0; |
750 | lb[1] = 0; |
751 | ub[0] = 3; |
752 | ub[1] = 0; |
753 | strides[0] = 1; |
754 | strides[1] = 2; |
755 | retCode = CFI_section(section, dv, lb, ub, strides); |
756 | MATCH(true, retCode == CFI_SUCCESS); |
757 | MATCH(true, CFI_is_contiguous(section) == 1); |
758 | MATCH(true, sectionDesc->IsContiguous()); |
759 | |
760 | // INTEGER :: C(0:0, 0:3) |
761 | CFI_index_t c_extents[rank] = {1, 4}; |
762 | CFI_CDESC_T(rank) c_dv_storage; |
763 | CFI_cdesc_t *cdv{&c_dv_storage}; |
764 | retCode = CFI_establish(cdv, base_addr, CFI_attribute_other, CFI_type_int, |
765 | /*elem_len=*/0, rank, c_extents); |
766 | MATCH(retCode == CFI_SUCCESS, true); |
767 | |
768 | // Test section B = C(0:0:2, 0:3:1) is contiguous. |
769 | lb[0] = 0; |
770 | lb[1] = 0; |
771 | ub[0] = 0; |
772 | ub[1] = 3; |
773 | strides[0] = 2; |
774 | strides[1] = 1; |
775 | retCode = CFI_section(section, cdv, lb, ub, strides); |
776 | MATCH(true, retCode == CFI_SUCCESS); |
777 | MATCH(true, CFI_is_contiguous(section) == 1); |
778 | MATCH(true, sectionDesc->IsContiguous()); |
779 | } |
780 | |
781 | int main() { |
782 | TestCdescMacroForAllRanksSmallerThan<CFI_MAX_RANK>(); |
783 | run_CFI_establish_tests(); |
784 | run_CFI_address_tests(); |
785 | run_CFI_allocate_tests(); |
786 | // TODO: test CFI_deallocate |
787 | run_CFI_is_contiguous_tests(); |
788 | run_CFI_section_tests(); |
789 | run_CFI_select_part_tests(); |
790 | run_CFI_setpointer_tests(); |
791 | return testing::Complete(); |
792 | } |
793 | |