1 //===-- runtime/copy.cpp -------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
11 #include "terminator.h"
12 #include "type-info.h"
13 #include "flang/Runtime/allocatable.h"
14 #include "flang/Runtime/descriptor.h"
17 namespace Fortran::runtime
{
19 using StaticDescTy
= StaticDescriptor
<maxRank
, true, 0>;
21 // A structure describing the data copy that needs to be done
22 // from one descriptor to another. It is a helper structure
24 struct CopyDescriptor
{
25 // A constructor specifying all members explicitly.
26 // The toAt and fromAt specify subscript storages that might be
27 // external to CopyElement, and cannot be modified.
28 // The copy descriptor only establishes toAtPtr_ and fromAtPtr_
29 // pointers to point to these storages.
30 RT_API_ATTRS
CopyDescriptor(const Descriptor
&to
, const SubscriptValue toAt
[],
31 const Descriptor
&from
, const SubscriptValue fromAt
[],
32 std::size_t elements
, bool usesStaticDescriptors
= false)
33 : to_(to
), from_(from
), elements_(elements
),
34 usesStaticDescriptors_(usesStaticDescriptors
) {
38 // The number of elements to copy is initialized from the to descriptor.
39 // The current element subscripts are initialized from the lower bounds
40 // of the to and from descriptors.
41 RT_API_ATTRS
CopyDescriptor(const Descriptor
&to
, const Descriptor
&from
,
42 bool usesStaticDescriptors
= false)
43 : to_(to
), from_(from
), elements_(to
.Elements()),
44 usesStaticDescriptors_(usesStaticDescriptors
) {
45 to
.GetLowerBounds(toAt_
);
46 from
.GetLowerBounds(fromAt_
);
49 // Increment the toAt_ and fromAt_ subscripts to the next
51 RT_API_ATTRS
void IncrementSubscripts(Terminator
&terminator
) {
52 // This method must not be called for copy descriptors
53 // using external non-modifiable subscript storage.
54 RUNTIME_CHECK(terminator
, toAt_
== toAtPtr_
&& fromAt_
== fromAtPtr_
);
55 to_
.IncrementSubscripts(toAt_
);
56 from_
.IncrementSubscripts(fromAt_
);
59 // Descriptor of the destination.
60 const Descriptor
&to_
;
61 // A subscript specifying the current element position to copy to.
62 SubscriptValue toAt_
[maxRank
];
63 // A pointer to the storage of the 'to' subscript.
64 // It may point to toAt_ or to an external non-modifiable
66 const SubscriptValue
*toAtPtr_
{toAt_
};
67 // Descriptor of the source.
68 const Descriptor
&from_
;
69 // A subscript specifying the current element position to copy from.
70 SubscriptValue fromAt_
[maxRank
];
71 // A pointer to the storage of the 'from' subscript.
72 // It may point to fromAt_ or to an external non-modifiable
74 const SubscriptValue
*fromAtPtr_
{fromAt_
};
75 // Number of elements left to copy.
76 std::size_t elements_
;
77 // Must be true, if the to and from descriptors are allocated
78 // by the CopyElement runtime. The allocated memory belongs
79 // to a separate stack that needs to be popped in correspondence
80 // with popping such a CopyDescriptor node.
81 bool usesStaticDescriptors_
;
84 // A pair of StaticDescTy elements.
85 struct StaticDescriptorsPair
{
91 RT_OFFLOAD_API_GROUP_BEGIN
93 RT_API_ATTRS
void CopyElement(const Descriptor
&to
, const SubscriptValue toAt
[],
94 const Descriptor
&from
, const SubscriptValue fromAt
[],
95 Terminator
&terminator
) {
97 // Avoid the overhead of creating the work stacks below
98 // for the simple non-derived type cases, because the overhead
99 // might be noticeable over the total amount of work that
100 // needs to be done for the copy.
101 char *toPtr
{to
.Element
<char>(toAt
)};
102 char *fromPtr
{from
.Element
<char>(fromAt
)};
103 RUNTIME_CHECK(terminator
, to
.ElementBytes() == from
.ElementBytes());
104 std::memcpy(toPtr
, fromPtr
, to
.ElementBytes());
108 #if !defined(RT_DEVICE_COMPILATION)
109 constexpr unsigned copyStackReserve
{16};
110 constexpr unsigned descriptorStackReserve
{6};
112 // Always use dynamic allocation on the device to avoid
113 // big stack sizes. This may be tuned as needed.
114 constexpr unsigned copyStackReserve
{0};
115 constexpr unsigned descriptorStackReserve
{0};
117 // Keep a stack of CopyDescriptor's to avoid recursive calls.
118 Stack
<CopyDescriptor
, copyStackReserve
> copyStack
{terminator
};
119 // Keep a separate stack of StaticDescTy pairs. These descriptors
120 // may be used for representing copies of Component::Genre::Data
121 // components (since they do not have their descriptors allocated
123 Stack
<StaticDescriptorsPair
, descriptorStackReserve
> descriptorsStack
{
125 copyStack
.emplace(to
, toAt
, from
, fromAt
, /*elements=*/std::size_t{1});
127 while (!copyStack
.empty()) {
128 CopyDescriptor
¤tCopy
{copyStack
.top()};
129 std::size_t &elements
{currentCopy
.elements_
};
131 // This copy has been exhausted.
132 if (currentCopy
.usesStaticDescriptors_
) {
133 // Pop the static descriptors, if they were used
134 // for the current copy.
135 descriptorsStack
.pop();
140 const Descriptor
&curTo
{currentCopy
.to_
};
141 const SubscriptValue
*curToAt
{currentCopy
.toAtPtr_
};
142 const Descriptor
&curFrom
{currentCopy
.from_
};
143 const SubscriptValue
*curFromAt
{currentCopy
.fromAtPtr_
};
144 char *toPtr
{curTo
.Element
<char>(curToAt
)};
145 char *fromPtr
{curFrom
.Element
<char>(curFromAt
)};
146 RUNTIME_CHECK(terminator
, curTo
.ElementBytes() == curFrom
.ElementBytes());
147 // TODO: the memcpy can be optimized when both to and from are contiguous.
148 // Moreover, if we came here from an Component::Genre::Data component,
149 // all the per-element copies are redundant, because the parent
150 // has already been copied as a whole.
151 std::memcpy(toPtr
, fromPtr
, curTo
.ElementBytes());
154 currentCopy
.IncrementSubscripts(terminator
);
157 // Deep copy allocatable and automatic components if any.
158 if (const auto *addendum
{curTo
.Addendum()}) {
159 if (const auto *derived
{addendum
->derivedType()};
160 derived
&& !derived
->noDestructionNeeded()) {
161 RUNTIME_CHECK(terminator
,
162 curFrom
.Addendum() && derived
== curFrom
.Addendum()->derivedType());
163 const Descriptor
&componentDesc
{derived
->component()};
164 const typeInfo::Component
*component
{
165 componentDesc
.OffsetElement
<typeInfo::Component
>()};
166 std::size_t nComponents
{componentDesc
.Elements()};
167 for (std::size_t j
{0}; j
< nComponents
; ++j
, ++component
) {
168 if (component
->genre() == typeInfo::Component::Genre::Allocatable
||
169 component
->genre() == typeInfo::Component::Genre::Automatic
) {
171 *reinterpret_cast<Descriptor
*>(toPtr
+ component
->offset())};
172 if (toDesc
.raw().base_addr
!= nullptr) {
173 toDesc
.set_base_addr(nullptr);
174 RUNTIME_CHECK(terminator
, toDesc
.Allocate() == CFI_SUCCESS
);
175 const Descriptor
&fromDesc
{*reinterpret_cast<const Descriptor
*>(
176 fromPtr
+ component
->offset())};
177 copyStack
.emplace(toDesc
, fromDesc
);
179 } else if (component
->genre() == typeInfo::Component::Genre::Data
&&
180 component
->derivedType() &&
181 !component
->derivedType()->noDestructionNeeded()) {
182 SubscriptValue extents
[maxRank
];
183 const typeInfo::Value
*bounds
{component
->bounds()};
184 std::size_t elements
{1};
185 for (int dim
{0}; dim
< component
->rank(); ++dim
) {
186 typeInfo::TypeParameterValue lb
{
187 bounds
[2 * dim
].GetValue(&curTo
).value_or(0)};
188 typeInfo::TypeParameterValue ub
{
189 bounds
[2 * dim
+ 1].GetValue(&curTo
).value_or(0)};
190 extents
[dim
] = ub
>= lb
? ub
- lb
+ 1 : 0;
191 elements
*= extents
[dim
];
194 const typeInfo::DerivedType
&compType
{*component
->derivedType()};
195 // Place a pair of static descriptors onto the descriptors stack.
196 descriptorsStack
.emplace();
197 StaticDescriptorsPair
&descs
{descriptorsStack
.top()};
198 Descriptor
&toCompDesc
{descs
.to
.descriptor()};
199 toCompDesc
.Establish(compType
, toPtr
+ component
->offset(),
200 component
->rank(), extents
);
201 Descriptor
&fromCompDesc
{descs
.from
.descriptor()};
202 fromCompDesc
.Establish(compType
, fromPtr
+ component
->offset(),
203 component
->rank(), extents
);
204 copyStack
.emplace(toCompDesc
, fromCompDesc
,
205 /*usesStaticDescriptors=*/true);
213 RT_OFFLOAD_API_GROUP_END
214 } // namespace Fortran::runtime