1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qv4executableallocator_p.h"
5#include <QtQml/private/qv4functiontable_p.h>
6
7#include <wtf/StdLibExtras.h>
8#include <wtf/PageAllocation.h>
9
10using namespace QV4;
11
12void *ExecutableAllocator::Allocation::exceptionHandlerStart() const
13{
14 return reinterpret_cast<void*>(addr);
15}
16
17size_t ExecutableAllocator::Allocation::exceptionHandlerSize() const
18{
19 return QV4::exceptionHandlerSize();
20}
21
22void *ExecutableAllocator::Allocation::memoryStart() const
23{
24 return reinterpret_cast<void*>(addr);
25}
26
27void *ExecutableAllocator::Allocation::codeStart() const
28{
29 return reinterpret_cast<void*>(addr + exceptionHandlerSize());
30}
31
32void ExecutableAllocator::Allocation::deallocate(ExecutableAllocator *allocator)
33{
34 if (isValid())
35 allocator->free(allocation: this);
36 else
37 delete this;
38}
39
40ExecutableAllocator::Allocation *ExecutableAllocator::Allocation::split(size_t dividingSize)
41{
42 Allocation *remainder = new Allocation;
43 if (next)
44 next->prev = remainder;
45
46 remainder->next = next;
47 next = remainder;
48
49 remainder->prev = this;
50
51 remainder->size = size - dividingSize;
52 remainder->free = free;
53 remainder->addr = addr + dividingSize;
54 size = dividingSize;
55
56 return remainder;
57}
58
59bool ExecutableAllocator::Allocation::mergeNext(ExecutableAllocator *allocator)
60{
61 Q_ASSERT(free);
62 if (!next || !next->free)
63 return false;
64
65 allocator->freeAllocations.remove(key: size, value: this);
66 allocator->freeAllocations.remove(key: next->size, value: next);
67
68 size += next->size;
69 Allocation *newNext = next->next;
70 delete next;
71 next = newNext;
72 if (next)
73 next->prev = this;
74
75 allocator->freeAllocations.insert(key: size, value: this);
76 return true;
77}
78
79bool ExecutableAllocator::Allocation::mergePrevious(ExecutableAllocator *allocator)
80{
81 Q_ASSERT(free);
82 if (!prev || !prev->free)
83 return false;
84
85 allocator->freeAllocations.remove(key: size, value: this);
86 allocator->freeAllocations.remove(key: prev->size, value: prev);
87
88 prev->size += size;
89 if (next)
90 next->prev = prev;
91 prev->next = next;
92
93 allocator->freeAllocations.insert(key: prev->size, value: prev);
94
95 delete this;
96 return true;
97}
98
99ExecutableAllocator::ChunkOfPages::~ChunkOfPages()
100{
101 Allocation *alloc = firstAllocation;
102 while (alloc) {
103 Allocation *next = alloc->next;
104 if (alloc->isValid())
105 delete alloc;
106 alloc = next;
107 }
108 pages->deallocate();
109 delete pages;
110}
111
112bool ExecutableAllocator::ChunkOfPages::contains(Allocation *alloc) const
113{
114 Allocation *it = firstAllocation;
115 while (it) {
116 if (it == alloc)
117 return true;
118 it = it->next;
119 }
120 return false;
121}
122
123ExecutableAllocator::ExecutableAllocator()
124 = default;
125
126ExecutableAllocator::~ExecutableAllocator()
127{
128 for (ChunkOfPages *chunk : std::as_const(t&: chunks)) {
129 for (Allocation *allocation = chunk->firstAllocation; allocation; allocation = allocation->next)
130 if (!allocation->free)
131 allocation->invalidate();
132 }
133
134 qDeleteAll(c: chunks);
135}
136
137ExecutableAllocator::Allocation *ExecutableAllocator::allocate(size_t size)
138{
139 QMutexLocker locker(&mutex);
140 Allocation *allocation = nullptr;
141
142 // Code is best aligned to 16-byte boundaries.
143 size = WTF::roundUpToMultipleOf(divisor: 16, x: size + exceptionHandlerSize());
144
145 QMultiMap<size_t, Allocation*>::Iterator it = freeAllocations.lowerBound(key: size);
146 if (it != freeAllocations.end()) {
147 allocation = *it;
148 freeAllocations.erase(it);
149 }
150
151 if (!allocation) {
152 ChunkOfPages *chunk = new ChunkOfPages;
153 size_t allocSize = WTF::roundUpToMultipleOf(divisor: WTF::pageSize(), x: size);
154 chunk->pages = new WTF::PageAllocation(WTF::PageAllocation::allocate(size: allocSize, usage: OSAllocator::JSJITCodePages));
155 chunks.insert(key: reinterpret_cast<quintptr>(chunk->pages->base()) - 1, value: chunk);
156 allocation = new Allocation;
157 allocation->addr = reinterpret_cast<quintptr>(chunk->pages->base());
158 allocation->size = allocSize;
159 allocation->free = true;
160 chunk->firstAllocation = allocation;
161 }
162
163 Q_ASSERT(allocation);
164 Q_ASSERT(allocation->free);
165
166 allocation->free = false;
167
168 if (allocation->size > size) {
169 Allocation *remainder = allocation->split(dividingSize: size);
170 remainder->free = true;
171 if (!remainder->mergeNext(allocator: this))
172 freeAllocations.insert(key: remainder->size, value: remainder);
173 }
174
175 return allocation;
176}
177
178void ExecutableAllocator::free(Allocation *allocation)
179{
180 QMutexLocker locker(&mutex);
181
182 Q_ASSERT(allocation);
183
184 allocation->free = true;
185
186 QMap<quintptr, ChunkOfPages*>::Iterator it = chunks.lowerBound(key: allocation->addr);
187 if (it != chunks.begin())
188 --it;
189 Q_ASSERT(it != chunks.end());
190 ChunkOfPages *chunk = *it;
191 Q_ASSERT(chunk->contains(allocation));
192
193 bool merged = allocation->mergeNext(allocator: this);
194 merged |= allocation->mergePrevious(allocator: this);
195 if (!merged)
196 freeAllocations.insert(key: allocation->size, value: allocation);
197
198 allocation = nullptr;
199
200 if (!chunk->firstAllocation->next) {
201 freeAllocations.remove(key: chunk->firstAllocation->size, value: chunk->firstAllocation);
202 chunks.erase(it);
203 delete chunk;
204 return;
205 }
206}
207
208ExecutableAllocator::ChunkOfPages *ExecutableAllocator::chunkForAllocation(Allocation *allocation) const
209{
210 QMutexLocker locker(&mutex);
211
212 QMap<quintptr, ChunkOfPages*>::ConstIterator it = chunks.lowerBound(key: allocation->addr);
213 if (it != chunks.begin())
214 --it;
215 if (it == chunks.end())
216 return nullptr;
217 return *it;
218}
219
220

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