xref: /netbsd-src/sys/external/bsd/sljit/dist/sljit_src/sljitExecAllocator.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*	$NetBSD: sljitExecAllocator.c,v 1.4 2014/06/17 19:33:20 alnsn Exp $	*/
2 
3 /*
4  *    Stack-less Just-In-Time compiler
5  *
6  *    Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without modification, are
9  * permitted provided that the following conditions are met:
10  *
11  *   1. Redistributions of source code must retain the above copyright notice, this list of
12  *      conditions and the following disclaimer.
13  *
14  *   2. Redistributions in binary form must reproduce the above copyright notice, this list
15  *      of conditions and the following disclaimer in the documentation and/or other materials
16  *      provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
21  * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
23  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /*
30    This file contains a simple executable memory allocator
31 
32    It is assumed, that executable code blocks are usually medium (or sometimes
33    large) memory blocks, and the allocator is not too frequently called (less
34    optimized than other allocators). Thus, using it as a generic allocator is
35    not suggested.
36 
37    How does it work:
38      Memory is allocated in continuous memory areas called chunks by alloc_chunk()
39      Chunk format:
40      [ block ][ block ] ... [ block ][ block terminator ]
41 
42    All blocks and the block terminator is started with block_header. The block
43    header contains the size of the previous and the next block. These sizes
44    can also contain special values.
45      Block size:
46        0 - The block is a free_block, with a different size member.
47        1 - The block is a block terminator.
48        n - The block is used at the moment, and the value contains its size.
49      Previous block size:
50        0 - This is the first block of the memory chunk.
51        n - The size of the previous block.
52 
53    Using these size values we can go forward or backward on the block chain.
54    The unused blocks are stored in a chain list pointed by free_blocks. This
55    list is useful if we need to find a suitable memory area when the allocator
56    is called.
57 
58    When a block is freed, the new free block is connected to its adjacent free
59    blocks if possible.
60 
61      [ free block ][ used block ][ free block ]
62    and "used block" is freed, the three blocks are connected together:
63      [           one big free block           ]
64 */
65 
66 /* --------------------------------------------------------------------- */
67 /*  System (OS) functions                                                */
68 /* --------------------------------------------------------------------- */
69 
70 /* 64 KByte. */
71 #define CHUNK_SIZE	0x10000
72 
73 /*
74    alloc_chunk / free_chunk :
75      * allocate executable system memory chunks
76      * the size is always divisible by CHUNK_SIZE
77    allocator_grab_lock / allocator_release_lock :
78      * make the allocator thread safe
79      * can be empty if the OS (or the application) does not support threading
80      * only the allocator requires this lock, sljit is fully thread safe
81        as it only uses local variables
82 */
83 
84 #ifdef _WIN32
85 
86 static SLJIT_INLINE void* alloc_chunk(sljit_uw size)
87 {
88 	return VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
89 }
90 
91 static SLJIT_INLINE void free_chunk(void* chunk, sljit_uw size)
92 {
93 	SLJIT_UNUSED_ARG(size);
94 	VirtualFree(chunk, 0, MEM_RELEASE);
95 }
96 
97 #else
98 
99 #ifdef _KERNEL
100 #include <sys/param.h>
101 #include <sys/module.h> /* for module_map */
102 #include <uvm/uvm.h>
103 #else
104 #include <sys/mman.h>
105 #endif
106 
107 static SLJIT_INLINE void* alloc_chunk(sljit_uw size)
108 {
109 #ifdef _KERNEL
110 	return (void *)uvm_km_alloc(module_map, size,
111 	    PAGE_SIZE, UVM_KMF_WIRED | UVM_KMF_ZERO | UVM_KMF_EXEC);
112 #else
113 	void* retval = mmap(NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON, -1, 0);
114 	return (retval != MAP_FAILED) ? retval : NULL;
115 #endif
116 }
117 
118 static SLJIT_INLINE void free_chunk(void* chunk, sljit_uw size)
119 {
120 #ifdef _KERNEL
121 	uvm_km_free(module_map, (vaddr_t)chunk, size, UVM_KMF_WIRED);
122 #else
123 	munmap(chunk, size);
124 #endif
125 }
126 
127 #endif
128 
129 /* --------------------------------------------------------------------- */
130 /*  Common functions                                                     */
131 /* --------------------------------------------------------------------- */
132 
133 #define CHUNK_MASK	(~(CHUNK_SIZE - 1))
134 
135 struct block_header {
136 	sljit_uw size;
137 	sljit_uw prev_size;
138 };
139 
140 struct free_block {
141 	struct block_header header;
142 	struct free_block *next;
143 	struct free_block *prev;
144 	sljit_uw size;
145 };
146 
147 #define AS_BLOCK_HEADER(base, offset) \
148 	((struct block_header*)(((sljit_ub*)base) + offset))
149 #define AS_FREE_BLOCK(base, offset) \
150 	((struct free_block*)(((sljit_ub*)base) + offset))
151 #define MEM_START(base)		((void*)(((sljit_ub*)base) + sizeof(struct block_header)))
152 #define ALIGN_SIZE(size)	(((size) + sizeof(struct block_header) + 7) & ~7)
153 
154 static struct free_block* free_blocks;
155 static sljit_uw allocated_size;
156 static sljit_uw total_size;
157 
158 static SLJIT_INLINE void sljit_insert_free_block(struct free_block *free_block, sljit_uw size)
159 {
160 	free_block->header.size = 0;
161 	free_block->size = size;
162 
163 	free_block->next = free_blocks;
164 	free_block->prev = 0;
165 	if (free_blocks)
166 		free_blocks->prev = free_block;
167 	free_blocks = free_block;
168 }
169 
170 static SLJIT_INLINE void sljit_remove_free_block(struct free_block *free_block)
171 {
172 	if (free_block->next)
173 		free_block->next->prev = free_block->prev;
174 
175 	if (free_block->prev)
176 		free_block->prev->next = free_block->next;
177 	else {
178 		SLJIT_ASSERT(free_blocks == free_block);
179 		free_blocks = free_block->next;
180 	}
181 }
182 
183 SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size)
184 {
185 	struct block_header *header;
186 	struct block_header *next_header;
187 	struct free_block *free_block;
188 	sljit_uw chunk_size;
189 
190 	allocator_grab_lock();
191 	if (size < sizeof(struct free_block))
192 		size = sizeof(struct free_block);
193 	size = ALIGN_SIZE(size);
194 
195 	free_block = free_blocks;
196 	while (free_block) {
197 		if (free_block->size >= size) {
198 			chunk_size = free_block->size;
199 			if (chunk_size > size + 64) {
200 				/* We just cut a block from the end of the free block. */
201 				chunk_size -= size;
202 				free_block->size = chunk_size;
203 				header = AS_BLOCK_HEADER(free_block, chunk_size);
204 				header->prev_size = chunk_size;
205 				AS_BLOCK_HEADER(header, size)->prev_size = size;
206 			}
207 			else {
208 				sljit_remove_free_block(free_block);
209 				header = (struct block_header*)free_block;
210 				size = chunk_size;
211 			}
212 			allocated_size += size;
213 			header->size = size;
214 			allocator_release_lock();
215 			return MEM_START(header);
216 		}
217 		free_block = free_block->next;
218 	}
219 
220 	chunk_size = (size + sizeof(struct block_header) + CHUNK_SIZE - 1) & CHUNK_MASK;
221 	header = (struct block_header*)alloc_chunk(chunk_size);
222 	if (!header) {
223 		allocator_release_lock();
224 		return NULL;
225 	}
226 
227 	chunk_size -= sizeof(struct block_header);
228 	total_size += chunk_size;
229 
230 	header->prev_size = 0;
231 	if (chunk_size > size + 64) {
232 		/* Cut the allocated space into a free and a used block. */
233 		allocated_size += size;
234 		header->size = size;
235 		chunk_size -= size;
236 
237 		free_block = AS_FREE_BLOCK(header, size);
238 		free_block->header.prev_size = size;
239 		sljit_insert_free_block(free_block, chunk_size);
240 		next_header = AS_BLOCK_HEADER(free_block, chunk_size);
241 	}
242 	else {
243 		/* All space belongs to this allocation. */
244 		allocated_size += chunk_size;
245 		header->size = chunk_size;
246 		next_header = AS_BLOCK_HEADER(header, chunk_size);
247 	}
248 	next_header->size = 1;
249 	next_header->prev_size = chunk_size;
250 	allocator_release_lock();
251 	return MEM_START(header);
252 }
253 
254 SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr)
255 {
256 	struct block_header *header;
257 	struct free_block* free_block;
258 
259 	allocator_grab_lock();
260 	header = AS_BLOCK_HEADER(ptr, -(sljit_sw)sizeof(struct block_header));
261 	allocated_size -= header->size;
262 
263 	/* Connecting free blocks together if possible. */
264 
265 	/* If header->prev_size == 0, free_block will equal to header.
266 	   In this case, free_block->header.size will be > 0. */
267 	free_block = AS_FREE_BLOCK(header, -(sljit_sw)header->prev_size);
268 	if (SLJIT_UNLIKELY(!free_block->header.size)) {
269 		free_block->size += header->size;
270 		header = AS_BLOCK_HEADER(free_block, free_block->size);
271 		header->prev_size = free_block->size;
272 	}
273 	else {
274 		free_block = (struct free_block*)header;
275 		sljit_insert_free_block(free_block, header->size);
276 	}
277 
278 	header = AS_BLOCK_HEADER(free_block, free_block->size);
279 	if (SLJIT_UNLIKELY(!header->size)) {
280 		free_block->size += ((struct free_block*)header)->size;
281 		sljit_remove_free_block((struct free_block*)header);
282 		header = AS_BLOCK_HEADER(free_block, free_block->size);
283 		header->prev_size = free_block->size;
284 	}
285 
286 	/* The whole chunk is free. */
287 	if (SLJIT_UNLIKELY(!free_block->header.prev_size && header->size == 1)) {
288 		/* If this block is freed, we still have (allocated_size / 2) free space. */
289 		if (total_size - free_block->size > (allocated_size * 3 / 2)) {
290 			total_size -= free_block->size;
291 			sljit_remove_free_block(free_block);
292 			free_chunk(free_block, free_block->size + sizeof(struct block_header));
293 		}
294 	}
295 
296 	allocator_release_lock();
297 }
298 
299 SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void)
300 {
301 	struct free_block* free_block;
302 	struct free_block* next_free_block;
303 
304 	allocator_grab_lock();
305 
306 	free_block = free_blocks;
307 	while (free_block) {
308 		next_free_block = free_block->next;
309 		if (!free_block->header.prev_size &&
310 				AS_BLOCK_HEADER(free_block, free_block->size)->size == 1) {
311 			total_size -= free_block->size;
312 			sljit_remove_free_block(free_block);
313 			free_chunk(free_block, free_block->size + sizeof(struct block_header));
314 		}
315 		free_block = next_free_block;
316 	}
317 
318 	SLJIT_ASSERT((total_size && free_blocks) || (!total_size && !free_blocks));
319 	allocator_release_lock();
320 }
321