ExecutableAllocator.h (6348B)
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 * 4 * Copyright (C) 2008 Apple Inc. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #ifndef jit_ExecutableAllocator_h 29 #define jit_ExecutableAllocator_h 30 31 #include "mozilla/EnumeratedArray.h" 32 33 #include <stddef.h> // for ptrdiff_t 34 35 #include "jit/ProcessExecutableMemory.h" 36 #include "js/AllocPolicy.h" 37 #include "js/HashTable.h" 38 #include "js/TypeDecls.h" 39 #include "js/Vector.h" 40 41 namespace JS { 42 struct CodeSizes; 43 } // namespace JS 44 45 namespace js { 46 namespace jit { 47 48 enum class CodeKind : uint8_t { Ion, Baseline, RegExp, Other, Count }; 49 50 class ExecutableAllocator; 51 52 // These are reference-counted. A new one starts with a count of 1. 53 class ExecutablePool { 54 friend class ExecutableAllocator; 55 56 private: 57 struct Allocation { 58 char* pages; 59 size_t size; 60 }; 61 62 ExecutableAllocator* m_allocator; 63 char* m_freePtr; 64 char* m_end; 65 Allocation m_allocation; 66 67 // Reference count for automatic reclamation. 68 unsigned m_refCount : 31; 69 70 // Flag that can be used by algorithms operating on pools. 71 bool m_mark : 1; 72 73 // Number of bytes currently allocated for each CodeKind. 74 mozilla::EnumeratedArray<CodeKind, size_t, size_t(CodeKind::Count)> 75 m_codeBytes; 76 77 public: 78 void release(bool willDestroy = false); 79 void release(size_t n, CodeKind kind); 80 81 void addRef(); 82 83 ExecutablePool(ExecutableAllocator* allocator, Allocation a) 84 : m_allocator(allocator), 85 m_freePtr(a.pages), 86 m_end(m_freePtr + a.size), 87 m_allocation(a), 88 m_refCount(1), 89 m_mark(false) { 90 for (size_t& count : m_codeBytes) { 91 count = 0; 92 } 93 } 94 95 ~ExecutablePool(); 96 97 void mark() { 98 MOZ_ASSERT(!m_mark); 99 m_mark = true; 100 } 101 void unmark() { 102 MOZ_ASSERT(m_mark); 103 m_mark = false; 104 } 105 bool isMarked() const { return m_mark; } 106 107 private: 108 ExecutablePool(const ExecutablePool&) = delete; 109 void operator=(const ExecutablePool&) = delete; 110 111 void* alloc(size_t n, CodeKind kind); 112 113 size_t available() const; 114 115 // Returns the number of bytes that are currently in use (referenced by 116 // live JitCode objects). 117 size_t usedCodeBytes() const { 118 size_t res = 0; 119 for (size_t count : m_codeBytes) { 120 res += count; 121 } 122 return res; 123 } 124 }; 125 126 struct JitPoisonRange { 127 jit::ExecutablePool* pool; 128 void* start; 129 size_t size; 130 131 JitPoisonRange(jit::ExecutablePool* pool, void* start, size_t size) 132 : pool(pool), start(start), size(size) {} 133 }; 134 135 using JitPoisonRangeVector = Vector<JitPoisonRange, 0, SystemAllocPolicy>; 136 137 class ExecutableAllocator { 138 public: 139 ExecutableAllocator() = default; 140 ~ExecutableAllocator(); 141 142 void purge(); 143 144 // alloc() returns a pointer to some memory, and also (by reference) a 145 // pointer to reference-counted pool. The caller owns a reference to the 146 // pool; i.e. alloc() increments the count before returning the object. 147 void* alloc(JSContext* cx, size_t n, ExecutablePool** poolp, CodeKind type); 148 149 void releasePoolPages(ExecutablePool* pool); 150 151 void addSizeOfCode(JS::CodeSizes* sizes) const; 152 153 private: 154 static const size_t OVERSIZE_ALLOCATION = size_t(-1); 155 156 static size_t roundUpAllocationSize(size_t request, size_t granularity); 157 158 // On OOM, this will return an Allocation where pages is nullptr. 159 ExecutablePool::Allocation systemAlloc(size_t n); 160 static void systemRelease(const ExecutablePool::Allocation& alloc); 161 162 ExecutablePool* createPool(size_t n); 163 ExecutablePool* poolForSize(size_t n); 164 165 static void reprotectPool(JSRuntime* rt, ExecutablePool* pool, 166 ProtectionSetting protection, 167 MustFlushICache flushICache); 168 169 public: 170 [[nodiscard]] static bool makeWritable(void* start, size_t size) { 171 return ReprotectRegion(start, size, ProtectionSetting::Writable, 172 MustFlushICache::No); 173 } 174 175 [[nodiscard]] static bool makeExecutableAndFlushICache(void* start, 176 size_t size) { 177 return ReprotectRegion(start, size, ProtectionSetting::Executable, 178 MustFlushICache::Yes); 179 } 180 181 static void poisonCode(JSRuntime* rt, JitPoisonRangeVector& ranges); 182 183 private: 184 ExecutableAllocator(const ExecutableAllocator&) = delete; 185 void operator=(const ExecutableAllocator&) = delete; 186 187 // These are strong references; they keep pools alive. 188 static const size_t maxSmallPools = 4; 189 using SmallExecPoolVector = 190 js::Vector<ExecutablePool*, maxSmallPools, js::SystemAllocPolicy>; 191 SmallExecPoolVector m_smallPools; 192 193 // All live pools are recorded here, just for stats purposes. These are 194 // weak references; they don't keep pools alive. When a pool is destroyed 195 // its reference is removed from m_pools. 196 using ExecPoolHashSet = 197 js::HashSet<ExecutablePool*, js::DefaultHasher<ExecutablePool*>, 198 js::SystemAllocPolicy>; 199 ExecPoolHashSet m_pools; // All pools, just for stats purposes. 200 }; 201 202 } // namespace jit 203 } // namespace js 204 205 #endif /* jit_ExecutableAllocator_h */