1 | //===-- runtime/misc-intrinsic.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/misc-intrinsic.h" |
10 | #include "terminator.h" |
11 | #include "tools.h" |
12 | #include "flang/Common/optional.h" |
13 | #include "flang/Runtime/descriptor.h" |
14 | #include <algorithm> |
15 | #include <cstring> |
16 | |
17 | namespace Fortran::runtime { |
18 | |
19 | static RT_API_ATTRS void TransferImpl(Descriptor &result, |
20 | const Descriptor &source, const Descriptor &mold, const char *sourceFile, |
21 | int line, Fortran::common::optional<std::int64_t> resultExtent) { |
22 | int rank{resultExtent.has_value() ? 1 : 0}; |
23 | std::size_t elementBytes{mold.ElementBytes()}; |
24 | result.Establish(mold.type(), elementBytes, nullptr, rank, nullptr, |
25 | CFI_attribute_allocatable, mold.Addendum() != nullptr); |
26 | if (resultExtent) { |
27 | result.GetDimension(0).SetBounds(1, *resultExtent); |
28 | } |
29 | if (const DescriptorAddendum * addendum{mold.Addendum()}) { |
30 | *result.Addendum() = *addendum; |
31 | } |
32 | if (int stat{result.Allocate()}) { |
33 | Terminator{sourceFile, line}.Crash( |
34 | "TRANSFER: could not allocate memory for result; STAT=%d" , stat); |
35 | } |
36 | char *to{result.OffsetElement<char>()}; |
37 | std::size_t resultBytes{result.Elements() * result.ElementBytes()}; |
38 | const std::size_t sourceElementBytes{source.ElementBytes()}; |
39 | std::size_t sourceElements{source.Elements()}; |
40 | SubscriptValue sourceAt[maxRank]; |
41 | source.GetLowerBounds(sourceAt); |
42 | while (resultBytes > 0 && sourceElements > 0) { |
43 | std::size_t toMove{std::min(a: resultBytes, b: sourceElementBytes)}; |
44 | std::memcpy(dest: to, src: source.Element<char>(sourceAt), n: toMove); |
45 | to += toMove; |
46 | resultBytes -= toMove; |
47 | --sourceElements; |
48 | source.IncrementSubscripts(sourceAt); |
49 | } |
50 | if (resultBytes > 0) { |
51 | std::memset(s: to, c: 0, n: resultBytes); |
52 | } |
53 | } |
54 | |
55 | extern "C" { |
56 | RT_EXT_API_GROUP_BEGIN |
57 | |
58 | void RTDEF(Transfer)(Descriptor &result, const Descriptor &source, |
59 | const Descriptor &mold, const char *sourceFile, int line) { |
60 | Fortran::common::optional<std::int64_t> elements; |
61 | if (mold.rank() > 0) { |
62 | if (std::size_t sourceElementBytes{ |
63 | source.Elements() * source.ElementBytes()}) { |
64 | if (std::size_t moldElementBytes{mold.ElementBytes()}) { |
65 | elements = static_cast<std::int64_t>( |
66 | (sourceElementBytes + moldElementBytes - 1) / moldElementBytes); |
67 | } else { |
68 | Terminator{sourceFile, line}.Crash("TRANSFER: zero-sized type of MOLD= " |
69 | "when SOURCE= is not zero-sized" ); |
70 | } |
71 | } else { |
72 | elements = std::int64_t{0}; |
73 | } |
74 | } |
75 | return TransferImpl( |
76 | result, source, mold, sourceFile, line, std::move(elements)); |
77 | } |
78 | |
79 | void RTDEF(TransferSize)(Descriptor &result, const Descriptor &source, |
80 | const Descriptor &mold, const char *sourceFile, int line, |
81 | std::int64_t size) { |
82 | return TransferImpl(result, source, mold, sourceFile, line, size); |
83 | } |
84 | |
85 | RT_EXT_API_GROUP_END |
86 | } // extern "C" |
87 | } // namespace Fortran::runtime |
88 | |