1 //===-- runtime/transformational.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 //===----------------------------------------------------------------------===//
9 // Implements the transformational intrinsic functions of Fortran 2018 that
10 // rearrange or duplicate data without (much) regard to type. These are
11 // CSHIFT, EOSHIFT, PACK, RESHAPE, SPREAD, TRANSPOSE, and UNPACK.
13 // Many of these are defined in the 2018 standard with text that makes sense
14 // only if argument arrays have lower bounds of one. Rather than interpret
15 // these cases as implying a hidden constraint, these implementations
16 // work with arbitrary lower bounds. This may be technically an extension
17 // of the standard but it more likely to conform with its intent.
19 #include "flang/Runtime/transformational.h"
21 #include "terminator.h"
23 #include "flang/Runtime/descriptor.h"
26 namespace Fortran::runtime
{
28 // Utility for CSHIFT & EOSHIFT rank > 1 cases that determines the shift count
29 // for each of the vector sections of the result.
32 ShiftControl(const Descriptor
&s
, Terminator
&t
, int dim
)
33 : shift_
{s
}, terminator_
{t
}, shiftRank_
{s
.rank()}, dim_
{dim
} {}
34 void Init(const Descriptor
&source
, const char *which
) {
35 int rank
{source
.rank()};
36 RUNTIME_CHECK(terminator_
, shiftRank_
== 0 || shiftRank_
== rank
- 1);
37 auto catAndKind
{shift_
.type().GetCategoryAndKind()};
39 terminator_
, catAndKind
&& catAndKind
->first
== TypeCategory::Integer
);
40 shiftElemLen_
= catAndKind
->second
;
43 for (int j
{0}; j
< rank
; ++j
) {
45 const Dimension
&shiftDim
{shift_
.GetDimension(k
)};
46 lb_
[k
++] = shiftDim
.LowerBound();
47 if (shiftDim
.Extent() != source
.GetDimension(j
).Extent()) {
48 terminator_
.Crash("%s: on dimension %d, SHIFT= has extent %jd but "
49 "SOURCE= has extent %jd",
50 which
, k
, static_cast<std::intmax_t>(shiftDim
.Extent()),
51 static_cast<std::intmax_t>(source
.GetDimension(j
).Extent()));
57 GetInt64(shift_
.OffsetElement
<char>(), shiftElemLen_
, terminator_
);
60 SubscriptValue
GetShift(const SubscriptValue resultAt
[]) const {
62 SubscriptValue shiftAt
[maxRank
];
64 for (int j
{0}; j
< shiftRank_
+ 1; ++j
) {
66 shiftAt
[k
] = lb_
[k
] + resultAt
[j
] - 1;
71 shift_
.Element
<char>(shiftAt
), shiftElemLen_
, terminator_
);
73 return shiftCount_
; // invariant count extracted in Init()
78 const Descriptor
&shift_
;
79 Terminator
&terminator_
;
82 SubscriptValue lb_
[maxRank
];
83 std::size_t shiftElemLen_
;
84 SubscriptValue shiftCount_
{};
87 // Fill an EOSHIFT result with default boundary values
88 static void DefaultInitialize(
89 const Descriptor
&result
, Terminator
&terminator
) {
90 auto catAndKind
{result
.type().GetCategoryAndKind()};
92 terminator
, catAndKind
&& catAndKind
->first
!= TypeCategory::Derived
);
93 std::size_t elementLen
{result
.ElementBytes()};
94 std::size_t bytes
{result
.Elements() * elementLen
};
95 if (catAndKind
->first
== TypeCategory::Character
) {
96 switch (int kind
{catAndKind
->second
}) {
98 std::fill_n(result
.OffsetElement
<char>(), bytes
, ' ');
101 std::fill_n(result
.OffsetElement
<char16_t
>(), bytes
/ 2,
102 static_cast<char16_t
>(' '));
105 std::fill_n(result
.OffsetElement
<char32_t
>(), bytes
/ 4,
106 static_cast<char32_t
>(' '));
109 terminator
.Crash("not yet implemented: EOSHIFT: CHARACTER kind %d", kind
);
112 std::memset(result
.raw().base_addr
, 0, bytes
);
116 static inline std::size_t AllocateResult(Descriptor
&result
,
117 const Descriptor
&source
, int rank
, const SubscriptValue extent
[],
118 Terminator
&terminator
, const char *function
) {
119 std::size_t elementLen
{source
.ElementBytes()};
120 const DescriptorAddendum
*sourceAddendum
{source
.Addendum()};
121 result
.Establish(source
.type(), elementLen
, nullptr, rank
, extent
,
122 CFI_attribute_allocatable
, sourceAddendum
!= nullptr);
123 if (sourceAddendum
) {
124 *result
.Addendum() = *sourceAddendum
;
126 for (int j
{0}; j
< rank
; ++j
) {
127 result
.GetDimension(j
).SetBounds(1, extent
[j
]);
129 if (int stat
{result
.Allocate()}) {
131 "%s: Could not allocate memory for result (stat=%d)", function
, stat
);
136 template <TypeCategory CAT
, int KIND
>
137 static inline std::size_t AllocateBesselResult(Descriptor
&result
, int32_t n1
,
138 int32_t n2
, Terminator
&terminator
, const char *function
) {
140 SubscriptValue extent
[maxRank
];
141 for (int j
{0}; j
< maxRank
; j
++) {
145 extent
[0] = n2
- n1
+ 1;
148 std::size_t elementLen
{Descriptor::BytesFor(CAT
, KIND
)};
149 result
.Establish(TypeCode
{CAT
, KIND
}, elementLen
, nullptr, rank
, extent
,
150 CFI_attribute_allocatable
, false);
151 for (int j
{0}; j
< rank
; ++j
) {
152 result
.GetDimension(j
).SetBounds(1, extent
[j
]);
154 if (int stat
{result
.Allocate()}) {
156 "%s: Could not allocate memory for result (stat=%d)", function
, stat
);
161 template <TypeCategory CAT
, int KIND
>
162 static inline void DoBesselJn(Descriptor
&result
, int32_t n1
, int32_t n2
,
163 CppTypeFor
<CAT
, KIND
> x
, CppTypeFor
<CAT
, KIND
> bn2
,
164 CppTypeFor
<CAT
, KIND
> bn2_1
, const char *sourceFile
, int line
) {
165 Terminator terminator
{sourceFile
, line
};
166 AllocateBesselResult
<CAT
, KIND
>(result
, n1
, n2
, terminator
, "BESSEL_JN");
168 // The standard requires that n1 and n2 be non-negative. However, some other
169 // compilers generate results even when n1 and/or n2 are negative. For now,
170 // we also do not enforce the non-negativity constraint.
175 SubscriptValue at
[maxRank
];
176 for (int j
{0}; j
< maxRank
; ++j
) {
180 // if n2 >= n1, there will be at least one element in the result.
182 *result
.Element
<CppTypeFor
<CAT
, KIND
>>(at
) = bn2
;
189 *result
.Element
<CppTypeFor
<CAT
, KIND
>>(at
) = bn2_1
;
191 // Bessel functions of the first kind are stable for a backward recursion
192 // (see https://dlmf.nist.gov/10.74.iv and https://dlmf.nist.gov/10.6.E1).
194 // J(n-1, x) = (2.0 / x) * n * J(n, x) - J(n+1, x)
196 // which is equivalent to
198 // J(n, x) = (2.0 / x) * (n + 1) * J(n+1, x) - J(n+2, x)
200 CppTypeFor
<CAT
, KIND
> bn_2
= bn2
;
201 CppTypeFor
<CAT
, KIND
> bn_1
= bn2_1
;
202 CppTypeFor
<CAT
, KIND
> twoOverX
= 2.0 / x
;
203 for (int n
{n2
- 2}; n
>= n1
; --n
) {
204 auto bn
= twoOverX
* (n
+ 1) * bn_1
- bn_2
;
207 *result
.Element
<CppTypeFor
<CAT
, KIND
>>(at
) = bn
;
214 template <TypeCategory CAT
, int KIND
>
215 static inline void DoBesselJnX0(Descriptor
&result
, int32_t n1
, int32_t n2
,
216 const char *sourceFile
, int line
) {
217 Terminator terminator
{sourceFile
, line
};
218 AllocateBesselResult
<CAT
, KIND
>(result
, n1
, n2
, terminator
, "BESSEL_JN");
220 // The standard requires that n1 and n2 be non-negative. However, some other
221 // compilers generate results even when n1 and/or n2 are negative. For now,
222 // we also do not enforce the non-negativity constraint.
227 SubscriptValue at
[maxRank
];
228 for (int j
{0}; j
< maxRank
; ++j
) {
232 // J(0, 0.0) = 1.0, when n == 0.
233 // J(n, 0.0) = 0.0, when n > 0.
235 *result
.Element
<CppTypeFor
<CAT
, KIND
>>(at
) = (n1
== 0) ? 1.0 : 0.0;
236 for (int j
{2}; j
<= n2
- n1
+ 1; ++j
) {
238 *result
.Element
<CppTypeFor
<CAT
, KIND
>>(at
) = 0.0;
242 template <TypeCategory CAT
, int KIND
>
243 static inline void DoBesselYn(Descriptor
&result
, int32_t n1
, int32_t n2
,
244 CppTypeFor
<CAT
, KIND
> x
, CppTypeFor
<CAT
, KIND
> bn1
,
245 CppTypeFor
<CAT
, KIND
> bn1_1
, const char *sourceFile
, int line
) {
246 Terminator terminator
{sourceFile
, line
};
247 AllocateBesselResult
<CAT
, KIND
>(result
, n1
, n2
, terminator
, "BESSEL_YN");
249 // The standard requires that n1 and n2 be non-negative. However, some other
250 // compilers generate results even when n1 and/or n2 are negative. For now,
251 // we also do not enforce the non-negativity constraint.
256 SubscriptValue at
[maxRank
];
257 for (int j
{0}; j
< maxRank
; ++j
) {
261 // if n2 >= n1, there will be at least one element in the result.
263 *result
.Element
<CppTypeFor
<CAT
, KIND
>>(at
) = bn1
;
270 *result
.Element
<CppTypeFor
<CAT
, KIND
>>(at
) = bn1_1
;
272 // Bessel functions of the second kind are stable for a forward recursion
273 // (see https://dlmf.nist.gov/10.74.iv and https://dlmf.nist.gov/10.6.E1).
275 // Y(n+1, x) = (2.0 / x) * n * Y(n, x) - Y(n-1, x)
277 // which is equivalent to
279 // Y(n, x) = (2.0 / x) * (n - 1) * Y(n-1, x) - Y(n-2, x)
281 CppTypeFor
<CAT
, KIND
> bn_2
= bn1
;
282 CppTypeFor
<CAT
, KIND
> bn_1
= bn1_1
;
283 CppTypeFor
<CAT
, KIND
> twoOverX
= 2.0 / x
;
284 for (int n
{n1
+ 2}; n
<= n2
; ++n
) {
285 auto bn
= twoOverX
* (n
- 1) * bn_1
- bn_2
;
288 *result
.Element
<CppTypeFor
<CAT
, KIND
>>(at
) = bn
;
295 template <TypeCategory CAT
, int KIND
>
296 static inline void DoBesselYnX0(Descriptor
&result
, int32_t n1
, int32_t n2
,
297 const char *sourceFile
, int line
) {
298 Terminator terminator
{sourceFile
, line
};
299 AllocateBesselResult
<CAT
, KIND
>(result
, n1
, n2
, terminator
, "BESSEL_YN");
301 // The standard requires that n1 and n2 be non-negative. However, some other
302 // compilers generate results even when n1 and/or n2 are negative. For now,
303 // we also do not enforce the non-negativity constraint.
308 SubscriptValue at
[maxRank
];
309 for (int j
{0}; j
< maxRank
; ++j
) {
313 // Y(n, 0.0) = -Inf, when n >= 0
314 for (int j
{1}; j
<= n2
- n1
+ 1; ++j
) {
316 *result
.Element
<CppTypeFor
<CAT
, KIND
>>(at
) =
317 -std::numeric_limits
<CppTypeFor
<CAT
, KIND
>>::infinity();
325 void RTNAME(BesselJn_4
)(Descriptor
&result
, int32_t n1
, int32_t n2
,
326 CppTypeFor
<TypeCategory::Real
, 4> x
, CppTypeFor
<TypeCategory::Real
, 4> bn2
,
327 CppTypeFor
<TypeCategory::Real
, 4> bn2_1
, const char *sourceFile
, int line
) {
328 DoBesselJn
<TypeCategory::Real
, 4>(
329 result
, n1
, n2
, x
, bn2
, bn2_1
, sourceFile
, line
);
332 void RTNAME(BesselJn_8
)(Descriptor
&result
, int32_t n1
, int32_t n2
,
333 CppTypeFor
<TypeCategory::Real
, 8> x
, CppTypeFor
<TypeCategory::Real
, 8> bn2
,
334 CppTypeFor
<TypeCategory::Real
, 8> bn2_1
, const char *sourceFile
, int line
) {
335 DoBesselJn
<TypeCategory::Real
, 8>(
336 result
, n1
, n2
, x
, bn2
, bn2_1
, sourceFile
, line
);
339 #if LDBL_MANT_DIG == 64
340 void RTNAME(BesselJn_10
)(Descriptor
&result
, int32_t n1
, int32_t n2
,
341 CppTypeFor
<TypeCategory::Real
, 10> x
,
342 CppTypeFor
<TypeCategory::Real
, 10> bn2
,
343 CppTypeFor
<TypeCategory::Real
, 10> bn2_1
, const char *sourceFile
,
345 DoBesselJn
<TypeCategory::Real
, 10>(
346 result
, n1
, n2
, x
, bn2
, bn2_1
, sourceFile
, line
);
350 #if LDBL_MANT_DIG == 113 || HAS_FLOAT128
351 void RTNAME(BesselJn_16
)(Descriptor
&result
, int32_t n1
, int32_t n2
,
352 CppTypeFor
<TypeCategory::Real
, 16> x
,
353 CppTypeFor
<TypeCategory::Real
, 16> bn2
,
354 CppTypeFor
<TypeCategory::Real
, 16> bn2_1
, const char *sourceFile
,
356 DoBesselJn
<TypeCategory::Real
, 16>(
357 result
, n1
, n2
, x
, bn2
, bn2_1
, sourceFile
, line
);
362 void RTNAME(BesselJnX0_4
)(Descriptor
&result
, int32_t n1
, int32_t n2
,
363 const char *sourceFile
, int line
) {
364 DoBesselJnX0
<TypeCategory::Real
, 4>(result
, n1
, n2
, sourceFile
, line
);
367 void RTNAME(BesselJnX0_8
)(Descriptor
&result
, int32_t n1
, int32_t n2
,
368 const char *sourceFile
, int line
) {
369 DoBesselJnX0
<TypeCategory::Real
, 8>(result
, n1
, n2
, sourceFile
, line
);
372 #if LDBL_MANT_DIG == 64
373 void RTNAME(BesselJnX0_10
)(Descriptor
&result
, int32_t n1
, int32_t n2
,
374 const char *sourceFile
, int line
) {
375 DoBesselJnX0
<TypeCategory::Real
, 10>(result
, n1
, n2
, sourceFile
, line
);
379 #if LDBL_MANT_DIG == 113 || HAS_FLOAT128
380 void RTNAME(BesselJnX0_16
)(Descriptor
&result
, int32_t n1
, int32_t n2
,
381 const char *sourceFile
, int line
) {
382 DoBesselJnX0
<TypeCategory::Real
, 16>(result
, n1
, n2
, sourceFile
, line
);
388 void RTNAME(BesselYn_4
)(Descriptor
&result
, int32_t n1
, int32_t n2
,
389 CppTypeFor
<TypeCategory::Real
, 4> x
, CppTypeFor
<TypeCategory::Real
, 4> bn1
,
390 CppTypeFor
<TypeCategory::Real
, 4> bn1_1
, const char *sourceFile
, int line
) {
391 DoBesselYn
<TypeCategory::Real
, 4>(
392 result
, n1
, n2
, x
, bn1
, bn1_1
, sourceFile
, line
);
395 void RTNAME(BesselYn_8
)(Descriptor
&result
, int32_t n1
, int32_t n2
,
396 CppTypeFor
<TypeCategory::Real
, 8> x
, CppTypeFor
<TypeCategory::Real
, 8> bn1
,
397 CppTypeFor
<TypeCategory::Real
, 8> bn1_1
, const char *sourceFile
, int line
) {
398 DoBesselYn
<TypeCategory::Real
, 8>(
399 result
, n1
, n2
, x
, bn1
, bn1_1
, sourceFile
, line
);
402 #if LDBL_MANT_DIG == 64
403 void RTNAME(BesselYn_10
)(Descriptor
&result
, int32_t n1
, int32_t n2
,
404 CppTypeFor
<TypeCategory::Real
, 10> x
,
405 CppTypeFor
<TypeCategory::Real
, 10> bn1
,
406 CppTypeFor
<TypeCategory::Real
, 10> bn1_1
, const char *sourceFile
,
408 DoBesselYn
<TypeCategory::Real
, 10>(
409 result
, n1
, n2
, x
, bn1
, bn1_1
, sourceFile
, line
);
413 #if LDBL_MANT_DIG == 113 || HAS_FLOAT128
414 void RTNAME(BesselYn_16
)(Descriptor
&result
, int32_t n1
, int32_t n2
,
415 CppTypeFor
<TypeCategory::Real
, 16> x
,
416 CppTypeFor
<TypeCategory::Real
, 16> bn1
,
417 CppTypeFor
<TypeCategory::Real
, 16> bn1_1
, const char *sourceFile
,
419 DoBesselYn
<TypeCategory::Real
, 16>(
420 result
, n1
, n2
, x
, bn1
, bn1_1
, sourceFile
, line
);
425 void RTNAME(BesselYnX0_4
)(Descriptor
&result
, int32_t n1
, int32_t n2
,
426 const char *sourceFile
, int line
) {
427 DoBesselYnX0
<TypeCategory::Real
, 4>(result
, n1
, n2
, sourceFile
, line
);
430 void RTNAME(BesselYnX0_8
)(Descriptor
&result
, int32_t n1
, int32_t n2
,
431 const char *sourceFile
, int line
) {
432 DoBesselYnX0
<TypeCategory::Real
, 8>(result
, n1
, n2
, sourceFile
, line
);
435 #if LDBL_MANT_DIG == 64
436 void RTNAME(BesselYnX0_10
)(Descriptor
&result
, int32_t n1
, int32_t n2
,
437 const char *sourceFile
, int line
) {
438 DoBesselYnX0
<TypeCategory::Real
, 10>(result
, n1
, n2
, sourceFile
, line
);
442 #if LDBL_MANT_DIG == 113 || HAS_FLOAT128
443 void RTNAME(BesselYnX0_16
)(Descriptor
&result
, int32_t n1
, int32_t n2
,
444 const char *sourceFile
, int line
) {
445 DoBesselYnX0
<TypeCategory::Real
, 16>(result
, n1
, n2
, sourceFile
, line
);
449 // CSHIFT where rank of ARRAY argument > 1
450 void RTNAME(Cshift
)(Descriptor
&result
, const Descriptor
&source
,
451 const Descriptor
&shift
, int dim
, const char *sourceFile
, int line
) {
452 Terminator terminator
{sourceFile
, line
};
453 int rank
{source
.rank()};
454 RUNTIME_CHECK(terminator
, rank
> 1);
455 if (dim
< 1 || dim
> rank
) {
457 "CSHIFT: DIM=%d must be >= 1 and <= SOURCE= rank %d", dim
, rank
);
459 ShiftControl shiftControl
{shift
, terminator
, dim
};
460 shiftControl
.Init(source
, "CSHIFT");
461 SubscriptValue extent
[maxRank
];
462 source
.GetShape(extent
);
463 AllocateResult(result
, source
, rank
, extent
, terminator
, "CSHIFT");
464 SubscriptValue resultAt
[maxRank
];
465 for (int j
{0}; j
< rank
; ++j
) {
468 SubscriptValue sourceLB
[maxRank
];
469 source
.GetLowerBounds(sourceLB
);
470 SubscriptValue dimExtent
{extent
[dim
- 1]};
471 SubscriptValue dimLB
{sourceLB
[dim
- 1]};
472 SubscriptValue
&resDim
{resultAt
[dim
- 1]};
473 for (std::size_t n
{result
.Elements()}; n
> 0; n
-= dimExtent
) {
474 SubscriptValue shiftCount
{shiftControl
.GetShift(resultAt
)};
475 SubscriptValue sourceAt
[maxRank
];
476 for (int j
{0}; j
< rank
; ++j
) {
477 sourceAt
[j
] = sourceLB
[j
] + resultAt
[j
] - 1;
479 SubscriptValue
&sourceDim
{sourceAt
[dim
- 1]};
480 sourceDim
= dimLB
+ shiftCount
% dimExtent
;
481 if (sourceDim
< dimLB
) {
482 sourceDim
+= dimExtent
;
484 for (resDim
= 1; resDim
<= dimExtent
; ++resDim
) {
485 CopyElement(result
, resultAt
, source
, sourceAt
, terminator
);
486 if (++sourceDim
== dimLB
+ dimExtent
) {
490 result
.IncrementSubscripts(resultAt
);
494 // CSHIFT where rank of ARRAY argument == 1
495 void RTNAME(CshiftVector
)(Descriptor
&result
, const Descriptor
&source
,
496 std::int64_t shift
, const char *sourceFile
, int line
) {
497 Terminator terminator
{sourceFile
, line
};
498 RUNTIME_CHECK(terminator
, source
.rank() == 1);
499 const Dimension
&sourceDim
{source
.GetDimension(0)};
500 SubscriptValue extent
{sourceDim
.Extent()};
501 AllocateResult(result
, source
, 1, &extent
, terminator
, "CSHIFT");
502 SubscriptValue lb
{sourceDim
.LowerBound()};
503 for (SubscriptValue j
{0}; j
< extent
; ++j
) {
504 SubscriptValue resultAt
{1 + j
};
505 SubscriptValue sourceAt
{lb
+ (j
+ shift
) % extent
};
509 CopyElement(result
, &resultAt
, source
, &sourceAt
, terminator
);
513 // EOSHIFT of rank > 1
514 void RTNAME(Eoshift
)(Descriptor
&result
, const Descriptor
&source
,
515 const Descriptor
&shift
, const Descriptor
*boundary
, int dim
,
516 const char *sourceFile
, int line
) {
517 Terminator terminator
{sourceFile
, line
};
518 SubscriptValue extent
[maxRank
];
519 int rank
{source
.GetShape(extent
)};
520 RUNTIME_CHECK(terminator
, rank
> 1);
521 if (dim
< 1 || dim
> rank
) {
523 "EOSHIFT: DIM=%d must be >= 1 and <= SOURCE= rank %d", dim
, rank
);
525 std::size_t elementLen
{
526 AllocateResult(result
, source
, rank
, extent
, terminator
, "EOSHIFT")};
527 int boundaryRank
{-1};
529 boundaryRank
= boundary
->rank();
530 RUNTIME_CHECK(terminator
, boundaryRank
== 0 || boundaryRank
== rank
- 1);
531 RUNTIME_CHECK(terminator
, boundary
->type() == source
.type());
532 if (boundary
->ElementBytes() != elementLen
) {
533 terminator
.Crash("EOSHIFT: BOUNDARY= has element byte length %zd, but "
534 "SOURCE= has length %zd",
535 boundary
->ElementBytes(), elementLen
);
537 if (boundaryRank
> 0) {
539 for (int j
{0}; j
< rank
; ++j
) {
541 if (boundary
->GetDimension(k
).Extent() != extent
[j
]) {
542 terminator
.Crash("EOSHIFT: BOUNDARY= has extent %jd on dimension "
543 "%d but must conform with extent %jd of SOURCE=",
544 static_cast<std::intmax_t>(boundary
->GetDimension(k
).Extent()),
545 k
+ 1, static_cast<std::intmax_t>(extent
[j
]));
552 ShiftControl shiftControl
{shift
, terminator
, dim
};
553 shiftControl
.Init(source
, "EOSHIFT");
554 SubscriptValue resultAt
[maxRank
];
555 for (int j
{0}; j
< rank
; ++j
) {
559 DefaultInitialize(result
, terminator
);
561 SubscriptValue sourceLB
[maxRank
];
562 source
.GetLowerBounds(sourceLB
);
563 SubscriptValue boundaryAt
[maxRank
];
564 if (boundaryRank
> 0) {
565 boundary
->GetLowerBounds(boundaryAt
);
567 SubscriptValue dimExtent
{extent
[dim
- 1]};
568 SubscriptValue dimLB
{sourceLB
[dim
- 1]};
569 SubscriptValue
&resDim
{resultAt
[dim
- 1]};
570 for (std::size_t n
{result
.Elements()}; n
> 0; n
-= dimExtent
) {
571 SubscriptValue shiftCount
{shiftControl
.GetShift(resultAt
)};
572 SubscriptValue sourceAt
[maxRank
];
573 for (int j
{0}; j
< rank
; ++j
) {
574 sourceAt
[j
] = sourceLB
[j
] + resultAt
[j
] - 1;
576 SubscriptValue
&sourceDim
{sourceAt
[dim
- 1]};
577 sourceDim
= dimLB
+ shiftCount
;
578 for (resDim
= 1; resDim
<= dimExtent
; ++resDim
) {
579 if (sourceDim
>= dimLB
&& sourceDim
< dimLB
+ dimExtent
) {
580 CopyElement(result
, resultAt
, source
, sourceAt
, terminator
);
581 } else if (boundary
) {
582 CopyElement(result
, resultAt
, *boundary
, boundaryAt
, terminator
);
586 result
.IncrementSubscripts(resultAt
);
587 if (boundaryRank
> 0) {
588 boundary
->IncrementSubscripts(boundaryAt
);
594 void RTNAME(EoshiftVector
)(Descriptor
&result
, const Descriptor
&source
,
595 std::int64_t shift
, const Descriptor
*boundary
, const char *sourceFile
,
597 Terminator terminator
{sourceFile
, line
};
598 RUNTIME_CHECK(terminator
, source
.rank() == 1);
599 SubscriptValue extent
{source
.GetDimension(0).Extent()};
600 std::size_t elementLen
{
601 AllocateResult(result
, source
, 1, &extent
, terminator
, "EOSHIFT")};
603 RUNTIME_CHECK(terminator
, boundary
->rank() == 0);
604 RUNTIME_CHECK(terminator
, boundary
->type() == source
.type());
605 if (boundary
->ElementBytes() != elementLen
) {
606 terminator
.Crash("EOSHIFT: BOUNDARY= has element byte length %zd but "
607 "SOURCE= has length %zd",
608 boundary
->ElementBytes(), elementLen
);
612 DefaultInitialize(result
, terminator
);
614 SubscriptValue lb
{source
.GetDimension(0).LowerBound()};
615 for (SubscriptValue j
{1}; j
<= extent
; ++j
) {
616 SubscriptValue sourceAt
{lb
+ j
- 1 + shift
};
617 if (sourceAt
>= lb
&& sourceAt
< lb
+ extent
) {
618 CopyElement(result
, &j
, source
, &sourceAt
, terminator
);
619 } else if (boundary
) {
620 CopyElement(result
, &j
, *boundary
, 0, terminator
);
626 void RTNAME(Pack
)(Descriptor
&result
, const Descriptor
&source
,
627 const Descriptor
&mask
, const Descriptor
*vector
, const char *sourceFile
,
629 Terminator terminator
{sourceFile
, line
};
630 CheckConformability(source
, mask
, terminator
, "PACK", "ARRAY=", "MASK=");
631 auto maskType
{mask
.type().GetCategoryAndKind()};
633 terminator
, maskType
&& maskType
->first
== TypeCategory::Logical
);
634 SubscriptValue trues
{0};
635 if (mask
.rank() == 0) {
636 if (IsLogicalElementTrue(mask
, nullptr)) {
637 trues
= source
.Elements();
640 SubscriptValue maskAt
[maxRank
];
641 mask
.GetLowerBounds(maskAt
);
642 for (std::size_t n
{mask
.Elements()}; n
> 0; --n
) {
643 if (IsLogicalElementTrue(mask
, maskAt
)) {
646 mask
.IncrementSubscripts(maskAt
);
649 SubscriptValue extent
{trues
};
651 RUNTIME_CHECK(terminator
, vector
->rank() == 1);
652 RUNTIME_CHECK(terminator
, source
.type() == vector
->type());
653 if (source
.ElementBytes() != vector
->ElementBytes()) {
654 terminator
.Crash("PACK: SOURCE= has element byte length %zd, but VECTOR= "
656 source
.ElementBytes(), vector
->ElementBytes());
658 extent
= vector
->GetDimension(0).Extent();
659 if (extent
< trues
) {
660 terminator
.Crash("PACK: VECTOR= has extent %jd but there are %jd MASK= "
661 "elements that are .TRUE.",
662 static_cast<std::intmax_t>(extent
),
663 static_cast<std::intmax_t>(trues
));
666 AllocateResult(result
, source
, 1, &extent
, terminator
, "PACK");
667 SubscriptValue sourceAt
[maxRank
], resultAt
{1};
668 source
.GetLowerBounds(sourceAt
);
669 if (mask
.rank() == 0) {
670 if (IsLogicalElementTrue(mask
, nullptr)) {
671 for (SubscriptValue n
{trues
}; n
> 0; --n
) {
672 CopyElement(result
, &resultAt
, source
, sourceAt
, terminator
);
674 source
.IncrementSubscripts(sourceAt
);
678 SubscriptValue maskAt
[maxRank
];
679 mask
.GetLowerBounds(maskAt
);
680 for (std::size_t n
{source
.Elements()}; n
> 0; --n
) {
681 if (IsLogicalElementTrue(mask
, maskAt
)) {
682 CopyElement(result
, &resultAt
, source
, sourceAt
, terminator
);
685 source
.IncrementSubscripts(sourceAt
);
686 mask
.IncrementSubscripts(maskAt
);
690 SubscriptValue vectorAt
{
691 vector
->GetDimension(0).LowerBound() + resultAt
- 1};
692 for (; resultAt
<= extent
; ++resultAt
, ++vectorAt
) {
693 CopyElement(result
, &resultAt
, *vector
, &vectorAt
, terminator
);
700 void RTNAME(Reshape
)(Descriptor
&result
, const Descriptor
&source
,
701 const Descriptor
&shape
, const Descriptor
*pad
, const Descriptor
*order
,
702 const char *sourceFile
, int line
) {
703 // Compute and check the rank of the result.
704 Terminator terminator
{sourceFile
, line
};
705 RUNTIME_CHECK(terminator
, shape
.rank() == 1);
706 RUNTIME_CHECK(terminator
, shape
.type().IsInteger());
707 SubscriptValue resultRank
{shape
.GetDimension(0).Extent()};
708 if (resultRank
< 0 || resultRank
> static_cast<SubscriptValue
>(maxRank
)) {
710 "RESHAPE: SHAPE= vector length %jd implies a bad result rank",
711 static_cast<std::intmax_t>(resultRank
));
714 // Extract and check the shape of the result; compute its element count.
715 SubscriptValue resultExtent
[maxRank
];
716 std::size_t shapeElementBytes
{shape
.ElementBytes()};
717 std::size_t resultElements
{1};
718 SubscriptValue shapeSubscript
{shape
.GetDimension(0).LowerBound()};
719 for (int j
{0}; j
< resultRank
; ++j
, ++shapeSubscript
) {
720 resultExtent
[j
] = GetInt64(
721 shape
.Element
<char>(&shapeSubscript
), shapeElementBytes
, terminator
);
722 if (resultExtent
[j
] < 0) {
723 terminator
.Crash("RESHAPE: bad value for SHAPE(%d)=%jd", j
+ 1,
724 static_cast<std::intmax_t>(resultExtent
[j
]));
726 resultElements
*= resultExtent
[j
];
729 // Check that there are sufficient elements in the SOURCE=, or that
730 // the optional PAD= argument is present and nonempty.
731 std::size_t elementBytes
{source
.ElementBytes()};
732 std::size_t sourceElements
{source
.Elements()};
733 std::size_t padElements
{pad
? pad
->Elements() : 0};
734 if (resultElements
> sourceElements
) {
735 if (padElements
<= 0) {
737 "RESHAPE: not enough elements, need %zd but only have %zd",
738 resultElements
, sourceElements
);
740 if (pad
->ElementBytes() != elementBytes
) {
741 terminator
.Crash("RESHAPE: PAD= has element byte length %zd but SOURCE= "
743 pad
->ElementBytes(), elementBytes
);
747 // Extract and check the optional ORDER= argument, which must be a
748 // permutation of [1..resultRank].
749 int dimOrder
[maxRank
];
751 RUNTIME_CHECK(terminator
, order
->rank() == 1);
752 RUNTIME_CHECK(terminator
, order
->type().IsInteger());
753 if (order
->GetDimension(0).Extent() != resultRank
) {
754 terminator
.Crash("RESHAPE: the extent of ORDER (%jd) must match the rank"
755 " of the SHAPE (%d)",
756 static_cast<std::intmax_t>(order
->GetDimension(0).Extent()),
759 std::uint64_t values
{0};
760 SubscriptValue orderSubscript
{order
->GetDimension(0).LowerBound()};
761 std::size_t orderElementBytes
{order
->ElementBytes()};
762 for (SubscriptValue j
{0}; j
< resultRank
; ++j
, ++orderSubscript
) {
763 auto k
{GetInt64(order
->Element
<char>(&orderSubscript
), orderElementBytes
,
765 if (k
< 1 || k
> resultRank
|| ((values
>> k
) & 1)) {
766 terminator
.Crash("RESHAPE: bad value for ORDER element (%jd)",
767 static_cast<std::intmax_t>(k
));
769 values
|= std::uint64_t{1} << k
;
773 for (int j
{0}; j
< resultRank
; ++j
) {
778 // Allocate result descriptor
780 result
, source
, resultRank
, resultExtent
, terminator
, "RESHAPE");
782 // Populate the result's elements.
783 SubscriptValue resultSubscript
[maxRank
];
784 result
.GetLowerBounds(resultSubscript
);
785 SubscriptValue sourceSubscript
[maxRank
];
786 source
.GetLowerBounds(sourceSubscript
);
787 std::size_t resultElement
{0};
788 std::size_t elementsFromSource
{std::min(resultElements
, sourceElements
)};
789 for (; resultElement
< elementsFromSource
; ++resultElement
) {
790 CopyElement(result
, resultSubscript
, source
, sourceSubscript
, terminator
);
791 source
.IncrementSubscripts(sourceSubscript
);
792 result
.IncrementSubscripts(resultSubscript
, dimOrder
);
794 if (resultElement
< resultElements
) {
795 // Remaining elements come from the optional PAD= argument.
796 SubscriptValue padSubscript
[maxRank
];
797 pad
->GetLowerBounds(padSubscript
);
798 for (; resultElement
< resultElements
; ++resultElement
) {
799 CopyElement(result
, resultSubscript
, *pad
, padSubscript
, terminator
);
800 pad
->IncrementSubscripts(padSubscript
);
801 result
.IncrementSubscripts(resultSubscript
, dimOrder
);
807 void RTNAME(Spread
)(Descriptor
&result
, const Descriptor
&source
, int dim
,
808 std::int64_t ncopies
, const char *sourceFile
, int line
) {
809 Terminator terminator
{sourceFile
, line
};
810 int rank
{source
.rank() + 1};
811 RUNTIME_CHECK(terminator
, rank
<= maxRank
);
812 if (dim
< 1 || dim
> rank
) {
813 terminator
.Crash("SPREAD: DIM=%d argument for rank-%d source array "
814 "must be greater than 1 and less than or equal to %d",
815 dim
, rank
- 1, rank
);
817 ncopies
= std::max
<std::int64_t>(ncopies
, 0);
818 SubscriptValue extent
[maxRank
];
820 for (int j
{0}; j
< rank
; ++j
) {
821 extent
[j
] = j
== dim
- 1 ? ncopies
: source
.GetDimension(k
++).Extent();
823 AllocateResult(result
, source
, rank
, extent
, terminator
, "SPREAD");
824 SubscriptValue resultAt
[maxRank
];
825 for (int j
{0}; j
< rank
; ++j
) {
828 SubscriptValue
&resultDim
{resultAt
[dim
- 1]};
829 SubscriptValue sourceAt
[maxRank
];
830 source
.GetLowerBounds(sourceAt
);
831 for (std::size_t n
{result
.Elements()}; n
> 0; n
-= ncopies
) {
832 for (resultDim
= 1; resultDim
<= ncopies
; ++resultDim
) {
833 CopyElement(result
, resultAt
, source
, sourceAt
, terminator
);
835 result
.IncrementSubscripts(resultAt
);
836 source
.IncrementSubscripts(sourceAt
);
841 void RTNAME(Transpose
)(Descriptor
&result
, const Descriptor
&matrix
,
842 const char *sourceFile
, int line
) {
843 Terminator terminator
{sourceFile
, line
};
844 RUNTIME_CHECK(terminator
, matrix
.rank() == 2);
845 SubscriptValue extent
[2]{
846 matrix
.GetDimension(1).Extent(), matrix
.GetDimension(0).Extent()};
847 AllocateResult(result
, matrix
, 2, extent
, terminator
, "TRANSPOSE");
848 SubscriptValue resultAt
[2]{1, 1};
849 SubscriptValue matrixLB
[2];
850 matrix
.GetLowerBounds(matrixLB
);
851 for (std::size_t n
{result
.Elements()}; n
-- > 0;
852 result
.IncrementSubscripts(resultAt
)) {
853 SubscriptValue matrixAt
[2]{
854 matrixLB
[0] + resultAt
[1] - 1, matrixLB
[1] + resultAt
[0] - 1};
855 CopyElement(result
, resultAt
, matrix
, matrixAt
, terminator
);
860 void RTNAME(Unpack
)(Descriptor
&result
, const Descriptor
&vector
,
861 const Descriptor
&mask
, const Descriptor
&field
, const char *sourceFile
,
863 Terminator terminator
{sourceFile
, line
};
864 RUNTIME_CHECK(terminator
, vector
.rank() == 1);
865 int rank
{mask
.rank()};
866 RUNTIME_CHECK(terminator
, rank
> 0);
867 SubscriptValue extent
[maxRank
];
868 mask
.GetShape(extent
);
869 CheckConformability(mask
, field
, terminator
, "UNPACK", "MASK=", "FIELD=");
870 std::size_t elementLen
{
871 AllocateResult(result
, field
, rank
, extent
, terminator
, "UNPACK")};
872 RUNTIME_CHECK(terminator
, vector
.type() == field
.type());
873 if (vector
.ElementBytes() != elementLen
) {
875 "UNPACK: VECTOR= has element byte length %zd but FIELD= has length %zd",
876 vector
.ElementBytes(), elementLen
);
878 SubscriptValue resultAt
[maxRank
], maskAt
[maxRank
], fieldAt
[maxRank
],
879 vectorAt
{vector
.GetDimension(0).LowerBound()};
880 for (int j
{0}; j
< rank
; ++j
) {
883 mask
.GetLowerBounds(maskAt
);
884 field
.GetLowerBounds(fieldAt
);
885 SubscriptValue vectorElements
{vector
.GetDimension(0).Extent()};
886 SubscriptValue vectorLeft
{vectorElements
};
887 for (std::size_t n
{result
.Elements()}; n
-- > 0;) {
888 if (IsLogicalElementTrue(mask
, maskAt
)) {
889 if (vectorLeft
-- == 0) {
891 "UNPACK: VECTOR= argument has fewer elements (%d) than "
892 "MASK= has .TRUE. entries",
895 CopyElement(result
, resultAt
, vector
, &vectorAt
, terminator
);
898 CopyElement(result
, resultAt
, field
, fieldAt
, terminator
);
900 result
.IncrementSubscripts(resultAt
);
901 mask
.IncrementSubscripts(maskAt
);
902 field
.IncrementSubscripts(fieldAt
);
907 } // namespace Fortran::runtime