xref: /llvm-project/flang/runtime/copy.cpp (revision 410f751144e8b2e9574f03e0d0fb8560fe3cb797)
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 &currentCopy{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