File: | build/source/flang/include/flang/Runtime/descriptor.h |
Warning: | line 219, column 17 2nd function call argument is an uninitialized value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===-- runtime/array-constructor.cpp -------------------------------------===// | |||
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 | #include "flang/Runtime/array-constructor.h" | |||
10 | #include "derived.h" | |||
11 | #include "terminator.h" | |||
12 | #include "type-info.h" | |||
13 | #include "flang/Runtime/allocatable.h" | |||
14 | #include "flang/Runtime/assign.h" | |||
15 | #include "flang/Runtime/descriptor.h" | |||
16 | ||||
17 | namespace Fortran::runtime { | |||
18 | ||||
19 | // Initial allocation size for an array constructor temporary whose extent | |||
20 | // cannot be pre-computed. This could be fined tuned if needed based on actual | |||
21 | // program performance. | |||
22 | // REAL(4), INTEGER(4), COMPLEX(2), ... -> 32 elements. | |||
23 | // REAL(8), INTEGER(8), COMPLEX(4), ... -> 16 elements. | |||
24 | // REAL(16), INTEGER(16), COMPLEX(8), ... -> 8 elements. | |||
25 | // Bigger types -> 4 elements. | |||
26 | static SubscriptValue initialAllocationSize( | |||
27 | SubscriptValue initialNumberOfElements, SubscriptValue elementBytes) { | |||
28 | // Try to guess an optimal initial allocation size in number of elements to | |||
29 | // avoid doing too many reallocation. | |||
30 | static constexpr SubscriptValue minNumberOfBytes{128}; | |||
31 | static constexpr SubscriptValue minNumberOfElements{4}; | |||
32 | SubscriptValue numberOfElements{initialNumberOfElements > minNumberOfElements | |||
33 | ? initialNumberOfElements | |||
34 | : minNumberOfElements}; | |||
35 | SubscriptValue elementsForMinBytes{minNumberOfBytes / elementBytes}; | |||
36 | return std::max(numberOfElements, elementsForMinBytes); | |||
37 | } | |||
38 | ||||
39 | static void AllocateOrReallocateVectorIfNeeded(ArrayConstructorVector &vector, | |||
40 | Terminator &terminator, SubscriptValue previousToElements, | |||
41 | SubscriptValue fromElements) { | |||
42 | Descriptor &to{vector.to}; | |||
43 | if (to.IsAllocatable() && !to.IsAllocated()) { | |||
44 | // The descriptor bounds may already be set here if the array constructor | |||
45 | // extent could be pre-computed, but information about length parameters | |||
46 | // was missing and required evaluating the first array constructor value. | |||
47 | if (previousToElements == 0) { | |||
48 | SubscriptValue allocationSize{ | |||
49 | initialAllocationSize(fromElements, to.ElementBytes())}; | |||
50 | to.GetDimension(0).SetBounds(1, allocationSize); | |||
51 | RTNAME(AllocatableAllocate)_FortranAAllocatableAllocate | |||
52 | (to, /*hasStat=*/false, /*errMsg=*/nullptr, vector.sourceFile, | |||
53 | vector.sourceLine); | |||
54 | to.GetDimension(0).SetBounds(1, fromElements); | |||
55 | vector.actualAllocationSize = allocationSize; | |||
56 | } else { | |||
57 | // Do not over-allocate if the final extent was known before pushing the | |||
58 | // first value: there should be no reallocation. | |||
59 | RUNTIME_CHECK(terminator, previousToElements >= fromElements)if (previousToElements >= fromElements) ; else (terminator ).CheckFailed("previousToElements >= fromElements", "flang/runtime/array-constructor.cpp" , 59); | |||
60 | RTNAME(AllocatableAllocate)_FortranAAllocatableAllocate | |||
61 | (to, /*hasStat=*/false, /*errMsg=*/nullptr, vector.sourceFile, | |||
62 | vector.sourceLine); | |||
63 | vector.actualAllocationSize = previousToElements; | |||
64 | } | |||
65 | } else { | |||
66 | SubscriptValue newToElements{vector.nextValuePosition + fromElements}; | |||
67 | if (to.IsAllocatable() && vector.actualAllocationSize < newToElements) { | |||
68 | // Reallocate. Ensure the current storage is at least doubled to avoid | |||
69 | // doing too many reallocations. | |||
70 | SubscriptValue requestedAllocationSize{ | |||
71 | std::max(newToElements, vector.actualAllocationSize * 2)}; | |||
72 | std::size_t newByteSize{requestedAllocationSize * to.ElementBytes()}; | |||
73 | // realloc is undefined with zero new size and ElementBytes() may be null | |||
74 | // if the character length is null, or if "from" is a zero sized array. | |||
75 | if (newByteSize > 0) { | |||
76 | void *p{std::realloc(to.raw().base_addr, newByteSize)}; | |||
77 | RUNTIME_CHECK(terminator, p)if (p) ; else (terminator).CheckFailed("p", "flang/runtime/array-constructor.cpp" , 77); | |||
78 | to.set_base_addr(p); | |||
79 | } | |||
80 | vector.actualAllocationSize = requestedAllocationSize; | |||
81 | to.GetDimension(0).SetBounds(1, newToElements); | |||
82 | } else if (previousToElements < newToElements) { | |||
83 | // Storage is big enough, but descriptor extent must be increased because | |||
84 | // the final extent was not known before pushing array constructor values. | |||
85 | to.GetDimension(0).SetBounds(1, newToElements); | |||
86 | } | |||
87 | } | |||
88 | } | |||
89 | ||||
90 | extern "C" { | |||
91 | void RTNAME(InitArrayConstructorVector)_FortranAInitArrayConstructorVector(ArrayConstructorVector &vector, | |||
92 | Descriptor &to, bool useValueLengthParameters, int vectorClassSize, | |||
93 | const char *sourceFile, int sourceLine) { | |||
94 | Terminator terminator{vector.sourceFile, vector.sourceLine}; | |||
95 | RUNTIME_CHECK(terminator,if (to.rank() == 1 && sizeof(ArrayConstructorVector) <= static_cast<std::size_t>(vectorClassSize)) ; else (terminator ).CheckFailed("to.rank() == 1 && sizeof(ArrayConstructorVector) <= static_cast<std::size_t>(vectorClassSize)" , "flang/runtime/array-constructor.cpp", 98) | |||
96 | to.rank() == 1 &&if (to.rank() == 1 && sizeof(ArrayConstructorVector) <= static_cast<std::size_t>(vectorClassSize)) ; else (terminator ).CheckFailed("to.rank() == 1 && sizeof(ArrayConstructorVector) <= static_cast<std::size_t>(vectorClassSize)" , "flang/runtime/array-constructor.cpp", 98) | |||
97 | sizeof(ArrayConstructorVector) <=if (to.rank() == 1 && sizeof(ArrayConstructorVector) <= static_cast<std::size_t>(vectorClassSize)) ; else (terminator ).CheckFailed("to.rank() == 1 && sizeof(ArrayConstructorVector) <= static_cast<std::size_t>(vectorClassSize)" , "flang/runtime/array-constructor.cpp", 98) | |||
98 | static_cast<std::size_t>(vectorClassSize))if (to.rank() == 1 && sizeof(ArrayConstructorVector) <= static_cast<std::size_t>(vectorClassSize)) ; else (terminator ).CheckFailed("to.rank() == 1 && sizeof(ArrayConstructorVector) <= static_cast<std::size_t>(vectorClassSize)" , "flang/runtime/array-constructor.cpp", 98); | |||
99 | SubscriptValue actualAllocationSize{ | |||
100 | to.IsAllocated() ? static_cast<SubscriptValue>(to.Elements()) : 0}; | |||
101 | (void)new (&vector) ArrayConstructorVector{to, /*nextValuePosition=*/0, | |||
102 | actualAllocationSize, sourceFile, sourceLine, useValueLengthParameters}; | |||
103 | } | |||
104 | ||||
105 | void RTNAME(PushArrayConstructorValue)_FortranAPushArrayConstructorValue( | |||
106 | ArrayConstructorVector &vector, const Descriptor &from) { | |||
107 | Terminator terminator{vector.sourceFile, vector.sourceLine}; | |||
108 | Descriptor &to{vector.to}; | |||
109 | SubscriptValue fromElements{static_cast<SubscriptValue>(from.Elements())}; | |||
110 | SubscriptValue previousToElements{static_cast<SubscriptValue>(to.Elements())}; | |||
111 | if (vector.useValueLengthParameters()) { | |||
112 | // Array constructor with no type spec. | |||
113 | if (to.IsAllocatable() && !to.IsAllocated()) { | |||
114 | // Takes length parameters, if any, from the first value. | |||
115 | // Note that "to" type must already be set by the caller of this API since | |||
116 | // it cannot be taken from "from" here: "from" may be polymorphic (have a | |||
117 | // dynamic type that differs from its declared type) and Fortran 2018 7.8 | |||
118 | // point 4. says that the dynamic type of an array constructor is its | |||
119 | // declared type: it does not inherit the dynamic type of its ac-value | |||
120 | // even if if there is no type-spec. | |||
121 | if (to.type().IsCharacter()) { | |||
122 | to.raw().elem_len = from.ElementBytes(); | |||
123 | } else if (auto *toAddendum{to.Addendum()}) { | |||
124 | if (const auto *fromAddendum{from.Addendum()}) { | |||
125 | if (const auto *toDerived{toAddendum->derivedType()}) { | |||
126 | std::size_t lenParms{toDerived->LenParameters()}; | |||
127 | for (std::size_t j{0}; j < lenParms; ++j) { | |||
128 | toAddendum->SetLenParameterValue( | |||
129 | j, fromAddendum->LenParameterValue(j)); | |||
130 | } | |||
131 | } | |||
132 | } | |||
133 | } | |||
134 | } else if (to.type().IsCharacter()) { | |||
135 | // Fortran 2018 7.8 point 2. | |||
136 | if (to.ElementBytes() != from.ElementBytes()) { | |||
137 | terminator.Crash("Array constructor: mismatched character lengths (%d " | |||
138 | "!= %d) between " | |||
139 | "values of an array constructor without type-spec", | |||
140 | to.ElementBytes() / to.type().GetCategoryAndKind()->second, | |||
141 | from.ElementBytes() / from.type().GetCategoryAndKind()->second); | |||
142 | } | |||
143 | } | |||
144 | } | |||
145 | // Otherwise, the array constructor had a type-spec and the length | |||
146 | // parameters are already in the "to" descriptor. | |||
147 | ||||
148 | AllocateOrReallocateVectorIfNeeded( | |||
149 | vector, terminator, previousToElements, fromElements); | |||
150 | ||||
151 | // Create descriptor for "to" element or section being copied to. | |||
152 | SubscriptValue lower[1]{ | |||
153 | to.GetDimension(0).LowerBound() + vector.nextValuePosition}; | |||
154 | SubscriptValue upper[1]{lower[0] + fromElements - 1}; | |||
155 | SubscriptValue stride[1]{from.rank() == 0 ? 0 : 1}; | |||
156 | StaticDescriptor<maxRank, true, 1> staticDesc; | |||
157 | Descriptor &toCurrentElement{staticDesc.descriptor()}; | |||
158 | toCurrentElement.EstablishPointerSection(to, lower, upper, stride); | |||
159 | // Note: toCurrentElement and from have the same number of elements | |||
160 | // and "toCurrentElement" is not an allocatable so AssignTemporary | |||
161 | // below works even if "from" rank is bigger than one (and differs | |||
162 | // from "toCurrentElement") and not time is wasted reshaping | |||
163 | // "toCurrentElement" to "from" shape. | |||
164 | RTNAME(AssignTemporary)_FortranAAssignTemporary | |||
165 | (toCurrentElement, from, vector.sourceFile, vector.sourceLine); | |||
166 | vector.nextValuePosition += fromElements; | |||
167 | } | |||
168 | ||||
169 | void RTNAME(PushArrayConstructorSimpleScalar)_FortranAPushArrayConstructorSimpleScalar( | |||
170 | ArrayConstructorVector &vector, void *from) { | |||
171 | Terminator terminator{vector.sourceFile, vector.sourceLine}; | |||
172 | Descriptor &to{vector.to}; | |||
173 | AllocateOrReallocateVectorIfNeeded(vector, terminator, to.Elements(), 1); | |||
174 | SubscriptValue subscript[1]{ | |||
175 | to.GetDimension(0).LowerBound() + vector.nextValuePosition}; | |||
176 | std::memcpy(to.Element<char>(subscript), from, to.ElementBytes()); | |||
| ||||
177 | ++vector.nextValuePosition; | |||
178 | } | |||
179 | } // extern "C" | |||
180 | } // namespace Fortran::runtime |
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.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 | ||||
31 | namespace Fortran::runtime::typeInfo { | |||
32 | using TypeParameterValue = std::int64_t; | |||
33 | class DerivedType; | |||
34 | } // namespace Fortran::runtime::typeInfo | |||
35 | ||||
36 | namespace Fortran::runtime { | |||
37 | ||||
38 | using SubscriptValue = ISO::CFI_index_t; | |||
39 | ||||
40 | static constexpr int maxRank{CFI_MAX_RANK15}; | |||
41 | ||||
42 | // A C++ view of the sole interoperable standard descriptor (ISO::CFI_cdesc_t) | |||
43 | // and its type and per-dimension information. | |||
44 | ||||
45 | class Dimension { | |||
46 | public: | |||
47 | SubscriptValue LowerBound() const { return raw_.lower_bound; } | |||
48 | SubscriptValue Extent() const { return raw_.extent; } | |||
49 | SubscriptValue UpperBound() const { return LowerBound() + Extent() - 1; } | |||
50 | SubscriptValue ByteStride() const { return raw_.sm; } | |||
51 | ||||
52 | Dimension &SetBounds(SubscriptValue lower, SubscriptValue upper) { | |||
53 | if (upper >= lower) { | |||
54 | raw_.lower_bound = lower; | |||
55 | raw_.extent = upper - lower + 1; | |||
56 | } else { | |||
57 | raw_.lower_bound = 1; | |||
58 | raw_.extent = 0; | |||
59 | } | |||
60 | return *this; | |||
61 | } | |||
62 | // Do not use this API to cause the LB of an empty dimension | |||
63 | // to be anything other than 1. Use SetBounds() instead if you can. | |||
64 | Dimension &SetLowerBound(SubscriptValue lower) { | |||
65 | raw_.lower_bound = lower; | |||
66 | return *this; | |||
67 | } | |||
68 | Dimension &SetUpperBound(SubscriptValue upper) { | |||
69 | auto lower{raw_.lower_bound}; | |||
70 | raw_.extent = upper >= lower ? upper - lower + 1 : 0; | |||
71 | return *this; | |||
72 | } | |||
73 | Dimension &SetExtent(SubscriptValue extent) { | |||
74 | raw_.extent = extent; | |||
75 | return *this; | |||
76 | } | |||
77 | Dimension &SetByteStride(SubscriptValue bytes) { | |||
78 | raw_.sm = bytes; | |||
79 | return *this; | |||
80 | } | |||
81 | ||||
82 | private: | |||
83 | ISO::CFI_dim_t raw_; | |||
84 | }; | |||
85 | ||||
86 | // The storage for this object follows the last used dim[] entry in a | |||
87 | // Descriptor (CFI_cdesc_t) generic descriptor. Space matters here, since | |||
88 | // descriptors serve as POINTER and ALLOCATABLE components of derived type | |||
89 | // instances. The presence of this structure is implied by the flag | |||
90 | // CFI_cdesc_t.f18Addendum, and the number of elements in the len_[] | |||
91 | // array is determined by derivedType_->LenParameters(). | |||
92 | class DescriptorAddendum { | |||
93 | public: | |||
94 | explicit DescriptorAddendum(const typeInfo::DerivedType *dt = nullptr) | |||
95 | : derivedType_{dt} {} | |||
96 | DescriptorAddendum &operator=(const DescriptorAddendum &); | |||
97 | ||||
98 | const typeInfo::DerivedType *derivedType() const { return derivedType_; } | |||
99 | DescriptorAddendum &set_derivedType(const typeInfo::DerivedType *dt) { | |||
100 | derivedType_ = dt; | |||
101 | return *this; | |||
102 | } | |||
103 | ||||
104 | std::size_t LenParameters() const; | |||
105 | ||||
106 | typeInfo::TypeParameterValue LenParameterValue(int which) const { | |||
107 | return len_[which]; | |||
108 | } | |||
109 | static constexpr std::size_t SizeInBytes(int lenParameters) { | |||
110 | // TODO: Don't waste that last word if lenParameters == 0 | |||
111 | return sizeof(DescriptorAddendum) + | |||
112 | std::max(lenParameters - 1, 0) * sizeof(typeInfo::TypeParameterValue); | |||
113 | } | |||
114 | std::size_t SizeInBytes() const; | |||
115 | ||||
116 | void SetLenParameterValue(int which, typeInfo::TypeParameterValue x) { | |||
117 | len_[which] = x; | |||
118 | } | |||
119 | ||||
120 | void Dump(FILE * = stdoutstdout) const; | |||
121 | ||||
122 | private: | |||
123 | const typeInfo::DerivedType *derivedType_; | |||
124 | typeInfo::TypeParameterValue len_[1]; // must be the last component | |||
125 | // The LEN type parameter values can also include captured values of | |||
126 | // specification expressions that were used for bounds and for LEN type | |||
127 | // parameters of components. The values have been truncated to the LEN | |||
128 | // type parameter's type, if shorter than 64 bits, then sign-extended. | |||
129 | }; | |||
130 | ||||
131 | // A C++ view of a standard descriptor object. | |||
132 | class Descriptor { | |||
133 | public: | |||
134 | // Be advised: this class type is not suitable for use when allocating | |||
135 | // a descriptor -- it is a dynamic view of the common descriptor format. | |||
136 | // If used in a simple declaration of a local variable or dynamic allocation, | |||
137 | // the size is going to be correct only by accident, since the true size of | |||
138 | // a descriptor depends on the number of its dimensions and the presence and | |||
139 | // size of an addendum, which depends on the type of the data. | |||
140 | // Use the class template StaticDescriptor (below) to declare a descriptor | |||
141 | // whose type and rank are fixed and known at compilation time. Use the | |||
142 | // Create() static member functions otherwise to dynamically allocate a | |||
143 | // descriptor. | |||
144 | ||||
145 | Descriptor(const Descriptor &); | |||
146 | Descriptor &operator=(const Descriptor &); | |||
147 | ||||
148 | // Returns the number of bytes occupied by an element of the given | |||
149 | // category and kind including any alignment padding required | |||
150 | // between adjacent elements. | |||
151 | static std::size_t BytesFor(TypeCategory category, int kind); | |||
152 | ||||
153 | void Establish(TypeCode t, std::size_t elementBytes, void *p = nullptr, | |||
154 | int rank = maxRank, const SubscriptValue *extent = nullptr, | |||
155 | ISO::CFI_attribute_t attribute = CFI_attribute_other0, | |||
156 | bool addendum = false); | |||
157 | void Establish(TypeCategory, int kind, void *p = nullptr, int rank = maxRank, | |||
158 | const SubscriptValue *extent = nullptr, | |||
159 | ISO::CFI_attribute_t attribute = CFI_attribute_other0, | |||
160 | bool addendum = false); | |||
161 | void Establish(int characterKind, std::size_t characters, void *p = nullptr, | |||
162 | int rank = maxRank, const SubscriptValue *extent = nullptr, | |||
163 | ISO::CFI_attribute_t attribute = CFI_attribute_other0, | |||
164 | bool addendum = false); | |||
165 | void Establish(const typeInfo::DerivedType &dt, void *p = nullptr, | |||
166 | int rank = maxRank, const SubscriptValue *extent = nullptr, | |||
167 | ISO::CFI_attribute_t attribute = CFI_attribute_other0); | |||
168 | ||||
169 | static OwningPtr<Descriptor> Create(TypeCode t, std::size_t elementBytes, | |||
170 | void *p = nullptr, int rank = maxRank, | |||
171 | const SubscriptValue *extent = nullptr, | |||
172 | ISO::CFI_attribute_t attribute = CFI_attribute_other0, | |||
173 | int derivedTypeLenParameters = 0); | |||
174 | static OwningPtr<Descriptor> Create(TypeCategory, int kind, void *p = nullptr, | |||
175 | int rank = maxRank, const SubscriptValue *extent = nullptr, | |||
176 | ISO::CFI_attribute_t attribute = CFI_attribute_other0); | |||
177 | static OwningPtr<Descriptor> Create(int characterKind, | |||
178 | SubscriptValue characters, void *p = nullptr, int rank = maxRank, | |||
179 | const SubscriptValue *extent = nullptr, | |||
180 | ISO::CFI_attribute_t attribute = CFI_attribute_other0); | |||
181 | static OwningPtr<Descriptor> Create(const typeInfo::DerivedType &dt, | |||
182 | void *p = nullptr, int rank = maxRank, | |||
183 | const SubscriptValue *extent = nullptr, | |||
184 | ISO::CFI_attribute_t attribute = CFI_attribute_other0); | |||
185 | ||||
186 | ISO::CFI_cdesc_t &raw() { return raw_; } | |||
187 | const ISO::CFI_cdesc_t &raw() const { return raw_; } | |||
188 | std::size_t ElementBytes() const { return raw_.elem_len; } | |||
189 | int rank() const { return raw_.rank; } | |||
190 | TypeCode type() const { return TypeCode{raw_.type}; } | |||
191 | ||||
192 | Descriptor &set_base_addr(void *p) { | |||
193 | raw_.base_addr = p; | |||
194 | return *this; | |||
195 | } | |||
196 | ||||
197 | bool IsPointer() const { return raw_.attribute == CFI_attribute_pointer1; } | |||
198 | bool IsAllocatable() const { | |||
199 | return raw_.attribute == CFI_attribute_allocatable2; | |||
200 | } | |||
201 | bool IsAllocated() const { return raw_.base_addr != nullptr; } | |||
202 | ||||
203 | Dimension &GetDimension(int dim) { | |||
204 | return *reinterpret_cast<Dimension *>(&raw_.dim[dim]); | |||
205 | } | |||
206 | const Dimension &GetDimension(int dim) const { | |||
207 | return *reinterpret_cast<const Dimension *>(&raw_.dim[dim]); | |||
208 | } | |||
209 | ||||
210 | std::size_t SubscriptByteOffset( | |||
211 | int dim, SubscriptValue subscriptValue) const { | |||
212 | const Dimension &dimension{GetDimension(dim)}; | |||
213 | return (subscriptValue - dimension.LowerBound()) * dimension.ByteStride(); | |||
214 | } | |||
215 | ||||
216 | std::size_t SubscriptsToByteOffset(const SubscriptValue subscript[]) const { | |||
217 | std::size_t offset{0}; | |||
218 | for (int j{0}; j < raw_.rank; ++j) { | |||
219 | offset += SubscriptByteOffset(j, subscript[j]); | |||
| ||||
220 | } | |||
221 | return offset; | |||
222 | } | |||
223 | ||||
224 | template <typename A = char> A *OffsetElement(std::size_t offset = 0) const { | |||
225 | return reinterpret_cast<A *>( | |||
226 | reinterpret_cast<char *>(raw_.base_addr) + offset); | |||
227 | } | |||
228 | ||||
229 | template <typename A> A *Element(const SubscriptValue subscript[]) const { | |||
230 | return OffsetElement<A>(SubscriptsToByteOffset(subscript)); | |||
231 | } | |||
232 | ||||
233 | template <typename A> A *ZeroBasedIndexedElement(std::size_t n) const { | |||
234 | SubscriptValue at[maxRank]; | |||
235 | if (SubscriptsForZeroBasedElementNumber(at, n)) { | |||
236 | return Element<A>(at); | |||
237 | } | |||
238 | return nullptr; | |||
239 | } | |||
240 | ||||
241 | int GetLowerBounds(SubscriptValue subscript[]) const { | |||
242 | for (int j{0}; j < raw_.rank; ++j) { | |||
243 | subscript[j] = GetDimension(j).LowerBound(); | |||
244 | } | |||
245 | return raw_.rank; | |||
246 | } | |||
247 | ||||
248 | int GetShape(SubscriptValue subscript[]) const { | |||
249 | for (int j{0}; j < raw_.rank; ++j) { | |||
250 | subscript[j] = GetDimension(j).Extent(); | |||
251 | } | |||
252 | return raw_.rank; | |||
253 | } | |||
254 | ||||
255 | // When the passed subscript vector contains the last (or first) | |||
256 | // subscripts of the array, these wrap the subscripts around to | |||
257 | // their first (or last) values and return false. | |||
258 | bool IncrementSubscripts( | |||
259 | SubscriptValue subscript[], const int *permutation = nullptr) const { | |||
260 | for (int j{0}; j < raw_.rank; ++j) { | |||
261 | int k{permutation ? permutation[j] : j}; | |||
262 | const Dimension &dim{GetDimension(k)}; | |||
263 | if (subscript[k]++ < dim.UpperBound()) { | |||
264 | return true; | |||
265 | } | |||
266 | subscript[k] = dim.LowerBound(); | |||
267 | } | |||
268 | return false; | |||
269 | } | |||
270 | ||||
271 | bool DecrementSubscripts( | |||
272 | SubscriptValue[], const int *permutation = nullptr) const; | |||
273 | ||||
274 | // False when out of range. | |||
275 | bool SubscriptsForZeroBasedElementNumber(SubscriptValue subscript[], | |||
276 | std::size_t elementNumber, const int *permutation = nullptr) const { | |||
277 | if (raw_.rank == 0) { | |||
278 | return elementNumber == 0; | |||
279 | } | |||
280 | std::size_t dimCoefficient[maxRank]; | |||
281 | int k0{permutation ? permutation[0] : 0}; | |||
282 | dimCoefficient[0] = 1; | |||
283 | auto coefficient{static_cast<std::size_t>(GetDimension(k0).Extent())}; | |||
284 | for (int j{1}; j < raw_.rank; ++j) { | |||
285 | int k{permutation ? permutation[j] : j}; | |||
286 | const Dimension &dim{GetDimension(k)}; | |||
287 | dimCoefficient[j] = coefficient; | |||
288 | coefficient *= dim.Extent(); | |||
289 | } | |||
290 | if (elementNumber >= coefficient) { | |||
291 | return false; // out of range | |||
292 | } | |||
293 | for (int j{raw_.rank - 1}; j > 0; --j) { | |||
294 | int k{permutation ? permutation[j] : j}; | |||
295 | const Dimension &dim{GetDimension(k)}; | |||
296 | std::size_t quotient{elementNumber / dimCoefficient[j]}; | |||
297 | subscript[k] = quotient + dim.LowerBound(); | |||
298 | elementNumber -= quotient * dimCoefficient[j]; | |||
299 | } | |||
300 | subscript[k0] = elementNumber + GetDimension(k0).LowerBound(); | |||
301 | return true; | |||
302 | } | |||
303 | ||||
304 | std::size_t ZeroBasedElementNumber( | |||
305 | const SubscriptValue *, const int *permutation = nullptr) const; | |||
306 | ||||
307 | DescriptorAddendum *Addendum() { | |||
308 | if (raw_.f18Addendum != 0) { | |||
309 | return reinterpret_cast<DescriptorAddendum *>(&GetDimension(rank())); | |||
310 | } else { | |||
311 | return nullptr; | |||
312 | } | |||
313 | } | |||
314 | const DescriptorAddendum *Addendum() const { | |||
315 | if (raw_.f18Addendum != 0) { | |||
316 | return reinterpret_cast<const DescriptorAddendum *>( | |||
317 | &GetDimension(rank())); | |||
318 | } else { | |||
319 | return nullptr; | |||
320 | } | |||
321 | } | |||
322 | ||||
323 | // Returns size in bytes of the descriptor (not the data) | |||
324 | static constexpr std::size_t SizeInBytes( | |||
325 | int rank, bool addendum = false, int lengthTypeParameters = 0) { | |||
326 | std::size_t bytes{sizeof(Descriptor) - sizeof(Dimension)}; | |||
327 | bytes += rank * sizeof(Dimension); | |||
328 | if (addendum || lengthTypeParameters > 0) { | |||
329 | bytes += DescriptorAddendum::SizeInBytes(lengthTypeParameters); | |||
330 | } | |||
331 | return bytes; | |||
332 | } | |||
333 | ||||
334 | std::size_t SizeInBytes() const; | |||
335 | ||||
336 | std::size_t Elements() const; | |||
337 | ||||
338 | // Allocate() assumes Elements() and ElementBytes() work; | |||
339 | // define the extents of the dimensions and the element length | |||
340 | // before calling. It (re)computes the byte strides after | |||
341 | // allocation. Does not allocate automatic components or | |||
342 | // perform default component initialization. | |||
343 | int Allocate(); | |||
344 | ||||
345 | // Deallocates storage; does not call FINAL subroutines or | |||
346 | // deallocate allocatable/automatic components. | |||
347 | int Deallocate(); | |||
348 | ||||
349 | // Deallocates storage, including allocatable and automatic | |||
350 | // components. Optionally invokes FINAL subroutines. | |||
351 | int Destroy(bool finalize = false, bool destroyPointers = false); | |||
352 | ||||
353 | bool IsContiguous(int leadingDimensions = maxRank) const { | |||
354 | auto bytes{static_cast<SubscriptValue>(ElementBytes())}; | |||
355 | if (leadingDimensions > raw_.rank) { | |||
356 | leadingDimensions = raw_.rank; | |||
357 | } | |||
358 | for (int j{0}; j < leadingDimensions; ++j) { | |||
359 | const Dimension &dim{GetDimension(j)}; | |||
360 | if (bytes != dim.ByteStride()) { | |||
361 | return false; | |||
362 | } | |||
363 | bytes *= dim.Extent(); | |||
364 | } | |||
365 | return true; | |||
366 | } | |||
367 | ||||
368 | // Establishes a pointer to a section or element. | |||
369 | bool EstablishPointerSection(const Descriptor &source, | |||
370 | const SubscriptValue *lower = nullptr, | |||
371 | const SubscriptValue *upper = nullptr, | |||
372 | const SubscriptValue *stride = nullptr); | |||
373 | ||||
374 | void Check() const; | |||
375 | ||||
376 | void Dump(FILE * = stdoutstdout) const; | |||
377 | ||||
378 | private: | |||
379 | ISO::CFI_cdesc_t raw_; | |||
380 | }; | |||
381 | static_assert(sizeof(Descriptor) == sizeof(ISO::CFI_cdesc_t)); | |||
382 | ||||
383 | // Properly configured instances of StaticDescriptor will occupy the | |||
384 | // exact amount of storage required for the descriptor, its dimensional | |||
385 | // information, and possible addendum. To build such a static descriptor, | |||
386 | // declare an instance of StaticDescriptor<>, extract a reference to its | |||
387 | // descriptor via the descriptor() accessor, and then built a Descriptor | |||
388 | // therein via descriptor.Establish(), e.g.: | |||
389 | // StaticDescriptor<R,A,LP> statDesc; | |||
390 | // Descriptor &descriptor{statDesc.descriptor()}; | |||
391 | // descriptor.Establish( ... ); | |||
392 | template <int MAX_RANK = maxRank, bool ADDENDUM = false, int MAX_LEN_PARMS = 0> | |||
393 | class alignas(Descriptor) StaticDescriptor { | |||
394 | public: | |||
395 | static constexpr int maxRank{MAX_RANK}; | |||
396 | static constexpr int maxLengthTypeParameters{MAX_LEN_PARMS}; | |||
397 | static constexpr bool hasAddendum{ADDENDUM || MAX_LEN_PARMS > 0}; | |||
398 | static constexpr std::size_t byteSize{ | |||
399 | Descriptor::SizeInBytes(maxRank, hasAddendum, maxLengthTypeParameters)}; | |||
400 | ||||
401 | Descriptor &descriptor() { return *reinterpret_cast<Descriptor *>(storage_); } | |||
402 | const Descriptor &descriptor() const { | |||
403 | return *reinterpret_cast<const Descriptor *>(storage_); | |||
404 | } | |||
405 | ||||
406 | void Check() { | |||
407 | assert(descriptor().rank() <= maxRank)(static_cast <bool> (descriptor().rank() <= maxRank) ? void (0) : __assert_fail ("descriptor().rank() <= maxRank" , "flang/include/flang/Runtime/descriptor.h", 407, __extension__ __PRETTY_FUNCTION__)); | |||
408 | assert(descriptor().SizeInBytes() <= byteSize)(static_cast <bool> (descriptor().SizeInBytes() <= byteSize ) ? void (0) : __assert_fail ("descriptor().SizeInBytes() <= byteSize" , "flang/include/flang/Runtime/descriptor.h", 408, __extension__ __PRETTY_FUNCTION__)); | |||
409 | if (DescriptorAddendum * addendum{descriptor().Addendum()}) { | |||
410 | assert(hasAddendum)(static_cast <bool> (hasAddendum) ? void (0) : __assert_fail ("hasAddendum", "flang/include/flang/Runtime/descriptor.h", 410 , __extension__ __PRETTY_FUNCTION__)); | |||
411 | assert(addendum->LenParameters() <= maxLengthTypeParameters)(static_cast <bool> (addendum->LenParameters() <= maxLengthTypeParameters) ? void (0) : __assert_fail ("addendum->LenParameters() <= maxLengthTypeParameters" , "flang/include/flang/Runtime/descriptor.h", 411, __extension__ __PRETTY_FUNCTION__)); | |||
412 | } else { | |||
413 | assert(!hasAddendum)(static_cast <bool> (!hasAddendum) ? void (0) : __assert_fail ("!hasAddendum", "flang/include/flang/Runtime/descriptor.h", 413, __extension__ __PRETTY_FUNCTION__)); | |||
414 | assert(maxLengthTypeParameters == 0)(static_cast <bool> (maxLengthTypeParameters == 0) ? void (0) : __assert_fail ("maxLengthTypeParameters == 0", "flang/include/flang/Runtime/descriptor.h" , 414, __extension__ __PRETTY_FUNCTION__)); | |||
415 | } | |||
416 | descriptor().Check(); | |||
417 | } | |||
418 | ||||
419 | private: | |||
420 | char storage_[byteSize]{}; | |||
421 | }; | |||
422 | } // namespace Fortran::runtime | |||
423 | #endif // FORTRAN_RUNTIME_DESCRIPTOR_H_ |