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