127 static constexpr std::size_t segment_size = std::size_t{1024u} * 1024u * 1024u;
131 std::lock_guard
l{_mutex};
132 size = round_to_page(size);
133 auto best = free_blocks_by_size.lower_bound(size);
134 if(best == free_blocks_by_size.end()) {
135 best = allocate_segment(size);
137 if (best->first > size) {
138 best = split_block(best, size);
140 transfer_node(free_blocks, allocated_blocks, best->second);
141 best = transfer_node(free_blocks_by_size, allocated_blocks_by_size, *best);
145 void free(
void* ptr)
noexcept {
146 std::lock_guard
l{_mutex};
147 auto pos = transfer_node(allocated_blocks, free_blocks, ptr);
148 transfer_node(allocated_blocks_by_size, free_blocks_by_size, {pos->second, pos->first});
151 if(pos != free_blocks.begin()) {
154 pos = maybe_consolidate_blocks(prev, pos);
158 if (next != free_blocks.end()) {
159 maybe_consolidate_blocks(pos, next);
164 return the_jit_allocator;
168 segment(
void * base, std::size_t size) : base(base), size(size) {}
169 segment(segment&& other) : base(other.base), size(other.size) {
170 other.base =
nullptr;
173 segment& operator=(
const segment& other) =
delete;
176 ::munmap(base, size);
182 using block = std::pair<std::size_t, void*>;
184 using is_transparent = void;
185 bool operator()(
const block& lhs,
const block& rhs)
const {
186 return lhs.first < rhs.first || (lhs.first == rhs.first && std::less<void*>{}(lhs.second, rhs.second));
188 bool operator()(
const block& lhs, std::size_t rhs)
const {
189 return lhs.first < rhs;
191 bool operator()(std::size_t lhs,
const block& rhs)
const {
192 return lhs < rhs.first;
195 std::vector<segment> _segments;
196 std::set<block, by_size> free_blocks_by_size;
197 std::set<block, by_size> allocated_blocks_by_size;
198 std::map<void*, std::size_t> free_blocks;
199 std::map<void*, std::size_t> allocated_blocks;
201 using blocks_by_size_t = std::set<block, by_size>;
202 using blocks_t = std::map<void*, size_t>;
207 static typename C::iterator transfer_node(C& from, C& to,
typename C::key_type key)
noexcept {
208 auto node = from.extract(key);
210 auto [pos, inserted,
_] = to.insert(std::move(node));
215 blocks_by_size_t::iterator allocate_segment(std::size_t min_size) {
216 std::size_t size = std::max(min_size, segment_size);
217 void* base = mmap(
nullptr, size, PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
218 segment
s{base, size};
219 SYS_VM_ASSERT(base != MAP_FAILED, wasm_bad_alloc,
"failed to allocate jit segment");
220 _segments.emplace_back(std::move(
s));
222 auto guard_1 = scope_guard{[&] {
if(!success) { _segments.pop_back(); } }};
223 auto pos2 = free_blocks_by_size.insert({size, base}).first;
224 auto guard_2 = scope_guard{[&] {
if(!success) { free_blocks_by_size.erase(pos2); }}};
225 free_blocks.insert({base, size});
230 blocks_by_size_t::iterator split_block(blocks_by_size_t::iterator pos, std::size_t size) {
232 auto new1 = free_blocks_by_size.insert({size, pos->second}).first;
233 auto guard1 = scope_guard{[&]{
if(!success) { free_blocks_by_size.erase(new1); } }};
234 auto new2 = free_blocks_by_size.insert({pos->first - size,
static_cast<char*
>(pos->second) + size}).first;
235 auto guard2 = scope_guard{[&]{
if(!success) { free_blocks_by_size.erase(new2); } }};
236 free_blocks.insert({new2->second, new2->first});
238 free_blocks_by_size.erase(pos);
239 free_blocks[new1->second] = new1->first;
244 blocks_t::iterator maybe_consolidate_blocks(blocks_t::iterator lhs, blocks_t::iterator rhs)
noexcept {
245 if(
static_cast<char*
>(lhs->first) + lhs->second == rhs->first) {
247 auto node = free_blocks_by_size.extract({lhs->second, lhs->first});
249 node.value().first += rhs->second;
250 free_blocks_by_size.insert(std::move(node));
251 free_blocks_by_size.erase({rhs->second, rhs->first});
253 lhs->second += rhs->second;
254 free_blocks.erase(rhs);
261 static std::size_t round_to_page(std::size_t offset) {
262 std::size_t pagesize =
static_cast<std::size_t
>(::sysconf(_SC_PAGESIZE));
263 return (offset + pagesize - 1) & ~(pagesize - 1);