JitAllocPolicy.h (5818B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef jit_JitAllocPolicy_h 8 #define jit_JitAllocPolicy_h 9 10 #include "mozilla/Assertions.h" 11 #include "mozilla/Attributes.h" 12 #include "mozilla/CheckedArithmetic.h" 13 #include "mozilla/Likely.h" 14 #include "mozilla/OperatorNewExtensions.h" 15 16 #include <algorithm> 17 #include <stddef.h> 18 #include <string.h> 19 #include <utility> 20 21 #include "ds/LifoAlloc.h" 22 #include "jit/InlineList.h" 23 #include "js/Utility.h" 24 25 namespace js { 26 namespace jit { 27 28 class TempAllocator { 29 LifoAllocScope lifoScope_; 30 31 public: 32 // Most infallible JIT allocations are small, so we use a ballast of 16 33 // KiB. And with a ballast of 16 KiB, a chunk size of 32 KiB works well, 34 // because TempAllocators with a peak allocation size of less than 16 KiB 35 // (which is most of them) only have to allocate a single chunk. 36 static const size_t BallastSize; // 16 KiB 37 static const size_t PreferredLifoChunkSize; // 32 KiB 38 39 explicit TempAllocator(LifoAlloc* lifoAlloc) : lifoScope_(lifoAlloc) { 40 lifoAlloc->setAsInfallibleByDefault(); 41 } 42 43 void* allocateInfallible(size_t bytes) { 44 return lifoScope_.alloc().allocInfallible(bytes); 45 } 46 47 [[nodiscard]] void* allocate(size_t bytes) { 48 LifoAlloc::AutoFallibleScope fallibleAllocator(lifoAlloc()); 49 return lifoScope_.alloc().allocEnsureUnused(bytes, BallastSize); 50 } 51 52 template <typename T> 53 [[nodiscard]] T* allocateArray(size_t n) { 54 LifoAlloc::AutoFallibleScope fallibleAllocator(lifoAlloc()); 55 size_t bytes; 56 if (MOZ_UNLIKELY(!CalculateAllocSize<T>(n, &bytes))) { 57 return nullptr; 58 } 59 return static_cast<T*>( 60 lifoScope_.alloc().allocEnsureUnused(bytes, BallastSize)); 61 } 62 63 // View this allocator as a fallible allocator. 64 struct Fallible { 65 TempAllocator& alloc; 66 }; 67 Fallible fallible() { return {*this}; } 68 69 LifoAlloc* lifoAlloc() { return &lifoScope_.alloc(); } 70 71 [[nodiscard]] bool ensureBallast() { 72 JS_OOM_POSSIBLY_FAIL_BOOL(); 73 return lifoScope_.alloc().ensureUnusedApproximate(BallastSize); 74 } 75 }; 76 77 class JitAllocPolicy { 78 TempAllocator& alloc_; 79 80 public: 81 MOZ_IMPLICIT JitAllocPolicy(TempAllocator& alloc) : alloc_(alloc) {} 82 template <typename T> 83 js::lifo_alloc_pointer<T*> maybe_pod_malloc(size_t numElems) { 84 size_t bytes; 85 if (MOZ_UNLIKELY(!CalculateAllocSize<T>(numElems, &bytes))) { 86 return nullptr; 87 } 88 return static_cast<T*>(alloc_.allocate(bytes)); 89 } 90 template <typename T> 91 js::lifo_alloc_pointer<T*> maybe_pod_calloc(size_t numElems) { 92 T* p = maybe_pod_malloc<T>(numElems); 93 if (MOZ_LIKELY(p)) { 94 memset(p, 0, numElems * sizeof(T)); 95 } 96 return p; 97 } 98 template <typename T> 99 js::lifo_alloc_pointer<T*> maybe_pod_realloc(T* p, size_t oldSize, 100 size_t newSize) { 101 T* n = pod_malloc<T>(newSize); 102 if (MOZ_UNLIKELY(!n)) { 103 return n; 104 } 105 size_t oldLength; 106 [[maybe_unused]] bool nooverflow = 107 mozilla::SafeMul(oldSize, sizeof(T), &oldLength); 108 MOZ_ASSERT(nooverflow); 109 memcpy(n, p, std::min(oldLength, newSize * sizeof(T))); 110 return n; 111 } 112 template <typename T> 113 js::lifo_alloc_pointer<T*> pod_malloc(size_t numElems) { 114 return maybe_pod_malloc<T>(numElems); 115 } 116 template <typename T> 117 js::lifo_alloc_pointer<T*> pod_calloc(size_t numElems) { 118 return maybe_pod_calloc<T>(numElems); 119 } 120 template <typename T> 121 js::lifo_alloc_pointer<T*> pod_realloc(T* ptr, size_t oldSize, 122 size_t newSize) { 123 return maybe_pod_realloc<T>(ptr, oldSize, newSize); 124 } 125 template <typename T> 126 void free_(T* p, size_t numElems = 0) {} 127 void reportAllocOverflow() const {} 128 [[nodiscard]] bool checkSimulatedOOM() const { 129 return !js::oom::ShouldFailWithOOM(); 130 } 131 }; 132 133 struct TempObject { 134 inline void* operator new(size_t nbytes, 135 TempAllocator::Fallible view) noexcept(true) { 136 return view.alloc.allocate(nbytes); 137 } 138 inline void* operator new(size_t nbytes, TempAllocator& alloc) { 139 return alloc.allocateInfallible(nbytes); 140 } 141 template <class T> 142 inline void* operator new(size_t nbytes, T* pos) { 143 static_assert(std::is_convertible_v<T*, TempObject*>, 144 "Placement new argument type must inherit from TempObject"); 145 return pos; 146 } 147 template <class T> 148 inline void* operator new(size_t nbytes, mozilla::NotNullTag, T* pos) { 149 static_assert(std::is_convertible_v<T*, TempObject*>, 150 "Placement new argument type must inherit from TempObject"); 151 MOZ_ASSERT(pos); 152 return pos; 153 } 154 }; 155 156 template <typename T> 157 class TempObjectPool { 158 TempAllocator* alloc_; 159 InlineForwardList<T> freed_; 160 161 public: 162 TempObjectPool() : alloc_(nullptr) {} 163 void setAllocator(TempAllocator& alloc) { 164 MOZ_ASSERT(freed_.empty()); 165 alloc_ = &alloc; 166 } 167 template <typename... Args> 168 T* allocate(Args&&... args) { 169 MOZ_ASSERT(alloc_); 170 if (freed_.empty()) { 171 return new (alloc_->fallible()) T(std::forward<Args>(args)...); 172 } 173 T* res = freed_.popFront(); 174 return new (res) T(std::forward<Args>(args)...); 175 } 176 void free(T* obj) { freed_.pushFront(obj); } 177 void clear() { freed_.clear(); } 178 }; 179 180 } // namespace jit 181 182 // A Vector using JitAllocPolicy can discard its own buffer, which is safe as 183 // long as the contained items can also be dropped. 184 template <typename T, size_t N> 185 struct CanLifoAlloc<mozilla::Vector<T, N, js::jit::JitAllocPolicy>> 186 : CanLifoAlloc<T>::type {}; 187 188 } // namespace js 189 190 #endif /* jit_JitAllocPolicy_h */