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