1 //===-- runtime/assign.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/assign.h"
10 #include "derived.h"
11 #include "stat.h"
12 #include "terminator.h"
13 #include "type-info.h"
14 #include "flang/Runtime/descriptor.h"
15 
16 namespace Fortran::runtime {
17 
DoScalarDefinedAssignment(const Descriptor & to,const Descriptor & from,const typeInfo::SpecialBinding & special)18 static void DoScalarDefinedAssignment(const Descriptor &to,
19     const Descriptor &from, const typeInfo::SpecialBinding &special) {
20   bool toIsDesc{special.IsArgDescriptor(0)};
21   bool fromIsDesc{special.IsArgDescriptor(1)};
22   if (toIsDesc) {
23     if (fromIsDesc) {
24       auto *p{
25           special.GetProc<void (*)(const Descriptor &, const Descriptor &)>()};
26       p(to, from);
27     } else {
28       auto *p{special.GetProc<void (*)(const Descriptor &, void *)>()};
29       p(to, from.raw().base_addr);
30     }
31   } else {
32     if (fromIsDesc) {
33       auto *p{special.GetProc<void (*)(void *, const Descriptor &)>()};
34       p(to.raw().base_addr, from);
35     } else {
36       auto *p{special.GetProc<void (*)(void *, void *)>()};
37       p(to.raw().base_addr, from.raw().base_addr);
38     }
39   }
40 }
41 
DoElementalDefinedAssignment(const Descriptor & to,const Descriptor & from,const typeInfo::SpecialBinding & special,std::size_t toElements,SubscriptValue toAt[],SubscriptValue fromAt[])42 static void DoElementalDefinedAssignment(const Descriptor &to,
43     const Descriptor &from, const typeInfo::SpecialBinding &special,
44     std::size_t toElements, SubscriptValue toAt[], SubscriptValue fromAt[]) {
45   StaticDescriptor<maxRank, true, 8 /*?*/> statDesc[2];
46   Descriptor &toElementDesc{statDesc[0].descriptor()};
47   Descriptor &fromElementDesc{statDesc[1].descriptor()};
48   toElementDesc = to;
49   toElementDesc.raw().attribute = CFI_attribute_pointer;
50   toElementDesc.raw().rank = 0;
51   fromElementDesc = from;
52   fromElementDesc.raw().attribute = CFI_attribute_pointer;
53   fromElementDesc.raw().rank = 0;
54   for (std::size_t j{0}; j < toElements;
55        ++j, to.IncrementSubscripts(toAt), from.IncrementSubscripts(fromAt)) {
56     toElementDesc.set_base_addr(to.Element<char>(toAt));
57     fromElementDesc.set_base_addr(from.Element<char>(fromAt));
58     DoScalarDefinedAssignment(toElementDesc, fromElementDesc, special);
59   }
60 }
61 
Assign(Descriptor & to,const Descriptor & from,Terminator & terminator)62 void Assign(Descriptor &to, const Descriptor &from, Terminator &terminator) {
63   DescriptorAddendum *toAddendum{to.Addendum()};
64   const typeInfo::DerivedType *toDerived{
65       toAddendum ? toAddendum->derivedType() : nullptr};
66   const DescriptorAddendum *fromAddendum{from.Addendum()};
67   const typeInfo::DerivedType *fromDerived{
68       fromAddendum ? fromAddendum->derivedType() : nullptr};
69   bool wasJustAllocated{false};
70   if (to.IsAllocatable()) {
71     std::size_t lenParms{fromDerived ? fromDerived->LenParameters() : 0};
72     if (to.IsAllocated()) {
73       // Top-level assignments to allocatable variables (*not* components)
74       // may first deallocate existing content if there's about to be a
75       // change in type or shape; see F'2018 10.2.1.3(3).
76       bool deallocate{false};
77       if (to.type() != from.type()) {
78         deallocate = true;
79       } else if (toDerived != fromDerived) {
80         deallocate = true;
81       } else {
82         if (toAddendum) {
83           // Distinct LEN parameters? Deallocate
84           for (std::size_t j{0}; j < lenParms; ++j) {
85             if (toAddendum->LenParameterValue(j) !=
86                 fromAddendum->LenParameterValue(j)) {
87               deallocate = true;
88               break;
89             }
90           }
91         }
92         if (from.rank() > 0) {
93           // Distinct shape? Deallocate
94           int rank{to.rank()};
95           for (int j{0}; j < rank; ++j) {
96             if (to.GetDimension(j).Extent() != from.GetDimension(j).Extent()) {
97               deallocate = true;
98               break;
99             }
100           }
101         }
102       }
103       if (deallocate) {
104         to.Destroy(true /*finalize*/);
105       }
106     } else if (to.rank() != from.rank()) {
107       terminator.Crash("Assign: mismatched ranks (%d != %d) in assignment to "
108                        "unallocated allocatable",
109           to.rank(), from.rank());
110     }
111     if (!to.IsAllocated()) {
112       to.raw().type = from.raw().type;
113       to.raw().elem_len = from.ElementBytes();
114       if (toAddendum) {
115         toDerived = fromDerived;
116         toAddendum->set_derivedType(toDerived);
117         for (std::size_t j{0}; j < lenParms; ++j) {
118           toAddendum->SetLenParameterValue(
119               j, fromAddendum->LenParameterValue(j));
120         }
121       }
122       // subtle: leave bounds in place when "from" is scalar (10.2.1.3(3))
123       int rank{from.rank()};
124       auto stride{static_cast<SubscriptValue>(to.ElementBytes())};
125       for (int j{0}; j < rank; ++j) {
126         auto &toDim{to.GetDimension(j)};
127         const auto &fromDim{from.GetDimension(j)};
128         toDim.SetBounds(fromDim.LowerBound(), fromDim.UpperBound());
129         toDim.SetByteStride(stride);
130         stride *= toDim.Extent();
131       }
132       ReturnError(terminator, to.Allocate());
133       if (fromDerived && !fromDerived->noInitializationNeeded()) {
134         ReturnError(terminator, Initialize(to, *toDerived, terminator));
135       }
136       wasJustAllocated = true;
137     }
138   }
139   SubscriptValue toAt[maxRank];
140   to.GetLowerBounds(toAt);
141   // Scalar expansion of the RHS is implied by using the same empty
142   // subscript values on each (seemingly) elemental reference into
143   // "from".
144   SubscriptValue fromAt[maxRank];
145   from.GetLowerBounds(fromAt);
146   std::size_t toElements{to.Elements()};
147   if (from.rank() > 0 && toElements != from.Elements()) {
148     terminator.Crash("Assign: mismatching element counts in array assignment "
149                      "(to %zd, from %zd)",
150         toElements, from.Elements());
151   }
152   if (to.type() != from.type()) {
153     terminator.Crash("Assign: mismatching types (to code %d != from code %d)",
154         to.type().raw(), from.type().raw());
155   }
156   std::size_t elementBytes{to.ElementBytes()};
157   if (elementBytes != from.ElementBytes()) {
158     terminator.Crash(
159         "Assign: mismatching element sizes (to %zd bytes != from %zd bytes)",
160         elementBytes, from.ElementBytes());
161   }
162   if (toDerived) { // Derived type assignment
163     // Check for defined assignment type-bound procedures (10.2.1.4-5)
164     if (to.rank() == 0) {
165       if (const auto *special{toDerived->FindSpecialBinding(
166               typeInfo::SpecialBinding::Which::ScalarAssignment)}) {
167         return DoScalarDefinedAssignment(to, from, *special);
168       }
169     }
170     if (const auto *special{toDerived->FindSpecialBinding(
171             typeInfo::SpecialBinding::Which::ElementalAssignment)}) {
172       return DoElementalDefinedAssignment(
173           to, from, *special, toElements, toAt, fromAt);
174     }
175     // Derived type intrinsic assignment, which is componentwise and elementwise
176     // for all components, including parent components (10.2.1.2-3).
177     // The target is first finalized if still necessary (7.5.6.3(1))
178     if (!wasJustAllocated && !toDerived->noFinalizationNeeded()) {
179       Finalize(to, *toDerived);
180     }
181     // Copy the data components (incl. the parent) first.
182     const Descriptor &componentDesc{toDerived->component()};
183     std::size_t numComponents{componentDesc.Elements()};
184     for (std::size_t k{0}; k < numComponents; ++k) {
185       const auto &comp{
186           *componentDesc.ZeroBasedIndexedElement<typeInfo::Component>(
187               k)}; // TODO: exploit contiguity here
188       switch (comp.genre()) {
189       case typeInfo::Component::Genre::Data:
190         if (comp.category() == TypeCategory::Derived) {
191           StaticDescriptor<maxRank, true, 10 /*?*/> statDesc[2];
192           Descriptor &toCompDesc{statDesc[0].descriptor()};
193           Descriptor &fromCompDesc{statDesc[1].descriptor()};
194           for (std::size_t j{0}; j < toElements; ++j,
195                to.IncrementSubscripts(toAt), from.IncrementSubscripts(fromAt)) {
196             comp.CreatePointerDescriptor(toCompDesc, to, terminator, toAt);
197             comp.CreatePointerDescriptor(
198                 fromCompDesc, from, terminator, fromAt);
199             Assign(toCompDesc, fromCompDesc, terminator);
200           }
201         } else { // Component has intrinsic type; simply copy raw bytes
202           std::size_t componentByteSize{comp.SizeInBytes(to)};
203           for (std::size_t j{0}; j < toElements; ++j,
204                to.IncrementSubscripts(toAt), from.IncrementSubscripts(fromAt)) {
205             std::memmove(to.Element<char>(toAt) + comp.offset(),
206                 from.Element<const char>(fromAt) + comp.offset(),
207                 componentByteSize);
208           }
209         }
210         break;
211       case typeInfo::Component::Genre::Pointer: {
212         std::size_t componentByteSize{comp.SizeInBytes(to)};
213         for (std::size_t j{0}; j < toElements; ++j,
214              to.IncrementSubscripts(toAt), from.IncrementSubscripts(fromAt)) {
215           std::memmove(to.Element<char>(toAt) + comp.offset(),
216               from.Element<const char>(fromAt) + comp.offset(),
217               componentByteSize);
218         }
219       } break;
220       case typeInfo::Component::Genre::Allocatable:
221       case typeInfo::Component::Genre::Automatic:
222         for (std::size_t j{0}; j < toElements; ++j,
223              to.IncrementSubscripts(toAt), from.IncrementSubscripts(fromAt)) {
224           auto *toDesc{reinterpret_cast<Descriptor *>(
225               to.Element<char>(toAt) + comp.offset())};
226           const auto *fromDesc{reinterpret_cast<const Descriptor *>(
227               from.Element<char>(fromAt) + comp.offset())};
228           if (toDesc->IsAllocatable()) {
229             if (toDesc->IsAllocated()) {
230               // Allocatable components of the LHS are unconditionally
231               // deallocated before assignment (F'2018 10.2.1.3(13)(1)),
232               // unlike a "top-level" assignment to a variable, where
233               // deallocation is optional.
234               // TODO: Consider skipping this step and deferring the
235               // deallocation to the recursive activation of Assign(),
236               // which might be able to avoid deallocation/reallocation
237               // when the existing allocation can be reoccupied.
238               toDesc->Destroy(false /*already finalized*/);
239             }
240             if (!fromDesc->IsAllocated()) {
241               continue; // F'2018 10.2.1.3(13)(2)
242             }
243           }
244           Assign(*toDesc, *fromDesc, terminator);
245         }
246         break;
247       }
248     }
249     // Copy procedure pointer components
250     const Descriptor &procPtrDesc{toDerived->procPtr()};
251     std::size_t numProcPtrs{procPtrDesc.Elements()};
252     for (std::size_t k{0}; k < numProcPtrs; ++k) {
253       const auto &procPtr{
254           *procPtrDesc.ZeroBasedIndexedElement<typeInfo::ProcPtrComponent>(k)};
255       for (std::size_t j{0}; j < toElements; ++j, to.IncrementSubscripts(toAt),
256            from.IncrementSubscripts(fromAt)) {
257         std::memmove(to.Element<char>(toAt) + procPtr.offset,
258             from.Element<const char>(fromAt) + procPtr.offset,
259             sizeof(typeInfo::ProcedurePointer));
260       }
261     }
262   } else { // intrinsic type, intrinsic assignment
263     if (to.rank() == from.rank() && to.IsContiguous() && from.IsContiguous()) {
264       // Everything is contiguous; do a single big copy
265       std::memmove(
266           to.raw().base_addr, from.raw().base_addr, toElements * elementBytes);
267     } else { // elemental copies
268       for (std::size_t n{toElements}; n-- > 0;
269            to.IncrementSubscripts(toAt), from.IncrementSubscripts(fromAt)) {
270         std::memmove(to.Element<char>(toAt), from.Element<const char>(fromAt),
271             elementBytes);
272       }
273     }
274   }
275 }
276 
277 extern "C" {
RTNAME(Assign)278 void RTNAME(Assign)(Descriptor &to, const Descriptor &from,
279     const char *sourceFile, int sourceLine) {
280   Terminator terminator{sourceFile, sourceLine};
281   Assign(to, from, terminator);
282 }
283 
284 } // extern "C"
285 } // namespace Fortran::runtime
286