tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 */