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