xref: /llvm-project/flang/runtime/descriptor.cpp (revision 4cb2a519db10f54815c8a4ccd5accbedc1cdfd07)
1 //===-- runtime/descriptor.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/descriptor.h"
10 #include "ISO_Fortran_util.h"
11 #include "derived.h"
12 #include "memory.h"
13 #include "stat.h"
14 #include "terminator.h"
15 #include "tools.h"
16 #include "type-info.h"
17 #include "flang/Runtime/allocator-registry.h"
18 #include <cassert>
19 #include <cstdlib>
20 #include <cstring>
21 
22 namespace Fortran::runtime {
23 
24 RT_OFFLOAD_API_GROUP_BEGIN
25 
26 RT_API_ATTRS Descriptor::Descriptor(const Descriptor &that) { *this = that; }
27 
28 RT_API_ATTRS Descriptor &Descriptor::operator=(const Descriptor &that) {
29   std::memcpy(this, &that, that.SizeInBytes());
30   return *this;
31 }
32 
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) {
40     terminator.Crash(
41         "Descriptor::Establish: CFI_establish returned %d for CFI_type_t(%d)",
42         cfiStatus, t.raw());
43   }
44   ISO::EstablishDescriptor(
45       &raw_, p, attribute, t.raw(), elementBytes, rank, extent);
46   if (elementBytes == 0) {
47     raw_.elem_len = 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);
52     }
53   }
54   if (addendum) {
55     SetHasAddendum();
56   }
57   DescriptorAddendum *a{Addendum()};
58   RUNTIME_CHECK(terminator, addendum == (a != nullptr));
59   if (a) {
60     new (a) DescriptorAddendum{};
61   }
62 }
63 
64 namespace {
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;
69   }
70 };
71 } // namespace
72 
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);
76 }
77 
78 RT_API_ATTRS void Descriptor::Establish(TypeCategory c, int kind, void *p,
79     int rank, const SubscriptValue *extent, ISO::CFI_attribute_t attribute,
80     bool addendum) {
81   Establish(TypeCode(c, kind), BytesFor(c, kind), p, rank, extent, attribute,
82       addendum);
83 }
84 
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);
90 }
91 
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};
101 }
102 
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)};
111   Descriptor *result{
112       reinterpret_cast<Descriptor *>(AllocateMemoryOrCrash(terminator, bytes))};
113   if (dt) {
114     result->Establish(*dt, p, rank, extent, attribute);
115   } else {
116     result->Establish(t, elementBytes, p, rank, extent, attribute, addendum);
117   }
118   return OwningPtr<Descriptor>{result};
119 }
120 
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) {
124   return Create(
125       TypeCode(c, kind), BytesFor(c, kind), p, rank, extent, attribute);
126 }
127 
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);
133 }
134 
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);
140 }
141 
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);
146 }
147 
148 RT_API_ATTRS std::size_t Descriptor::Elements() const {
149   int n{rank()};
150   std::size_t elements{1};
151   for (int j{0}; j < n; ++j) {
152     elements *= GetDimension(j).Extent();
153   }
154   return elements;
155 }
156 
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;
161 #else
162   return desc.GetAllocIdx();
163 #endif
164 }
165 
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;
172   }
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)};
179   if (!p) {
180     return CFI_ERROR_MEM_ALLOCATION;
181   }
182   // TODO: image synchronization
183   raw_.base_addr = p;
184   SetByteStrides();
185   return 0;
186 }
187 
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();
195     }
196   }
197 }
198 
199 RT_API_ATTRS int Descriptor::Destroy(
200     bool finalize, bool destroyPointers, Terminator *terminator) {
201   if (!destroyPointers && raw_.attribute == CFI_attribute_pointer) {
202     return StatOk;
203   } else {
204     if (auto *addendum{Addendum()}) {
205       if (const auto *derived{addendum->derivedType()}) {
206         if (!derived->noDestructionNeeded()) {
207           runtime::Destroy(*this, finalize, *derived, terminator);
208         }
209       }
210     }
211     return Deallocate();
212   }
213 }
214 
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;
219   } else {
220     FreeFct free{allocatorRegistry.GetDeallocator(MapAllocIdx(*this))};
221     free(descriptor.base_addr);
222     descriptor.base_addr = nullptr;
223     return CFI_SUCCESS;
224   }
225 }
226 
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()) {
233       return true;
234     }
235     subscript[k] = dim.UpperBound();
236   }
237   return false;
238 }
239 
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();
249   }
250   return result;
251 }
252 
253 RT_API_ATTRS bool Descriptor::EstablishPointerSection(const Descriptor &source,
254     const SubscriptValue *lower, const SubscriptValue *upper,
255     const SubscriptValue *stride) {
256   *this = source;
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) {
261       if (newRank > 0) {
262         --newRank;
263       } else {
264         return false;
265       }
266     }
267   }
268   raw_.rank = newRank;
269   if (const auto *sourceAddendum = source.Addendum()) {
270     if (auto *addendum{Addendum()}) {
271       *addendum = *sourceAddendum;
272     } else {
273       return false;
274     }
275   }
276   return CFI_section(&raw_, &source.raw_, lower, upper, stride) == CFI_SUCCESS;
277 }
278 
279 RT_API_ATTRS void Descriptor::ApplyMold(const Descriptor &mold, int rank) {
280   raw_.elem_len = mold.raw_.elem_len;
281   raw_.rank = rank;
282   raw_.type = mold.raw_.type;
283   for (int j{0}; j < rank && j < mold.raw_.rank; ++j) {
284     GetDimension(j) = mold.GetDimension(j);
285   }
286   if (auto *addendum{Addendum()}) {
287     if (auto *moldAddendum{mold.Addendum()}) {
288       *addendum = *moldAddendum;
289     } else {
290       INTERNAL_CHECK(!addendum->derivedType());
291     }
292   }
293 }
294 
295 RT_API_ATTRS void Descriptor::Check() const {
296   // TODO
297 }
298 
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));
317   }
318   if (const DescriptorAddendum * addendum{Addendum()}) {
319     addendum->Dump(f);
320   }
321 }
322 
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];
329   }
330   return *this;
331 }
332 
333 RT_API_ATTRS std::size_t DescriptorAddendum::SizeInBytes() const {
334   return SizeInBytes(LenParameters());
335 }
336 
337 RT_API_ATTRS std::size_t DescriptorAddendum::LenParameters() const {
338   const auto *type{derivedType()};
339   return type ? type->LenParameters() : 0;
340 }
341 
342 void DescriptorAddendum::Dump(FILE *f) const {
343   std::fprintf(
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]));
348   }
349 }
350 
351 RT_OFFLOAD_API_GROUP_END
352 
353 } // namespace Fortran::runtime
354