1 //===-- runtime/descriptor.cpp --------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "flang/Runtime/descriptor.h"
10 #include "ISO_Fortran_util.h"
14 #include "terminator.h"
16 #include "type-info.h"
17 #include "flang/Runtime/allocator-registry.h"
22 namespace Fortran::runtime
{
24 RT_OFFLOAD_API_GROUP_BEGIN
26 RT_API_ATTRS
Descriptor::Descriptor(const Descriptor
&that
) { *this = that
; }
28 RT_API_ATTRS Descriptor
&Descriptor::operator=(const Descriptor
&that
) {
29 std::memcpy(this, &that
, that
.SizeInBytes());
33 RT_API_ATTRS
void Descriptor::Establish(TypeCode t
, std::size_t elementBytes
,
34 void *p
, int rank
, const SubscriptValue
*extent
,
35 ISO::CFI_attribute_t attribute
, bool addendum
) {
36 Terminator terminator
{__FILE__
, __LINE__
};
37 int cfiStatus
{ISO::VerifyEstablishParameters(&raw_
, p
, attribute
, t
.raw(),
38 elementBytes
, rank
, extent
, /*external=*/false)};
39 if (cfiStatus
!= CFI_SUCCESS
) {
41 "Descriptor::Establish: CFI_establish returned %d for CFI_type_t(%d)",
44 ISO::EstablishDescriptor(
45 &raw_
, p
, attribute
, t
.raw(), elementBytes
, rank
, extent
);
46 if (elementBytes
== 0) {
48 // Reset byte strides of the dimensions, since EstablishDescriptor()
49 // only does that when the base address is not nullptr.
50 for (int j
{0}; j
< rank
; ++j
) {
51 GetDimension(j
).SetByteStride(0);
57 DescriptorAddendum
*a
{Addendum()};
58 RUNTIME_CHECK(terminator
, addendum
== (a
!= nullptr));
60 new (a
) DescriptorAddendum
{};
65 template <TypeCategory CAT
, int KIND
> struct TypeSizeGetter
{
66 constexpr RT_API_ATTRS
std::size_t operator()() const {
67 CppTypeFor
<CAT
, KIND
> arr
[2];
68 return sizeof arr
/ 2;
73 RT_API_ATTRS
std::size_t Descriptor::BytesFor(TypeCategory category
, int kind
) {
74 Terminator terminator
{__FILE__
, __LINE__
};
75 return ApplyType
<TypeSizeGetter
, std::size_t>(category
, kind
, terminator
);
78 RT_API_ATTRS
void Descriptor::Establish(TypeCategory c
, int kind
, void *p
,
79 int rank
, const SubscriptValue
*extent
, ISO::CFI_attribute_t attribute
,
81 Establish(TypeCode(c
, kind
), BytesFor(c
, kind
), p
, rank
, extent
, attribute
,
85 RT_API_ATTRS
void Descriptor::Establish(int characterKind
,
86 std::size_t characters
, void *p
, int rank
, const SubscriptValue
*extent
,
87 ISO::CFI_attribute_t attribute
, bool addendum
) {
88 Establish(TypeCode
{TypeCategory::Character
, characterKind
},
89 characterKind
* characters
, p
, rank
, extent
, attribute
, addendum
);
92 RT_API_ATTRS
void Descriptor::Establish(const typeInfo::DerivedType
&dt
,
93 void *p
, int rank
, const SubscriptValue
*extent
,
94 ISO::CFI_attribute_t attribute
) {
95 Establish(TypeCode
{TypeCategory::Derived
, 0}, dt
.sizeInBytes(), p
, rank
,
96 extent
, attribute
, true);
97 DescriptorAddendum
*a
{Addendum()};
98 Terminator terminator
{__FILE__
, __LINE__
};
99 RUNTIME_CHECK(terminator
, a
!= nullptr);
100 new (a
) DescriptorAddendum
{&dt
};
103 RT_API_ATTRS OwningPtr
<Descriptor
> Descriptor::Create(TypeCode t
,
104 std::size_t elementBytes
, void *p
, int rank
, const SubscriptValue
*extent
,
105 ISO::CFI_attribute_t attribute
, bool addendum
,
106 const typeInfo::DerivedType
*dt
) {
107 Terminator terminator
{__FILE__
, __LINE__
};
108 RUNTIME_CHECK(terminator
, t
.IsDerived() == (dt
!= nullptr));
109 int derivedTypeLenParameters
= dt
? dt
->LenParameters() : 0;
110 std::size_t bytes
{SizeInBytes(rank
, addendum
, derivedTypeLenParameters
)};
112 reinterpret_cast<Descriptor
*>(AllocateMemoryOrCrash(terminator
, bytes
))};
114 result
->Establish(*dt
, p
, rank
, extent
, attribute
);
116 result
->Establish(t
, elementBytes
, p
, rank
, extent
, attribute
, addendum
);
118 return OwningPtr
<Descriptor
>{result
};
121 RT_API_ATTRS OwningPtr
<Descriptor
> Descriptor::Create(TypeCategory c
, int kind
,
122 void *p
, int rank
, const SubscriptValue
*extent
,
123 ISO::CFI_attribute_t attribute
) {
125 TypeCode(c
, kind
), BytesFor(c
, kind
), p
, rank
, extent
, attribute
);
128 RT_API_ATTRS OwningPtr
<Descriptor
> Descriptor::Create(int characterKind
,
129 SubscriptValue characters
, void *p
, int rank
, const SubscriptValue
*extent
,
130 ISO::CFI_attribute_t attribute
) {
131 return Create(TypeCode
{TypeCategory::Character
, characterKind
},
132 characterKind
* characters
, p
, rank
, extent
, attribute
);
135 RT_API_ATTRS OwningPtr
<Descriptor
> Descriptor::Create(
136 const typeInfo::DerivedType
&dt
, void *p
, int rank
,
137 const SubscriptValue
*extent
, ISO::CFI_attribute_t attribute
) {
138 return Create(TypeCode
{TypeCategory::Derived
, 0}, dt
.sizeInBytes(), p
, rank
,
139 extent
, attribute
, /*addendum=*/true, &dt
);
142 RT_API_ATTRS
std::size_t Descriptor::SizeInBytes() const {
143 const DescriptorAddendum
*addendum
{Addendum()};
144 return sizeof *this - sizeof(Dimension
) + raw_
.rank
* sizeof(Dimension
) +
145 (addendum
? addendum
->SizeInBytes() : 0);
148 RT_API_ATTRS
std::size_t Descriptor::Elements() const {
150 std::size_t elements
{1};
151 for (int j
{0}; j
< n
; ++j
) {
152 elements
*= GetDimension(j
).Extent();
157 RT_API_ATTRS
static inline int MapAllocIdx(const Descriptor
&desc
) {
158 #ifdef RT_DEVICE_COMPILATION
159 // Force default allocator in device code.
160 return kDefaultAllocator
;
162 return desc
.GetAllocIdx();
166 RT_API_ATTRS
int Descriptor::Allocate() {
167 std::size_t elementBytes
{ElementBytes()};
168 if (static_cast<std::int64_t>(elementBytes
) < 0) {
169 // F'2023 7.4.4.2 p5: "If the character length parameter value evaluates
170 // to a negative value, the length of character entities declared is zero."
171 elementBytes
= raw_
.elem_len
= 0;
173 std::size_t byteSize
{Elements() * elementBytes
};
174 AllocFct alloc
{allocatorRegistry
.GetAllocator(MapAllocIdx(*this))};
175 // Zero size allocation is possible in Fortran and the resulting
176 // descriptor must be allocated/associated. Since std::malloc(0)
177 // result is implementation defined, always allocate at least one byte.
178 void *p
{alloc(byteSize
? byteSize
: 1)};
180 return CFI_ERROR_MEM_ALLOCATION
;
182 // TODO: image synchronization
188 RT_API_ATTRS
void Descriptor::SetByteStrides() {
189 if (int dims
{rank()}) {
190 std::size_t stride
{ElementBytes()};
191 for (int j
{0}; j
< dims
; ++j
) {
192 auto &dimension
{GetDimension(j
)};
193 dimension
.SetByteStride(stride
);
194 stride
*= dimension
.Extent();
199 RT_API_ATTRS
int Descriptor::Destroy(
200 bool finalize
, bool destroyPointers
, Terminator
*terminator
) {
201 if (!destroyPointers
&& raw_
.attribute
== CFI_attribute_pointer
) {
204 if (auto *addendum
{Addendum()}) {
205 if (const auto *derived
{addendum
->derivedType()}) {
206 if (!derived
->noDestructionNeeded()) {
207 runtime::Destroy(*this, finalize
, *derived
, terminator
);
215 RT_API_ATTRS
int Descriptor::Deallocate() {
216 ISO::CFI_cdesc_t
&descriptor
{raw()};
217 if (!descriptor
.base_addr
) {
218 return CFI_ERROR_BASE_ADDR_NULL
;
220 FreeFct free
{allocatorRegistry
.GetDeallocator(MapAllocIdx(*this))};
221 free(descriptor
.base_addr
);
222 descriptor
.base_addr
= nullptr;
227 RT_API_ATTRS
bool Descriptor::DecrementSubscripts(
228 SubscriptValue
*subscript
, const int *permutation
) const {
229 for (int j
{raw_
.rank
- 1}; j
>= 0; --j
) {
230 int k
{permutation
? permutation
[j
] : j
};
231 const Dimension
&dim
{GetDimension(k
)};
232 if (--subscript
[k
] >= dim
.LowerBound()) {
235 subscript
[k
] = dim
.UpperBound();
240 RT_API_ATTRS
std::size_t Descriptor::ZeroBasedElementNumber(
241 const SubscriptValue
*subscript
, const int *permutation
) const {
242 std::size_t result
{0};
243 std::size_t coefficient
{1};
244 for (int j
{0}; j
< raw_
.rank
; ++j
) {
245 int k
{permutation
? permutation
[j
] : j
};
246 const Dimension
&dim
{GetDimension(k
)};
247 result
+= coefficient
* (subscript
[k
] - dim
.LowerBound());
248 coefficient
*= dim
.Extent();
253 RT_API_ATTRS
bool Descriptor::EstablishPointerSection(const Descriptor
&source
,
254 const SubscriptValue
*lower
, const SubscriptValue
*upper
,
255 const SubscriptValue
*stride
) {
257 raw_
.attribute
= CFI_attribute_pointer
;
258 int newRank
{raw_
.rank
};
259 for (int j
{0}; j
< raw_
.rank
; ++j
) {
260 if (!stride
|| stride
[j
] == 0) {
269 if (const auto *sourceAddendum
= source
.Addendum()) {
270 if (auto *addendum
{Addendum()}) {
271 *addendum
= *sourceAddendum
;
276 return CFI_section(&raw_
, &source
.raw_
, lower
, upper
, stride
) == CFI_SUCCESS
;
279 RT_API_ATTRS
void Descriptor::ApplyMold(const Descriptor
&mold
, int rank
) {
280 raw_
.elem_len
= mold
.raw_
.elem_len
;
282 raw_
.type
= mold
.raw_
.type
;
283 for (int j
{0}; j
< rank
&& j
< mold
.raw_
.rank
; ++j
) {
284 GetDimension(j
) = mold
.GetDimension(j
);
286 if (auto *addendum
{Addendum()}) {
287 if (auto *moldAddendum
{mold
.Addendum()}) {
288 *addendum
= *moldAddendum
;
290 INTERNAL_CHECK(!addendum
->derivedType());
295 RT_API_ATTRS
void Descriptor::Check() const {
299 void Descriptor::Dump(FILE *f
) const {
300 std::fprintf(f
, "Descriptor @ %p:\n", reinterpret_cast<const void *>(this));
301 std::fprintf(f
, " base_addr %p\n", raw_
.base_addr
);
302 std::fprintf(f
, " elem_len %zd\n", static_cast<std::size_t>(raw_
.elem_len
));
303 std::fprintf(f
, " version %d\n", static_cast<int>(raw_
.version
));
304 std::fprintf(f
, " rank %d\n", static_cast<int>(raw_
.rank
));
305 std::fprintf(f
, " type %d\n", static_cast<int>(raw_
.type
));
306 std::fprintf(f
, " attribute %d\n", static_cast<int>(raw_
.attribute
));
307 std::fprintf(f
, " extra %d\n", static_cast<int>(raw_
.extra
));
308 std::fprintf(f
, " addendum %d\n", static_cast<int>(HasAddendum()));
309 std::fprintf(f
, " alloc_idx %d\n", static_cast<int>(GetAllocIdx()));
310 for (int j
{0}; j
< raw_
.rank
; ++j
) {
311 std::fprintf(f
, " dim[%d] lower_bound %jd\n", j
,
312 static_cast<std::intmax_t>(raw_
.dim
[j
].lower_bound
));
313 std::fprintf(f
, " extent %jd\n",
314 static_cast<std::intmax_t>(raw_
.dim
[j
].extent
));
315 std::fprintf(f
, " sm %jd\n",
316 static_cast<std::intmax_t>(raw_
.dim
[j
].sm
));
318 if (const DescriptorAddendum
* addendum
{Addendum()}) {
323 RT_API_ATTRS DescriptorAddendum
&DescriptorAddendum::operator=(
324 const DescriptorAddendum
&that
) {
325 derivedType_
= that
.derivedType_
;
326 auto lenParms
{that
.LenParameters()};
327 for (std::size_t j
{0}; j
< lenParms
; ++j
) {
328 len_
[j
] = that
.len_
[j
];
333 RT_API_ATTRS
std::size_t DescriptorAddendum::SizeInBytes() const {
334 return SizeInBytes(LenParameters());
337 RT_API_ATTRS
std::size_t DescriptorAddendum::LenParameters() const {
338 const auto *type
{derivedType()};
339 return type
? type
->LenParameters() : 0;
342 void DescriptorAddendum::Dump(FILE *f
) const {
344 f
, " derivedType @ %p\n", reinterpret_cast<const void *>(derivedType()));
345 std::size_t lenParms
{LenParameters()};
346 for (std::size_t j
{0}; j
< lenParms
; ++j
) {
347 std::fprintf(f
, " len[%zd] %jd\n", j
, static_cast<std::intmax_t>(len_
[j
]));
351 RT_OFFLOAD_API_GROUP_END
353 } // namespace Fortran::runtime