Warning: This file is not a C or C++ file. It does not have highlighting.
1 | //===-- include/flang/Runtime/descriptor.h ----------------------*- 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 | #ifndef FORTRAN_RUNTIME_DESCRIPTOR_H_ |
10 | #define FORTRAN_RUNTIME_DESCRIPTOR_H_ |
11 | |
12 | // Defines data structures used during execution of a Fortran program |
13 | // to implement nontrivial dummy arguments, pointers, allocatables, |
14 | // function results, and the special behaviors of instances of derived types. |
15 | // This header file includes and extends the published language |
16 | // interoperability header that is required by the Fortran 2018 standard |
17 | // as a subset of definitions suitable for exposure to user C/C++ code. |
18 | // User C code is welcome to depend on that ISO_Fortran_binding.h file, |
19 | // but should never reference this internal header. |
20 | |
21 | #include "flang/ISO_Fortran_binding_wrapper.h" |
22 | #include "flang/Runtime/memory.h" |
23 | #include "flang/Runtime/type-code.h" |
24 | #include <algorithm> |
25 | #include <cassert> |
26 | #include <cinttypes> |
27 | #include <cstddef> |
28 | #include <cstdio> |
29 | #include <cstring> |
30 | |
31 | namespace Fortran::runtime::typeInfo { |
32 | using TypeParameterValue = std::int64_t; |
33 | class DerivedType; |
34 | } // namespace Fortran::runtime::typeInfo |
35 | |
36 | namespace Fortran::runtime { |
37 | |
38 | using SubscriptValue = ISO::CFI_index_t; |
39 | class Terminator; |
40 | |
41 | RT_VAR_GROUP_BEGIN |
42 | static constexpr RT_CONST_VAR_ATTRS int maxRank{CFI_MAX_RANK}; |
43 | RT_VAR_GROUP_END |
44 | |
45 | // A C++ view of the sole interoperable standard descriptor (ISO::CFI_cdesc_t) |
46 | // and its type and per-dimension information. |
47 | |
48 | class Dimension { |
49 | public: |
50 | RT_API_ATTRS SubscriptValue LowerBound() const { return raw_.lower_bound; } |
51 | RT_API_ATTRS SubscriptValue Extent() const { return raw_.extent; } |
52 | RT_API_ATTRS SubscriptValue UpperBound() const { |
53 | return LowerBound() + Extent() - 1; |
54 | } |
55 | RT_API_ATTRS SubscriptValue ByteStride() const { return raw_.sm; } |
56 | |
57 | RT_API_ATTRS Dimension &SetBounds( |
58 | SubscriptValue lower, SubscriptValue upper) { |
59 | if (upper >= lower) { |
60 | raw_.lower_bound = lower; |
61 | raw_.extent = upper - lower + 1; |
62 | } else { |
63 | raw_.lower_bound = 1; |
64 | raw_.extent = 0; |
65 | } |
66 | return *this; |
67 | } |
68 | // Do not use this API to cause the LB of an empty dimension |
69 | // to be anything other than 1. Use SetBounds() instead if you can. |
70 | RT_API_ATTRS Dimension &SetLowerBound(SubscriptValue lower) { |
71 | raw_.lower_bound = lower; |
72 | return *this; |
73 | } |
74 | RT_API_ATTRS Dimension &SetUpperBound(SubscriptValue upper) { |
75 | auto lower{raw_.lower_bound}; |
76 | raw_.extent = upper >= lower ? upper - lower + 1 : 0; |
77 | return *this; |
78 | } |
79 | RT_API_ATTRS Dimension &SetExtent(SubscriptValue extent) { |
80 | raw_.extent = extent; |
81 | return *this; |
82 | } |
83 | RT_API_ATTRS Dimension &SetByteStride(SubscriptValue bytes) { |
84 | raw_.sm = bytes; |
85 | return *this; |
86 | } |
87 | |
88 | private: |
89 | ISO::CFI_dim_t raw_; |
90 | }; |
91 | |
92 | // The storage for this object follows the last used dim[] entry in a |
93 | // Descriptor (CFI_cdesc_t) generic descriptor. Space matters here, since |
94 | // descriptors serve as POINTER and ALLOCATABLE components of derived type |
95 | // instances. The presence of this structure is implied by the flag |
96 | // CFI_cdesc_t.f18Addendum, and the number of elements in the len_[] |
97 | // array is determined by derivedType_->LenParameters(). |
98 | class DescriptorAddendum { |
99 | public: |
100 | explicit RT_API_ATTRS DescriptorAddendum( |
101 | const typeInfo::DerivedType *dt = nullptr) |
102 | : derivedType_{dt}, len_{0} {} |
103 | RT_API_ATTRS DescriptorAddendum &operator=(const DescriptorAddendum &); |
104 | |
105 | RT_API_ATTRS const typeInfo::DerivedType *derivedType() const { |
106 | return derivedType_; |
107 | } |
108 | RT_API_ATTRS DescriptorAddendum &set_derivedType( |
109 | const typeInfo::DerivedType *dt) { |
110 | derivedType_ = dt; |
111 | return *this; |
112 | } |
113 | |
114 | RT_API_ATTRS std::size_t LenParameters() const; |
115 | |
116 | RT_API_ATTRS typeInfo::TypeParameterValue LenParameterValue(int which) const { |
117 | return len_[which]; |
118 | } |
119 | static constexpr RT_API_ATTRS std::size_t SizeInBytes(int lenParameters) { |
120 | // TODO: Don't waste that last word if lenParameters == 0 |
121 | return sizeof(DescriptorAddendum) + |
122 | std::max(lenParameters - 1, 0) * sizeof(typeInfo::TypeParameterValue); |
123 | } |
124 | RT_API_ATTRS std::size_t SizeInBytes() const; |
125 | |
126 | RT_API_ATTRS void SetLenParameterValue( |
127 | int which, typeInfo::TypeParameterValue x) { |
128 | len_[which] = x; |
129 | } |
130 | |
131 | void Dump(FILE * = stdout) const; |
132 | |
133 | private: |
134 | const typeInfo::DerivedType *derivedType_; |
135 | typeInfo::TypeParameterValue len_[1]; // must be the last component |
136 | // The LEN type parameter values can also include captured values of |
137 | // specification expressions that were used for bounds and for LEN type |
138 | // parameters of components. The values have been truncated to the LEN |
139 | // type parameter's type, if shorter than 64 bits, then sign-extended. |
140 | }; |
141 | |
142 | // A C++ view of a standard descriptor object. |
143 | class Descriptor { |
144 | public: |
145 | // Be advised: this class type is not suitable for use when allocating |
146 | // a descriptor -- it is a dynamic view of the common descriptor format. |
147 | // If used in a simple declaration of a local variable or dynamic allocation, |
148 | // the size is going to be correct only by accident, since the true size of |
149 | // a descriptor depends on the number of its dimensions and the presence and |
150 | // size of an addendum, which depends on the type of the data. |
151 | // Use the class template StaticDescriptor (below) to declare a descriptor |
152 | // whose type and rank are fixed and known at compilation time. Use the |
153 | // Create() static member functions otherwise to dynamically allocate a |
154 | // descriptor. |
155 | |
156 | RT_API_ATTRS Descriptor(const Descriptor &); |
157 | RT_API_ATTRS Descriptor &operator=(const Descriptor &); |
158 | |
159 | // Returns the number of bytes occupied by an element of the given |
160 | // category and kind including any alignment padding required |
161 | // between adjacent elements. |
162 | static RT_API_ATTRS std::size_t BytesFor(TypeCategory category, int kind); |
163 | |
164 | RT_API_ATTRS void Establish(TypeCode t, std::size_t elementBytes, |
165 | void *p = nullptr, int rank = maxRank, |
166 | const SubscriptValue *extent = nullptr, |
167 | ISO::CFI_attribute_t attribute = CFI_attribute_other, |
168 | bool addendum = false); |
169 | RT_API_ATTRS void Establish(TypeCategory, int kind, void *p = nullptr, |
170 | int rank = maxRank, const SubscriptValue *extent = nullptr, |
171 | ISO::CFI_attribute_t attribute = CFI_attribute_other, |
172 | bool addendum = false); |
173 | RT_API_ATTRS void Establish(int characterKind, std::size_t characters, |
174 | void *p = nullptr, int rank = maxRank, |
175 | const SubscriptValue *extent = nullptr, |
176 | ISO::CFI_attribute_t attribute = CFI_attribute_other, |
177 | bool addendum = false); |
178 | RT_API_ATTRS void Establish(const typeInfo::DerivedType &dt, |
179 | void *p = nullptr, int rank = maxRank, |
180 | const SubscriptValue *extent = nullptr, |
181 | ISO::CFI_attribute_t attribute = CFI_attribute_other); |
182 | |
183 | // To create a descriptor for a derived type the caller |
184 | // must provide non-null dt argument. |
185 | // The addendum argument is only used for testing purposes, |
186 | // and it may force a descriptor with an addendum while |
187 | // dt may be null. |
188 | static RT_API_ATTRS OwningPtr<Descriptor> Create(TypeCode t, |
189 | std::size_t elementBytes, void *p = nullptr, int rank = maxRank, |
190 | const SubscriptValue *extent = nullptr, |
191 | ISO::CFI_attribute_t attribute = CFI_attribute_other, |
192 | bool addendum = false, const typeInfo::DerivedType *dt = nullptr); |
193 | static RT_API_ATTRS OwningPtr<Descriptor> Create(TypeCategory, int kind, |
194 | void *p = nullptr, int rank = maxRank, |
195 | const SubscriptValue *extent = nullptr, |
196 | ISO::CFI_attribute_t attribute = CFI_attribute_other); |
197 | static RT_API_ATTRS OwningPtr<Descriptor> Create(int characterKind, |
198 | SubscriptValue characters, void *p = nullptr, int rank = maxRank, |
199 | const SubscriptValue *extent = nullptr, |
200 | ISO::CFI_attribute_t attribute = CFI_attribute_other); |
201 | static RT_API_ATTRS OwningPtr<Descriptor> Create( |
202 | const typeInfo::DerivedType &dt, void *p = nullptr, int rank = maxRank, |
203 | const SubscriptValue *extent = nullptr, |
204 | ISO::CFI_attribute_t attribute = CFI_attribute_other); |
205 | |
206 | RT_API_ATTRS ISO::CFI_cdesc_t &raw() { return raw_; } |
207 | RT_API_ATTRS const ISO::CFI_cdesc_t &raw() const { return raw_; } |
208 | RT_API_ATTRS std::size_t ElementBytes() const { return raw_.elem_len; } |
209 | RT_API_ATTRS int rank() const { return raw_.rank; } |
210 | RT_API_ATTRS TypeCode type() const { return TypeCode{raw_.type}; } |
211 | |
212 | RT_API_ATTRS Descriptor &set_base_addr(void *p) { |
213 | raw_.base_addr = p; |
214 | return *this; |
215 | } |
216 | |
217 | RT_API_ATTRS bool IsPointer() const { |
218 | return raw_.attribute == CFI_attribute_pointer; |
219 | } |
220 | RT_API_ATTRS bool IsAllocatable() const { |
221 | return raw_.attribute == CFI_attribute_allocatable; |
222 | } |
223 | RT_API_ATTRS bool IsAllocated() const { return raw_.base_addr != nullptr; } |
224 | |
225 | RT_API_ATTRS Dimension &GetDimension(int dim) { |
226 | return *reinterpret_cast<Dimension *>(&raw_.dim[dim]); |
227 | } |
228 | RT_API_ATTRS const Dimension &GetDimension(int dim) const { |
229 | return *reinterpret_cast<const Dimension *>(&raw_.dim[dim]); |
230 | } |
231 | |
232 | RT_API_ATTRS std::size_t SubscriptByteOffset( |
233 | int dim, SubscriptValue subscriptValue) const { |
234 | const Dimension &dimension{GetDimension(dim)}; |
235 | return (subscriptValue - dimension.LowerBound()) * dimension.ByteStride(); |
236 | } |
237 | |
238 | RT_API_ATTRS std::size_t SubscriptsToByteOffset( |
239 | const SubscriptValue subscript[]) const { |
240 | std::size_t offset{0}; |
241 | for (int j{0}; j < raw_.rank; ++j) { |
242 | offset += SubscriptByteOffset(j, subscript[j]); |
243 | } |
244 | return offset; |
245 | } |
246 | |
247 | template <typename A = char> |
248 | RT_API_ATTRS A *OffsetElement(std::size_t offset = 0) const { |
249 | return reinterpret_cast<A *>( |
250 | reinterpret_cast<char *>(raw_.base_addr) + offset); |
251 | } |
252 | |
253 | template <typename A> |
254 | RT_API_ATTRS A *Element(const SubscriptValue subscript[]) const { |
255 | return OffsetElement<A>(SubscriptsToByteOffset(subscript)); |
256 | } |
257 | |
258 | template <typename A> |
259 | RT_API_ATTRS A *ElementComponent( |
260 | const SubscriptValue subscript[], std::size_t componentOffset) const { |
261 | return OffsetElement<A>( |
262 | SubscriptsToByteOffset(subscript) + componentOffset); |
263 | } |
264 | |
265 | template <typename A> |
266 | RT_API_ATTRS A *ZeroBasedIndexedElement(std::size_t n) const { |
267 | SubscriptValue at[maxRank]; |
268 | if (SubscriptsForZeroBasedElementNumber(at, n)) { |
269 | return Element<A>(at); |
270 | } |
271 | return nullptr; |
272 | } |
273 | |
274 | RT_API_ATTRS int GetLowerBounds(SubscriptValue subscript[]) const { |
275 | for (int j{0}; j < raw_.rank; ++j) { |
276 | subscript[j] = GetDimension(j).LowerBound(); |
277 | } |
278 | return raw_.rank; |
279 | } |
280 | |
281 | RT_API_ATTRS int GetShape(SubscriptValue subscript[]) const { |
282 | for (int j{0}; j < raw_.rank; ++j) { |
283 | subscript[j] = GetDimension(j).Extent(); |
284 | } |
285 | return raw_.rank; |
286 | } |
287 | |
288 | // When the passed subscript vector contains the last (or first) |
289 | // subscripts of the array, these wrap the subscripts around to |
290 | // their first (or last) values and return false. |
291 | RT_API_ATTRS bool IncrementSubscripts( |
292 | SubscriptValue subscript[], const int *permutation = nullptr) const { |
293 | for (int j{0}; j < raw_.rank; ++j) { |
294 | int k{permutation ? permutation[j] : j}; |
295 | const Dimension &dim{GetDimension(k)}; |
296 | if (subscript[k]++ < dim.UpperBound()) { |
297 | return true; |
298 | } |
299 | subscript[k] = dim.LowerBound(); |
300 | } |
301 | return false; |
302 | } |
303 | |
304 | RT_API_ATTRS bool DecrementSubscripts( |
305 | SubscriptValue[], const int *permutation = nullptr) const; |
306 | |
307 | // False when out of range. |
308 | RT_API_ATTRS bool SubscriptsForZeroBasedElementNumber( |
309 | SubscriptValue subscript[], std::size_t elementNumber, |
310 | const int *permutation = nullptr) const { |
311 | if (raw_.rank == 0) { |
312 | return elementNumber == 0; |
313 | } |
314 | std::size_t dimCoefficient[maxRank]; |
315 | int k0{permutation ? permutation[0] : 0}; |
316 | dimCoefficient[0] = 1; |
317 | auto coefficient{static_cast<std::size_t>(GetDimension(k0).Extent())}; |
318 | for (int j{1}; j < raw_.rank; ++j) { |
319 | int k{permutation ? permutation[j] : j}; |
320 | const Dimension &dim{GetDimension(k)}; |
321 | dimCoefficient[j] = coefficient; |
322 | coefficient *= dim.Extent(); |
323 | } |
324 | if (elementNumber >= coefficient) { |
325 | return false; // out of range |
326 | } |
327 | for (int j{raw_.rank - 1}; j > 0; --j) { |
328 | int k{permutation ? permutation[j] : j}; |
329 | const Dimension &dim{GetDimension(k)}; |
330 | std::size_t quotient{elementNumber / dimCoefficient[j]}; |
331 | subscript[k] = quotient + dim.LowerBound(); |
332 | elementNumber -= quotient * dimCoefficient[j]; |
333 | } |
334 | subscript[k0] = elementNumber + GetDimension(k0).LowerBound(); |
335 | return true; |
336 | } |
337 | |
338 | RT_API_ATTRS std::size_t ZeroBasedElementNumber( |
339 | const SubscriptValue *, const int *permutation = nullptr) const; |
340 | |
341 | RT_API_ATTRS DescriptorAddendum *Addendum() { |
342 | if (raw_.f18Addendum != 0) { |
343 | return reinterpret_cast<DescriptorAddendum *>(&GetDimension(rank())); |
344 | } else { |
345 | return nullptr; |
346 | } |
347 | } |
348 | RT_API_ATTRS const DescriptorAddendum *Addendum() const { |
349 | if (raw_.f18Addendum != 0) { |
350 | return reinterpret_cast<const DescriptorAddendum *>( |
351 | &GetDimension(rank())); |
352 | } else { |
353 | return nullptr; |
354 | } |
355 | } |
356 | |
357 | // Returns size in bytes of the descriptor (not the data) |
358 | static constexpr RT_API_ATTRS std::size_t SizeInBytes( |
359 | int rank, bool addendum = false, int lengthTypeParameters = 0) { |
360 | std::size_t bytes{sizeof(Descriptor) - sizeof(Dimension)}; |
361 | bytes += rank * sizeof(Dimension); |
362 | if (addendum || lengthTypeParameters > 0) { |
363 | bytes += DescriptorAddendum::SizeInBytes(lengthTypeParameters); |
364 | } |
365 | return bytes; |
366 | } |
367 | |
368 | RT_API_ATTRS std::size_t SizeInBytes() const; |
369 | |
370 | RT_API_ATTRS std::size_t Elements() const; |
371 | |
372 | // Allocate() assumes Elements() and ElementBytes() work; |
373 | // define the extents of the dimensions and the element length |
374 | // before calling. It (re)computes the byte strides after |
375 | // allocation. Does not allocate automatic components or |
376 | // perform default component initialization. |
377 | RT_API_ATTRS int Allocate(); |
378 | RT_API_ATTRS void SetByteStrides(); |
379 | |
380 | // Deallocates storage; does not call FINAL subroutines or |
381 | // deallocate allocatable/automatic components. |
382 | RT_API_ATTRS int Deallocate(); |
383 | |
384 | // Deallocates storage, including allocatable and automatic |
385 | // components. Optionally invokes FINAL subroutines. |
386 | RT_API_ATTRS int Destroy(bool finalize = false, bool destroyPointers = false, |
387 | Terminator * = nullptr); |
388 | |
389 | RT_API_ATTRS bool IsContiguous(int leadingDimensions = maxRank) const { |
390 | auto bytes{static_cast<SubscriptValue>(ElementBytes())}; |
391 | if (leadingDimensions > raw_.rank) { |
392 | leadingDimensions = raw_.rank; |
393 | } |
394 | bool stridesAreContiguous{true}; |
395 | for (int j{0}; j < leadingDimensions; ++j) { |
396 | const Dimension &dim{GetDimension(j)}; |
397 | stridesAreContiguous &= |
398 | (bytes == dim.ByteStride()) || (dim.Extent() == 1); |
399 | bytes *= dim.Extent(); |
400 | } |
401 | // One and zero element arrays are contiguous even if the descriptor |
402 | // byte strides are not perfect multiples. |
403 | // Arrays with more than 2 elements may also be contiguous even if a |
404 | // byte stride in one dimension is not a perfect multiple, as long as |
405 | // this is the last dimension, or if the dimension has one extent and |
406 | // the following dimension have either one extents or contiguous byte |
407 | // strides. |
408 | return stridesAreContiguous || bytes == 0; |
409 | } |
410 | |
411 | // Establishes a pointer to a section or element. |
412 | RT_API_ATTRS bool EstablishPointerSection(const Descriptor &source, |
413 | const SubscriptValue *lower = nullptr, |
414 | const SubscriptValue *upper = nullptr, |
415 | const SubscriptValue *stride = nullptr); |
416 | |
417 | RT_API_ATTRS void ApplyMold(const Descriptor &, int rank); |
418 | |
419 | RT_API_ATTRS void Check() const; |
420 | |
421 | void Dump(FILE * = stdout) const; |
422 | |
423 | private: |
424 | ISO::CFI_cdesc_t raw_; |
425 | }; |
426 | static_assert(sizeof(Descriptor) == sizeof(ISO::CFI_cdesc_t)); |
427 | |
428 | // Properly configured instances of StaticDescriptor will occupy the |
429 | // exact amount of storage required for the descriptor, its dimensional |
430 | // information, and possible addendum. To build such a static descriptor, |
431 | // declare an instance of StaticDescriptor<>, extract a reference to its |
432 | // descriptor via the descriptor() accessor, and then built a Descriptor |
433 | // therein via descriptor.Establish(), e.g.: |
434 | // StaticDescriptor<R,A,LP> statDesc; |
435 | // Descriptor &descriptor{statDesc.descriptor()}; |
436 | // descriptor.Establish( ... ); |
437 | template <int MAX_RANK = maxRank, bool ADDENDUM = false, int MAX_LEN_PARMS = 0> |
438 | class alignas(Descriptor) StaticDescriptor { |
439 | public: |
440 | RT_OFFLOAD_VAR_GROUP_BEGIN |
441 | static constexpr int maxRank{MAX_RANK}; |
442 | static constexpr int maxLengthTypeParameters{MAX_LEN_PARMS}; |
443 | static constexpr bool hasAddendum{ADDENDUM || MAX_LEN_PARMS > 0}; |
444 | static constexpr std::size_t byteSize{ |
445 | Descriptor::SizeInBytes(maxRank, hasAddendum, maxLengthTypeParameters)}; |
446 | RT_OFFLOAD_VAR_GROUP_END |
447 | |
448 | RT_API_ATTRS Descriptor &descriptor() { |
449 | return *reinterpret_cast<Descriptor *>(storage_); |
450 | } |
451 | RT_API_ATTRS const Descriptor &descriptor() const { |
452 | return *reinterpret_cast<const Descriptor *>(storage_); |
453 | } |
454 | |
455 | RT_API_ATTRS void Check() { |
456 | assert(descriptor().rank() <= maxRank); |
457 | assert(descriptor().SizeInBytes() <= byteSize); |
458 | if (DescriptorAddendum * addendum{descriptor().Addendum()}) { |
459 | assert(hasAddendum); |
460 | assert(addendum->LenParameters() <= maxLengthTypeParameters); |
461 | } else { |
462 | assert(!hasAddendum); |
463 | assert(maxLengthTypeParameters == 0); |
464 | } |
465 | descriptor().Check(); |
466 | } |
467 | |
468 | private: |
469 | char storage_[byteSize]{}; |
470 | }; |
471 | |
472 | } // namespace Fortran::runtime |
473 | #endif // FORTRAN_RUNTIME_DESCRIPTOR_H_ |
474 |
Warning: This file is not a C or C++ file. It does not have highlighting.