1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtQml module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qv4executableallocator_p.h" |
41 | #include "qv4functiontable_p.h" |
42 | |
43 | #include <wtf/StdLibExtras.h> |
44 | #include <wtf/PageAllocation.h> |
45 | |
46 | using namespace QV4; |
47 | |
48 | void *ExecutableAllocator::Allocation::exceptionHandlerStart() const |
49 | { |
50 | return reinterpret_cast<void*>(addr); |
51 | } |
52 | |
53 | size_t ExecutableAllocator::Allocation::exceptionHandlerSize() const |
54 | { |
55 | return QV4::exceptionHandlerSize(); |
56 | } |
57 | |
58 | void *ExecutableAllocator::Allocation::memoryStart() const |
59 | { |
60 | return reinterpret_cast<void*>(addr); |
61 | } |
62 | |
63 | void *ExecutableAllocator::Allocation::codeStart() const |
64 | { |
65 | return reinterpret_cast<void*>(addr + exceptionHandlerSize()); |
66 | } |
67 | |
68 | void ExecutableAllocator::Allocation::deallocate(ExecutableAllocator *allocator) |
69 | { |
70 | if (isValid()) |
71 | allocator->free(allocation: this); |
72 | else |
73 | delete this; |
74 | } |
75 | |
76 | ExecutableAllocator::Allocation *ExecutableAllocator::Allocation::split(size_t dividingSize) |
77 | { |
78 | Allocation *remainder = new Allocation; |
79 | if (next) |
80 | next->prev = remainder; |
81 | |
82 | remainder->next = next; |
83 | next = remainder; |
84 | |
85 | remainder->prev = this; |
86 | |
87 | remainder->size = size - dividingSize; |
88 | remainder->free = free; |
89 | remainder->addr = addr + dividingSize; |
90 | size = dividingSize; |
91 | |
92 | return remainder; |
93 | } |
94 | |
95 | bool ExecutableAllocator::Allocation::mergeNext(ExecutableAllocator *allocator) |
96 | { |
97 | Q_ASSERT(free); |
98 | if (!next || !next->free) |
99 | return false; |
100 | |
101 | allocator->freeAllocations.remove(key: size, value: this); |
102 | allocator->freeAllocations.remove(key: next->size, value: next); |
103 | |
104 | size += next->size; |
105 | Allocation *newNext = next->next; |
106 | delete next; |
107 | next = newNext; |
108 | if (next) |
109 | next->prev = this; |
110 | |
111 | allocator->freeAllocations.insert(akey: size, avalue: this); |
112 | return true; |
113 | } |
114 | |
115 | bool ExecutableAllocator::Allocation::mergePrevious(ExecutableAllocator *allocator) |
116 | { |
117 | Q_ASSERT(free); |
118 | if (!prev || !prev->free) |
119 | return false; |
120 | |
121 | allocator->freeAllocations.remove(key: size, value: this); |
122 | allocator->freeAllocations.remove(key: prev->size, value: prev); |
123 | |
124 | prev->size += size; |
125 | if (next) |
126 | next->prev = prev; |
127 | prev->next = next; |
128 | |
129 | allocator->freeAllocations.insert(akey: prev->size, avalue: prev); |
130 | |
131 | delete this; |
132 | return true; |
133 | } |
134 | |
135 | ExecutableAllocator::ChunkOfPages::~ChunkOfPages() |
136 | { |
137 | Allocation *alloc = firstAllocation; |
138 | while (alloc) { |
139 | Allocation *next = alloc->next; |
140 | if (alloc->isValid()) |
141 | delete alloc; |
142 | alloc = next; |
143 | } |
144 | pages->deallocate(); |
145 | delete pages; |
146 | } |
147 | |
148 | bool ExecutableAllocator::ChunkOfPages::contains(Allocation *alloc) const |
149 | { |
150 | Allocation *it = firstAllocation; |
151 | while (it) { |
152 | if (it == alloc) |
153 | return true; |
154 | it = it->next; |
155 | } |
156 | return false; |
157 | } |
158 | |
159 | ExecutableAllocator::ExecutableAllocator() |
160 | = default; |
161 | |
162 | ExecutableAllocator::~ExecutableAllocator() |
163 | { |
164 | for (ChunkOfPages *chunk : qAsConst(t&: chunks)) { |
165 | for (Allocation *allocation = chunk->firstAllocation; allocation; allocation = allocation->next) |
166 | if (!allocation->free) |
167 | allocation->invalidate(); |
168 | } |
169 | |
170 | qDeleteAll(c: chunks); |
171 | } |
172 | |
173 | ExecutableAllocator::Allocation *ExecutableAllocator::allocate(size_t size) |
174 | { |
175 | QMutexLocker locker(&mutex); |
176 | Allocation *allocation = nullptr; |
177 | |
178 | // Code is best aligned to 16-byte boundaries. |
179 | size = WTF::roundUpToMultipleOf(divisor: 16, x: size + exceptionHandlerSize()); |
180 | |
181 | QMultiMap<size_t, Allocation*>::Iterator it = freeAllocations.lowerBound(akey: size); |
182 | if (it != freeAllocations.end()) { |
183 | allocation = *it; |
184 | freeAllocations.erase(it); |
185 | } |
186 | |
187 | if (!allocation) { |
188 | ChunkOfPages *chunk = new ChunkOfPages; |
189 | size_t allocSize = WTF::roundUpToMultipleOf(divisor: WTF::pageSize(), x: size); |
190 | chunk->pages = new WTF::PageAllocation(WTF::PageAllocation::allocate(size: allocSize, usage: OSAllocator::JSJITCodePages)); |
191 | chunks.insert(akey: reinterpret_cast<quintptr>(chunk->pages->base()) - 1, avalue: chunk); |
192 | allocation = new Allocation; |
193 | allocation->addr = reinterpret_cast<quintptr>(chunk->pages->base()); |
194 | allocation->size = allocSize; |
195 | allocation->free = true; |
196 | chunk->firstAllocation = allocation; |
197 | } |
198 | |
199 | Q_ASSERT(allocation); |
200 | Q_ASSERT(allocation->free); |
201 | |
202 | allocation->free = false; |
203 | |
204 | if (allocation->size > size) { |
205 | Allocation *remainder = allocation->split(dividingSize: size); |
206 | remainder->free = true; |
207 | if (!remainder->mergeNext(allocator: this)) |
208 | freeAllocations.insert(akey: remainder->size, avalue: remainder); |
209 | } |
210 | |
211 | return allocation; |
212 | } |
213 | |
214 | void ExecutableAllocator::free(Allocation *allocation) |
215 | { |
216 | QMutexLocker locker(&mutex); |
217 | |
218 | Q_ASSERT(allocation); |
219 | |
220 | allocation->free = true; |
221 | |
222 | QMap<quintptr, ChunkOfPages*>::Iterator it = chunks.lowerBound(akey: allocation->addr); |
223 | if (it != chunks.begin()) |
224 | --it; |
225 | Q_ASSERT(it != chunks.end()); |
226 | ChunkOfPages *chunk = *it; |
227 | Q_ASSERT(chunk->contains(allocation)); |
228 | |
229 | bool merged = allocation->mergeNext(allocator: this); |
230 | merged |= allocation->mergePrevious(allocator: this); |
231 | if (!merged) |
232 | freeAllocations.insert(akey: allocation->size, avalue: allocation); |
233 | |
234 | allocation = nullptr; |
235 | |
236 | if (!chunk->firstAllocation->next) { |
237 | freeAllocations.remove(key: chunk->firstAllocation->size, value: chunk->firstAllocation); |
238 | chunks.erase(it); |
239 | delete chunk; |
240 | return; |
241 | } |
242 | } |
243 | |
244 | ExecutableAllocator::ChunkOfPages *ExecutableAllocator::chunkForAllocation(Allocation *allocation) const |
245 | { |
246 | QMutexLocker locker(&mutex); |
247 | |
248 | QMap<quintptr, ChunkOfPages*>::ConstIterator it = chunks.lowerBound(akey: allocation->addr); |
249 | if (it != chunks.begin()) |
250 | --it; |
251 | if (it == chunks.end()) |
252 | return nullptr; |
253 | return *it; |
254 | } |
255 | |
256 | |