brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.9 KiB · 4bc161f Raw
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