1 //===-- runtime/temporary-stack.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 std::vector like storage for a dynamically resizable number of
10 // temporaries. For use in HLFIR lowering.
12 #include "flang/Runtime/temporary-stack.h"
13 #include "terminator.h"
14 #include "flang/ISO_Fortran_binding_wrapper.h"
15 #include "flang/Runtime/assign.h"
16 #include "flang/Runtime/descriptor.h"
17 #include "flang/Runtime/memory.h"
21 using namespace Fortran::runtime
;
23 // the number of elements to allocate when first creating the vector
24 constexpr size_t INITIAL_ALLOC
= 8;
26 /// To store C style data. Does not run constructors/destructors.
27 /// Not using std::vector to avoid linking the runtime library to stdc++
28 template <bool COPY_VALUES
> class DescriptorStorage final
{
29 using size_type
= uint64_t; // see checkedMultiply()
31 size_type capacity_
{0};
33 Descriptor
**data_
{nullptr};
34 Terminator terminator_
;
36 // return true on overflow
37 static bool checkedMultiply(size_type x
, size_type y
, size_type
&res
);
39 void resize(size_type newCapacity
);
41 Descriptor
*cloneDescriptor(const Descriptor
&source
);
44 DescriptorStorage(const char *sourceFile
, int line
);
47 // `new` but using the runtime allocation API
48 static inline DescriptorStorage
*allocate(const char *sourceFile
, int line
) {
49 Terminator term
{sourceFile
, line
};
50 void *ptr
= AllocateMemoryOrCrash(term
, sizeof(DescriptorStorage
));
51 return new (ptr
) DescriptorStorage
{sourceFile
, line
};
54 // `delete` but using the runtime allocation API
55 static inline void destroy(DescriptorStorage
*instance
) {
56 instance
->~DescriptorStorage();
60 // clones a descriptor into this storage
61 void push(const Descriptor
&source
);
63 // out must be big enough to hold a descriptor of the right rank and addendum
64 void pop(Descriptor
&out
);
66 // out must be big enough to hold a descriptor of the right rank and addendum
67 void at(size_type i
, Descriptor
&out
);
70 using ValueStack
= DescriptorStorage
</*COPY_VALUES=*/true>;
71 using DescriptorStack
= DescriptorStorage
</*COPY_VALUES=*/false>;
74 template <bool COPY_VALUES
>
75 bool DescriptorStorage
<COPY_VALUES
>::checkedMultiply(
76 size_type x
, size_type y
, size_type
&res
) {
77 // TODO: c++20 [[unlikely]]
78 if (x
> UINT64_MAX
/ y
) {
85 template <bool COPY_VALUES
>
86 void DescriptorStorage
<COPY_VALUES
>::resize(size_type newCapacity
) {
87 if (newCapacity
<= capacity_
) {
91 if (checkedMultiply(newCapacity
, sizeof(Descriptor
*), bytes
)) {
92 terminator_
.Crash("temporary-stack: out of memory");
94 Descriptor
**newData
=
95 static_cast<Descriptor
**>(AllocateMemoryOrCrash(terminator_
, bytes
));
96 // "memcpy" in glibc has a "nonnull" attribute on the source pointer.
97 // Avoid passing a null pointer, since it would result in an undefined
99 if (data_
!= nullptr) {
100 memcpy(newData
, data_
, capacity_
* sizeof(Descriptor
*));
104 capacity_
= newCapacity
;
107 template <bool COPY_VALUES
>
108 Descriptor
*DescriptorStorage
<COPY_VALUES
>::cloneDescriptor(
109 const Descriptor
&source
) {
110 const std::size_t bytes
= source
.SizeInBytes();
111 void *memory
= AllocateMemoryOrCrash(terminator_
, bytes
);
112 Descriptor
*desc
= new (memory
) Descriptor
{source
};
116 template <bool COPY_VALUES
>
117 DescriptorStorage
<COPY_VALUES
>::DescriptorStorage(
118 const char *sourceFile
, int line
)
119 : terminator_
{sourceFile
, line
} {
120 resize(INITIAL_ALLOC
);
123 template <bool COPY_VALUES
>
124 DescriptorStorage
<COPY_VALUES
>::~DescriptorStorage() {
125 for (size_type i
= 0; i
< size_
; ++i
) {
126 Descriptor
*element
= data_
[i
];
127 if constexpr (COPY_VALUES
) {
128 element
->Destroy(false, true);
135 template <bool COPY_VALUES
>
136 void DescriptorStorage
<COPY_VALUES
>::push(const Descriptor
&source
) {
137 if (size_
== capacity_
) {
139 if (checkedMultiply(capacity_
, 2, newSize
)) {
140 terminator_
.Crash("temporary-stack: out of address space");
144 data_
[size_
] = cloneDescriptor(source
);
145 Descriptor
&box
= *data_
[size_
];
148 if constexpr (COPY_VALUES
) {
149 // copy the data pointed to by the box
150 box
.set_base_addr(nullptr);
152 RTNAME(AssignTemporary
)
153 (box
, source
, terminator_
.sourceFileName(), terminator_
.sourceLine());
157 template <bool COPY_VALUES
>
158 void DescriptorStorage
<COPY_VALUES
>::pop(Descriptor
&out
) {
160 terminator_
.Crash("temporary-stack: pop empty storage");
163 Descriptor
*ptr
= data_
[size_
];
164 out
= *ptr
; // Descriptor::operator= handles the different sizes
168 template <bool COPY_VALUES
>
169 void DescriptorStorage
<COPY_VALUES
>::at(size_type i
, Descriptor
&out
) {
171 terminator_
.Crash("temporary-stack: out of bounds access");
173 Descriptor
*ptr
= data_
[i
];
174 out
= *ptr
; // Descriptor::operator= handles the different sizes
177 inline static ValueStack
*getValueStorage(void *opaquePtr
) {
178 return static_cast<ValueStack
*>(opaquePtr
);
180 inline static DescriptorStack
*getDescriptorStorage(void *opaquePtr
) {
181 return static_cast<DescriptorStack
*>(opaquePtr
);
184 namespace Fortran::runtime
{
186 void *RTNAME(CreateValueStack
)(const char *sourceFile
, int line
) {
187 return ValueStack::allocate(sourceFile
, line
);
190 void RTNAME(PushValue
)(void *opaquePtr
, const Descriptor
&value
) {
191 getValueStorage(opaquePtr
)->push(value
);
194 void RTNAME(PopValue
)(void *opaquePtr
, Descriptor
&value
) {
195 getValueStorage(opaquePtr
)->pop(value
);
198 void RTNAME(ValueAt
)(void *opaquePtr
, uint64_t i
, Descriptor
&value
) {
199 getValueStorage(opaquePtr
)->at(i
, value
);
202 void RTNAME(DestroyValueStack
)(void *opaquePtr
) {
203 ValueStack::destroy(getValueStorage(opaquePtr
));
206 void *RTNAME(CreateDescriptorStack
)(const char *sourceFile
, int line
) {
207 return DescriptorStack::allocate(sourceFile
, line
);
210 void RTNAME(PushDescriptor
)(void *opaquePtr
, const Descriptor
&value
) {
211 getDescriptorStorage(opaquePtr
)->push(value
);
214 void RTNAME(PopDescriptor
)(void *opaquePtr
, Descriptor
&value
) {
215 getDescriptorStorage(opaquePtr
)->pop(value
);
218 void RTNAME(DescriptorAt
)(void *opaquePtr
, uint64_t i
, Descriptor
&value
) {
219 getValueStorage(opaquePtr
)->at(i
, value
);
222 void RTNAME(DestroyDescriptorStack
)(void *opaquePtr
) {
223 DescriptorStack::destroy(getDescriptorStorage(opaquePtr
));
227 } // namespace Fortran::runtime