Bug 1834537 - Part 1: Simplify JIT nursery allocation r=jandem
[gecko.git] / js / src / jit / JitAllocPolicy.h
blob3980f3f2fb7e8e98ad1de6209624ac0cf834bf84
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/. */
7 #ifndef jit_JitAllocPolicy_h
8 #define jit_JitAllocPolicy_h
10 #include "mozilla/Assertions.h"
11 #include "mozilla/Attributes.h"
12 #include "mozilla/Likely.h"
13 #include "mozilla/OperatorNewExtensions.h"
14 #include "mozilla/TemplateLib.h"
16 #include <algorithm>
17 #include <stddef.h>
18 #include <string.h>
19 #include <type_traits>
20 #include <utility>
22 #include "ds/LifoAlloc.h"
23 #include "jit/InlineList.h"
24 #include "js/Utility.h"
26 namespace js {
27 namespace jit {
29 class TempAllocator {
30 LifoAllocScope lifoScope_;
32 public:
33 // Most infallible JIT allocations are small, so we use a ballast of 16
34 // KiB. And with a ballast of 16 KiB, a chunk size of 32 KiB works well,
35 // because TempAllocators with a peak allocation size of less than 16 KiB
36 // (which is most of them) only have to allocate a single chunk.
37 static const size_t BallastSize; // 16 KiB
38 static const size_t PreferredLifoChunkSize; // 32 KiB
40 explicit TempAllocator(LifoAlloc* lifoAlloc) : lifoScope_(lifoAlloc) {
41 lifoAlloc->setAsInfallibleByDefault();
44 void* allocateInfallible(size_t bytes) {
45 return lifoScope_.alloc().allocInfallible(bytes);
48 [[nodiscard]] void* allocate(size_t bytes) {
49 LifoAlloc::AutoFallibleScope fallibleAllocator(lifoAlloc());
50 return lifoScope_.alloc().allocEnsureUnused(bytes, BallastSize);
53 template <typename T>
54 [[nodiscard]] T* allocateArray(size_t n) {
55 LifoAlloc::AutoFallibleScope fallibleAllocator(lifoAlloc());
56 size_t bytes;
57 if (MOZ_UNLIKELY(!CalculateAllocSize<T>(n, &bytes))) {
58 return nullptr;
60 return static_cast<T*>(
61 lifoScope_.alloc().allocEnsureUnused(bytes, BallastSize));
64 // View this allocator as a fallible allocator.
65 struct Fallible {
66 TempAllocator& alloc;
68 Fallible fallible() { return {*this}; }
70 LifoAlloc* lifoAlloc() { return &lifoScope_.alloc(); }
72 [[nodiscard]] bool ensureBallast() {
73 JS_OOM_POSSIBLY_FAIL_BOOL();
74 return lifoScope_.alloc().ensureUnusedApproximate(BallastSize);
78 class JitAllocPolicy {
79 TempAllocator& alloc_;
81 public:
82 MOZ_IMPLICIT JitAllocPolicy(TempAllocator& alloc) : alloc_(alloc) {}
83 template <typename T>
84 T* maybe_pod_malloc(size_t numElems) {
85 size_t bytes;
86 if (MOZ_UNLIKELY(!CalculateAllocSize<T>(numElems, &bytes))) {
87 return nullptr;
89 return static_cast<T*>(alloc_.allocate(bytes));
91 template <typename T>
92 T* maybe_pod_calloc(size_t numElems) {
93 T* p = maybe_pod_malloc<T>(numElems);
94 if (MOZ_LIKELY(p)) {
95 memset(p, 0, numElems * sizeof(T));
97 return p;
99 template <typename T>
100 T* maybe_pod_realloc(T* p, size_t oldSize, size_t newSize) {
101 T* n = pod_malloc<T>(newSize);
102 if (MOZ_UNLIKELY(!n)) {
103 return n;
105 MOZ_ASSERT(!(oldSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value));
106 memcpy(n, p, std::min(oldSize * sizeof(T), newSize * sizeof(T)));
107 return n;
109 template <typename T>
110 T* pod_malloc(size_t numElems) {
111 return maybe_pod_malloc<T>(numElems);
113 template <typename T>
114 T* pod_calloc(size_t numElems) {
115 return maybe_pod_calloc<T>(numElems);
117 template <typename T>
118 T* pod_realloc(T* ptr, size_t oldSize, size_t newSize) {
119 return maybe_pod_realloc<T>(ptr, oldSize, newSize);
121 template <typename T>
122 void free_(T* p, size_t numElems = 0) {}
123 void reportAllocOverflow() const {}
124 [[nodiscard]] bool checkSimulatedOOM() const {
125 return !js::oom::ShouldFailWithOOM();
129 struct TempObject {
130 inline void* operator new(size_t nbytes,
131 TempAllocator::Fallible view) noexcept(true) {
132 return view.alloc.allocate(nbytes);
134 inline void* operator new(size_t nbytes, TempAllocator& alloc) {
135 return alloc.allocateInfallible(nbytes);
137 template <class T>
138 inline void* operator new(size_t nbytes, T* pos) {
139 static_assert(std::is_convertible_v<T*, TempObject*>,
140 "Placement new argument type must inherit from TempObject");
141 return pos;
143 template <class T>
144 inline void* operator new(size_t nbytes, mozilla::NotNullTag, T* pos) {
145 static_assert(std::is_convertible_v<T*, TempObject*>,
146 "Placement new argument type must inherit from TempObject");
147 MOZ_ASSERT(pos);
148 return pos;
152 template <typename T>
153 class TempObjectPool {
154 TempAllocator* alloc_;
155 InlineForwardList<T> freed_;
157 public:
158 TempObjectPool() : alloc_(nullptr) {}
159 void setAllocator(TempAllocator& alloc) {
160 MOZ_ASSERT(freed_.empty());
161 alloc_ = &alloc;
163 template <typename... Args>
164 T* allocate(Args&&... args) {
165 MOZ_ASSERT(alloc_);
166 if (freed_.empty()) {
167 return new (alloc_->fallible()) T(std::forward<Args>(args)...);
169 T* res = freed_.popFront();
170 return new (res) T(std::forward<Args>(args)...);
172 void free(T* obj) { freed_.pushFront(obj); }
173 void clear() { freed_.clear(); }
176 } // namespace jit
177 } // namespace js
179 #endif /* jit_JitAllocPolicy_h */