llvm-project
185 строк · 8.4 Кб
1//===-- runtime/array-constructor.cpp -------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "flang/Runtime/array-constructor.h"
10#include "derived.h"
11#include "terminator.h"
12#include "tools.h"
13#include "type-info.h"
14#include "flang/Runtime/allocatable.h"
15#include "flang/Runtime/assign.h"
16#include "flang/Runtime/descriptor.h"
17
18namespace Fortran::runtime {
19
20// Initial allocation size for an array constructor temporary whose extent
21// cannot be pre-computed. This could be fined tuned if needed based on actual
22// program performance.
23// REAL(4), INTEGER(4), COMPLEX(2), ... -> 32 elements.
24// REAL(8), INTEGER(8), COMPLEX(4), ... -> 16 elements.
25// REAL(16), INTEGER(16), COMPLEX(8), ... -> 8 elements.
26// Bigger types -> 4 elements.
27static RT_API_ATTRS SubscriptValue initialAllocationSize(
28SubscriptValue initialNumberOfElements, SubscriptValue elementBytes) {
29// Try to guess an optimal initial allocation size in number of elements to
30// avoid doing too many reallocation.
31static constexpr SubscriptValue minNumberOfBytes{128};
32static constexpr SubscriptValue minNumberOfElements{4};
33SubscriptValue numberOfElements{initialNumberOfElements > minNumberOfElements
34? initialNumberOfElements
35: minNumberOfElements};
36SubscriptValue elementsForMinBytes{minNumberOfBytes / elementBytes};
37return std::max(numberOfElements, elementsForMinBytes);
38}
39
40static RT_API_ATTRS void AllocateOrReallocateVectorIfNeeded(
41ArrayConstructorVector &vector, Terminator &terminator,
42SubscriptValue previousToElements, SubscriptValue fromElements) {
43Descriptor &to{vector.to};
44if (to.IsAllocatable() && !to.IsAllocated()) {
45// The descriptor bounds may already be set here if the array constructor
46// extent could be pre-computed, but information about length parameters
47// was missing and required evaluating the first array constructor value.
48if (previousToElements == 0) {
49SubscriptValue allocationSize{
50initialAllocationSize(fromElements, to.ElementBytes())};
51to.GetDimension(0).SetBounds(1, allocationSize);
52RTNAME(AllocatableAllocate)
53(to, /*hasStat=*/false, /*errMsg=*/nullptr, vector.sourceFile,
54vector.sourceLine);
55to.GetDimension(0).SetBounds(1, fromElements);
56vector.actualAllocationSize = allocationSize;
57} else {
58// Do not over-allocate if the final extent was known before pushing the
59// first value: there should be no reallocation.
60RUNTIME_CHECK(terminator, previousToElements >= fromElements);
61RTNAME(AllocatableAllocate)
62(to, /*hasStat=*/false, /*errMsg=*/nullptr, vector.sourceFile,
63vector.sourceLine);
64vector.actualAllocationSize = previousToElements;
65}
66} else {
67SubscriptValue newToElements{vector.nextValuePosition + fromElements};
68if (to.IsAllocatable() && vector.actualAllocationSize < newToElements) {
69// Reallocate. Ensure the current storage is at least doubled to avoid
70// doing too many reallocations.
71SubscriptValue requestedAllocationSize{
72std::max(newToElements, vector.actualAllocationSize * 2)};
73std::size_t newByteSize{requestedAllocationSize * to.ElementBytes()};
74// realloc is undefined with zero new size and ElementBytes() may be null
75// if the character length is null, or if "from" is a zero sized array.
76if (newByteSize > 0) {
77void *p{ReallocateMemoryOrCrash(
78terminator, to.raw().base_addr, newByteSize)};
79to.set_base_addr(p);
80}
81vector.actualAllocationSize = requestedAllocationSize;
82to.GetDimension(0).SetBounds(1, newToElements);
83} else if (previousToElements < newToElements) {
84// Storage is big enough, but descriptor extent must be increased because
85// the final extent was not known before pushing array constructor values.
86to.GetDimension(0).SetBounds(1, newToElements);
87}
88}
89}
90
91extern "C" {
92RT_EXT_API_GROUP_BEGIN
93
94void RTDEF(InitArrayConstructorVector)(ArrayConstructorVector &vector,
95Descriptor &to, bool useValueLengthParameters, int vectorClassSize,
96const char *sourceFile, int sourceLine) {
97Terminator terminator{vector.sourceFile, vector.sourceLine};
98RUNTIME_CHECK(terminator,
99to.rank() == 1 &&
100sizeof(ArrayConstructorVector) <=
101static_cast<std::size_t>(vectorClassSize));
102SubscriptValue actualAllocationSize{
103to.IsAllocated() ? static_cast<SubscriptValue>(to.Elements()) : 0};
104(void)new (&vector) ArrayConstructorVector{to, /*nextValuePosition=*/0,
105actualAllocationSize, sourceFile, sourceLine, useValueLengthParameters};
106}
107
108void RTDEF(PushArrayConstructorValue)(
109ArrayConstructorVector &vector, const Descriptor &from) {
110Terminator terminator{vector.sourceFile, vector.sourceLine};
111Descriptor &to{vector.to};
112SubscriptValue fromElements{static_cast<SubscriptValue>(from.Elements())};
113SubscriptValue previousToElements{static_cast<SubscriptValue>(to.Elements())};
114if (vector.useValueLengthParameters()) {
115// Array constructor with no type spec.
116if (to.IsAllocatable() && !to.IsAllocated()) {
117// Takes length parameters, if any, from the first value.
118// Note that "to" type must already be set by the caller of this API since
119// it cannot be taken from "from" here: "from" may be polymorphic (have a
120// dynamic type that differs from its declared type) and Fortran 2018 7.8
121// point 4. says that the dynamic type of an array constructor is its
122// declared type: it does not inherit the dynamic type of its ac-value
123// even if if there is no type-spec.
124if (to.type().IsCharacter()) {
125to.raw().elem_len = from.ElementBytes();
126} else if (auto *toAddendum{to.Addendum()}) {
127if (const auto *fromAddendum{from.Addendum()}) {
128if (const auto *toDerived{toAddendum->derivedType()}) {
129std::size_t lenParms{toDerived->LenParameters()};
130for (std::size_t j{0}; j < lenParms; ++j) {
131toAddendum->SetLenParameterValue(
132j, fromAddendum->LenParameterValue(j));
133}
134}
135}
136}
137} else if (to.type().IsCharacter()) {
138// Fortran 2018 7.8 point 2.
139if (to.ElementBytes() != from.ElementBytes()) {
140terminator.Crash("Array constructor: mismatched character lengths (%d "
141"!= %d) between "
142"values of an array constructor without type-spec",
143to.ElementBytes() / to.type().GetCategoryAndKind()->second,
144from.ElementBytes() / from.type().GetCategoryAndKind()->second);
145}
146}
147}
148// Otherwise, the array constructor had a type-spec and the length
149// parameters are already in the "to" descriptor.
150
151AllocateOrReallocateVectorIfNeeded(
152vector, terminator, previousToElements, fromElements);
153
154// Create descriptor for "to" element or section being copied to.
155SubscriptValue lower[1]{
156to.GetDimension(0).LowerBound() + vector.nextValuePosition};
157SubscriptValue upper[1]{lower[0] + fromElements - 1};
158SubscriptValue stride[1]{from.rank() == 0 ? 0 : 1};
159StaticDescriptor<maxRank, true, 1> staticDesc;
160Descriptor &toCurrentElement{staticDesc.descriptor()};
161toCurrentElement.EstablishPointerSection(to, lower, upper, stride);
162// Note: toCurrentElement and from have the same number of elements
163// and "toCurrentElement" is not an allocatable so AssignTemporary
164// below works even if "from" rank is bigger than one (and differs
165// from "toCurrentElement") and not time is wasted reshaping
166// "toCurrentElement" to "from" shape.
167RTNAME(AssignTemporary)
168(toCurrentElement, from, vector.sourceFile, vector.sourceLine);
169vector.nextValuePosition += fromElements;
170}
171
172void RTDEF(PushArrayConstructorSimpleScalar)(
173ArrayConstructorVector &vector, void *from) {
174Terminator terminator{vector.sourceFile, vector.sourceLine};
175Descriptor &to{vector.to};
176AllocateOrReallocateVectorIfNeeded(vector, terminator, to.Elements(), 1);
177SubscriptValue subscript[1]{
178to.GetDimension(0).LowerBound() + vector.nextValuePosition};
179std::memcpy(to.Element<char>(subscript), from, to.ElementBytes());
180++vector.nextValuePosition;
181}
182
183RT_EXT_API_GROUP_END
184} // extern "C"
185} // namespace Fortran::runtime
186