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
46using namespace QV4;
47
48void *ExecutableAllocator::Allocation::exceptionHandlerStart() const
49{
50 return reinterpret_cast<void*>(addr);
51}
52
53size_t ExecutableAllocator::Allocation::exceptionHandlerSize() const
54{
55 return QV4::exceptionHandlerSize();
56}
57
58void *ExecutableAllocator::Allocation::memoryStart() const
59{
60 return reinterpret_cast<void*>(addr);
61}
62
63void *ExecutableAllocator::Allocation::codeStart() const
64{
65 return reinterpret_cast<void*>(addr + exceptionHandlerSize());
66}
67
68void ExecutableAllocator::Allocation::deallocate(ExecutableAllocator *allocator)
69{
70 if (isValid())
71 allocator->free(allocation: this);
72 else
73 delete this;
74}
75
76ExecutableAllocator::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
95bool 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
115bool 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
135ExecutableAllocator::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
148bool 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
159ExecutableAllocator::ExecutableAllocator()
160 = default;
161
162ExecutableAllocator::~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
173ExecutableAllocator::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
214void 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
244ExecutableAllocator::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

source code of qtdeclarative/src/qml/jsruntime/qv4executableallocator.cpp