]>
Commit | Line | Data |
---|---|---|
b37bf2e1 | 1 | /* |
9dae56ea | 2 | * Copyright (C) 2008 Apple Inc. All rights reserved. |
b37bf2e1 A |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without | |
5 | * modification, are permitted provided that the following conditions | |
6 | * are met: | |
7 | * 1. Redistributions of source code must retain the above copyright | |
8 | * notice, this list of conditions and the following disclaimer. | |
9 | * 2. Redistributions in binary form must reproduce the above copyright | |
10 | * notice, this list of conditions and the following disclaimer in the | |
11 | * documentation and/or other materials provided with the distribution. | |
12 | * | |
9dae56ea | 13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
b37bf2e1 A |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
9dae56ea | 16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
b37bf2e1 A |
17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
24 | */ | |
25 | ||
9dae56ea | 26 | #include "config.h" |
9dae56ea | 27 | #include "ExecutableAllocator.h" |
b37bf2e1 | 28 | |
81345200 A |
29 | #include "JSCInlines.h" |
30 | ||
6fe7ccc8 A |
31 | #if ENABLE(EXECUTABLE_ALLOCATOR_DEMAND) |
32 | #include "CodeProfiling.h" | |
33 | #include <wtf/HashSet.h> | |
34 | #include <wtf/MetaAllocator.h> | |
81345200 | 35 | #include <wtf/NeverDestroyed.h> |
6fe7ccc8 | 36 | #include <wtf/PageReservation.h> |
6fe7ccc8 A |
37 | #include <wtf/ThreadingPrimitives.h> |
38 | #include <wtf/VMTags.h> | |
39 | #endif | |
40 | ||
41 | // Uncomment to create an artificial executable memory usage limit. This limit | |
42 | // is imperfect and is primarily useful for testing the VM's ability to handle | |
43 | // out-of-executable-memory situations. | |
44 | // #define EXECUTABLE_MEMORY_LIMIT 1000000 | |
45 | ||
9dae56ea | 46 | #if ENABLE(ASSEMBLER) |
b37bf2e1 | 47 | |
6fe7ccc8 | 48 | using namespace WTF; |
9dae56ea | 49 | |
6fe7ccc8 | 50 | namespace JSC { |
9dae56ea | 51 | |
14957cd0 A |
52 | #if ENABLE(EXECUTABLE_ALLOCATOR_DEMAND) |
53 | ||
6fe7ccc8 A |
54 | class DemandExecutableAllocator : public MetaAllocator { |
55 | public: | |
56 | DemandExecutableAllocator() | |
93a37866 | 57 | : MetaAllocator(jitAllocationGranule) |
6fe7ccc8 | 58 | { |
81345200 | 59 | std::lock_guard<std::mutex> lock(allocatorsMutex()); |
6fe7ccc8 A |
60 | allocators().add(this); |
61 | // Don't preallocate any memory here. | |
62 | } | |
63 | ||
64 | virtual ~DemandExecutableAllocator() | |
65 | { | |
66 | { | |
81345200 | 67 | std::lock_guard<std::mutex> lock(allocatorsMutex()); |
6fe7ccc8 A |
68 | allocators().remove(this); |
69 | } | |
70 | for (unsigned i = 0; i < reservations.size(); ++i) | |
71 | reservations.at(i).deallocate(); | |
72 | } | |
73 | ||
74 | static size_t bytesAllocatedByAllAllocators() | |
75 | { | |
76 | size_t total = 0; | |
81345200 | 77 | std::lock_guard<std::mutex> lock(allocatorsMutex()); |
6fe7ccc8 A |
78 | for (HashSet<DemandExecutableAllocator*>::const_iterator allocator = allocators().begin(); allocator != allocators().end(); ++allocator) |
79 | total += (*allocator)->bytesAllocated(); | |
80 | return total; | |
81 | } | |
82 | ||
83 | static size_t bytesCommittedByAllocactors() | |
84 | { | |
85 | size_t total = 0; | |
81345200 | 86 | std::lock_guard<std::mutex> lock(allocatorsMutex()); |
6fe7ccc8 A |
87 | for (HashSet<DemandExecutableAllocator*>::const_iterator allocator = allocators().begin(); allocator != allocators().end(); ++allocator) |
88 | total += (*allocator)->bytesCommitted(); | |
89 | return total; | |
90 | } | |
91 | ||
92 | #if ENABLE(META_ALLOCATOR_PROFILE) | |
93 | static void dumpProfileFromAllAllocators() | |
94 | { | |
81345200 | 95 | std::lock_guard<std::mutex> lock(allocatorsMutex()); |
6fe7ccc8 A |
96 | for (HashSet<DemandExecutableAllocator*>::const_iterator allocator = allocators().begin(); allocator != allocators().end(); ++allocator) |
97 | (*allocator)->dumpProfile(); | |
98 | } | |
99 | #endif | |
100 | ||
101 | protected: | |
102 | virtual void* allocateNewSpace(size_t& numPages) | |
103 | { | |
104 | size_t newNumPages = (((numPages * pageSize() + JIT_ALLOCATOR_LARGE_ALLOC_SIZE - 1) / JIT_ALLOCATOR_LARGE_ALLOC_SIZE * JIT_ALLOCATOR_LARGE_ALLOC_SIZE) + pageSize() - 1) / pageSize(); | |
105 | ||
106 | ASSERT(newNumPages >= numPages); | |
107 | ||
108 | numPages = newNumPages; | |
109 | ||
110 | #ifdef EXECUTABLE_MEMORY_LIMIT | |
111 | if (bytesAllocatedByAllAllocators() >= EXECUTABLE_MEMORY_LIMIT) | |
112 | return 0; | |
113 | #endif | |
114 | ||
115 | PageReservation reservation = PageReservation::reserve(numPages * pageSize(), OSAllocator::JSJITCodePages, EXECUTABLE_POOL_WRITABLE, true); | |
93a37866 | 116 | RELEASE_ASSERT(reservation); |
6fe7ccc8 A |
117 | |
118 | reservations.append(reservation); | |
119 | ||
120 | return reservation.base(); | |
121 | } | |
122 | ||
123 | virtual void notifyNeedPage(void* page) | |
124 | { | |
125 | OSAllocator::commit(page, pageSize(), EXECUTABLE_POOL_WRITABLE, true); | |
126 | } | |
127 | ||
128 | virtual void notifyPageIsFree(void* page) | |
129 | { | |
130 | OSAllocator::decommit(page, pageSize()); | |
131 | } | |
132 | ||
133 | private: | |
134 | Vector<PageReservation, 16> reservations; | |
135 | static HashSet<DemandExecutableAllocator*>& allocators() | |
136 | { | |
81345200 | 137 | DEPRECATED_DEFINE_STATIC_LOCAL(HashSet<DemandExecutableAllocator*>, sAllocators, ()); |
6fe7ccc8 A |
138 | return sAllocators; |
139 | } | |
81345200 A |
140 | |
141 | static std::mutex& allocatorsMutex() | |
6fe7ccc8 | 142 | { |
81345200 A |
143 | static NeverDestroyed<std::mutex> mutex; |
144 | ||
6fe7ccc8 A |
145 | return mutex; |
146 | } | |
147 | }; | |
148 | ||
149 | #if ENABLE(ASSEMBLER_WX_EXCLUSIVE) | |
150 | void ExecutableAllocator::initializeAllocator() | |
14957cd0 | 151 | { |
6fe7ccc8 | 152 | } |
14957cd0 | 153 | #else |
6fe7ccc8 A |
154 | static DemandExecutableAllocator* gAllocator; |
155 | ||
156 | namespace { | |
157 | static inline DemandExecutableAllocator* allocator() | |
158 | { | |
159 | return gAllocator; | |
160 | } | |
14957cd0 A |
161 | } |
162 | ||
6fe7ccc8 | 163 | void ExecutableAllocator::initializeAllocator() |
14957cd0 | 164 | { |
6fe7ccc8 A |
165 | ASSERT(!gAllocator); |
166 | gAllocator = new DemandExecutableAllocator(); | |
167 | CodeProfiling::notifyAllocator(gAllocator); | |
168 | } | |
169 | #endif | |
170 | ||
93a37866 | 171 | ExecutableAllocator::ExecutableAllocator(VM&) |
6fe7ccc8 | 172 | #if ENABLE(ASSEMBLER_WX_EXCLUSIVE) |
ed1e77d3 | 173 | : m_allocator(std::make_unique<DemandExecutableAllocator>()) |
6fe7ccc8 A |
174 | #endif |
175 | { | |
176 | ASSERT(allocator()); | |
14957cd0 A |
177 | } |
178 | ||
6fe7ccc8 | 179 | ExecutableAllocator::~ExecutableAllocator() |
14957cd0 | 180 | { |
14957cd0 A |
181 | } |
182 | ||
183 | bool ExecutableAllocator::isValid() const | |
184 | { | |
185 | return true; | |
186 | } | |
6fe7ccc8 | 187 | |
14957cd0 A |
188 | bool ExecutableAllocator::underMemoryPressure() |
189 | { | |
6fe7ccc8 A |
190 | #ifdef EXECUTABLE_MEMORY_LIMIT |
191 | return DemandExecutableAllocator::bytesAllocatedByAllAllocators() > EXECUTABLE_MEMORY_LIMIT / 2; | |
192 | #else | |
14957cd0 | 193 | return false; |
6fe7ccc8 | 194 | #endif |
14957cd0 | 195 | } |
6fe7ccc8 A |
196 | |
197 | double ExecutableAllocator::memoryPressureMultiplier(size_t addedMemoryUsage) | |
198 | { | |
199 | double result; | |
200 | #ifdef EXECUTABLE_MEMORY_LIMIT | |
201 | size_t bytesAllocated = DemandExecutableAllocator::bytesAllocatedByAllAllocators() + addedMemoryUsage; | |
202 | if (bytesAllocated >= EXECUTABLE_MEMORY_LIMIT) | |
203 | bytesAllocated = EXECUTABLE_MEMORY_LIMIT; | |
204 | result = static_cast<double>(EXECUTABLE_MEMORY_LIMIT) / | |
205 | (EXECUTABLE_MEMORY_LIMIT - bytesAllocated); | |
206 | #else | |
207 | UNUSED_PARAM(addedMemoryUsage); | |
208 | result = 1.0; | |
209 | #endif | |
210 | if (result < 1.0) | |
211 | result = 1.0; | |
212 | return result; | |
213 | ||
214 | } | |
215 | ||
ed1e77d3 | 216 | RefPtr<ExecutableMemoryHandle> ExecutableAllocator::allocate(VM&, size_t sizeInBytes, void* ownerUID, JITCompilationEffort effort) |
6fe7ccc8 A |
217 | { |
218 | RefPtr<ExecutableMemoryHandle> result = allocator()->allocate(sizeInBytes, ownerUID); | |
93a37866 | 219 | RELEASE_ASSERT(result || effort != JITCompilationMustSucceed); |
ed1e77d3 | 220 | return result; |
6fe7ccc8 A |
221 | } |
222 | ||
14957cd0 A |
223 | size_t ExecutableAllocator::committedByteCount() |
224 | { | |
6fe7ccc8 A |
225 | return DemandExecutableAllocator::bytesCommittedByAllocactors(); |
226 | } | |
14957cd0 | 227 | |
6fe7ccc8 A |
228 | #if ENABLE(META_ALLOCATOR_PROFILE) |
229 | void ExecutableAllocator::dumpProfile() | |
230 | { | |
231 | DemandExecutableAllocator::dumpProfileFromAllAllocators(); | |
232 | } | |
14957cd0 A |
233 | #endif |
234 | ||
6fe7ccc8 A |
235 | #endif // ENABLE(EXECUTABLE_ALLOCATOR_DEMAND) |
236 | ||
4e4e5a6f | 237 | #if ENABLE(ASSEMBLER_WX_EXCLUSIVE) |
14957cd0 | 238 | |
6fe7ccc8 | 239 | #if OS(WINDOWS) |
14957cd0 A |
240 | #error "ASSEMBLER_WX_EXCLUSIVE not yet suported on this platform." |
241 | #endif | |
242 | ||
243 | void ExecutableAllocator::reprotectRegion(void* start, size_t size, ProtectionSetting setting) | |
4e4e5a6f | 244 | { |
6fe7ccc8 | 245 | size_t pageSize = WTF::pageSize(); |
4e4e5a6f A |
246 | |
247 | // Calculate the start of the page containing this region, | |
248 | // and account for this extra memory within size. | |
249 | intptr_t startPtr = reinterpret_cast<intptr_t>(start); | |
250 | intptr_t pageStartPtr = startPtr & ~(pageSize - 1); | |
251 | void* pageStart = reinterpret_cast<void*>(pageStartPtr); | |
252 | size += (startPtr - pageStartPtr); | |
253 | ||
254 | // Round size up | |
255 | size += (pageSize - 1); | |
256 | size &= ~(pageSize - 1); | |
257 | ||
258 | mprotect(pageStart, size, (setting == Writable) ? PROTECTION_FLAGS_RW : PROTECTION_FLAGS_RX); | |
259 | } | |
14957cd0 | 260 | |
4e4e5a6f A |
261 | #endif |
262 | ||
9dae56ea A |
263 | } |
264 | ||
265 | #endif // HAVE(ASSEMBLER) |