FLANG
descriptor.h
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
32namespace Fortran::runtime {
33
34class Terminator;
35
36RT_VAR_GROUP_BEGIN
37static constexpr RT_CONST_VAR_ATTRS int maxRank{CFI_MAX_RANK};
38RT_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
43class Dimension {
44public:
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
83private:
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().
94public:
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
128private:
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.
139public:
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
432private:
433 ISO::CFI_cdesc_t raw_;
434};
435static_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( ... );
446template <int MAX_RANK = maxRank, bool ADDENDUM = false, int MAX_LEN_PARMS = 0>
447class alignas(Descriptor) StaticDescriptor {
448public:
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
480private:
481 char storage_[byteSize]{};
482};
483
484} // namespace Fortran::runtime
485#endif // FORTRAN_RUNTIME_DESCRIPTOR_H_
Definition: descriptor.h:93
Definition: descriptor.h:138
Definition: descriptor.h:43
Definition: memory.h:45
Definition: descriptor.h:447
Definition: terminator.h:23
Definition: type-code.h:21
Definition: type-info.h:204