1 | //===-- runtime/temporary-stack.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 | // Implements std::vector like storage for a dynamically resizable number of |
10 | // temporaries. For use in HLFIR lowering. |
11 | |
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" |
18 | |
19 | namespace { |
20 | |
21 | using namespace Fortran::runtime; |
22 | |
23 | // the number of elements to allocate when first creating the vector |
24 | constexpr size_t INITIAL_ALLOC = 8; |
25 | |
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() |
30 | |
31 | size_type capacity_{0}; |
32 | size_type size_{0}; |
33 | Descriptor **data_{nullptr}; |
34 | Terminator terminator_; |
35 | |
36 | // return true on overflow |
37 | static bool checkedMultiply(size_type x, size_type y, size_type &res); |
38 | |
39 | void resize(size_type newCapacity); |
40 | |
41 | Descriptor *cloneDescriptor(const Descriptor &source); |
42 | |
43 | public: |
44 | DescriptorStorage(const char *sourceFile, int line); |
45 | ~DescriptorStorage(); |
46 | |
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}; |
52 | } |
53 | |
54 | // `delete` but using the runtime allocation API |
55 | static inline void destroy(DescriptorStorage *instance) { |
56 | instance->~DescriptorStorage(); |
57 | FreeMemory(instance); |
58 | } |
59 | |
60 | // clones a descriptor into this storage |
61 | void push(const Descriptor &source); |
62 | |
63 | // out must be big enough to hold a descriptor of the right rank and addendum |
64 | void pop(Descriptor &out); |
65 | |
66 | // out must be big enough to hold a descriptor of the right rank and addendum |
67 | void at(size_type i, Descriptor &out); |
68 | }; |
69 | |
70 | using ValueStack = DescriptorStorage</*COPY_VALUES=*/true>; |
71 | using DescriptorStack = DescriptorStorage</*COPY_VALUES=*/false>; |
72 | } // namespace |
73 | |
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) { |
79 | return true; |
80 | } |
81 | res = x * y; |
82 | return false; |
83 | } |
84 | |
85 | template <bool COPY_VALUES> |
86 | void DescriptorStorage<COPY_VALUES>::resize(size_type newCapacity) { |
87 | if (newCapacity <= capacity_) { |
88 | return; |
89 | } |
90 | size_type bytes; |
91 | if (checkedMultiply(newCapacity, sizeof(Descriptor *), bytes)) { |
92 | terminator_.Crash("temporary-stack: out of memory" ); |
93 | } |
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 |
98 | // behavior. |
99 | if (data_ != nullptr) { |
100 | memcpy(newData, data_, capacity_ * sizeof(Descriptor *)); |
101 | FreeMemory(data_); |
102 | } |
103 | data_ = newData; |
104 | capacity_ = newCapacity; |
105 | } |
106 | |
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}; |
113 | return desc; |
114 | } |
115 | |
116 | template <bool COPY_VALUES> |
117 | DescriptorStorage<COPY_VALUES>::DescriptorStorage( |
118 | const char *sourceFile, int line) |
119 | : terminator_{sourceFile, line} { |
120 | resize(newCapacity: INITIAL_ALLOC); |
121 | } |
122 | |
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); |
129 | } |
130 | FreeMemory(element); |
131 | } |
132 | FreeMemory(data_); |
133 | } |
134 | |
135 | template <bool COPY_VALUES> |
136 | void DescriptorStorage<COPY_VALUES>::push(const Descriptor &source) { |
137 | if (size_ == capacity_) { |
138 | size_type newSize; |
139 | if (checkedMultiply(x: capacity_, y: 2, res&: newSize)) { |
140 | terminator_.Crash("temporary-stack: out of address space" ); |
141 | } |
142 | resize(newCapacity: newSize); |
143 | } |
144 | data_[size_] = cloneDescriptor(source); |
145 | Descriptor &box = *data_[size_]; |
146 | size_ += 1; |
147 | |
148 | if constexpr (COPY_VALUES) { |
149 | // copy the data pointed to by the box |
150 | box.set_base_addr(nullptr); |
151 | box.Allocate(); |
152 | RTNAME(AssignTemporary) |
153 | (box, source, terminator_.sourceFileName(), terminator_.sourceLine()); |
154 | } |
155 | } |
156 | |
157 | template <bool COPY_VALUES> |
158 | void DescriptorStorage<COPY_VALUES>::pop(Descriptor &out) { |
159 | if (size_ == 0) { |
160 | terminator_.Crash("temporary-stack: pop empty storage" ); |
161 | } |
162 | size_ -= 1; |
163 | Descriptor *ptr = data_[size_]; |
164 | out = *ptr; // Descriptor::operator= handles the different sizes |
165 | FreeMemory(ptr); |
166 | } |
167 | |
168 | template <bool COPY_VALUES> |
169 | void DescriptorStorage<COPY_VALUES>::at(size_type i, Descriptor &out) { |
170 | if (i >= size_) { |
171 | terminator_.Crash("temporary-stack: out of bounds access" ); |
172 | } |
173 | Descriptor *ptr = data_[i]; |
174 | out = *ptr; // Descriptor::operator= handles the different sizes |
175 | } |
176 | |
177 | inline static ValueStack *getValueStorage(void *opaquePtr) { |
178 | return static_cast<ValueStack *>(opaquePtr); |
179 | } |
180 | inline static DescriptorStack *getDescriptorStorage(void *opaquePtr) { |
181 | return static_cast<DescriptorStack *>(opaquePtr); |
182 | } |
183 | |
184 | namespace Fortran::runtime { |
185 | extern "C" { |
186 | void *RTNAME(CreateValueStack)(const char *sourceFile, int line) { |
187 | return ValueStack::allocate(sourceFile, line); |
188 | } |
189 | |
190 | void RTNAME(PushValue)(void *opaquePtr, const Descriptor &value) { |
191 | getValueStorage(opaquePtr)->push(value); |
192 | } |
193 | |
194 | void RTNAME(PopValue)(void *opaquePtr, Descriptor &value) { |
195 | getValueStorage(opaquePtr)->pop(value); |
196 | } |
197 | |
198 | void RTNAME(ValueAt)(void *opaquePtr, uint64_t i, Descriptor &value) { |
199 | getValueStorage(opaquePtr)->at(i, value); |
200 | } |
201 | |
202 | void RTNAME(DestroyValueStack)(void *opaquePtr) { |
203 | ValueStack::destroy(getValueStorage(opaquePtr)); |
204 | } |
205 | |
206 | void *RTNAME(CreateDescriptorStack)(const char *sourceFile, int line) { |
207 | return DescriptorStack::allocate(sourceFile, line); |
208 | } |
209 | |
210 | void RTNAME(PushDescriptor)(void *opaquePtr, const Descriptor &value) { |
211 | getDescriptorStorage(opaquePtr)->push(value); |
212 | } |
213 | |
214 | void RTNAME(PopDescriptor)(void *opaquePtr, Descriptor &value) { |
215 | getDescriptorStorage(opaquePtr)->pop(value); |
216 | } |
217 | |
218 | void RTNAME(DescriptorAt)(void *opaquePtr, uint64_t i, Descriptor &value) { |
219 | getValueStorage(opaquePtr)->at(i, value); |
220 | } |
221 | |
222 | void RTNAME(DestroyDescriptorStack)(void *opaquePtr) { |
223 | DescriptorStack::destroy(getDescriptorStorage(opaquePtr)); |
224 | } |
225 | |
226 | } // extern "C" |
227 | } // namespace Fortran::runtime |
228 | |