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
31namespace Fortran::runtime::typeInfo {
32using TypeParameterValue = std::int64_t;
33class DerivedType;
34} // namespace Fortran::runtime::typeInfo
35
36namespace Fortran::runtime {
37
38using SubscriptValue = ISO::CFI_index_t;
39class Terminator;
40
41RT_VAR_GROUP_BEGIN
42static constexpr RT_CONST_VAR_ATTRS int maxRank{CFI_MAX_RANK};
43RT_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
48class Dimension {
49public:
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
88private:
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().
98class DescriptorAddendum {
99public:
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
133private:
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.
143class Descriptor {
144public:
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
423private:
424 ISO::CFI_cdesc_t raw_;
425};
426static_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( ... );
437template <int MAX_RANK = maxRank, bool ADDENDUM = false, int MAX_LEN_PARMS = 0>
438class alignas(Descriptor) StaticDescriptor {
439public:
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
468private:
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.

source code of flang/include/flang/Runtime/descriptor.h