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