1352d347aSAlexis Perry //===-- runtime/transformational.cpp --------------------------------------===//
2352d347aSAlexis Perry //
3352d347aSAlexis Perry // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4352d347aSAlexis Perry // See https://llvm.org/LICENSE.txt for license information.
5352d347aSAlexis Perry // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6352d347aSAlexis Perry //
7352d347aSAlexis Perry //===----------------------------------------------------------------------===//
8352d347aSAlexis Perry
9c1db35f0Speter klausler // Implements the transformational intrinsic functions of Fortran 2018 that
10c1db35f0Speter klausler // rearrange or duplicate data without (much) regard to type. These are
11c1db35f0Speter klausler // CSHIFT, EOSHIFT, PACK, RESHAPE, SPREAD, TRANSPOSE, and UNPACK.
12c1db35f0Speter klausler //
13c1db35f0Speter klausler // Many of these are defined in the 2018 standard with text that makes sense
14c1db35f0Speter klausler // only if argument arrays have lower bounds of one. Rather than interpret
15c1db35f0Speter klausler // these cases as implying a hidden constraint, these implementations
16c1db35f0Speter klausler // work with arbitrary lower bounds. This may be technically an extension
17c1db35f0Speter klausler // of the standard but it more likely to conform with its intent.
18c1db35f0Speter klausler
19830c0b90SPeter Klausler #include "flang/Runtime/transformational.h"
20c1db35f0Speter klausler #include "copy.h"
213b635714Speter klausler #include "terminator.h"
22e372e0f9Speter klausler #include "tools.h"
2377ff6f7dSPeter Klausler #include "flang/Runtime/descriptor.h"
24352d347aSAlexis Perry #include <algorithm>
25352d347aSAlexis Perry
26352d347aSAlexis Perry namespace Fortran::runtime {
27352d347aSAlexis Perry
28c1db35f0Speter klausler // Utility for CSHIFT & EOSHIFT rank > 1 cases that determines the shift count
29c1db35f0Speter klausler // for each of the vector sections of the result.
30c1db35f0Speter klausler class ShiftControl {
31c1db35f0Speter klausler public:
ShiftControl(const Descriptor & s,Terminator & t,int dim)32c1db35f0Speter klausler ShiftControl(const Descriptor &s, Terminator &t, int dim)
33c1db35f0Speter klausler : shift_{s}, terminator_{t}, shiftRank_{s.rank()}, dim_{dim} {}
Init(const Descriptor & source,const char * which)34251d062eSPeter Klausler void Init(const Descriptor &source, const char *which) {
35c1db35f0Speter klausler int rank{source.rank()};
36c1db35f0Speter klausler RUNTIME_CHECK(terminator_, shiftRank_ == 0 || shiftRank_ == rank - 1);
37c1db35f0Speter klausler auto catAndKind{shift_.type().GetCategoryAndKind()};
38c1db35f0Speter klausler RUNTIME_CHECK(
39c1db35f0Speter klausler terminator_, catAndKind && catAndKind->first == TypeCategory::Integer);
40c1db35f0Speter klausler shiftElemLen_ = catAndKind->second;
41c1db35f0Speter klausler if (shiftRank_ > 0) {
42c1db35f0Speter klausler int k{0};
43c1db35f0Speter klausler for (int j{0}; j < rank; ++j) {
44c1db35f0Speter klausler if (j + 1 != dim_) {
45c1db35f0Speter klausler const Dimension &shiftDim{shift_.GetDimension(k)};
46c1db35f0Speter klausler lb_[k++] = shiftDim.LowerBound();
47251d062eSPeter Klausler if (shiftDim.Extent() != source.GetDimension(j).Extent()) {
48251d062eSPeter Klausler terminator_.Crash("%s: on dimension %d, SHIFT= has extent %jd but "
49251d062eSPeter Klausler "SOURCE= has extent %jd",
50251d062eSPeter Klausler which, k, static_cast<std::intmax_t>(shiftDim.Extent()),
51251d062eSPeter Klausler static_cast<std::intmax_t>(source.GetDimension(j).Extent()));
52251d062eSPeter Klausler }
53c1db35f0Speter klausler }
54c1db35f0Speter klausler }
55c1db35f0Speter klausler } else {
56c1db35f0Speter klausler shiftCount_ =
57c1db35f0Speter klausler GetInt64(shift_.OffsetElement<char>(), shiftElemLen_, terminator_);
58c1db35f0Speter klausler }
59c1db35f0Speter klausler }
GetShift(const SubscriptValue resultAt[]) const60c1db35f0Speter klausler SubscriptValue GetShift(const SubscriptValue resultAt[]) const {
61c1db35f0Speter klausler if (shiftRank_ > 0) {
62c1db35f0Speter klausler SubscriptValue shiftAt[maxRank];
63c1db35f0Speter klausler int k{0};
64c1db35f0Speter klausler for (int j{0}; j < shiftRank_ + 1; ++j) {
65c1db35f0Speter klausler if (j + 1 != dim_) {
66c1db35f0Speter klausler shiftAt[k] = lb_[k] + resultAt[j] - 1;
67c1db35f0Speter klausler ++k;
68c1db35f0Speter klausler }
69c1db35f0Speter klausler }
70c1db35f0Speter klausler return GetInt64(
71c1db35f0Speter klausler shift_.Element<char>(shiftAt), shiftElemLen_, terminator_);
72c1db35f0Speter klausler } else {
73c1db35f0Speter klausler return shiftCount_; // invariant count extracted in Init()
74c1db35f0Speter klausler }
75c1db35f0Speter klausler }
76c1db35f0Speter klausler
77c1db35f0Speter klausler private:
78c1db35f0Speter klausler const Descriptor &shift_;
79c1db35f0Speter klausler Terminator &terminator_;
80c1db35f0Speter klausler int shiftRank_;
81c1db35f0Speter klausler int dim_;
82c1db35f0Speter klausler SubscriptValue lb_[maxRank];
83c1db35f0Speter klausler std::size_t shiftElemLen_;
84c1db35f0Speter klausler SubscriptValue shiftCount_{};
85c1db35f0Speter klausler };
86c1db35f0Speter klausler
87c1db35f0Speter klausler // Fill an EOSHIFT result with default boundary values
DefaultInitialize(const Descriptor & result,Terminator & terminator)88c1db35f0Speter klausler static void DefaultInitialize(
89c1db35f0Speter klausler const Descriptor &result, Terminator &terminator) {
90c1db35f0Speter klausler auto catAndKind{result.type().GetCategoryAndKind()};
91c1db35f0Speter klausler RUNTIME_CHECK(
92c1db35f0Speter klausler terminator, catAndKind && catAndKind->first != TypeCategory::Derived);
93c1db35f0Speter klausler std::size_t elementLen{result.ElementBytes()};
94c1db35f0Speter klausler std::size_t bytes{result.Elements() * elementLen};
95c1db35f0Speter klausler if (catAndKind->first == TypeCategory::Character) {
96c1db35f0Speter klausler switch (int kind{catAndKind->second}) {
97c1db35f0Speter klausler case 1:
98c1db35f0Speter klausler std::fill_n(result.OffsetElement<char>(), bytes, ' ');
99c1db35f0Speter klausler break;
100c1db35f0Speter klausler case 2:
101c1db35f0Speter klausler std::fill_n(result.OffsetElement<char16_t>(), bytes / 2,
102c1db35f0Speter klausler static_cast<char16_t>(' '));
103c1db35f0Speter klausler break;
104c1db35f0Speter klausler case 4:
105c1db35f0Speter klausler std::fill_n(result.OffsetElement<char32_t>(), bytes / 4,
106c1db35f0Speter klausler static_cast<char32_t>(' '));
107c1db35f0Speter klausler break;
108c1db35f0Speter klausler default:
109d4609ae4SPeter Steinfeld terminator.Crash("not yet implemented: EOSHIFT: CHARACTER kind %d", kind);
110c1db35f0Speter klausler }
111c1db35f0Speter klausler } else {
112c1db35f0Speter klausler std::memset(result.raw().base_addr, 0, bytes);
113c1db35f0Speter klausler }
114c1db35f0Speter klausler }
115c1db35f0Speter klausler
AllocateResult(Descriptor & result,const Descriptor & source,int rank,const SubscriptValue extent[],Terminator & terminator,const char * function)116c1db35f0Speter klausler static inline std::size_t AllocateResult(Descriptor &result,
117c1db35f0Speter klausler const Descriptor &source, int rank, const SubscriptValue extent[],
118c1db35f0Speter klausler Terminator &terminator, const char *function) {
119c1db35f0Speter klausler std::size_t elementLen{source.ElementBytes()};
120c1db35f0Speter klausler const DescriptorAddendum *sourceAddendum{source.Addendum()};
121c1db35f0Speter klausler result.Establish(source.type(), elementLen, nullptr, rank, extent,
122c1db35f0Speter klausler CFI_attribute_allocatable, sourceAddendum != nullptr);
123c1db35f0Speter klausler if (sourceAddendum) {
124c1db35f0Speter klausler *result.Addendum() = *sourceAddendum;
125c1db35f0Speter klausler }
126c1db35f0Speter klausler for (int j{0}; j < rank; ++j) {
127c1db35f0Speter klausler result.GetDimension(j).SetBounds(1, extent[j]);
128c1db35f0Speter klausler }
129c1db35f0Speter klausler if (int stat{result.Allocate()}) {
130c1db35f0Speter klausler terminator.Crash(
131c1db35f0Speter klausler "%s: Could not allocate memory for result (stat=%d)", function, stat);
132c1db35f0Speter klausler }
133c1db35f0Speter klausler return elementLen;
134c1db35f0Speter klausler }
135c1db35f0Speter klausler
136c1db35f0Speter klausler extern "C" {
137c1db35f0Speter klausler
1387898e7c8SPeter Steinfeld // CSHIFT where rank of ARRAY argument > 1
RTNAME(Cshift)139c1db35f0Speter klausler void RTNAME(Cshift)(Descriptor &result, const Descriptor &source,
140c1db35f0Speter klausler const Descriptor &shift, int dim, const char *sourceFile, int line) {
141c1db35f0Speter klausler Terminator terminator{sourceFile, line};
142c1db35f0Speter klausler int rank{source.rank()};
143c1db35f0Speter klausler RUNTIME_CHECK(terminator, rank > 1);
144251d062eSPeter Klausler if (dim < 1 || dim > rank) {
145251d062eSPeter Klausler terminator.Crash(
146251d062eSPeter Klausler "CSHIFT: DIM=%d must be >= 1 and <= SOURCE= rank %d", dim, rank);
147251d062eSPeter Klausler }
148c1db35f0Speter klausler ShiftControl shiftControl{shift, terminator, dim};
149251d062eSPeter Klausler shiftControl.Init(source, "CSHIFT");
150c1db35f0Speter klausler SubscriptValue extent[maxRank];
151c1db35f0Speter klausler source.GetShape(extent);
152c1db35f0Speter klausler AllocateResult(result, source, rank, extent, terminator, "CSHIFT");
153c1db35f0Speter klausler SubscriptValue resultAt[maxRank];
154c1db35f0Speter klausler for (int j{0}; j < rank; ++j) {
155c1db35f0Speter klausler resultAt[j] = 1;
156c1db35f0Speter klausler }
157c1db35f0Speter klausler SubscriptValue sourceLB[maxRank];
158c1db35f0Speter klausler source.GetLowerBounds(sourceLB);
159c1db35f0Speter klausler SubscriptValue dimExtent{extent[dim - 1]};
160c1db35f0Speter klausler SubscriptValue dimLB{sourceLB[dim - 1]};
161c1db35f0Speter klausler SubscriptValue &resDim{resultAt[dim - 1]};
162c1db35f0Speter klausler for (std::size_t n{result.Elements()}; n > 0; n -= dimExtent) {
163c1db35f0Speter klausler SubscriptValue shiftCount{shiftControl.GetShift(resultAt)};
164c1db35f0Speter klausler SubscriptValue sourceAt[maxRank];
165c1db35f0Speter klausler for (int j{0}; j < rank; ++j) {
166c1db35f0Speter klausler sourceAt[j] = sourceLB[j] + resultAt[j] - 1;
167c1db35f0Speter klausler }
168c1db35f0Speter klausler SubscriptValue &sourceDim{sourceAt[dim - 1]};
169c1db35f0Speter klausler sourceDim = dimLB + shiftCount % dimExtent;
170*bd577afeSPeter Klausler if (sourceDim < dimLB) {
171c1db35f0Speter klausler sourceDim += dimExtent;
172c1db35f0Speter klausler }
173c1db35f0Speter klausler for (resDim = 1; resDim <= dimExtent; ++resDim) {
174c1db35f0Speter klausler CopyElement(result, resultAt, source, sourceAt, terminator);
175c1db35f0Speter klausler if (++sourceDim == dimLB + dimExtent) {
176c1db35f0Speter klausler sourceDim = dimLB;
177c1db35f0Speter klausler }
178c1db35f0Speter klausler }
179c1db35f0Speter klausler result.IncrementSubscripts(resultAt);
180c1db35f0Speter klausler }
181c1db35f0Speter klausler }
182c1db35f0Speter klausler
1837898e7c8SPeter Steinfeld // CSHIFT where rank of ARRAY argument == 1
RTNAME(CshiftVector)184c1db35f0Speter klausler void RTNAME(CshiftVector)(Descriptor &result, const Descriptor &source,
185c1db35f0Speter klausler std::int64_t shift, const char *sourceFile, int line) {
186c1db35f0Speter klausler Terminator terminator{sourceFile, line};
187c1db35f0Speter klausler RUNTIME_CHECK(terminator, source.rank() == 1);
188c1db35f0Speter klausler const Dimension &sourceDim{source.GetDimension(0)};
189c1db35f0Speter klausler SubscriptValue extent{sourceDim.Extent()};
190c1db35f0Speter klausler AllocateResult(result, source, 1, &extent, terminator, "CSHIFT");
191c1db35f0Speter klausler SubscriptValue lb{sourceDim.LowerBound()};
192c1db35f0Speter klausler for (SubscriptValue j{0}; j < extent; ++j) {
193c1db35f0Speter klausler SubscriptValue resultAt{1 + j};
194c1db35f0Speter klausler SubscriptValue sourceAt{lb + (j + shift) % extent};
1956544d9a4SJean Perier if (sourceAt < lb) {
1967898e7c8SPeter Steinfeld sourceAt += extent;
1977898e7c8SPeter Steinfeld }
198c1db35f0Speter klausler CopyElement(result, &resultAt, source, &sourceAt, terminator);
199c1db35f0Speter klausler }
200c1db35f0Speter klausler }
201c1db35f0Speter klausler
202c1db35f0Speter klausler // EOSHIFT of rank > 1
RTNAME(Eoshift)203c1db35f0Speter klausler void RTNAME(Eoshift)(Descriptor &result, const Descriptor &source,
204c1db35f0Speter klausler const Descriptor &shift, const Descriptor *boundary, int dim,
205c1db35f0Speter klausler const char *sourceFile, int line) {
206c1db35f0Speter klausler Terminator terminator{sourceFile, line};
207c1db35f0Speter klausler SubscriptValue extent[maxRank];
208c1db35f0Speter klausler int rank{source.GetShape(extent)};
209c1db35f0Speter klausler RUNTIME_CHECK(terminator, rank > 1);
210251d062eSPeter Klausler if (dim < 1 || dim > rank) {
211251d062eSPeter Klausler terminator.Crash(
212251d062eSPeter Klausler "EOSHIFT: DIM=%d must be >= 1 and <= SOURCE= rank %d", dim, rank);
213251d062eSPeter Klausler }
214c1db35f0Speter klausler std::size_t elementLen{
215c1db35f0Speter klausler AllocateResult(result, source, rank, extent, terminator, "EOSHIFT")};
216c1db35f0Speter klausler int boundaryRank{-1};
217c1db35f0Speter klausler if (boundary) {
218c1db35f0Speter klausler boundaryRank = boundary->rank();
219c1db35f0Speter klausler RUNTIME_CHECK(terminator, boundaryRank == 0 || boundaryRank == rank - 1);
220251d062eSPeter Klausler RUNTIME_CHECK(terminator, boundary->type() == source.type());
221251d062eSPeter Klausler if (boundary->ElementBytes() != elementLen) {
222251d062eSPeter Klausler terminator.Crash("EOSHIFT: BOUNDARY= has element byte length %zd, but "
223251d062eSPeter Klausler "SOURCE= has length %zd",
224251d062eSPeter Klausler boundary->ElementBytes(), elementLen);
225251d062eSPeter Klausler }
226c1db35f0Speter klausler if (boundaryRank > 0) {
227c1db35f0Speter klausler int k{0};
228c1db35f0Speter klausler for (int j{0}; j < rank; ++j) {
229c1db35f0Speter klausler if (j != dim - 1) {
230251d062eSPeter Klausler if (boundary->GetDimension(k).Extent() != extent[j]) {
231251d062eSPeter Klausler terminator.Crash("EOSHIFT: BOUNDARY= has extent %jd on dimension "
232251d062eSPeter Klausler "%d but must conform with extent %jd of SOURCE=",
233251d062eSPeter Klausler static_cast<std::intmax_t>(boundary->GetDimension(k).Extent()),
234251d062eSPeter Klausler k + 1, static_cast<std::intmax_t>(extent[j]));
235251d062eSPeter Klausler }
236c1db35f0Speter klausler ++k;
237c1db35f0Speter klausler }
238c1db35f0Speter klausler }
239c1db35f0Speter klausler }
240c1db35f0Speter klausler }
241c1db35f0Speter klausler ShiftControl shiftControl{shift, terminator, dim};
242251d062eSPeter Klausler shiftControl.Init(source, "EOSHIFT");
243c1db35f0Speter klausler SubscriptValue resultAt[maxRank];
244c1db35f0Speter klausler for (int j{0}; j < rank; ++j) {
245c1db35f0Speter klausler resultAt[j] = 1;
246c1db35f0Speter klausler }
247c1db35f0Speter klausler if (!boundary) {
248c1db35f0Speter klausler DefaultInitialize(result, terminator);
249c1db35f0Speter klausler }
250c1db35f0Speter klausler SubscriptValue sourceLB[maxRank];
251c1db35f0Speter klausler source.GetLowerBounds(sourceLB);
252c1db35f0Speter klausler SubscriptValue boundaryAt[maxRank];
253c1db35f0Speter klausler if (boundaryRank > 0) {
254c1db35f0Speter klausler boundary->GetLowerBounds(boundaryAt);
255c1db35f0Speter klausler }
256c1db35f0Speter klausler SubscriptValue dimExtent{extent[dim - 1]};
257c1db35f0Speter klausler SubscriptValue dimLB{sourceLB[dim - 1]};
258c1db35f0Speter klausler SubscriptValue &resDim{resultAt[dim - 1]};
259c1db35f0Speter klausler for (std::size_t n{result.Elements()}; n > 0; n -= dimExtent) {
260c1db35f0Speter klausler SubscriptValue shiftCount{shiftControl.GetShift(resultAt)};
261c1db35f0Speter klausler SubscriptValue sourceAt[maxRank];
262c1db35f0Speter klausler for (int j{0}; j < rank; ++j) {
263c1db35f0Speter klausler sourceAt[j] = sourceLB[j] + resultAt[j] - 1;
264c1db35f0Speter klausler }
265c1db35f0Speter klausler SubscriptValue &sourceDim{sourceAt[dim - 1]};
266c1db35f0Speter klausler sourceDim = dimLB + shiftCount;
267c1db35f0Speter klausler for (resDim = 1; resDim <= dimExtent; ++resDim) {
268c1db35f0Speter klausler if (sourceDim >= dimLB && sourceDim < dimLB + dimExtent) {
269c1db35f0Speter klausler CopyElement(result, resultAt, source, sourceAt, terminator);
270c1db35f0Speter klausler } else if (boundary) {
271c1db35f0Speter klausler CopyElement(result, resultAt, *boundary, boundaryAt, terminator);
272c1db35f0Speter klausler }
273c1db35f0Speter klausler ++sourceDim;
274c1db35f0Speter klausler }
275c1db35f0Speter klausler result.IncrementSubscripts(resultAt);
276c1db35f0Speter klausler if (boundaryRank > 0) {
277c1db35f0Speter klausler boundary->IncrementSubscripts(boundaryAt);
278c1db35f0Speter klausler }
279c1db35f0Speter klausler }
280c1db35f0Speter klausler }
281c1db35f0Speter klausler
282c1db35f0Speter klausler // EOSHIFT of vector
RTNAME(EoshiftVector)283c1db35f0Speter klausler void RTNAME(EoshiftVector)(Descriptor &result, const Descriptor &source,
284c1db35f0Speter klausler std::int64_t shift, const Descriptor *boundary, const char *sourceFile,
285c1db35f0Speter klausler int line) {
286c1db35f0Speter klausler Terminator terminator{sourceFile, line};
287c1db35f0Speter klausler RUNTIME_CHECK(terminator, source.rank() == 1);
288c1db35f0Speter klausler SubscriptValue extent{source.GetDimension(0).Extent()};
289c1db35f0Speter klausler std::size_t elementLen{
290c1db35f0Speter klausler AllocateResult(result, source, 1, &extent, terminator, "EOSHIFT")};
291c1db35f0Speter klausler if (boundary) {
292c1db35f0Speter klausler RUNTIME_CHECK(terminator, boundary->rank() == 0);
293251d062eSPeter Klausler RUNTIME_CHECK(terminator, boundary->type() == source.type());
294251d062eSPeter Klausler if (boundary->ElementBytes() != elementLen) {
295251d062eSPeter Klausler terminator.Crash("EOSHIFT: BOUNDARY= has element byte length %zd but "
296251d062eSPeter Klausler "SOURCE= has length %zd",
297251d062eSPeter Klausler boundary->ElementBytes(), elementLen);
298251d062eSPeter Klausler }
299c1db35f0Speter klausler }
300c1db35f0Speter klausler if (!boundary) {
301c1db35f0Speter klausler DefaultInitialize(result, terminator);
302c1db35f0Speter klausler }
303c1db35f0Speter klausler SubscriptValue lb{source.GetDimension(0).LowerBound()};
304c1db35f0Speter klausler for (SubscriptValue j{1}; j <= extent; ++j) {
305c1db35f0Speter klausler SubscriptValue sourceAt{lb + j - 1 + shift};
306c1db35f0Speter klausler if (sourceAt >= lb && sourceAt < lb + extent) {
307c1db35f0Speter klausler CopyElement(result, &j, source, &sourceAt, terminator);
308b8ecdcddSPeter Steinfeld } else if (boundary) {
309b8ecdcddSPeter Steinfeld CopyElement(result, &j, *boundary, 0, terminator);
310c1db35f0Speter klausler }
311c1db35f0Speter klausler }
312c1db35f0Speter klausler }
313c1db35f0Speter klausler
314c1db35f0Speter klausler // PACK
RTNAME(Pack)315c1db35f0Speter klausler void RTNAME(Pack)(Descriptor &result, const Descriptor &source,
316c1db35f0Speter klausler const Descriptor &mask, const Descriptor *vector, const char *sourceFile,
317c1db35f0Speter klausler int line) {
318c1db35f0Speter klausler Terminator terminator{sourceFile, line};
319c1db35f0Speter klausler CheckConformability(source, mask, terminator, "PACK", "ARRAY=", "MASK=");
320c1db35f0Speter klausler auto maskType{mask.type().GetCategoryAndKind()};
321c1db35f0Speter klausler RUNTIME_CHECK(
322c1db35f0Speter klausler terminator, maskType && maskType->first == TypeCategory::Logical);
323c1db35f0Speter klausler SubscriptValue trues{0};
324c1db35f0Speter klausler if (mask.rank() == 0) {
325c1db35f0Speter klausler if (IsLogicalElementTrue(mask, nullptr)) {
326c1db35f0Speter klausler trues = source.Elements();
327c1db35f0Speter klausler }
328c1db35f0Speter klausler } else {
329c1db35f0Speter klausler SubscriptValue maskAt[maxRank];
330c1db35f0Speter klausler mask.GetLowerBounds(maskAt);
331c1db35f0Speter klausler for (std::size_t n{mask.Elements()}; n > 0; --n) {
332c1db35f0Speter klausler if (IsLogicalElementTrue(mask, maskAt)) {
333c1db35f0Speter klausler ++trues;
334c1db35f0Speter klausler }
335c1db35f0Speter klausler mask.IncrementSubscripts(maskAt);
336c1db35f0Speter klausler }
337c1db35f0Speter klausler }
338c1db35f0Speter klausler SubscriptValue extent{trues};
339c1db35f0Speter klausler if (vector) {
340c1db35f0Speter klausler RUNTIME_CHECK(terminator, vector->rank() == 1);
341251d062eSPeter Klausler RUNTIME_CHECK(terminator, source.type() == vector->type());
342251d062eSPeter Klausler if (source.ElementBytes() != vector->ElementBytes()) {
343251d062eSPeter Klausler terminator.Crash("PACK: SOURCE= has element byte length %zd, but VECTOR= "
344251d062eSPeter Klausler "has length %zd",
345251d062eSPeter Klausler source.ElementBytes(), vector->ElementBytes());
346251d062eSPeter Klausler }
347c1db35f0Speter klausler extent = vector->GetDimension(0).Extent();
348251d062eSPeter Klausler if (extent < trues) {
349251d062eSPeter Klausler terminator.Crash("PACK: VECTOR= has extent %jd but there are %jd MASK= "
350251d062eSPeter Klausler "elements that are .TRUE.",
351251d062eSPeter Klausler static_cast<std::intmax_t>(extent),
352251d062eSPeter Klausler static_cast<std::intmax_t>(trues));
353251d062eSPeter Klausler }
354c1db35f0Speter klausler }
355c1db35f0Speter klausler AllocateResult(result, source, 1, &extent, terminator, "PACK");
356c1db35f0Speter klausler SubscriptValue sourceAt[maxRank], resultAt{1};
357c1db35f0Speter klausler source.GetLowerBounds(sourceAt);
358c1db35f0Speter klausler if (mask.rank() == 0) {
359c1db35f0Speter klausler if (IsLogicalElementTrue(mask, nullptr)) {
360c1db35f0Speter klausler for (SubscriptValue n{trues}; n > 0; --n) {
361c1db35f0Speter klausler CopyElement(result, &resultAt, source, sourceAt, terminator);
362c1db35f0Speter klausler ++resultAt;
363c1db35f0Speter klausler source.IncrementSubscripts(sourceAt);
364c1db35f0Speter klausler }
365c1db35f0Speter klausler }
366c1db35f0Speter klausler } else {
367c1db35f0Speter klausler SubscriptValue maskAt[maxRank];
368c1db35f0Speter klausler mask.GetLowerBounds(maskAt);
369c1db35f0Speter klausler for (std::size_t n{source.Elements()}; n > 0; --n) {
370c1db35f0Speter klausler if (IsLogicalElementTrue(mask, maskAt)) {
371c1db35f0Speter klausler CopyElement(result, &resultAt, source, sourceAt, terminator);
372c1db35f0Speter klausler ++resultAt;
373c1db35f0Speter klausler }
374c1db35f0Speter klausler source.IncrementSubscripts(sourceAt);
375c1db35f0Speter klausler mask.IncrementSubscripts(maskAt);
376c1db35f0Speter klausler }
377c1db35f0Speter klausler }
378c1db35f0Speter klausler if (vector) {
379c1db35f0Speter klausler SubscriptValue vectorAt{
380c1db35f0Speter klausler vector->GetDimension(0).LowerBound() + resultAt - 1};
381c1db35f0Speter klausler for (; resultAt <= extent; ++resultAt, ++vectorAt) {
382c1db35f0Speter klausler CopyElement(result, &resultAt, *vector, &vectorAt, terminator);
383c1db35f0Speter klausler }
384c1db35f0Speter klausler }
385c1db35f0Speter klausler }
386c1db35f0Speter klausler
387a1034022SMark Leair // RESHAPE
388352d347aSAlexis Perry // F2018 16.9.163
RTNAME(Reshape)389a1034022SMark Leair void RTNAME(Reshape)(Descriptor &result, const Descriptor &source,
390c1db35f0Speter klausler const Descriptor &shape, const Descriptor *pad, const Descriptor *order,
391c1db35f0Speter klausler const char *sourceFile, int line) {
392352d347aSAlexis Perry // Compute and check the rank of the result.
393c1db35f0Speter klausler Terminator terminator{sourceFile, line};
3943b635714Speter klausler RUNTIME_CHECK(terminator, shape.rank() == 1);
3953b635714Speter klausler RUNTIME_CHECK(terminator, shape.type().IsInteger());
396352d347aSAlexis Perry SubscriptValue resultRank{shape.GetDimension(0).Extent()};
397251d062eSPeter Klausler if (resultRank < 0 || resultRank > static_cast<SubscriptValue>(maxRank)) {
398251d062eSPeter Klausler terminator.Crash(
399251d062eSPeter Klausler "RESHAPE: SHAPE= vector length %jd implies a bad result rank",
400251d062eSPeter Klausler static_cast<std::intmax_t>(resultRank));
401251d062eSPeter Klausler }
402352d347aSAlexis Perry
403352d347aSAlexis Perry // Extract and check the shape of the result; compute its element count.
404352d347aSAlexis Perry SubscriptValue resultExtent[maxRank];
405352d347aSAlexis Perry std::size_t shapeElementBytes{shape.ElementBytes()};
406352d347aSAlexis Perry std::size_t resultElements{1};
407352d347aSAlexis Perry SubscriptValue shapeSubscript{shape.GetDimension(0).LowerBound()};
408251d062eSPeter Klausler for (int j{0}; j < resultRank; ++j, ++shapeSubscript) {
409c1db35f0Speter klausler resultExtent[j] = GetInt64(
410c1db35f0Speter klausler shape.Element<char>(&shapeSubscript), shapeElementBytes, terminator);
411251d062eSPeter Klausler if (resultExtent[j] < 0) {
412251d062eSPeter Klausler terminator.Crash("RESHAPE: bad value for SHAPE(%d)=%jd", j + 1,
413251d062eSPeter Klausler static_cast<std::intmax_t>(resultExtent[j]));
414251d062eSPeter Klausler }
415352d347aSAlexis Perry resultElements *= resultExtent[j];
416352d347aSAlexis Perry }
417352d347aSAlexis Perry
418352d347aSAlexis Perry // Check that there are sufficient elements in the SOURCE=, or that
419352d347aSAlexis Perry // the optional PAD= argument is present and nonempty.
420352d347aSAlexis Perry std::size_t elementBytes{source.ElementBytes()};
421352d347aSAlexis Perry std::size_t sourceElements{source.Elements()};
422352d347aSAlexis Perry std::size_t padElements{pad ? pad->Elements() : 0};
42345a8caf1SPeter Klausler if (resultElements > sourceElements) {
424251d062eSPeter Klausler if (padElements <= 0) {
425251d062eSPeter Klausler terminator.Crash(
426251d062eSPeter Klausler "RESHAPE: not enough elements, need %zd but only have %zd",
427bdf57365SPeter Steinfeld resultElements, sourceElements);
428251d062eSPeter Klausler }
429251d062eSPeter Klausler if (pad->ElementBytes() != elementBytes) {
430251d062eSPeter Klausler terminator.Crash("RESHAPE: PAD= has element byte length %zd but SOURCE= "
431251d062eSPeter Klausler "has length %zd",
432251d062eSPeter Klausler pad->ElementBytes(), elementBytes);
433251d062eSPeter Klausler }
434352d347aSAlexis Perry }
435352d347aSAlexis Perry
436352d347aSAlexis Perry // Extract and check the optional ORDER= argument, which must be a
437352d347aSAlexis Perry // permutation of [1..resultRank].
438352d347aSAlexis Perry int dimOrder[maxRank];
439352d347aSAlexis Perry if (order) {
4403b635714Speter klausler RUNTIME_CHECK(terminator, order->rank() == 1);
4413b635714Speter klausler RUNTIME_CHECK(terminator, order->type().IsInteger());
442251d062eSPeter Klausler if (order->GetDimension(0).Extent() != resultRank) {
443251d062eSPeter Klausler terminator.Crash("RESHAPE: the extent of ORDER (%jd) must match the rank"
444bdf57365SPeter Steinfeld " of the SHAPE (%d)",
445251d062eSPeter Klausler static_cast<std::intmax_t>(order->GetDimension(0).Extent()),
446251d062eSPeter Klausler resultRank);
447251d062eSPeter Klausler }
4483b635714Speter klausler std::uint64_t values{0};
449352d347aSAlexis Perry SubscriptValue orderSubscript{order->GetDimension(0).LowerBound()};
450a1034022SMark Leair std::size_t orderElementBytes{order->ElementBytes()};
451352d347aSAlexis Perry for (SubscriptValue j{0}; j < resultRank; ++j, ++orderSubscript) {
452a1034022SMark Leair auto k{GetInt64(order->Element<char>(&orderSubscript), orderElementBytes,
453a1034022SMark Leair terminator)};
454251d062eSPeter Klausler if (k < 1 || k > resultRank || ((values >> k) & 1)) {
455251d062eSPeter Klausler terminator.Crash("RESHAPE: bad value for ORDER element (%jd)",
456251d062eSPeter Klausler static_cast<std::intmax_t>(k));
457251d062eSPeter Klausler }
4583b635714Speter klausler values |= std::uint64_t{1} << k;
45985ec4493SPeter Klausler dimOrder[j] = k - 1;
460352d347aSAlexis Perry }
461352d347aSAlexis Perry } else {
462352d347aSAlexis Perry for (int j{0}; j < resultRank; ++j) {
463352d347aSAlexis Perry dimOrder[j] = j;
464352d347aSAlexis Perry }
465352d347aSAlexis Perry }
466352d347aSAlexis Perry
467a1034022SMark Leair // Allocate result descriptor
468a1034022SMark Leair AllocateResult(
469a1034022SMark Leair result, source, resultRank, resultExtent, terminator, "RESHAPE");
470352d347aSAlexis Perry
471352d347aSAlexis Perry // Populate the result's elements.
472352d347aSAlexis Perry SubscriptValue resultSubscript[maxRank];
473a1034022SMark Leair result.GetLowerBounds(resultSubscript);
474352d347aSAlexis Perry SubscriptValue sourceSubscript[maxRank];
475352d347aSAlexis Perry source.GetLowerBounds(sourceSubscript);
476352d347aSAlexis Perry std::size_t resultElement{0};
477352d347aSAlexis Perry std::size_t elementsFromSource{std::min(resultElements, sourceElements)};
478352d347aSAlexis Perry for (; resultElement < elementsFromSource; ++resultElement) {
479a1034022SMark Leair CopyElement(result, resultSubscript, source, sourceSubscript, terminator);
480352d347aSAlexis Perry source.IncrementSubscripts(sourceSubscript);
481a1034022SMark Leair result.IncrementSubscripts(resultSubscript, dimOrder);
482352d347aSAlexis Perry }
483352d347aSAlexis Perry if (resultElement < resultElements) {
484352d347aSAlexis Perry // Remaining elements come from the optional PAD= argument.
485352d347aSAlexis Perry SubscriptValue padSubscript[maxRank];
486352d347aSAlexis Perry pad->GetLowerBounds(padSubscript);
487352d347aSAlexis Perry for (; resultElement < resultElements; ++resultElement) {
488a1034022SMark Leair CopyElement(result, resultSubscript, *pad, padSubscript, terminator);
489352d347aSAlexis Perry pad->IncrementSubscripts(padSubscript);
490a1034022SMark Leair result.IncrementSubscripts(resultSubscript, dimOrder);
491352d347aSAlexis Perry }
492352d347aSAlexis Perry }
493352d347aSAlexis Perry }
494c1db35f0Speter klausler
495c1db35f0Speter klausler // SPREAD
RTNAME(Spread)496c1db35f0Speter klausler void RTNAME(Spread)(Descriptor &result, const Descriptor &source, int dim,
497c1db35f0Speter klausler std::int64_t ncopies, const char *sourceFile, int line) {
498c1db35f0Speter klausler Terminator terminator{sourceFile, line};
499c1db35f0Speter klausler int rank{source.rank() + 1};
500c1db35f0Speter klausler RUNTIME_CHECK(terminator, rank <= maxRank);
50176436336SPeter Klausler if (dim < 1 || dim > rank) {
50276436336SPeter Klausler terminator.Crash("SPREAD: DIM=%d argument for rank-%d source array "
50376436336SPeter Klausler "must be greater than 1 and less than or equal to %d",
50476436336SPeter Klausler dim, rank - 1, rank);
50576436336SPeter Klausler }
506c1db35f0Speter klausler ncopies = std::max<std::int64_t>(ncopies, 0);
507c1db35f0Speter klausler SubscriptValue extent[maxRank];
508c1db35f0Speter klausler int k{0};
509c1db35f0Speter klausler for (int j{0}; j < rank; ++j) {
510c1db35f0Speter klausler extent[j] = j == dim - 1 ? ncopies : source.GetDimension(k++).Extent();
511c1db35f0Speter klausler }
512c1db35f0Speter klausler AllocateResult(result, source, rank, extent, terminator, "SPREAD");
513c1db35f0Speter klausler SubscriptValue resultAt[maxRank];
514c1db35f0Speter klausler for (int j{0}; j < rank; ++j) {
515c1db35f0Speter klausler resultAt[j] = 1;
516c1db35f0Speter klausler }
517c1db35f0Speter klausler SubscriptValue &resultDim{resultAt[dim - 1]};
518c1db35f0Speter klausler SubscriptValue sourceAt[maxRank];
519c1db35f0Speter klausler source.GetLowerBounds(sourceAt);
520c1db35f0Speter klausler for (std::size_t n{result.Elements()}; n > 0; n -= ncopies) {
521c1db35f0Speter klausler for (resultDim = 1; resultDim <= ncopies; ++resultDim) {
522c1db35f0Speter klausler CopyElement(result, resultAt, source, sourceAt, terminator);
523c1db35f0Speter klausler }
524c1db35f0Speter klausler result.IncrementSubscripts(resultAt);
525c1db35f0Speter klausler source.IncrementSubscripts(sourceAt);
526c1db35f0Speter klausler }
527c1db35f0Speter klausler }
528c1db35f0Speter klausler
529c1db35f0Speter klausler // TRANSPOSE
RTNAME(Transpose)530c1db35f0Speter klausler void RTNAME(Transpose)(Descriptor &result, const Descriptor &matrix,
531c1db35f0Speter klausler const char *sourceFile, int line) {
532c1db35f0Speter klausler Terminator terminator{sourceFile, line};
533c1db35f0Speter klausler RUNTIME_CHECK(terminator, matrix.rank() == 2);
534c1db35f0Speter klausler SubscriptValue extent[2]{
535c1db35f0Speter klausler matrix.GetDimension(1).Extent(), matrix.GetDimension(0).Extent()};
536c1db35f0Speter klausler AllocateResult(result, matrix, 2, extent, terminator, "TRANSPOSE");
537c1db35f0Speter klausler SubscriptValue resultAt[2]{1, 1};
538c1db35f0Speter klausler SubscriptValue matrixLB[2];
539c1db35f0Speter klausler matrix.GetLowerBounds(matrixLB);
540c1db35f0Speter klausler for (std::size_t n{result.Elements()}; n-- > 0;
541c1db35f0Speter klausler result.IncrementSubscripts(resultAt)) {
542c1db35f0Speter klausler SubscriptValue matrixAt[2]{
543c1db35f0Speter klausler matrixLB[0] + resultAt[1] - 1, matrixLB[1] + resultAt[0] - 1};
544c1db35f0Speter klausler CopyElement(result, resultAt, matrix, matrixAt, terminator);
545c1db35f0Speter klausler }
546c1db35f0Speter klausler }
547c1db35f0Speter klausler
548c1db35f0Speter klausler // UNPACK
RTNAME(Unpack)549c1db35f0Speter klausler void RTNAME(Unpack)(Descriptor &result, const Descriptor &vector,
550c1db35f0Speter klausler const Descriptor &mask, const Descriptor &field, const char *sourceFile,
551c1db35f0Speter klausler int line) {
552c1db35f0Speter klausler Terminator terminator{sourceFile, line};
553c1db35f0Speter klausler RUNTIME_CHECK(terminator, vector.rank() == 1);
554c1db35f0Speter klausler int rank{mask.rank()};
555c1db35f0Speter klausler RUNTIME_CHECK(terminator, rank > 0);
556c1db35f0Speter klausler SubscriptValue extent[maxRank];
557c1db35f0Speter klausler mask.GetShape(extent);
558c1db35f0Speter klausler CheckConformability(mask, field, terminator, "UNPACK", "MASK=", "FIELD=");
559c1db35f0Speter klausler std::size_t elementLen{
560c1db35f0Speter klausler AllocateResult(result, field, rank, extent, terminator, "UNPACK")};
561251d062eSPeter Klausler RUNTIME_CHECK(terminator, vector.type() == field.type());
562251d062eSPeter Klausler if (vector.ElementBytes() != elementLen) {
563251d062eSPeter Klausler terminator.Crash(
564251d062eSPeter Klausler "UNPACK: VECTOR= has element byte length %zd but FIELD= has length %zd",
565251d062eSPeter Klausler vector.ElementBytes(), elementLen);
566251d062eSPeter Klausler }
567c1db35f0Speter klausler SubscriptValue resultAt[maxRank], maskAt[maxRank], fieldAt[maxRank],
568c1db35f0Speter klausler vectorAt{vector.GetDimension(0).LowerBound()};
569c1db35f0Speter klausler for (int j{0}; j < rank; ++j) {
570c1db35f0Speter klausler resultAt[j] = 1;
571c1db35f0Speter klausler }
572c1db35f0Speter klausler mask.GetLowerBounds(maskAt);
573c1db35f0Speter klausler field.GetLowerBounds(fieldAt);
574e3550f19SPeter Steinfeld SubscriptValue vectorElements{vector.GetDimension(0).Extent()};
575e3550f19SPeter Steinfeld SubscriptValue vectorLeft{vectorElements};
576c1db35f0Speter klausler for (std::size_t n{result.Elements()}; n-- > 0;) {
577c1db35f0Speter klausler if (IsLogicalElementTrue(mask, maskAt)) {
578c1db35f0Speter klausler if (vectorLeft-- == 0) {
579e3550f19SPeter Steinfeld terminator.Crash(
580e3550f19SPeter Steinfeld "UNPACK: VECTOR= argument has fewer elements (%d) than "
581e3550f19SPeter Steinfeld "MASK= has .TRUE. entries",
582e3550f19SPeter Steinfeld vectorElements);
583c1db35f0Speter klausler }
584c1db35f0Speter klausler CopyElement(result, resultAt, vector, &vectorAt, terminator);
585c1db35f0Speter klausler ++vectorAt;
586c1db35f0Speter klausler } else {
587c1db35f0Speter klausler CopyElement(result, resultAt, field, fieldAt, terminator);
588c1db35f0Speter klausler }
589c1db35f0Speter klausler result.IncrementSubscripts(resultAt);
590c1db35f0Speter klausler mask.IncrementSubscripts(maskAt);
591c1db35f0Speter klausler field.IncrementSubscripts(fieldAt);
592c1db35f0Speter klausler }
593c1db35f0Speter klausler }
594c1db35f0Speter klausler
595c1db35f0Speter klausler } // extern "C"
5961f879005STim Keith } // namespace Fortran::runtime
597