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