1c1db35f0Speter klausler //===-- runtime/copy.cpp -------------------------------------------------===// 2c1db35f0Speter klausler // 3c1db35f0Speter klausler // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4c1db35f0Speter klausler // See https://llvm.org/LICENSE.txt for license information. 5c1db35f0Speter klausler // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6c1db35f0Speter klausler // 7c1db35f0Speter klausler //===----------------------------------------------------------------------===// 8c1db35f0Speter klausler 9c1db35f0Speter klausler #include "copy.h" 102177a176SSlava Zakharin #include "stack.h" 11c1db35f0Speter klausler #include "terminator.h" 12c1db35f0Speter klausler #include "type-info.h" 13830c0b90SPeter Klausler #include "flang/Runtime/allocatable.h" 14830c0b90SPeter Klausler #include "flang/Runtime/descriptor.h" 15c1db35f0Speter klausler #include <cstring> 16c1db35f0Speter klausler 17c1db35f0Speter klausler namespace Fortran::runtime { 182177a176SSlava Zakharin namespace { 192177a176SSlava Zakharin using StaticDescTy = StaticDescriptor<maxRank, true, 0>; 202177a176SSlava Zakharin 212177a176SSlava Zakharin // A structure describing the data copy that needs to be done 222177a176SSlava Zakharin // from one descriptor to another. It is a helper structure 232177a176SSlava Zakharin // for CopyElement. 242177a176SSlava Zakharin struct CopyDescriptor { 252177a176SSlava Zakharin // A constructor specifying all members explicitly. 269684c87dSSlava Zakharin // The toAt and fromAt specify subscript storages that might be 279684c87dSSlava Zakharin // external to CopyElement, and cannot be modified. 289684c87dSSlava Zakharin // The copy descriptor only establishes toAtPtr_ and fromAtPtr_ 299684c87dSSlava Zakharin // pointers to point to these storages. 302177a176SSlava Zakharin RT_API_ATTRS CopyDescriptor(const Descriptor &to, const SubscriptValue toAt[], 312177a176SSlava Zakharin const Descriptor &from, const SubscriptValue fromAt[], 322177a176SSlava Zakharin std::size_t elements, bool usesStaticDescriptors = false) 332177a176SSlava Zakharin : to_(to), from_(from), elements_(elements), 342177a176SSlava Zakharin usesStaticDescriptors_(usesStaticDescriptors) { 359684c87dSSlava Zakharin toAtPtr_ = toAt; 369684c87dSSlava Zakharin fromAtPtr_ = fromAt; 372177a176SSlava Zakharin } 382177a176SSlava Zakharin // The number of elements to copy is initialized from the to descriptor. 392177a176SSlava Zakharin // The current element subscripts are initialized from the lower bounds 402177a176SSlava Zakharin // of the to and from descriptors. 412177a176SSlava Zakharin RT_API_ATTRS CopyDescriptor(const Descriptor &to, const Descriptor &from, 422177a176SSlava Zakharin bool usesStaticDescriptors = false) 432177a176SSlava Zakharin : to_(to), from_(from), elements_(to.Elements()), 442177a176SSlava Zakharin usesStaticDescriptors_(usesStaticDescriptors) { 452177a176SSlava Zakharin to.GetLowerBounds(toAt_); 462177a176SSlava Zakharin from.GetLowerBounds(fromAt_); 472177a176SSlava Zakharin } 482177a176SSlava Zakharin 499684c87dSSlava Zakharin // Increment the toAt_ and fromAt_ subscripts to the next 509684c87dSSlava Zakharin // element. 519684c87dSSlava Zakharin RT_API_ATTRS void IncrementSubscripts(Terminator &terminator) { 529684c87dSSlava Zakharin // This method must not be called for copy descriptors 539684c87dSSlava Zakharin // using external non-modifiable subscript storage. 549684c87dSSlava Zakharin RUNTIME_CHECK(terminator, toAt_ == toAtPtr_ && fromAt_ == fromAtPtr_); 559684c87dSSlava Zakharin to_.IncrementSubscripts(toAt_); 569684c87dSSlava Zakharin from_.IncrementSubscripts(fromAt_); 579684c87dSSlava Zakharin } 589684c87dSSlava Zakharin 592177a176SSlava Zakharin // Descriptor of the destination. 602177a176SSlava Zakharin const Descriptor &to_; 612177a176SSlava Zakharin // A subscript specifying the current element position to copy to. 622177a176SSlava Zakharin SubscriptValue toAt_[maxRank]; 639684c87dSSlava Zakharin // A pointer to the storage of the 'to' subscript. 649684c87dSSlava Zakharin // It may point to toAt_ or to an external non-modifiable 659684c87dSSlava Zakharin // subscript storage. 669684c87dSSlava Zakharin const SubscriptValue *toAtPtr_{toAt_}; 672177a176SSlava Zakharin // Descriptor of the source. 682177a176SSlava Zakharin const Descriptor &from_; 692177a176SSlava Zakharin // A subscript specifying the current element position to copy from. 702177a176SSlava Zakharin SubscriptValue fromAt_[maxRank]; 719684c87dSSlava Zakharin // A pointer to the storage of the 'from' subscript. 729684c87dSSlava Zakharin // It may point to fromAt_ or to an external non-modifiable 739684c87dSSlava Zakharin // subscript storage. 749684c87dSSlava Zakharin const SubscriptValue *fromAtPtr_{fromAt_}; 752177a176SSlava Zakharin // Number of elements left to copy. 762177a176SSlava Zakharin std::size_t elements_; 772177a176SSlava Zakharin // Must be true, if the to and from descriptors are allocated 782177a176SSlava Zakharin // by the CopyElement runtime. The allocated memory belongs 792177a176SSlava Zakharin // to a separate stack that needs to be popped in correspondence 802177a176SSlava Zakharin // with popping such a CopyDescriptor node. 812177a176SSlava Zakharin bool usesStaticDescriptors_; 822177a176SSlava Zakharin }; 832177a176SSlava Zakharin 842177a176SSlava Zakharin // A pair of StaticDescTy elements. 852177a176SSlava Zakharin struct StaticDescriptorsPair { 862177a176SSlava Zakharin StaticDescTy to; 872177a176SSlava Zakharin StaticDescTy from; 882177a176SSlava Zakharin }; 892177a176SSlava Zakharin } // namespace 902177a176SSlava Zakharin 9176facde3SSlava Zakharin RT_OFFLOAD_API_GROUP_BEGIN 92c1db35f0Speter klausler 9376facde3SSlava Zakharin RT_API_ATTRS void CopyElement(const Descriptor &to, const SubscriptValue toAt[], 94c1db35f0Speter klausler const Descriptor &from, const SubscriptValue fromAt[], 95c1db35f0Speter klausler Terminator &terminator) { 969684c87dSSlava Zakharin if (!to.Addendum()) { 979684c87dSSlava Zakharin // Avoid the overhead of creating the work stacks below 989684c87dSSlava Zakharin // for the simple non-derived type cases, because the overhead 999684c87dSSlava Zakharin // might be noticeable over the total amount of work that 1009684c87dSSlava Zakharin // needs to be done for the copy. 1019684c87dSSlava Zakharin char *toPtr{to.Element<char>(toAt)}; 1029684c87dSSlava Zakharin char *fromPtr{from.Element<char>(fromAt)}; 1039684c87dSSlava Zakharin RUNTIME_CHECK(terminator, to.ElementBytes() == from.ElementBytes()); 1049684c87dSSlava Zakharin std::memcpy(toPtr, fromPtr, to.ElementBytes()); 1059684c87dSSlava Zakharin return; 1069684c87dSSlava Zakharin } 1079684c87dSSlava Zakharin 1082177a176SSlava Zakharin #if !defined(RT_DEVICE_COMPILATION) 1092177a176SSlava Zakharin constexpr unsigned copyStackReserve{16}; 1102177a176SSlava Zakharin constexpr unsigned descriptorStackReserve{6}; 1112177a176SSlava Zakharin #else 1122177a176SSlava Zakharin // Always use dynamic allocation on the device to avoid 1132177a176SSlava Zakharin // big stack sizes. This may be tuned as needed. 1142177a176SSlava Zakharin constexpr unsigned copyStackReserve{0}; 1152177a176SSlava Zakharin constexpr unsigned descriptorStackReserve{0}; 1162177a176SSlava Zakharin #endif 1172177a176SSlava Zakharin // Keep a stack of CopyDescriptor's to avoid recursive calls. 1182177a176SSlava Zakharin Stack<CopyDescriptor, copyStackReserve> copyStack{terminator}; 1192177a176SSlava Zakharin // Keep a separate stack of StaticDescTy pairs. These descriptors 1202177a176SSlava Zakharin // may be used for representing copies of Component::Genre::Data 1212177a176SSlava Zakharin // components (since they do not have their descriptors allocated 1222177a176SSlava Zakharin // in memory). 1232177a176SSlava Zakharin Stack<StaticDescriptorsPair, descriptorStackReserve> descriptorsStack{ 1242177a176SSlava Zakharin terminator}; 1252177a176SSlava Zakharin copyStack.emplace(to, toAt, from, fromAt, /*elements=*/std::size_t{1}); 1262177a176SSlava Zakharin 1272177a176SSlava Zakharin while (!copyStack.empty()) { 1282177a176SSlava Zakharin CopyDescriptor ¤tCopy{copyStack.top()}; 1292177a176SSlava Zakharin std::size_t &elements{currentCopy.elements_}; 1302177a176SSlava Zakharin if (elements == 0) { 1312177a176SSlava Zakharin // This copy has been exhausted. 1322177a176SSlava Zakharin if (currentCopy.usesStaticDescriptors_) { 1332177a176SSlava Zakharin // Pop the static descriptors, if they were used 1342177a176SSlava Zakharin // for the current copy. 1352177a176SSlava Zakharin descriptorsStack.pop(); 1362177a176SSlava Zakharin } 1372177a176SSlava Zakharin copyStack.pop(); 1382177a176SSlava Zakharin continue; 1392177a176SSlava Zakharin } 1402177a176SSlava Zakharin const Descriptor &curTo{currentCopy.to_}; 1419684c87dSSlava Zakharin const SubscriptValue *curToAt{currentCopy.toAtPtr_}; 1422177a176SSlava Zakharin const Descriptor &curFrom{currentCopy.from_}; 1439684c87dSSlava Zakharin const SubscriptValue *curFromAt{currentCopy.fromAtPtr_}; 1442177a176SSlava Zakharin char *toPtr{curTo.Element<char>(curToAt)}; 1452177a176SSlava Zakharin char *fromPtr{curFrom.Element<char>(curFromAt)}; 1462177a176SSlava Zakharin RUNTIME_CHECK(terminator, curTo.ElementBytes() == curFrom.ElementBytes()); 1472177a176SSlava Zakharin // TODO: the memcpy can be optimized when both to and from are contiguous. 1482177a176SSlava Zakharin // Moreover, if we came here from an Component::Genre::Data component, 1492177a176SSlava Zakharin // all the per-element copies are redundant, because the parent 1502177a176SSlava Zakharin // has already been copied as a whole. 1512177a176SSlava Zakharin std::memcpy(toPtr, fromPtr, curTo.ElementBytes()); 1522177a176SSlava Zakharin --elements; 1532177a176SSlava Zakharin if (elements != 0) { 1549684c87dSSlava Zakharin currentCopy.IncrementSubscripts(terminator); 1552177a176SSlava Zakharin } 1562177a176SSlava Zakharin 1570d0bd3efSjeanPerier // Deep copy allocatable and automatic components if any. 1582177a176SSlava Zakharin if (const auto *addendum{curTo.Addendum()}) { 1590d0bd3efSjeanPerier if (const auto *derived{addendum->derivedType()}; 1600d0bd3efSjeanPerier derived && !derived->noDestructionNeeded()) { 161c1db35f0Speter klausler RUNTIME_CHECK(terminator, 1622177a176SSlava Zakharin curFrom.Addendum() && derived == curFrom.Addendum()->derivedType()); 16379caf69cSpeter klausler const Descriptor &componentDesc{derived->component()}; 164c1db35f0Speter klausler const typeInfo::Component *component{ 165c1db35f0Speter klausler componentDesc.OffsetElement<typeInfo::Component>()}; 166c1db35f0Speter klausler std::size_t nComponents{componentDesc.Elements()}; 167c1db35f0Speter klausler for (std::size_t j{0}; j < nComponents; ++j, ++component) { 16879caf69cSpeter klausler if (component->genre() == typeInfo::Component::Genre::Allocatable || 16979caf69cSpeter klausler component->genre() == typeInfo::Component::Genre::Automatic) { 170c1db35f0Speter klausler Descriptor &toDesc{ 17179caf69cSpeter klausler *reinterpret_cast<Descriptor *>(toPtr + component->offset())}; 172c1db35f0Speter klausler if (toDesc.raw().base_addr != nullptr) { 173c1db35f0Speter klausler toDesc.set_base_addr(nullptr); 174c1db35f0Speter klausler RUNTIME_CHECK(terminator, toDesc.Allocate() == CFI_SUCCESS); 175c1db35f0Speter klausler const Descriptor &fromDesc{*reinterpret_cast<const Descriptor *>( 17679caf69cSpeter klausler fromPtr + component->offset())}; 1772177a176SSlava Zakharin copyStack.emplace(toDesc, fromDesc); 178c1db35f0Speter klausler } 1790d0bd3efSjeanPerier } else if (component->genre() == typeInfo::Component::Genre::Data && 1800d0bd3efSjeanPerier component->derivedType() && 1810d0bd3efSjeanPerier !component->derivedType()->noDestructionNeeded()) { 1820d0bd3efSjeanPerier SubscriptValue extents[maxRank]; 1830d0bd3efSjeanPerier const typeInfo::Value *bounds{component->bounds()}; 1842177a176SSlava Zakharin std::size_t elements{1}; 1850d0bd3efSjeanPerier for (int dim{0}; dim < component->rank(); ++dim) { 186*410f7511Sserge-sans-paille typeInfo::TypeParameterValue lb{ 187*410f7511Sserge-sans-paille bounds[2 * dim].GetValue(&curTo).value_or(0)}; 188*410f7511Sserge-sans-paille typeInfo::TypeParameterValue ub{ 1892177a176SSlava Zakharin bounds[2 * dim + 1].GetValue(&curTo).value_or(0)}; 1900d0bd3efSjeanPerier extents[dim] = ub >= lb ? ub - lb + 1 : 0; 1912177a176SSlava Zakharin elements *= extents[dim]; 1920d0bd3efSjeanPerier } 1932177a176SSlava Zakharin if (elements != 0) { 1940d0bd3efSjeanPerier const typeInfo::DerivedType &compType{*component->derivedType()}; 1952177a176SSlava Zakharin // Place a pair of static descriptors onto the descriptors stack. 1962177a176SSlava Zakharin descriptorsStack.emplace(); 1972177a176SSlava Zakharin StaticDescriptorsPair &descs{descriptorsStack.top()}; 1982177a176SSlava Zakharin Descriptor &toCompDesc{descs.to.descriptor()}; 1990d0bd3efSjeanPerier toCompDesc.Establish(compType, toPtr + component->offset(), 2000d0bd3efSjeanPerier component->rank(), extents); 2012177a176SSlava Zakharin Descriptor &fromCompDesc{descs.from.descriptor()}; 2020d0bd3efSjeanPerier fromCompDesc.Establish(compType, fromPtr + component->offset(), 2030d0bd3efSjeanPerier component->rank(), extents); 2042177a176SSlava Zakharin copyStack.emplace(toCompDesc, fromCompDesc, 2052177a176SSlava Zakharin /*usesStaticDescriptors=*/true); 206c1db35f0Speter klausler } 207c1db35f0Speter klausler } 208c1db35f0Speter klausler } 209c1db35f0Speter klausler } 210c1db35f0Speter klausler } 211c1db35f0Speter klausler } 212c1db35f0Speter klausler } 21376facde3SSlava Zakharin RT_OFFLOAD_API_GROUP_END 214c1db35f0Speter klausler } // namespace Fortran::runtime 215