234 lines · cpp
1//===-- lib/runtime/temporary-stack.cpp -------------------------*- C++ -*-===//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-exception6//7//===----------------------------------------------------------------------===//8 9// Implements std::vector like storage for a dynamically resizable number of10// temporaries. For use in HLFIR lowering.11 12#include "flang/Runtime/temporary-stack.h"13#include "flang-rt/runtime/descriptor.h"14#include "flang-rt/runtime/memory.h"15#include "flang-rt/runtime/terminator.h"16#include "flang/Common/ISO_Fortran_binding_wrapper.h"17#include "flang/Runtime/assign.h"18 19RT_OFFLOAD_API_GROUP_BEGIN20 21namespace {22 23using namespace Fortran;24using namespace Fortran::runtime;25 26// the number of elements to allocate when first creating the vector27constexpr size_t INITIAL_ALLOC = 8;28 29/// To store C style data. Does not run constructors/destructors.30/// Not using std::vector to avoid linking the runtime library to stdc++31template <bool COPY_VALUES> class DescriptorStorage final {32 using size_type = uint64_t; // see checkedMultiply()33 34 size_type capacity_{0};35 size_type size_{0};36 Descriptor **data_{nullptr};37 Terminator terminator_;38 39 // return true on overflow40 static bool checkedMultiply(size_type x, size_type y, size_type &res);41 42 void resize(size_type newCapacity);43 44 Descriptor *cloneDescriptor(const Descriptor &source);45 46public:47 DescriptorStorage(const char *sourceFile, int line);48 ~DescriptorStorage();49 50 // `new` but using the runtime allocation API51 static inline DescriptorStorage *allocate(const char *sourceFile, int line) {52 Terminator term{sourceFile, line};53 void *ptr = AllocateMemoryOrCrash(term, sizeof(DescriptorStorage));54 return new (ptr) DescriptorStorage{sourceFile, line};55 }56 57 // `delete` but using the runtime allocation API58 static inline void destroy(DescriptorStorage *instance) {59 instance->~DescriptorStorage();60 FreeMemory(instance);61 }62 63 // clones a descriptor into this storage64 void push(const Descriptor &source);65 66 // out must be big enough to hold a descriptor of the right rank and addendum67 void pop(Descriptor &out);68 69 // out must be big enough to hold a descriptor of the right rank and addendum70 void at(size_type i, Descriptor &out);71};72 73using ValueStack = DescriptorStorage</*COPY_VALUES=*/true>;74using DescriptorStack = DescriptorStorage</*COPY_VALUES=*/false>;75} // namespace76 77template <bool COPY_VALUES>78bool DescriptorStorage<COPY_VALUES>::checkedMultiply(79 size_type x, size_type y, size_type &res) {80 // TODO: c++20 [[unlikely]]81 if (x > UINT64_MAX / y) {82 return true;83 }84 res = x * y;85 return false;86}87 88template <bool COPY_VALUES>89void DescriptorStorage<COPY_VALUES>::resize(size_type newCapacity) {90 if (newCapacity <= capacity_) {91 return;92 }93 size_type bytes;94 if (checkedMultiply(newCapacity, sizeof(Descriptor *), bytes)) {95 terminator_.Crash("temporary-stack: out of memory");96 }97 Descriptor **newData =98 static_cast<Descriptor **>(AllocateMemoryOrCrash(terminator_, bytes));99 // "memcpy" in glibc has a "nonnull" attribute on the source pointer.100 // Avoid passing a null pointer, since it would result in an undefined101 // behavior.102 if (data_ != nullptr) {103 runtime::memcpy(newData, data_, capacity_ * sizeof(Descriptor *));104 FreeMemory(data_);105 }106 data_ = newData;107 capacity_ = newCapacity;108}109 110template <bool COPY_VALUES>111Descriptor *DescriptorStorage<COPY_VALUES>::cloneDescriptor(112 const Descriptor &source) {113 const std::size_t bytes = source.SizeInBytes();114 void *memory = AllocateMemoryOrCrash(terminator_, bytes);115 Descriptor *desc = new (memory) Descriptor{source};116 return desc;117}118 119template <bool COPY_VALUES>120DescriptorStorage<COPY_VALUES>::DescriptorStorage(121 const char *sourceFile, int line)122 : terminator_{sourceFile, line} {123 resize(INITIAL_ALLOC);124}125 126template <bool COPY_VALUES>127DescriptorStorage<COPY_VALUES>::~DescriptorStorage() {128 for (size_type i = 0; i < size_; ++i) {129 Descriptor *element = data_[i];130 if constexpr (COPY_VALUES) {131 element->Destroy(false, true);132 }133 FreeMemory(element);134 }135 FreeMemory(data_);136}137 138template <bool COPY_VALUES>139void DescriptorStorage<COPY_VALUES>::push(const Descriptor &source) {140 if (size_ == capacity_) {141 size_type newSize;142 if (checkedMultiply(capacity_, 2, newSize)) {143 terminator_.Crash("temporary-stack: out of address space");144 }145 resize(newSize);146 }147 data_[size_] = cloneDescriptor(source);148 Descriptor &box = *data_[size_];149 size_ += 1;150 151 if constexpr (COPY_VALUES) {152 // copy the data pointed to by the box153 box.set_base_addr(nullptr);154 box.Allocate(kNoAsyncObject);155 RTNAME(AssignTemporary)156 (box, source, terminator_.sourceFileName(), terminator_.sourceLine());157 }158}159 160template <bool COPY_VALUES>161void DescriptorStorage<COPY_VALUES>::pop(Descriptor &out) {162 if (size_ == 0) {163 terminator_.Crash("temporary-stack: pop empty storage");164 }165 size_ -= 1;166 Descriptor *ptr = data_[size_];167 out = *ptr; // Descriptor::operator= handles the different sizes168 FreeMemory(ptr);169}170 171template <bool COPY_VALUES>172void DescriptorStorage<COPY_VALUES>::at(size_type i, Descriptor &out) {173 if (i >= size_) {174 terminator_.Crash("temporary-stack: out of bounds access");175 }176 Descriptor *ptr = data_[i];177 out = *ptr; // Descriptor::operator= handles the different sizes178}179 180inline static ValueStack *getValueStorage(void *opaquePtr) {181 return static_cast<ValueStack *>(opaquePtr);182}183inline static DescriptorStack *getDescriptorStorage(void *opaquePtr) {184 return static_cast<DescriptorStack *>(opaquePtr);185}186 187RT_OFFLOAD_API_GROUP_END188 189namespace Fortran::runtime {190extern "C" {191RT_EXT_API_GROUP_BEGIN192void *RTNAME(CreateValueStack)(const char *sourceFile, int line) {193 return ValueStack::allocate(sourceFile, line);194}195 196void RTNAME(PushValue)(void *opaquePtr, const Descriptor &value) {197 getValueStorage(opaquePtr)->push(value);198}199 200void RTNAME(PopValue)(void *opaquePtr, Descriptor &value) {201 getValueStorage(opaquePtr)->pop(value);202}203 204void RTNAME(ValueAt)(void *opaquePtr, uint64_t i, Descriptor &value) {205 getValueStorage(opaquePtr)->at(i, value);206}207 208void RTNAME(DestroyValueStack)(void *opaquePtr) {209 ValueStack::destroy(getValueStorage(opaquePtr));210}211 212void *RTNAME(CreateDescriptorStack)(const char *sourceFile, int line) {213 return DescriptorStack::allocate(sourceFile, line);214}215 216void RTNAME(PushDescriptor)(void *opaquePtr, const Descriptor &value) {217 getDescriptorStorage(opaquePtr)->push(value);218}219 220void RTNAME(PopDescriptor)(void *opaquePtr, Descriptor &value) {221 getDescriptorStorage(opaquePtr)->pop(value);222}223 224void RTNAME(DescriptorAt)(void *opaquePtr, uint64_t i, Descriptor &value) {225 getValueStorage(opaquePtr)->at(i, value);226}227 228void RTNAME(DestroyDescriptorStack)(void *opaquePtr) {229 DescriptorStack::destroy(getDescriptorStorage(opaquePtr));230}231RT_EXT_API_GROUP_END232} // extern "C"233} // namespace Fortran::runtime234