xref: /llvm-project/flang/runtime/temporary-stack.cpp (revision 871086bf7fad42d610bfe02224662bdc71494a70)
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
allocate(const char * sourceFile,int line)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
destroy(DescriptorStorage * instance)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>
checkedMultiply(size_type x,size_type y,size_type & res)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>
resize(size_type newCapacity)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>
cloneDescriptor(const Descriptor & source)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>
DescriptorStorage(const char * sourceFile,int line)117 DescriptorStorage<COPY_VALUES>::DescriptorStorage(
118     const char *sourceFile, int line)
119     : terminator_{sourceFile, line} {
120   resize(INITIAL_ALLOC);
121 }
122 
123 template <bool COPY_VALUES>
~DescriptorStorage()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>
push(const Descriptor & source)136 void DescriptorStorage<COPY_VALUES>::push(const Descriptor &source) {
137   if (size_ == capacity_) {
138     size_type newSize;
139     if (checkedMultiply(capacity_, 2, newSize)) {
140       terminator_.Crash("temporary-stack: out of address space");
141     }
142     resize(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>
pop(Descriptor & out)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>
at(size_type i,Descriptor & out)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 
getValueStorage(void * opaquePtr)177 inline static ValueStack *getValueStorage(void *opaquePtr) {
178   return static_cast<ValueStack *>(opaquePtr);
179 }
getDescriptorStorage(void * opaquePtr)180 inline static DescriptorStack *getDescriptorStorage(void *opaquePtr) {
181   return static_cast<DescriptorStack *>(opaquePtr);
182 }
183 
184 namespace Fortran::runtime {
185 extern "C" {
RTNAME(CreateValueStack)186 void *RTNAME(CreateValueStack)(const char *sourceFile, int line) {
187   return ValueStack::allocate(sourceFile, line);
188 }
189 
RTNAME(PushValue)190 void RTNAME(PushValue)(void *opaquePtr, const Descriptor &value) {
191   getValueStorage(opaquePtr)->push(value);
192 }
193 
RTNAME(PopValue)194 void RTNAME(PopValue)(void *opaquePtr, Descriptor &value) {
195   getValueStorage(opaquePtr)->pop(value);
196 }
197 
RTNAME(ValueAt)198 void RTNAME(ValueAt)(void *opaquePtr, uint64_t i, Descriptor &value) {
199   getValueStorage(opaquePtr)->at(i, value);
200 }
201 
RTNAME(DestroyValueStack)202 void RTNAME(DestroyValueStack)(void *opaquePtr) {
203   ValueStack::destroy(getValueStorage(opaquePtr));
204 }
205 
RTNAME(CreateDescriptorStack)206 void *RTNAME(CreateDescriptorStack)(const char *sourceFile, int line) {
207   return DescriptorStack::allocate(sourceFile, line);
208 }
209 
RTNAME(PushDescriptor)210 void RTNAME(PushDescriptor)(void *opaquePtr, const Descriptor &value) {
211   getDescriptorStorage(opaquePtr)->push(value);
212 }
213 
RTNAME(PopDescriptor)214 void RTNAME(PopDescriptor)(void *opaquePtr, Descriptor &value) {
215   getDescriptorStorage(opaquePtr)->pop(value);
216 }
217 
RTNAME(DescriptorAt)218 void RTNAME(DescriptorAt)(void *opaquePtr, uint64_t i, Descriptor &value) {
219   getValueStorage(opaquePtr)->at(i, value);
220 }
221 
RTNAME(DestroyDescriptorStack)222 void RTNAME(DestroyDescriptorStack)(void *opaquePtr) {
223   DescriptorStack::destroy(getDescriptorStorage(opaquePtr));
224 }
225 
226 } // extern "C"
227 } // namespace Fortran::runtime
228