// Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef FORTRAN_RUNTIME_DESCRIPTOR_H_ #define FORTRAN_RUNTIME_DESCRIPTOR_H_ // Defines data structures used during execution of a Fortran program // to implement nontrivial dummy arguments, pointers, allocatables, // function results, and the special behaviors of instances of derived types. // This header file includes and extends the published language // interoperability header that is required by the Fortran 2018 standard // as a subset of definitions suitable for exposure to user C/C++ code. // User C code is welcome to depend on that ISO_Fortran_binding.h file, // but should never reference this internal header. #include "derived-type.h" #include "type-code.h" #include "../include/flang/ISO_Fortran_binding.h" #include #include #include #include #include #include namespace Fortran::runtime { using SubscriptValue = ISO::CFI_index_t; static constexpr int maxRank{CFI_MAX_RANK}; // A C++ view of the sole interoperable standard descriptor (ISO::CFI_cdesc_t) // and its type and per-dimension information. class Dimension { public: SubscriptValue LowerBound() const { return raw_.lower_bound; } SubscriptValue Extent() const { return raw_.extent; } SubscriptValue UpperBound() const { return LowerBound() + Extent() - 1; } SubscriptValue ByteStride() const { return raw_.sm; } private: ISO::CFI_dim_t raw_; }; // The storage for this object follows the last used dim[] entry in a // Descriptor (CFI_cdesc_t) generic descriptor. Space matters here, since // descriptors serve as POINTER and ALLOCATABLE components of derived type // instances. The presence of this structure is implied by the flag // (CFI_cdesc_t.extra & ADDENDUM) != 0, and the number of elements in // the len_[] array is determined by DerivedType::lenParameters(). class DescriptorAddendum { public: enum Flags { StaticDescriptor = 0x001, ImplicitAllocatable = 0x002, // compiler-created allocatable DoNotFinalize = 0x004, // compiler temporary Target = 0x008, // TARGET attribute }; explicit DescriptorAddendum( const DerivedType *dt = nullptr, std::uint64_t flags = 0) : derivedType_{dt}, flags_{flags} {} const DerivedType *derivedType() const { return derivedType_; } DescriptorAddendum &set_derivedType(const DerivedType *dt) { derivedType_ = dt; return *this; } std::uint64_t &flags() { return flags_; } const std::uint64_t &flags() const { return flags_; } std::size_t LenParameters() const { if (derivedType_ != nullptr) { return derivedType_->lenParameters(); } return 0; } TypeParameterValue LenParameterValue(int which) const { return len_[which]; } static constexpr std::size_t SizeInBytes(int lenParameters) { return sizeof(DescriptorAddendum) - sizeof(TypeParameterValue) + lenParameters * sizeof(TypeParameterValue); } std::size_t SizeInBytes() const; void SetLenParameterValue(int which, TypeParameterValue x) { len_[which] = x; } std::ostream &Dump(std::ostream &) const; private: const DerivedType *derivedType_{nullptr}; std::uint64_t flags_{0}; TypeParameterValue len_[1]; // must be the last component // The LEN type parameter values can also include captured values of // specification expressions that were used for bounds and for LEN type // parameters of components. The values have been truncated to the LEN // type parameter's type, if shorter than 64 bits, then sign-extended. }; // A C++ view of a standard descriptor object. class Descriptor { public: // Be advised: this class type is not suitable for use when allocating // a descriptor -- it is a dynamic view of the common descriptor format. // If used in a simple declaration of a local variable or dynamic allocation, // the size is going to be correct only by accident, since the true size of // a descriptor depends on the number of its dimensions and the presence and // size of an addendum, which depends on the type of the data. // Use the class template StaticDescriptor (below) to declare a descriptor // whose type and rank are fixed and known at compilation time. Use the // Create() static member functions otherwise to dynamically allocate a // descriptor. Descriptor() { // Minimal initialization to prevent the destructor from running amuck // later if the descriptor is never established. raw_.base_addr = nullptr; raw_.f18Addendum = false; } ~Descriptor(); void Establish(TypeCode t, std::size_t elementBytes, void *p = nullptr, int rank = maxRank, const SubscriptValue *extent = nullptr, ISO::CFI_attribute_t attribute = CFI_attribute_other, bool addendum = false); void Establish(TypeCategory, int kind, void *p = nullptr, int rank = maxRank, const SubscriptValue *extent = nullptr, ISO::CFI_attribute_t attribute = CFI_attribute_other, bool addendum = false); void Establish(const DerivedType &dt, void *p = nullptr, int rank = maxRank, const SubscriptValue *extent = nullptr, ISO::CFI_attribute_t attribute = CFI_attribute_other); static std::unique_ptr Create(TypeCode t, std::size_t elementBytes, void *p = nullptr, int rank = maxRank, const SubscriptValue *extent = nullptr, ISO::CFI_attribute_t attribute = CFI_attribute_other); static std::unique_ptr Create(TypeCategory, int kind, void *p = nullptr, int rank = maxRank, const SubscriptValue *extent = nullptr, ISO::CFI_attribute_t attribute = CFI_attribute_other); static std::unique_ptr Create(const DerivedType &dt, void *p = nullptr, int rank = maxRank, const SubscriptValue *extent = nullptr, ISO::CFI_attribute_t attribute = CFI_attribute_other); ISO::CFI_cdesc_t &raw() { return raw_; } const ISO::CFI_cdesc_t &raw() const { return raw_; } std::size_t ElementBytes() const { return raw_.elem_len; } int rank() const { return raw_.rank; } TypeCode type() const { return TypeCode{raw_.type}; } Descriptor &set_base_addr(void *p) { raw_.base_addr = p; return *this; } bool IsPointer() const { return raw_.attribute == CFI_attribute_pointer; } bool IsAllocatable() const { return raw_.attribute == CFI_attribute_allocatable; } bool IsAllocated() const { return raw_.base_addr != nullptr; } Dimension &GetDimension(int dim) { return *reinterpret_cast(&raw_.dim[dim]); } const Dimension &GetDimension(int dim) const { return *reinterpret_cast(&raw_.dim[dim]); } std::size_t SubscriptByteOffset( int dim, SubscriptValue subscriptValue) const { const Dimension &dimension{GetDimension(dim)}; return (subscriptValue - dimension.LowerBound()) * dimension.ByteStride(); } std::size_t SubscriptsToByteOffset(const SubscriptValue *subscript) const { std::size_t offset{0}; for (int j{0}; j < raw_.rank; ++j) { offset += SubscriptByteOffset(j, subscript[j]); } return offset; } template A *Element(std::size_t offset) const { return reinterpret_cast( reinterpret_cast(raw_.base_addr) + offset); } template A *Element(const SubscriptValue *subscript) const { return Element(SubscriptsToByteOffset(subscript)); } void GetLowerBounds(SubscriptValue *subscript) const { for (int j{0}; j < raw_.rank; ++j) { subscript[j] = GetDimension(j).LowerBound(); } } void IncrementSubscripts( SubscriptValue *subscript, const int *permutation = nullptr) const { for (int j{0}; j < raw_.rank; ++j) { int k{permutation ? permutation[j] : j}; const Dimension &dim{GetDimension(k)}; if (subscript[k]++ < dim.UpperBound()) { break; } subscript[k] = dim.LowerBound(); } } DescriptorAddendum *Addendum() { if (raw_.f18Addendum != 0) { return reinterpret_cast(&GetDimension(rank())); } else { return nullptr; } } const DescriptorAddendum *Addendum() const { if (raw_.f18Addendum != 0) { return reinterpret_cast( &GetDimension(rank())); } else { return nullptr; } } static constexpr std::size_t SizeInBytes( int rank, bool addendum = false, int lengthTypeParameters = 0) { std::size_t bytes{sizeof(Descriptor) - sizeof(Dimension)}; bytes += rank * sizeof(Dimension); if (addendum || lengthTypeParameters > 0) { bytes += DescriptorAddendum::SizeInBytes(lengthTypeParameters); } return bytes; } std::size_t SizeInBytes() const; std::size_t Elements() const; int Allocate(const SubscriptValue lb[], const SubscriptValue ub[], std::size_t charLen = 0); // TODO: SOURCE= and MOLD= int Deallocate(bool finalize = true); void Destroy(char *data, bool finalize = true) const; bool IsContiguous(int leadingDimensions = maxRank) const { auto bytes{static_cast(ElementBytes())}; for (int j{0}; j < leadingDimensions && j < raw_.rank; ++j) { const Dimension &dim{GetDimension(j)}; if (bytes != dim.ByteStride()) { return false; } bytes *= dim.Extent(); } return true; } void Check() const; // TODO: creation of array sections std::ostream &Dump(std::ostream &) const; private: ISO::CFI_cdesc_t raw_; }; static_assert(sizeof(Descriptor) == sizeof(ISO::CFI_cdesc_t)); // Properly configured instances of StaticDescriptor will occupy the // exact amount of storage required for the descriptor, its dimensional // information, and possible addendum. To build such a static descriptor, // declare an instance of StaticDescriptor<>, extract a reference to its // descriptor via the descriptor() accessor, and then built a Descriptor // therein via descriptor.Establish(), e.g.: // StaticDescriptor statDesc; // Descriptor &descriptor{statDesc.descriptor()}; // descriptor.Establish( ... ); template class alignas(Descriptor) StaticDescriptor { public: static constexpr int maxRank{MAX_RANK}; static constexpr int maxLengthTypeParameters{MAX_LEN_PARMS}; static constexpr bool hasAddendum{ADDENDUM || MAX_LEN_PARMS > 0}; static constexpr std::size_t byteSize{ Descriptor::SizeInBytes(maxRank, hasAddendum, maxLengthTypeParameters)}; StaticDescriptor() { new (storage_) Descriptor{}; } ~StaticDescriptor() { descriptor().~Descriptor(); } Descriptor &descriptor() { return *reinterpret_cast(storage_); } const Descriptor &descriptor() const { return *reinterpret_cast(storage_); } void Check() { assert(descriptor().rank() <= maxRank); assert(descriptor().SizeInBytes() <= byteSize); if (DescriptorAddendum * addendum{descriptor().Addendum()}) { assert(hasAddendum); if (const DerivedType * dt{addendum->derivedType()}) { assert(dt->lenParameters() <= maxLengthTypeParameters); } else { assert(maxLengthTypeParameters == 0); } } else { assert(!hasAddendum); assert(maxLengthTypeParameters == 0); } descriptor().Check(); } private: char storage_[byteSize]; }; } #endif // FORTRAN_RUNTIME_DESCRIPTOR_H_