xref: /llvm-project/flang/include/flang/Runtime/memory.h (revision 8ebf741136c66f51053315bf4f0ef828c6f66094)
1 //===-- include/flang/Runtime/memory.h --------------------------*- 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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // Thin wrapper around malloc()/free() to isolate the dependency,
10 // ease porting, and provide an owning pointer.
11 
12 #ifndef FORTRAN_RUNTIME_MEMORY_H_
13 #define FORTRAN_RUNTIME_MEMORY_H_
14 
15 #include "flang/Common/api-attrs.h"
16 #include <cassert>
17 #include <memory>
18 #include <type_traits>
19 
20 namespace Fortran::runtime {
21 
22 class Terminator;
23 
24 [[nodiscard]] RT_API_ATTRS void *AllocateMemoryOrCrash(
25     const Terminator &, std::size_t bytes);
26 template <typename A>
AllocateOrCrash(const Terminator & t)27 [[nodiscard]] RT_API_ATTRS A &AllocateOrCrash(const Terminator &t) {
28   return *reinterpret_cast<A *>(AllocateMemoryOrCrash(t, sizeof(A)));
29 }
30 RT_API_ATTRS void *ReallocateMemoryOrCrash(
31     const Terminator &, void *ptr, std::size_t newByteSize);
32 RT_API_ATTRS void FreeMemory(void *);
FreeMemory(A * p)33 template <typename A> RT_API_ATTRS void FreeMemory(A *p) {
34   FreeMemory(reinterpret_cast<void *>(p));
35 }
FreeMemoryAndNullify(A * & p)36 template <typename A> RT_API_ATTRS void FreeMemoryAndNullify(A *&p) {
37   FreeMemory(p);
38   p = nullptr;
39 }
40 
41 // Very basic implementation mimicking std::unique_ptr.
42 // It should work for any offload device compiler.
43 // It uses a fixed memory deleter based on FreeMemory(),
44 // and does not support array objects with runtime length.
45 template <typename A> class OwningPtr {
46 public:
47   using pointer_type = A *;
48 
49   OwningPtr() = default;
OwningPtr(pointer_type p)50   RT_API_ATTRS explicit OwningPtr(pointer_type p) : ptr_(p) {}
51   RT_API_ATTRS OwningPtr(const OwningPtr &) = delete;
52   RT_API_ATTRS OwningPtr &operator=(const OwningPtr &) = delete;
OwningPtr(OwningPtr && other)53   RT_API_ATTRS OwningPtr(OwningPtr &&other) {
54     ptr_ = other.ptr_;
55     other.ptr_ = pointer_type{};
56   }
57   RT_API_ATTRS OwningPtr &operator=(OwningPtr &&other) {
58     if (this != &other) {
59       delete_ptr(ptr_);
60       ptr_ = other.ptr_;
61       other.ptr_ = pointer_type{};
62     }
63     return *this;
64   }
OwningPtr(std::nullptr_t)65   constexpr RT_API_ATTRS OwningPtr(std::nullptr_t) : OwningPtr() {}
66 
67   // Delete the pointer, if owns one.
~OwningPtr()68   RT_API_ATTRS ~OwningPtr() {
69     if (ptr_ != pointer_type{}) {
70       delete_ptr(ptr_);
71       ptr_ = pointer_type{};
72     }
73   }
74 
75   // Release the ownership.
release()76   RT_API_ATTRS pointer_type release() {
77     pointer_type p = ptr_;
78     ptr_ = pointer_type{};
79     return p;
80   }
81 
82   RT_DIAG_PUSH
83   RT_DIAG_DISABLE_CALL_HOST_FROM_DEVICE_WARN
84   // Replace the pointer.
85   RT_API_ATTRS void reset(pointer_type p = pointer_type{}) {
86     std::swap(ptr_, p);
87     if (p != pointer_type{}) {
88       // Delete the owned pointer.
89       delete_ptr(p);
90     }
91   }
92 
93   // Exchange the pointer with another object.
swap(OwningPtr & other)94   RT_API_ATTRS void swap(OwningPtr &other) { std::swap(ptr_, other.ptr_); }
95   RT_DIAG_POP
96 
97   // Get the stored pointer.
get()98   RT_API_ATTRS pointer_type get() const { return ptr_; }
99 
100   RT_API_ATTRS explicit operator bool() const {
101     return get() != pointer_type{};
102   }
103 
104   RT_API_ATTRS typename std::add_lvalue_reference<A>::type operator*() const {
105     assert(get() != pointer_type{});
106     return *get();
107   }
108 
109   RT_API_ATTRS pointer_type operator->() const { return get(); }
110 
111 private:
delete_ptr(pointer_type p)112   RT_API_ATTRS void delete_ptr(pointer_type p) { FreeMemory(p); }
113   pointer_type ptr_{};
114 };
115 
116 template <typename X, typename Y>
117 inline RT_API_ATTRS bool operator!=(
118     const OwningPtr<X> &x, const OwningPtr<Y> &y) {
119   return x.get() != y.get();
120 }
121 
122 template <typename X>
123 inline RT_API_ATTRS bool operator!=(const OwningPtr<X> &x, std::nullptr_t) {
124   return (bool)x;
125 }
126 
127 template <typename X>
128 inline RT_API_ATTRS bool operator!=(std::nullptr_t, const OwningPtr<X> &x) {
129   return (bool)x;
130 }
131 
132 template <typename A> class SizedNew {
133 public:
SizedNew(const Terminator & terminator)134   explicit RT_API_ATTRS SizedNew(const Terminator &terminator)
135       : terminator_{terminator} {}
136 
137   template <typename... X>
operator()138   [[nodiscard]] RT_API_ATTRS OwningPtr<A> operator()(
139       std::size_t bytes, X &&...x) {
140     return OwningPtr<A>{new (AllocateMemoryOrCrash(terminator_, bytes))
141             A{std::forward<X>(x)...}};
142   }
143 
144 private:
145   const Terminator &terminator_;
146 };
147 
148 template <typename A> struct New : public SizedNew<A> {
149   using SizedNew<A>::SizedNew;
150   template <typename... X>
operatorNew151   [[nodiscard]] RT_API_ATTRS OwningPtr<A> operator()(X &&...x) {
152     return SizedNew<A>::operator()(sizeof(A), std::forward<X>(x)...);
153   }
154 };
155 
156 template <typename A> struct Allocator {
157   using value_type = A;
AllocatorAllocator158   explicit Allocator(const Terminator &t) : terminator{t} {}
159   template <typename B>
AllocatorAllocator160   explicit constexpr Allocator(const Allocator<B> &that) noexcept
161       : terminator{that.terminator} {}
162   Allocator(const Allocator &) = default;
163   Allocator(Allocator &&) = default;
allocateAllocator164   [[nodiscard]] constexpr A *allocate(std::size_t n) {
165     return reinterpret_cast<A *>(
166         AllocateMemoryOrCrash(terminator, n * sizeof(A)));
167   }
deallocateAllocator168   constexpr void deallocate(A *p, std::size_t) { FreeMemory(p); }
169   const Terminator &terminator;
170 };
171 } // namespace Fortran::runtime
172 
173 #endif // FORTRAN_RUNTIME_MEMORY_H_
174