[clangd] Fix erroneous qualification of template type parameters (#116821)
[llvm-project.git] / flang / runtime / temporary-stack.cpp
blob667b10e04dbd293c534891ced5fec62d3bba634b
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 //===----------------------------------------------------------------------===//
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"
19 namespace {
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};
32 size_type size_{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);
43 public:
44 DescriptorStorage(const char *sourceFile, int line);
45 ~DescriptorStorage();
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();
57 FreeMemory(instance);
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>;
72 } // namespace
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;
81 res = x * y;
82 return false;
85 template <bool COPY_VALUES>
86 void DescriptorStorage<COPY_VALUES>::resize(size_type newCapacity) {
87 if (newCapacity <= capacity_) {
88 return;
90 size_type bytes;
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
98 // behavior.
99 if (data_ != nullptr) {
100 memcpy(newData, data_, capacity_ * sizeof(Descriptor *));
101 FreeMemory(data_);
103 data_ = newData;
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};
113 return desc;
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);
130 FreeMemory(element);
132 FreeMemory(data_);
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(capacity_, 2, newSize)) {
140 terminator_.Crash("temporary-stack: out of address space");
142 resize(newSize);
144 data_[size_] = cloneDescriptor(source);
145 Descriptor &box = *data_[size_];
146 size_ += 1;
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());
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");
162 size_ -= 1;
163 Descriptor *ptr = data_[size_];
164 out = *ptr; // Descriptor::operator= handles the different sizes
165 FreeMemory(ptr);
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");
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 {
185 extern "C" {
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));
226 } // extern "C"
227 } // namespace Fortran::runtime