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 "derived.h"
11 #include "memory.h"
12 #include "stat.h"
13 #include "terminator.h"
14 #include "type-info.h"
15 #include <cassert>
16 #include <cstdlib>
17 #include <cstring>
18 
19 namespace Fortran::runtime {
20 
Descriptor(const Descriptor & that)21 Descriptor::Descriptor(const Descriptor &that) { *this = that; }
22 
operator =(const Descriptor & that)23 Descriptor &Descriptor::operator=(const Descriptor &that) {
24   std::memcpy(this, &that, that.SizeInBytes());
25   return *this;
26 }
27 
Establish(TypeCode t,std::size_t elementBytes,void * p,int rank,const SubscriptValue * extent,ISO::CFI_attribute_t attribute,bool addendum)28 void Descriptor::Establish(TypeCode t, std::size_t elementBytes, void *p,
29     int rank, const SubscriptValue *extent, ISO::CFI_attribute_t attribute,
30     bool addendum) {
31   Terminator terminator{__FILE__, __LINE__};
32   // Subtle: the standard CFI_establish() function doesn't allow a zero
33   // elem_len argument in cases where elem_len is not ignored; and when it
34   // returns an error code (CFI_INVALID_ELEM_LEN in this case), it must not
35   // modify the descriptor.  That design makes sense, maybe, for actual
36   // C interoperability, but we need to work around it here.  A zero
37   // incoming element length is replaced by 4 so that it will be valid
38   // for all CHARACTER kinds.
39   std::size_t workaroundElemLen{elementBytes ? elementBytes : 4};
40   int cfiStatus{ISO::CFI_establish(
41       &raw_, p, attribute, t.raw(), workaroundElemLen, rank, extent)};
42   if (cfiStatus != CFI_SUCCESS) {
43     terminator.Crash(
44         "Descriptor::Establish: CFI_establish returned %d", cfiStatus, t.raw());
45   }
46   if (elementBytes == 0) {
47     raw_.elem_len = 0;
48     for (int j{0}; j < rank; ++j) {
49       GetDimension(j).SetByteStride(0);
50     }
51   }
52   raw_.f18Addendum = addendum;
53   DescriptorAddendum *a{Addendum()};
54   RUNTIME_CHECK(terminator, addendum == (a != nullptr));
55   if (a) {
56     new (a) DescriptorAddendum{};
57   }
58 }
59 
Establish(TypeCategory c,int kind,void * p,int rank,const SubscriptValue * extent,ISO::CFI_attribute_t attribute,bool addendum)60 void Descriptor::Establish(TypeCategory c, int kind, void *p, int rank,
61     const SubscriptValue *extent, ISO::CFI_attribute_t attribute,
62     bool addendum) {
63   Establish(TypeCode(c, kind), BytesFor(c, kind), p, rank, extent, attribute,
64       addendum);
65 }
66 
Establish(int characterKind,std::size_t characters,void * p,int rank,const SubscriptValue * extent,ISO::CFI_attribute_t attribute,bool addendum)67 void Descriptor::Establish(int characterKind, std::size_t characters, void *p,
68     int rank, const SubscriptValue *extent, ISO::CFI_attribute_t attribute,
69     bool addendum) {
70   Establish(TypeCode{TypeCategory::Character, characterKind},
71       characterKind * characters, p, rank, extent, attribute, addendum);
72 }
73 
Establish(const typeInfo::DerivedType & dt,void * p,int rank,const SubscriptValue * extent,ISO::CFI_attribute_t attribute)74 void Descriptor::Establish(const typeInfo::DerivedType &dt, void *p, int rank,
75     const SubscriptValue *extent, ISO::CFI_attribute_t attribute) {
76   Establish(TypeCode{TypeCategory::Derived, 0}, dt.sizeInBytes(), p, rank,
77       extent, attribute, true);
78   DescriptorAddendum *a{Addendum()};
79   Terminator terminator{__FILE__, __LINE__};
80   RUNTIME_CHECK(terminator, a != nullptr);
81   new (a) DescriptorAddendum{&dt};
82 }
83 
Create(TypeCode t,std::size_t elementBytes,void * p,int rank,const SubscriptValue * extent,ISO::CFI_attribute_t attribute,int derivedTypeLenParameters)84 OwningPtr<Descriptor> Descriptor::Create(TypeCode t, std::size_t elementBytes,
85     void *p, int rank, const SubscriptValue *extent,
86     ISO::CFI_attribute_t attribute, int derivedTypeLenParameters) {
87   std::size_t bytes{SizeInBytes(rank, true, derivedTypeLenParameters)};
88   Terminator terminator{__FILE__, __LINE__};
89   Descriptor *result{
90       reinterpret_cast<Descriptor *>(AllocateMemoryOrCrash(terminator, bytes))};
91   result->Establish(t, elementBytes, p, rank, extent, attribute, true);
92   return OwningPtr<Descriptor>{result};
93 }
94 
Create(TypeCategory c,int kind,void * p,int rank,const SubscriptValue * extent,ISO::CFI_attribute_t attribute)95 OwningPtr<Descriptor> Descriptor::Create(TypeCategory c, int kind, void *p,
96     int rank, const SubscriptValue *extent, ISO::CFI_attribute_t attribute) {
97   return Create(
98       TypeCode(c, kind), BytesFor(c, kind), p, rank, extent, attribute);
99 }
100 
Create(int characterKind,SubscriptValue characters,void * p,int rank,const SubscriptValue * extent,ISO::CFI_attribute_t attribute)101 OwningPtr<Descriptor> Descriptor::Create(int characterKind,
102     SubscriptValue characters, void *p, int rank, const SubscriptValue *extent,
103     ISO::CFI_attribute_t attribute) {
104   return Create(TypeCode{TypeCategory::Character, characterKind},
105       characterKind * characters, p, rank, extent, attribute);
106 }
107 
Create(const typeInfo::DerivedType & dt,void * p,int rank,const SubscriptValue * extent,ISO::CFI_attribute_t attribute)108 OwningPtr<Descriptor> Descriptor::Create(const typeInfo::DerivedType &dt,
109     void *p, int rank, const SubscriptValue *extent,
110     ISO::CFI_attribute_t attribute) {
111   return Create(TypeCode{TypeCategory::Derived, 0}, dt.sizeInBytes(), p, rank,
112       extent, attribute, dt.LenParameters());
113 }
114 
SizeInBytes() const115 std::size_t Descriptor::SizeInBytes() const {
116   const DescriptorAddendum *addendum{Addendum()};
117   return sizeof *this - sizeof(Dimension) + raw_.rank * sizeof(Dimension) +
118       (addendum ? addendum->SizeInBytes() : 0);
119 }
120 
Elements() const121 std::size_t Descriptor::Elements() const {
122   int n{rank()};
123   std::size_t elements{1};
124   for (int j{0}; j < n; ++j) {
125     elements *= GetDimension(j).Extent();
126   }
127   return elements;
128 }
129 
Allocate()130 int Descriptor::Allocate() {
131   std::size_t byteSize{Elements() * ElementBytes()};
132   void *p{std::malloc(byteSize)};
133   if (!p && byteSize) {
134     return CFI_ERROR_MEM_ALLOCATION;
135   }
136   // TODO: image synchronization
137   raw_.base_addr = p;
138   if (int dims{rank()}) {
139     std::size_t stride{ElementBytes()};
140     for (int j{0}; j < dims; ++j) {
141       auto &dimension{GetDimension(j)};
142       dimension.SetByteStride(stride);
143       stride *= dimension.Extent();
144     }
145   }
146   return 0;
147 }
148 
Destroy(bool finalize,bool destroyPointers)149 int Descriptor::Destroy(bool finalize, bool destroyPointers) {
150   if (!destroyPointers && raw_.attribute == CFI_attribute_pointer) {
151     return StatOk;
152   } else {
153     if (auto *addendum{Addendum()}) {
154       if (const auto *derived{addendum->derivedType()}) {
155         if (!derived->noDestructionNeeded()) {
156           runtime::Destroy(*this, finalize, *derived);
157         }
158       }
159     }
160     return Deallocate();
161   }
162 }
163 
Deallocate()164 int Descriptor::Deallocate() { return ISO::CFI_deallocate(&raw_); }
165 
DecrementSubscripts(SubscriptValue * subscript,const int * permutation) const166 bool Descriptor::DecrementSubscripts(
167     SubscriptValue *subscript, const int *permutation) const {
168   for (int j{raw_.rank - 1}; j >= 0; --j) {
169     int k{permutation ? permutation[j] : j};
170     const Dimension &dim{GetDimension(k)};
171     if (--subscript[k] >= dim.LowerBound()) {
172       return true;
173     }
174     subscript[k] = dim.UpperBound();
175   }
176   return false;
177 }
178 
ZeroBasedElementNumber(const SubscriptValue * subscript,const int * permutation) const179 std::size_t Descriptor::ZeroBasedElementNumber(
180     const SubscriptValue *subscript, const int *permutation) const {
181   std::size_t result{0};
182   std::size_t coefficient{1};
183   for (int j{0}; j < raw_.rank; ++j) {
184     int k{permutation ? permutation[j] : j};
185     const Dimension &dim{GetDimension(k)};
186     result += coefficient * (subscript[k] - dim.LowerBound());
187     coefficient *= dim.Extent();
188   }
189   return result;
190 }
191 
EstablishPointerSection(const Descriptor & source,const SubscriptValue * lower,const SubscriptValue * upper,const SubscriptValue * stride)192 bool Descriptor::EstablishPointerSection(const Descriptor &source,
193     const SubscriptValue *lower, const SubscriptValue *upper,
194     const SubscriptValue *stride) {
195   *this = source;
196   raw_.attribute = CFI_attribute_pointer;
197   int newRank{raw_.rank};
198   for (int j{0}; j < raw_.rank; ++j) {
199     if (!stride || stride[j] == 0) {
200       if (newRank > 0) {
201         --newRank;
202       } else {
203         return false;
204       }
205     }
206   }
207   raw_.rank = newRank;
208   if (const auto *sourceAddendum = source.Addendum()) {
209     if (auto *addendum{Addendum()}) {
210       *addendum = *sourceAddendum;
211     } else {
212       return false;
213     }
214   }
215   return CFI_section(&raw_, &source.raw_, lower, upper, stride) == CFI_SUCCESS;
216 }
217 
Check() const218 void Descriptor::Check() const {
219   // TODO
220 }
221 
Dump(FILE * f) const222 void Descriptor::Dump(FILE *f) const {
223   std::fprintf(f, "Descriptor @ %p:\n", reinterpret_cast<const void *>(this));
224   std::fprintf(f, "  base_addr %p\n", raw_.base_addr);
225   std::fprintf(f, "  elem_len  %zd\n", static_cast<std::size_t>(raw_.elem_len));
226   std::fprintf(f, "  version   %d\n", static_cast<int>(raw_.version));
227   std::fprintf(f, "  rank      %d\n", static_cast<int>(raw_.rank));
228   std::fprintf(f, "  type      %d\n", static_cast<int>(raw_.type));
229   std::fprintf(f, "  attribute %d\n", static_cast<int>(raw_.attribute));
230   std::fprintf(f, "  addendum  %d\n", static_cast<int>(raw_.f18Addendum));
231   for (int j{0}; j < raw_.rank; ++j) {
232     std::fprintf(f, "  dim[%d] lower_bound %jd\n", j,
233         static_cast<std::intmax_t>(raw_.dim[j].lower_bound));
234     std::fprintf(f, "         extent      %jd\n",
235         static_cast<std::intmax_t>(raw_.dim[j].extent));
236     std::fprintf(f, "         sm          %jd\n",
237         static_cast<std::intmax_t>(raw_.dim[j].sm));
238   }
239   if (const DescriptorAddendum * addendum{Addendum()}) {
240     addendum->Dump(f);
241   }
242 }
243 
operator =(const DescriptorAddendum & that)244 DescriptorAddendum &DescriptorAddendum::operator=(
245     const DescriptorAddendum &that) {
246   derivedType_ = that.derivedType_;
247   auto lenParms{that.LenParameters()};
248   for (std::size_t j{0}; j < lenParms; ++j) {
249     len_[j] = that.len_[j];
250   }
251   return *this;
252 }
253 
SizeInBytes() const254 std::size_t DescriptorAddendum::SizeInBytes() const {
255   return SizeInBytes(LenParameters());
256 }
257 
LenParameters() const258 std::size_t DescriptorAddendum::LenParameters() const {
259   const auto *type{derivedType()};
260   return type ? type->LenParameters() : 0;
261 }
262 
Dump(FILE * f) const263 void DescriptorAddendum::Dump(FILE *f) const {
264   std::fprintf(
265       f, "  derivedType @ %p\n", reinterpret_cast<const void *>(derivedType()));
266   std::size_t lenParms{LenParameters()};
267   for (std::size_t j{0}; j < lenParms; ++j) {
268     std::fprintf(f, "  len[%zd] %jd\n", j, static_cast<std::intmax_t>(len_[j]));
269   }
270 }
271 } // namespace Fortran::runtime
272