xref: /llvm-project/flang/runtime/array-constructor.cpp (revision 4cb2a519db10f54815c8a4ccd5accbedc1cdfd07)
15226f8a9SJean Perier //===-- runtime/array-constructor.cpp -------------------------------------===//
25226f8a9SJean Perier //
35226f8a9SJean Perier // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45226f8a9SJean Perier // See https://llvm.org/LICENSE.txt for license information.
55226f8a9SJean Perier // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65226f8a9SJean Perier //
75226f8a9SJean Perier //===----------------------------------------------------------------------===//
85226f8a9SJean Perier 
95226f8a9SJean Perier #include "flang/Runtime/array-constructor.h"
105226f8a9SJean Perier #include "derived.h"
115226f8a9SJean Perier #include "terminator.h"
1276facde3SSlava Zakharin #include "tools.h"
135226f8a9SJean Perier #include "type-info.h"
145226f8a9SJean Perier #include "flang/Runtime/allocatable.h"
155226f8a9SJean Perier #include "flang/Runtime/assign.h"
165226f8a9SJean Perier #include "flang/Runtime/descriptor.h"
175226f8a9SJean Perier 
185226f8a9SJean Perier namespace Fortran::runtime {
195226f8a9SJean Perier 
205226f8a9SJean Perier // Initial allocation size for an array constructor temporary whose extent
215226f8a9SJean Perier // cannot be pre-computed. This could be fined tuned if needed based on actual
225226f8a9SJean Perier // program performance.
235226f8a9SJean Perier //  REAL(4), INTEGER(4), COMPLEX(2), ...   -> 32 elements.
245226f8a9SJean Perier //  REAL(8), INTEGER(8), COMPLEX(4), ...   -> 16 elements.
255226f8a9SJean Perier //  REAL(16), INTEGER(16), COMPLEX(8), ... -> 8 elements.
265226f8a9SJean Perier //  Bigger types -> 4 elements.
2776facde3SSlava Zakharin static RT_API_ATTRS SubscriptValue initialAllocationSize(
285226f8a9SJean Perier     SubscriptValue initialNumberOfElements, SubscriptValue elementBytes) {
295226f8a9SJean Perier   // Try to guess an optimal initial allocation size in number of elements to
305226f8a9SJean Perier   // avoid doing too many reallocation.
315226f8a9SJean Perier   static constexpr SubscriptValue minNumberOfBytes{128};
325226f8a9SJean Perier   static constexpr SubscriptValue minNumberOfElements{4};
335226f8a9SJean Perier   SubscriptValue numberOfElements{initialNumberOfElements > minNumberOfElements
345226f8a9SJean Perier           ? initialNumberOfElements
355226f8a9SJean Perier           : minNumberOfElements};
365226f8a9SJean Perier   SubscriptValue elementsForMinBytes{minNumberOfBytes / elementBytes};
375226f8a9SJean Perier   return std::max(numberOfElements, elementsForMinBytes);
385226f8a9SJean Perier }
395226f8a9SJean Perier 
4076facde3SSlava Zakharin static RT_API_ATTRS void AllocateOrReallocateVectorIfNeeded(
4176facde3SSlava Zakharin     ArrayConstructorVector &vector, Terminator &terminator,
4276facde3SSlava Zakharin     SubscriptValue previousToElements, SubscriptValue fromElements) {
435226f8a9SJean Perier   Descriptor &to{vector.to};
445226f8a9SJean Perier   if (to.IsAllocatable() && !to.IsAllocated()) {
455226f8a9SJean Perier     // The descriptor bounds may already be set here if the array constructor
465226f8a9SJean Perier     // extent could be pre-computed, but information about length parameters
475226f8a9SJean Perier     // was missing and required evaluating the first array constructor value.
485226f8a9SJean Perier     if (previousToElements == 0) {
495226f8a9SJean Perier       SubscriptValue allocationSize{
505226f8a9SJean Perier           initialAllocationSize(fromElements, to.ElementBytes())};
515226f8a9SJean Perier       to.GetDimension(0).SetBounds(1, allocationSize);
525226f8a9SJean Perier       RTNAME(AllocatableAllocate)
53*4cb2a519SValentin Clement (バレンタイン クレメン)       (to, /*hasStat=*/false, /*errMsg=*/nullptr, vector.sourceFile,
54*4cb2a519SValentin Clement (バレンタイン クレメン)           vector.sourceLine);
555226f8a9SJean Perier       to.GetDimension(0).SetBounds(1, fromElements);
565226f8a9SJean Perier       vector.actualAllocationSize = allocationSize;
575226f8a9SJean Perier     } else {
585226f8a9SJean Perier       // Do not over-allocate if the final extent was known before pushing the
595226f8a9SJean Perier       // first value: there should be no reallocation.
605226f8a9SJean Perier       RUNTIME_CHECK(terminator, previousToElements >= fromElements);
615226f8a9SJean Perier       RTNAME(AllocatableAllocate)
62*4cb2a519SValentin Clement (バレンタイン クレメン)       (to, /*hasStat=*/false, /*errMsg=*/nullptr, vector.sourceFile,
63*4cb2a519SValentin Clement (バレンタイン クレメン)           vector.sourceLine);
645226f8a9SJean Perier       vector.actualAllocationSize = previousToElements;
655226f8a9SJean Perier     }
665226f8a9SJean Perier   } else {
675226f8a9SJean Perier     SubscriptValue newToElements{vector.nextValuePosition + fromElements};
685226f8a9SJean Perier     if (to.IsAllocatable() && vector.actualAllocationSize < newToElements) {
695226f8a9SJean Perier       // Reallocate. Ensure the current storage is at least doubled to avoid
705226f8a9SJean Perier       // doing too many reallocations.
715226f8a9SJean Perier       SubscriptValue requestedAllocationSize{
725226f8a9SJean Perier           std::max(newToElements, vector.actualAllocationSize * 2)};
735226f8a9SJean Perier       std::size_t newByteSize{requestedAllocationSize * to.ElementBytes()};
745226f8a9SJean Perier       // realloc is undefined with zero new size and ElementBytes() may be null
755226f8a9SJean Perier       // if the character length is null, or if "from" is a zero sized array.
765226f8a9SJean Perier       if (newByteSize > 0) {
7776facde3SSlava Zakharin         void *p{ReallocateMemoryOrCrash(
7876facde3SSlava Zakharin             terminator, to.raw().base_addr, newByteSize)};
795226f8a9SJean Perier         to.set_base_addr(p);
805226f8a9SJean Perier       }
815226f8a9SJean Perier       vector.actualAllocationSize = requestedAllocationSize;
825226f8a9SJean Perier       to.GetDimension(0).SetBounds(1, newToElements);
835226f8a9SJean Perier     } else if (previousToElements < newToElements) {
845226f8a9SJean Perier       // Storage is big enough, but descriptor extent must be increased because
855226f8a9SJean Perier       // the final extent was not known before pushing array constructor values.
865226f8a9SJean Perier       to.GetDimension(0).SetBounds(1, newToElements);
875226f8a9SJean Perier     }
885226f8a9SJean Perier   }
895226f8a9SJean Perier }
905226f8a9SJean Perier 
915226f8a9SJean Perier extern "C" {
9276facde3SSlava Zakharin RT_EXT_API_GROUP_BEGIN
9376facde3SSlava Zakharin 
9476facde3SSlava Zakharin void RTDEF(InitArrayConstructorVector)(ArrayConstructorVector &vector,
95c91ba043SMichael Kruse     Descriptor &to, bool useValueLengthParameters, const char *sourceFile,
96c91ba043SMichael Kruse     int sourceLine) {
975226f8a9SJean Perier   Terminator terminator{vector.sourceFile, vector.sourceLine};
98c91ba043SMichael Kruse   RUNTIME_CHECK(terminator, to.rank() == 1);
995226f8a9SJean Perier   SubscriptValue actualAllocationSize{
1005226f8a9SJean Perier       to.IsAllocated() ? static_cast<SubscriptValue>(to.Elements()) : 0};
1015226f8a9SJean Perier   (void)new (&vector) ArrayConstructorVector{to, /*nextValuePosition=*/0,
1025226f8a9SJean Perier       actualAllocationSize, sourceFile, sourceLine, useValueLengthParameters};
1035226f8a9SJean Perier }
1045226f8a9SJean Perier 
10576facde3SSlava Zakharin void RTDEF(PushArrayConstructorValue)(
1065226f8a9SJean Perier     ArrayConstructorVector &vector, const Descriptor &from) {
1075226f8a9SJean Perier   Terminator terminator{vector.sourceFile, vector.sourceLine};
1085226f8a9SJean Perier   Descriptor &to{vector.to};
1095226f8a9SJean Perier   SubscriptValue fromElements{static_cast<SubscriptValue>(from.Elements())};
1105226f8a9SJean Perier   SubscriptValue previousToElements{static_cast<SubscriptValue>(to.Elements())};
1115226f8a9SJean Perier   if (vector.useValueLengthParameters()) {
1125226f8a9SJean Perier     // Array constructor with no type spec.
1135226f8a9SJean Perier     if (to.IsAllocatable() && !to.IsAllocated()) {
1145226f8a9SJean Perier       // Takes length parameters, if any, from the first value.
1155226f8a9SJean Perier       // Note that "to" type must already be set by the caller of this API since
1165226f8a9SJean Perier       // it cannot be taken from "from" here: "from" may be polymorphic (have a
1175226f8a9SJean Perier       // dynamic type that differs from its declared type) and Fortran 2018 7.8
1185226f8a9SJean Perier       // point 4. says that the dynamic type of an array constructor is its
1195226f8a9SJean Perier       // declared type: it does not inherit the dynamic type of its ac-value
1205226f8a9SJean Perier       // even if if there is no type-spec.
1215226f8a9SJean Perier       if (to.type().IsCharacter()) {
1225226f8a9SJean Perier         to.raw().elem_len = from.ElementBytes();
1235226f8a9SJean Perier       } else if (auto *toAddendum{to.Addendum()}) {
1245226f8a9SJean Perier         if (const auto *fromAddendum{from.Addendum()}) {
1255226f8a9SJean Perier           if (const auto *toDerived{toAddendum->derivedType()}) {
1265226f8a9SJean Perier             std::size_t lenParms{toDerived->LenParameters()};
1275226f8a9SJean Perier             for (std::size_t j{0}; j < lenParms; ++j) {
1285226f8a9SJean Perier               toAddendum->SetLenParameterValue(
1295226f8a9SJean Perier                   j, fromAddendum->LenParameterValue(j));
1305226f8a9SJean Perier             }
1315226f8a9SJean Perier           }
1325226f8a9SJean Perier         }
1335226f8a9SJean Perier       }
1345226f8a9SJean Perier     } else if (to.type().IsCharacter()) {
1355226f8a9SJean Perier       // Fortran 2018 7.8 point 2.
1365226f8a9SJean Perier       if (to.ElementBytes() != from.ElementBytes()) {
1375226f8a9SJean Perier         terminator.Crash("Array constructor: mismatched character lengths (%d "
1385226f8a9SJean Perier                          "!= %d) between "
1395226f8a9SJean Perier                          "values of an array constructor without type-spec",
1405226f8a9SJean Perier             to.ElementBytes() / to.type().GetCategoryAndKind()->second,
1415226f8a9SJean Perier             from.ElementBytes() / from.type().GetCategoryAndKind()->second);
1425226f8a9SJean Perier       }
1435226f8a9SJean Perier     }
1445226f8a9SJean Perier   }
1455226f8a9SJean Perier   // Otherwise, the array constructor had a type-spec and the length
1465226f8a9SJean Perier   // parameters are already in the "to" descriptor.
1475226f8a9SJean Perier 
1485226f8a9SJean Perier   AllocateOrReallocateVectorIfNeeded(
1495226f8a9SJean Perier       vector, terminator, previousToElements, fromElements);
1505226f8a9SJean Perier 
1515226f8a9SJean Perier   // Create descriptor for "to" element or section being copied to.
1525226f8a9SJean Perier   SubscriptValue lower[1]{
1535226f8a9SJean Perier       to.GetDimension(0).LowerBound() + vector.nextValuePosition};
1545226f8a9SJean Perier   SubscriptValue upper[1]{lower[0] + fromElements - 1};
1555226f8a9SJean Perier   SubscriptValue stride[1]{from.rank() == 0 ? 0 : 1};
1565226f8a9SJean Perier   StaticDescriptor<maxRank, true, 1> staticDesc;
1575226f8a9SJean Perier   Descriptor &toCurrentElement{staticDesc.descriptor()};
1585226f8a9SJean Perier   toCurrentElement.EstablishPointerSection(to, lower, upper, stride);
1595226f8a9SJean Perier   // Note: toCurrentElement and from have the same number of elements
1605226f8a9SJean Perier   // and "toCurrentElement" is not an allocatable so AssignTemporary
1615226f8a9SJean Perier   // below works even if "from" rank is bigger than one (and differs
1625226f8a9SJean Perier   // from "toCurrentElement") and not time is wasted reshaping
1635226f8a9SJean Perier   // "toCurrentElement" to "from" shape.
1645226f8a9SJean Perier   RTNAME(AssignTemporary)
1655226f8a9SJean Perier   (toCurrentElement, from, vector.sourceFile, vector.sourceLine);
1665226f8a9SJean Perier   vector.nextValuePosition += fromElements;
1675226f8a9SJean Perier }
1685226f8a9SJean Perier 
16976facde3SSlava Zakharin void RTDEF(PushArrayConstructorSimpleScalar)(
1705226f8a9SJean Perier     ArrayConstructorVector &vector, void *from) {
1715226f8a9SJean Perier   Terminator terminator{vector.sourceFile, vector.sourceLine};
1725226f8a9SJean Perier   Descriptor &to{vector.to};
1735226f8a9SJean Perier   AllocateOrReallocateVectorIfNeeded(vector, terminator, to.Elements(), 1);
1745226f8a9SJean Perier   SubscriptValue subscript[1]{
1755226f8a9SJean Perier       to.GetDimension(0).LowerBound() + vector.nextValuePosition};
1765226f8a9SJean Perier   std::memcpy(to.Element<char>(subscript), from, to.ElementBytes());
1775226f8a9SJean Perier   ++vector.nextValuePosition;
1785226f8a9SJean Perier }
17976facde3SSlava Zakharin 
18076facde3SSlava Zakharin RT_EXT_API_GROUP_END
1815226f8a9SJean Perier } // extern "C"
1825226f8a9SJean Perier } // namespace Fortran::runtime
183