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(newData
, data_
, capacity_
* sizeof(Descriptor
*));
99 capacity_
= newCapacity
;
102 template <bool COPY_VALUES
>
103 Descriptor
*DescriptorStorage
<COPY_VALUES
>::cloneDescriptor(
104 const Descriptor
&source
) {
105 const std::size_t bytes
= source
.SizeInBytes();
106 void *memory
= AllocateMemoryOrCrash(terminator_
, bytes
);
107 Descriptor
*desc
= new (memory
) Descriptor
{source
};
111 template <bool COPY_VALUES
>
112 DescriptorStorage
<COPY_VALUES
>::DescriptorStorage(
113 const char *sourceFile
, int line
)
114 : terminator_
{sourceFile
, line
} {
115 resize(INITIAL_ALLOC
);
118 template <bool COPY_VALUES
>
119 DescriptorStorage
<COPY_VALUES
>::~DescriptorStorage() {
120 for (size_type i
= 0; i
< size_
; ++i
) {
121 Descriptor
*element
= data_
[i
];
122 if constexpr (COPY_VALUES
) {
123 element
->Destroy(false, true);
130 template <bool COPY_VALUES
>
131 void DescriptorStorage
<COPY_VALUES
>::push(const Descriptor
&source
) {
132 if (size_
== capacity_
) {
134 if (checkedMultiply(capacity_
, 2, newSize
)) {
135 terminator_
.Crash("temporary-stack: out of address space");
139 data_
[size_
] = cloneDescriptor(source
);
140 Descriptor
&box
= *data_
[size_
];
143 if constexpr (COPY_VALUES
) {
144 // copy the data pointed to by the box
145 box
.set_base_addr(nullptr);
147 RTNAME(AssignTemporary
)
148 (box
, source
, terminator_
.sourceFileName(), terminator_
.sourceLine());
152 template <bool COPY_VALUES
>
153 void DescriptorStorage
<COPY_VALUES
>::pop(Descriptor
&out
) {
155 terminator_
.Crash("temporary-stack: pop empty storage");
158 Descriptor
*ptr
= data_
[size_
];
159 out
= *ptr
; // Descriptor::operator= handles the different sizes
163 template <bool COPY_VALUES
>
164 void DescriptorStorage
<COPY_VALUES
>::at(size_type i
, Descriptor
&out
) {
166 terminator_
.Crash("temporary-stack: out of bounds access");
168 Descriptor
*ptr
= data_
[i
];
169 out
= *ptr
; // Descriptor::operator= handles the different sizes
172 inline static ValueStack
*getValueStorage(void *opaquePtr
) {
173 return static_cast<ValueStack
*>(opaquePtr
);
175 inline static DescriptorStack
*getDescriptorStorage(void *opaquePtr
) {
176 return static_cast<DescriptorStack
*>(opaquePtr
);
179 namespace Fortran::runtime
{
181 void *RTNAME(CreateValueStack
)(const char *sourceFile
, int line
) {
182 return ValueStack::allocate(sourceFile
, line
);
185 void RTNAME(PushValue
)(void *opaquePtr
, const Descriptor
&value
) {
186 getValueStorage(opaquePtr
)->push(value
);
189 void RTNAME(PopValue
)(void *opaquePtr
, Descriptor
&value
) {
190 getValueStorage(opaquePtr
)->pop(value
);
193 void RTNAME(ValueAt
)(void *opaquePtr
, uint64_t i
, Descriptor
&value
) {
194 getValueStorage(opaquePtr
)->at(i
, value
);
197 void RTNAME(DestroyValueStack
)(void *opaquePtr
) {
198 ValueStack::destroy(getValueStorage(opaquePtr
));
201 void *RTNAME(CreateDescriptorStack
)(const char *sourceFile
, int line
) {
202 return DescriptorStack::allocate(sourceFile
, line
);
205 void RTNAME(PushDescriptor
)(void *opaquePtr
, const Descriptor
&value
) {
206 getDescriptorStorage(opaquePtr
)->push(value
);
209 void RTNAME(PopDescriptor
)(void *opaquePtr
, Descriptor
&value
) {
210 getDescriptorStorage(opaquePtr
)->pop(value
);
213 void RTNAME(DescriptorAt
)(void *opaquePtr
, uint64_t i
, Descriptor
&value
) {
214 getValueStorage(opaquePtr
)->at(i
, value
);
217 void RTNAME(DestroyDescriptorStack
)(void *opaquePtr
) {
218 DescriptorStack::destroy(getDescriptorStorage(opaquePtr
));
222 } // namespace Fortran::runtime