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 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 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 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" { 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