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