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