[AArch64,ELF] Restrict MOVZ/MOVK to non-PIC large code model (#70178)
[llvm-project.git] / flang / runtime / temporary-stack.cpp
blobb4d7c6064457f2aba2233338da4b43c490481663
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(newData, data_, capacity_ * sizeof(Descriptor *));
97 FreeMemory(data_);
98 data_ = newData;
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};
108 return desc;
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);
125 FreeMemory(element);
127 FreeMemory(data_);
130 template <bool COPY_VALUES>
131 void DescriptorStorage<COPY_VALUES>::push(const Descriptor &source) {
132 if (size_ == capacity_) {
133 size_type newSize;
134 if (checkedMultiply(capacity_, 2, newSize)) {
135 terminator_.Crash("temporary-stack: out of address space");
137 resize(newSize);
139 data_[size_] = cloneDescriptor(source);
140 Descriptor &box = *data_[size_];
141 size_ += 1;
143 if constexpr (COPY_VALUES) {
144 // copy the data pointed to by the box
145 box.set_base_addr(nullptr);
146 box.Allocate();
147 RTNAME(AssignTemporary)
148 (box, source, terminator_.sourceFileName(), terminator_.sourceLine());
152 template <bool COPY_VALUES>
153 void DescriptorStorage<COPY_VALUES>::pop(Descriptor &out) {
154 if (size_ == 0) {
155 terminator_.Crash("temporary-stack: pop empty storage");
157 size_ -= 1;
158 Descriptor *ptr = data_[size_];
159 out = *ptr; // Descriptor::operator= handles the different sizes
160 FreeMemory(ptr);
163 template <bool COPY_VALUES>
164 void DescriptorStorage<COPY_VALUES>::at(size_type i, Descriptor &out) {
165 if (i >= size_) {
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 {
180 extern "C" {
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));
221 } // extern "C"
222 } // namespace Fortran::runtime