FLANG

descriptor.h
1 //===-- 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 "derived-type.h"
22 #include "memory.h"
23 #include "type-code.h"
24 #include "flang/ISO_Fortran_binding.h"
25 #include <cassert>
26 #include <cinttypes>
27 #include <cstddef>
28 #include <cstdio>
29 #include <cstring>
30
31 namespace Fortran::runtime {
32
33 using SubscriptValue = ISO::CFI_index_t;
34
35 static constexpr int maxRank{CFI_MAX_RANK};
36
37 // A C++ view of the sole interoperable standard descriptor (ISO::CFI_cdesc_t)
38 // and its type and per-dimension information.
39
40 class Dimension {
41 public:
42 SubscriptValue LowerBound() const { return raw_.lower_bound; }
43 SubscriptValue Extent() const { return raw_.extent; }
44 SubscriptValue UpperBound() const { return LowerBound() + Extent() - 1; }
45 SubscriptValue ByteStride() const { return raw_.sm; }
46
47 private:
48 ISO::CFI_dim_t raw_;
49 };
50
51 // The storage for this object follows the last used dim[] entry in a
52 // Descriptor (CFI_cdesc_t) generic descriptor. Space matters here, since
53 // descriptors serve as POINTER and ALLOCATABLE components of derived type
54 // instances. The presence of this structure is implied by the flag
55 // CFI_cdesc_t.f18Addendum, and the number of elements in the len_[]
56 // array is determined by DerivedType::lenParameters().
57 class DescriptorAddendum {
58 public:
59 enum Flags {
60 StaticDescriptor = 0x001,
61 ImplicitAllocatable = 0x002, // compiler-created allocatable
62 DoNotFinalize = 0x004, // compiler temporary
63 Target = 0x008, // TARGET attribute
64 };
65
66 explicit DescriptorAddendum(
67 const DerivedType *dt = nullptr, std::uint64_t flags = 0)
68 : derivedType_{dt}, flags_{flags} {}
69
70 const DerivedType *derivedType() const { return derivedType_; }
71 DescriptorAddendum &set_derivedType(const DerivedType *dt) {
72 derivedType_ = dt;
73 return *this;
74 }
75 std::uint64_t &flags() { return flags_; }
76 const std::uint64_t &flags() const { return flags_; }
77
78 std::size_t LenParameters() const {
79 if (derivedType_) {
80 return derivedType_->lenParameters();
81 }
82 return 0;
83 }
84
85 TypeParameterValue LenParameterValue(int which) const { return len_[which]; }
86 static constexpr std::size_t SizeInBytes(int lenParameters) {
87 return sizeof(DescriptorAddendum) - sizeof(TypeParameterValue) +
88 lenParameters * sizeof(TypeParameterValue);
89 }
90 std::size_t SizeInBytes() const;
91
92 void SetLenParameterValue(int which, TypeParameterValue x) {
93 len_[which] = x;
94 }
95
96 void Dump(FILE * = stdout) const;
97
98 private:
99 const DerivedType *derivedType_{nullptr};
100 std::uint64_t flags_{0};
101 TypeParameterValue len_[1]; // must be the last component
102 // The LEN type parameter values can also include captured values of
103 // specification expressions that were used for bounds and for LEN type
104 // parameters of components. The values have been truncated to the LEN
105 // type parameter's type, if shorter than 64 bits, then sign-extended.
106 };
107
108 // A C++ view of a standard descriptor object.
109 class Descriptor {
110 public:
111 // Be advised: this class type is not suitable for use when allocating
112 // a descriptor -- it is a dynamic view of the common descriptor format.
113 // If used in a simple declaration of a local variable or dynamic allocation,
114 // the size is going to be correct only by accident, since the true size of
115 // a descriptor depends on the number of its dimensions and the presence and
116 // size of an addendum, which depends on the type of the data.
117 // Use the class template StaticDescriptor (below) to declare a descriptor
118 // whose type and rank are fixed and known at compilation time. Use the
119 // Create() static member functions otherwise to dynamically allocate a
120 // descriptor.
121
122 Descriptor() {
123 // Minimal initialization to prevent the destructor from running amuck
124 // later if the descriptor is never established.
125 raw_.base_addr = nullptr;
126 raw_.f18Addendum = false;
127 }
128 Descriptor(const Descriptor &);
129
130 ~Descriptor();
131
132 static constexpr std::size_t BytesFor(TypeCategory category, int kind) {
133 return category == TypeCategory::Complex ? kind * 2 : kind;
134 }
135
136 void Establish(TypeCode t, std::size_t elementBytes, void *p = nullptr,
137 int rank = maxRank, const SubscriptValue *extent = nullptr,
138 ISO::CFI_attribute_t attribute = CFI_attribute_other,
139 bool addendum = false);
140 void Establish(TypeCategory, int kind, void *p = nullptr, int rank = maxRank,
141 const SubscriptValue *extent = nullptr,
142 ISO::CFI_attribute_t attribute = CFI_attribute_other,
143 bool addendum = false);
144 void Establish(int characterKind, std::size_t characters, void *p = nullptr,
145 int rank = maxRank, const SubscriptValue *extent = nullptr,
146 ISO::CFI_attribute_t attribute = CFI_attribute_other,
147 bool addendum = false);
148 void Establish(const DerivedType &dt, void *p = nullptr, int rank = maxRank,
149 const SubscriptValue *extent = nullptr,
150 ISO::CFI_attribute_t attribute = CFI_attribute_other);
151
152 static OwningPtr<Descriptor> Create(TypeCode t, std::size_t elementBytes,
153 void *p = nullptr, int rank = maxRank,
154 const SubscriptValue *extent = nullptr,
155 ISO::CFI_attribute_t attribute = CFI_attribute_other,
156 int derivedTypeLenParameters = 0);
157 static OwningPtr<Descriptor> Create(TypeCategory, int kind, void *p = nullptr,
158 int rank = maxRank, const SubscriptValue *extent = nullptr,
159 ISO::CFI_attribute_t attribute = CFI_attribute_other);
160 static OwningPtr<Descriptor> Create(int characterKind,
161 SubscriptValue characters, void *p = nullptr, int rank = maxRank,
162 const SubscriptValue *extent = nullptr,
163 ISO::CFI_attribute_t attribute = CFI_attribute_other);
164 static OwningPtr<Descriptor> Create(const DerivedType &dt, void *p = nullptr,
165 int rank = maxRank, const SubscriptValue *extent = nullptr,
166 ISO::CFI_attribute_t attribute = CFI_attribute_other);
167
168 ISO::CFI_cdesc_t &raw() { return raw_; }
169 const ISO::CFI_cdesc_t &raw() const { return raw_; }
170 std::size_t ElementBytes() const { return raw_.elem_len; }
171 int rank() const { return raw_.rank; }
172 TypeCode type() const { return TypeCode{raw_.type}; }
173
174 Descriptor &set_base_addr(void *p) {
175 raw_.base_addr = p;
176 return *this;
177 }
178
179 bool IsPointer() const { return raw_.attribute == CFI_attribute_pointer; }
180 bool IsAllocatable() const {
181 return raw_.attribute == CFI_attribute_allocatable;
182 }
183 bool IsAllocated() const { return raw_.base_addr != nullptr; }
184
185 Dimension &GetDimension(int dim) {
186 return *reinterpret_cast<Dimension *>(&raw_.dim[dim]);
187 }
188 const Dimension &GetDimension(int dim) const {
189 return *reinterpret_cast<const Dimension *>(&raw_.dim[dim]);
190 }
191
192 std::size_t SubscriptByteOffset(
193 int dim, SubscriptValue subscriptValue) const {
194 const Dimension &dimension{GetDimension(dim)};
195 return (subscriptValue - dimension.LowerBound()) * dimension.ByteStride();
196 }
197
198 std::size_t SubscriptsToByteOffset(const SubscriptValue subscript[]) const {
199 std::size_t offset{0};
200 for (int j{0}; j < raw_.rank; ++j) {
201 offset += SubscriptByteOffset(j, subscript[j]);
202 }
203 return offset;
204 }
205
206 template <typename A = char> A *OffsetElement(std::size_t offset = 0) const {
207 return reinterpret_cast<A *>(
208 reinterpret_cast<char *>(raw_.base_addr) + offset);
209 }
210
211 template <typename A> A *Element(const SubscriptValue subscript[]) const {
212 return OffsetElement<A>(SubscriptsToByteOffset(subscript));
213 }
214
215 template <typename A> A *ZeroBasedIndexedElement(std::size_t n) const {
216 SubscriptValue at[maxRank];
217 if (SubscriptsForZeroBasedElementNumber(at, n)) {
218 return Element<A>(at);
219 }
220 return nullptr;
221 }
222
223 void GetLowerBounds(SubscriptValue subscript[]) const {
224 for (int j{0}; j < raw_.rank; ++j) {
225 subscript[j] = GetDimension(j).LowerBound();
226 }
227 }
228
229 // When the passed subscript vector contains the last (or first)
230 // subscripts of the array, these wrap the subscripts around to
231 // their first (or last) values and return false.
232 bool IncrementSubscripts(
233 SubscriptValue[], const int *permutation = nullptr) const;
234 bool DecrementSubscripts(
235 SubscriptValue[], const int *permutation = nullptr) const;
236 // False when out of range.
237 bool SubscriptsForZeroBasedElementNumber(SubscriptValue *,
238 std::size_t elementNumber, const int *permutation = nullptr) const;
239 std::size_t ZeroBasedElementNumber(
240 const SubscriptValue *, const int *permutation = nullptr) const;
241
242 DescriptorAddendum *Addendum() {
243 if (raw_.f18Addendum != 0) {
244 return reinterpret_cast<DescriptorAddendum *>(&GetDimension(rank()));
245 } else {
246 return nullptr;
247 }
248 }
249 const DescriptorAddendum *Addendum() const {
250 if (raw_.f18Addendum != 0) {
251 return reinterpret_cast<const DescriptorAddendum *>(
252 &GetDimension(rank()));
253 } else {
254 return nullptr;
255 }
256 }
257
258 static constexpr std::size_t SizeInBytes(
259 int rank, bool addendum = false, int lengthTypeParameters = 0) {
260 std::size_t bytes{sizeof(Descriptor) - sizeof(Dimension)};
261 bytes += rank * sizeof(Dimension);
262 if (addendum || lengthTypeParameters > 0) {
263 bytes += DescriptorAddendum::SizeInBytes(lengthTypeParameters);
264 }
265 return bytes;
266 }
267
268 std::size_t SizeInBytes() const;
269
270 std::size_t Elements() const;
271
272 // TODO: SOURCE= and MOLD=
273 int Allocate(const SubscriptValue lb[], const SubscriptValue ub[]);
274 int Deallocate(bool finalize = true);
275 void Destroy(char *data, bool finalize = true) const;
276
277 bool IsContiguous(int leadingDimensions = maxRank) const {
278 auto bytes{static_cast<SubscriptValue>(ElementBytes())};
279 for (int j{0}; j < leadingDimensions && j < raw_.rank; ++j) {
280 const Dimension &dim{GetDimension(j)};
281 if (bytes != dim.ByteStride()) {
282 return false;
283 }
284 bytes *= dim.Extent();
285 }
286 return true;
287 }
288
289 void Check() const;
290
291 // TODO: creation of array sections
292
293 void Dump(FILE * = stdout) const;
294
295 private:
296 ISO::CFI_cdesc_t raw_;
297 };
298 static_assert(sizeof(Descriptor) == sizeof(ISO::CFI_cdesc_t));
299
300 // Properly configured instances of StaticDescriptor will occupy the
301 // exact amount of storage required for the descriptor, its dimensional
302 // information, and possible addendum. To build such a static descriptor,
303 // declare an instance of StaticDescriptor<>, extract a reference to its
304 // descriptor via the descriptor() accessor, and then built a Descriptor
305 // therein via descriptor.Establish(), e.g.:
306 // StaticDescriptor<R,A,LP> statDesc;
307 // Descriptor &descriptor{statDesc.descriptor()};
308 // descriptor.Establish( ... );
309 template <int MAX_RANK = maxRank, bool ADDENDUM = false, int MAX_LEN_PARMS = 0>
310 class alignas(Descriptor) StaticDescriptor {
311 public:
312 static constexpr int maxRank{MAX_RANK};
313 static constexpr int maxLengthTypeParameters{MAX_LEN_PARMS};
314 static constexpr bool hasAddendum{ADDENDUM || MAX_LEN_PARMS > 0};
315 static constexpr std::size_t byteSize{
316 Descriptor::SizeInBytes(maxRank, hasAddendum, maxLengthTypeParameters)};
317
318 StaticDescriptor() { new (storage_) Descriptor{}; }
319
320 ~StaticDescriptor() { descriptor().~Descriptor(); }
321
322 Descriptor &descriptor() { return *reinterpret_cast<Descriptor *>(storage_); }
323 const Descriptor &descriptor() const {
324 return *reinterpret_cast<const Descriptor *>(storage_);
325 }
326
327 void Check() {
328 assert(descriptor().rank() <= maxRank);
329 assert(descriptor().SizeInBytes() <= byteSize);
330 if (DescriptorAddendum * addendum{descriptor().Addendum()}) {
331 assert(hasAddendum);
332 if (const DerivedType * dt{addendum->derivedType()}) {
333 assert(dt->lenParameters() <= maxLengthTypeParameters);
334 } else {
335 assert(maxLengthTypeParameters == 0);
336 }
337 } else {
338 assert(!hasAddendum);
339 assert(maxLengthTypeParameters == 0);
340 }
341 descriptor().Check();
342 }
343
344 private:
345 char storage_[byteSize];
346 };
347 } // namespace Fortran::runtime
348 #endif // FORTRAN_RUNTIME_DESCRIPTOR_H_
Fortran::runtime::StaticDescriptor
Definition: descriptor.h:310
Fortran::runtime::DerivedType
Definition: derived-type.h:119
Fortran::runtime::Descriptor
Definition: descriptor.h:109
Fortran::runtime::Dimension
Definition: descriptor.h:40
Fortran::runtime::TypeCode
Definition: type-code.h:19
Fortran::runtime::DescriptorAddendum
Definition: descriptor.h:57