Blame src/sljit/sljitExecAllocator.c

Packit Service 02e2fd
/*
Packit Service 02e2fd
 *    Stack-less Just-In-Time compiler
Packit Service 02e2fd
 *
Packit Service 02e2fd
 *    Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
Packit Service 02e2fd
 *
Packit Service 02e2fd
 * Redistribution and use in source and binary forms, with or without modification, are
Packit Service 02e2fd
 * permitted provided that the following conditions are met:
Packit Service 02e2fd
 *
Packit Service 02e2fd
 *   1. Redistributions of source code must retain the above copyright notice, this list of
Packit Service 02e2fd
 *      conditions and the following disclaimer.
Packit Service 02e2fd
 *
Packit Service 02e2fd
 *   2. Redistributions in binary form must reproduce the above copyright notice, this list
Packit Service 02e2fd
 *      of conditions and the following disclaimer in the documentation and/or other materials
Packit Service 02e2fd
 *      provided with the distribution.
Packit Service 02e2fd
 *
Packit Service 02e2fd
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
Packit Service 02e2fd
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
Packit Service 02e2fd
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
Packit Service 02e2fd
 * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
Packit Service 02e2fd
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
Packit Service 02e2fd
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
Packit Service 02e2fd
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
Packit Service 02e2fd
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
Packit Service 02e2fd
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Packit Service 02e2fd
 */
Packit Service 02e2fd
Packit Service 02e2fd
/*
Packit Service 02e2fd
   This file contains a simple executable memory allocator
Packit Service 02e2fd
Packit Service 02e2fd
   It is assumed, that executable code blocks are usually medium (or sometimes
Packit Service 02e2fd
   large) memory blocks, and the allocator is not too frequently called (less
Packit Service 02e2fd
   optimized than other allocators). Thus, using it as a generic allocator is
Packit Service 02e2fd
   not suggested.
Packit Service 02e2fd
Packit Service 02e2fd
   How does it work:
Packit Service 02e2fd
     Memory is allocated in continuous memory areas called chunks by alloc_chunk()
Packit Service 02e2fd
     Chunk format:
Packit Service 02e2fd
     [ block ][ block ] ... [ block ][ block terminator ]
Packit Service 02e2fd
Packit Service 02e2fd
   All blocks and the block terminator is started with block_header. The block
Packit Service 02e2fd
   header contains the size of the previous and the next block. These sizes
Packit Service 02e2fd
   can also contain special values.
Packit Service 02e2fd
     Block size:
Packit Service 02e2fd
       0 - The block is a free_block, with a different size member.
Packit Service 02e2fd
       1 - The block is a block terminator.
Packit Service 02e2fd
       n - The block is used at the moment, and the value contains its size.
Packit Service 02e2fd
     Previous block size:
Packit Service 02e2fd
       0 - This is the first block of the memory chunk.
Packit Service 02e2fd
       n - The size of the previous block.
Packit Service 02e2fd
Packit Service 02e2fd
   Using these size values we can go forward or backward on the block chain.
Packit Service 02e2fd
   The unused blocks are stored in a chain list pointed by free_blocks. This
Packit Service 02e2fd
   list is useful if we need to find a suitable memory area when the allocator
Packit Service 02e2fd
   is called.
Packit Service 02e2fd
Packit Service 02e2fd
   When a block is freed, the new free block is connected to its adjacent free
Packit Service 02e2fd
   blocks if possible.
Packit Service 02e2fd
Packit Service 02e2fd
     [ free block ][ used block ][ free block ]
Packit Service 02e2fd
   and "used block" is freed, the three blocks are connected together:
Packit Service 02e2fd
     [           one big free block           ]
Packit Service 02e2fd
*/
Packit Service 02e2fd
Packit Service 02e2fd
/* --------------------------------------------------------------------- */
Packit Service 02e2fd
/*  System (OS) functions                                                */
Packit Service 02e2fd
/* --------------------------------------------------------------------- */
Packit Service 02e2fd
Packit Service 02e2fd
/* 64 KByte. */
Packit Service 02e2fd
#define CHUNK_SIZE	0x10000
Packit Service 02e2fd
Packit Service 02e2fd
/*
Packit Service 02e2fd
   alloc_chunk / free_chunk :
Packit Service 02e2fd
     * allocate executable system memory chunks
Packit Service 02e2fd
     * the size is always divisible by CHUNK_SIZE
Packit Service 02e2fd
   allocator_grab_lock / allocator_release_lock :
Packit Service 02e2fd
     * make the allocator thread safe
Packit Service 02e2fd
     * can be empty if the OS (or the application) does not support threading
Packit Service 02e2fd
     * only the allocator requires this lock, sljit is fully thread safe
Packit Service 02e2fd
       as it only uses local variables
Packit Service 02e2fd
*/
Packit Service 02e2fd
Packit Service 02e2fd
#ifdef _WIN32
Packit Service 02e2fd
Packit Service 02e2fd
static SLJIT_INLINE void* alloc_chunk(sljit_uw size)
Packit Service 02e2fd
{
Packit Service 02e2fd
	return VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
Packit Service 02e2fd
}
Packit Service 02e2fd
Packit Service 02e2fd
static SLJIT_INLINE void free_chunk(void *chunk, sljit_uw size)
Packit Service 02e2fd
{
Packit Service 02e2fd
	SLJIT_UNUSED_ARG(size);
Packit Service 02e2fd
	VirtualFree(chunk, 0, MEM_RELEASE);
Packit Service 02e2fd
}
Packit Service 02e2fd
Packit Service 02e2fd
#else
Packit Service 02e2fd
Packit Service 02e2fd
static SLJIT_INLINE void* alloc_chunk(sljit_uw size)
Packit Service 02e2fd
{
Packit Service 02e2fd
	void *retval;
Packit Service 02e2fd
Packit Service 02e2fd
#ifdef MAP_ANON
Packit Service 02e2fd
Packit Service 02e2fd
	int flags = MAP_PRIVATE | MAP_ANON;
Packit Service 02e2fd
Packit Service 02e2fd
#ifdef MAP_JIT
Packit Service 02e2fd
	flags |= MAP_JIT;
Packit Service 02e2fd
#endif
Packit Service 02e2fd
Packit Service 02e2fd
	retval = mmap(NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC, flags, -1, 0);
Packit Service 02e2fd
#else
Packit Service 02e2fd
	if (dev_zero < 0) {
Packit Service 02e2fd
		if (open_dev_zero())
Packit Service 02e2fd
			return NULL;
Packit Service 02e2fd
	}
Packit Service 02e2fd
	retval = mmap(NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, dev_zero, 0);
Packit Service 02e2fd
#endif
Packit Service 02e2fd
Packit Service 02e2fd
	return (retval != MAP_FAILED) ? retval : NULL;
Packit Service 02e2fd
}
Packit Service 02e2fd
Packit Service 02e2fd
static SLJIT_INLINE void free_chunk(void *chunk, sljit_uw size)
Packit Service 02e2fd
{
Packit Service 02e2fd
	munmap(chunk, size);
Packit Service 02e2fd
}
Packit Service 02e2fd
Packit Service 02e2fd
#endif
Packit Service 02e2fd
Packit Service 02e2fd
/* --------------------------------------------------------------------- */
Packit Service 02e2fd
/*  Common functions                                                     */
Packit Service 02e2fd
/* --------------------------------------------------------------------- */
Packit Service 02e2fd
Packit Service 02e2fd
#define CHUNK_MASK	(~(CHUNK_SIZE - 1))
Packit Service 02e2fd
Packit Service 02e2fd
struct block_header {
Packit Service 02e2fd
	sljit_uw size;
Packit Service 02e2fd
	sljit_uw prev_size;
Packit Service 02e2fd
};
Packit Service 02e2fd
Packit Service 02e2fd
struct free_block {
Packit Service 02e2fd
	struct block_header header;
Packit Service 02e2fd
	struct free_block *next;
Packit Service 02e2fd
	struct free_block *prev;
Packit Service 02e2fd
	sljit_uw size;
Packit Service 02e2fd
};
Packit Service 02e2fd
Packit Service 02e2fd
#define AS_BLOCK_HEADER(base, offset) \
Packit Service 02e2fd
	((struct block_header*)(((sljit_u8*)base) + offset))
Packit Service 02e2fd
#define AS_FREE_BLOCK(base, offset) \
Packit Service 02e2fd
	((struct free_block*)(((sljit_u8*)base) + offset))
Packit Service 02e2fd
#define MEM_START(base)		((void*)(((sljit_u8*)base) + sizeof(struct block_header)))
Packit Service 02e2fd
#define ALIGN_SIZE(size)	(((size) + sizeof(struct block_header) + 7) & ~7)
Packit Service 02e2fd
Packit Service 02e2fd
static struct free_block* free_blocks;
Packit Service 02e2fd
static sljit_uw allocated_size;
Packit Service 02e2fd
static sljit_uw total_size;
Packit Service 02e2fd
Packit Service 02e2fd
static SLJIT_INLINE void sljit_insert_free_block(struct free_block *free_block, sljit_uw size)
Packit Service 02e2fd
{
Packit Service 02e2fd
	free_block->header.size = 0;
Packit Service 02e2fd
	free_block->size = size;
Packit Service 02e2fd
Packit Service 02e2fd
	free_block->next = free_blocks;
Packit Service 02e2fd
	free_block->prev = NULL;
Packit Service 02e2fd
	if (free_blocks)
Packit Service 02e2fd
		free_blocks->prev = free_block;
Packit Service 02e2fd
	free_blocks = free_block;
Packit Service 02e2fd
}
Packit Service 02e2fd
Packit Service 02e2fd
static SLJIT_INLINE void sljit_remove_free_block(struct free_block *free_block)
Packit Service 02e2fd
{
Packit Service 02e2fd
	if (free_block->next)
Packit Service 02e2fd
		free_block->next->prev = free_block->prev;
Packit Service 02e2fd
Packit Service 02e2fd
	if (free_block->prev)
Packit Service 02e2fd
		free_block->prev->next = free_block->next;
Packit Service 02e2fd
	else {
Packit Service 02e2fd
		SLJIT_ASSERT(free_blocks == free_block);
Packit Service 02e2fd
		free_blocks = free_block->next;
Packit Service 02e2fd
	}
Packit Service 02e2fd
}
Packit Service 02e2fd
Packit Service 02e2fd
SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size)
Packit Service 02e2fd
{
Packit Service 02e2fd
	struct block_header *header;
Packit Service 02e2fd
	struct block_header *next_header;
Packit Service 02e2fd
	struct free_block *free_block;
Packit Service 02e2fd
	sljit_uw chunk_size;
Packit Service 02e2fd
Packit Service 02e2fd
	allocator_grab_lock();
Packit Service 02e2fd
	if (size < (64 - sizeof(struct block_header)))
Packit Service 02e2fd
		size = (64 - sizeof(struct block_header));
Packit Service 02e2fd
	size = ALIGN_SIZE(size);
Packit Service 02e2fd
Packit Service 02e2fd
	free_block = free_blocks;
Packit Service 02e2fd
	while (free_block) {
Packit Service 02e2fd
		if (free_block->size >= size) {
Packit Service 02e2fd
			chunk_size = free_block->size;
Packit Service 02e2fd
			if (chunk_size > size + 64) {
Packit Service 02e2fd
				/* We just cut a block from the end of the free block. */
Packit Service 02e2fd
				chunk_size -= size;
Packit Service 02e2fd
				free_block->size = chunk_size;
Packit Service 02e2fd
				header = AS_BLOCK_HEADER(free_block, chunk_size);
Packit Service 02e2fd
				header->prev_size = chunk_size;
Packit Service 02e2fd
				AS_BLOCK_HEADER(header, size)->prev_size = size;
Packit Service 02e2fd
			}
Packit Service 02e2fd
			else {
Packit Service 02e2fd
				sljit_remove_free_block(free_block);
Packit Service 02e2fd
				header = (struct block_header*)free_block;
Packit Service 02e2fd
				size = chunk_size;
Packit Service 02e2fd
			}
Packit Service 02e2fd
			allocated_size += size;
Packit Service 02e2fd
			header->size = size;
Packit Service 02e2fd
			allocator_release_lock();
Packit Service 02e2fd
			return MEM_START(header);
Packit Service 02e2fd
		}
Packit Service 02e2fd
		free_block = free_block->next;
Packit Service 02e2fd
	}
Packit Service 02e2fd
Packit Service 02e2fd
	chunk_size = (size + sizeof(struct block_header) + CHUNK_SIZE - 1) & CHUNK_MASK;
Packit Service 02e2fd
	header = (struct block_header*)alloc_chunk(chunk_size);
Packit Service 02e2fd
	if (!header) {
Packit Service 02e2fd
		allocator_release_lock();
Packit Service 02e2fd
		return NULL;
Packit Service 02e2fd
	}
Packit Service 02e2fd
Packit Service 02e2fd
	chunk_size -= sizeof(struct block_header);
Packit Service 02e2fd
	total_size += chunk_size;
Packit Service 02e2fd
Packit Service 02e2fd
	header->prev_size = 0;
Packit Service 02e2fd
	if (chunk_size > size + 64) {
Packit Service 02e2fd
		/* Cut the allocated space into a free and a used block. */
Packit Service 02e2fd
		allocated_size += size;
Packit Service 02e2fd
		header->size = size;
Packit Service 02e2fd
		chunk_size -= size;
Packit Service 02e2fd
Packit Service 02e2fd
		free_block = AS_FREE_BLOCK(header, size);
Packit Service 02e2fd
		free_block->header.prev_size = size;
Packit Service 02e2fd
		sljit_insert_free_block(free_block, chunk_size);
Packit Service 02e2fd
		next_header = AS_BLOCK_HEADER(free_block, chunk_size);
Packit Service 02e2fd
	}
Packit Service 02e2fd
	else {
Packit Service 02e2fd
		/* All space belongs to this allocation. */
Packit Service 02e2fd
		allocated_size += chunk_size;
Packit Service 02e2fd
		header->size = chunk_size;
Packit Service 02e2fd
		next_header = AS_BLOCK_HEADER(header, chunk_size);
Packit Service 02e2fd
	}
Packit Service 02e2fd
	next_header->size = 1;
Packit Service 02e2fd
	next_header->prev_size = chunk_size;
Packit Service 02e2fd
	allocator_release_lock();
Packit Service 02e2fd
	return MEM_START(header);
Packit Service 02e2fd
}
Packit Service 02e2fd
Packit Service 02e2fd
SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr)
Packit Service 02e2fd
{
Packit Service 02e2fd
	struct block_header *header;
Packit Service 02e2fd
	struct free_block* free_block;
Packit Service 02e2fd
Packit Service 02e2fd
	allocator_grab_lock();
Packit Service 02e2fd
	header = AS_BLOCK_HEADER(ptr, -(sljit_sw)sizeof(struct block_header));
Packit Service 02e2fd
	allocated_size -= header->size;
Packit Service 02e2fd
Packit Service 02e2fd
	/* Connecting free blocks together if possible. */
Packit Service 02e2fd
Packit Service 02e2fd
	/* If header->prev_size == 0, free_block will equal to header.
Packit Service 02e2fd
	   In this case, free_block->header.size will be > 0. */
Packit Service 02e2fd
	free_block = AS_FREE_BLOCK(header, -(sljit_sw)header->prev_size);
Packit Service 02e2fd
	if (SLJIT_UNLIKELY(!free_block->header.size)) {
Packit Service 02e2fd
		free_block->size += header->size;
Packit Service 02e2fd
		header = AS_BLOCK_HEADER(free_block, free_block->size);
Packit Service 02e2fd
		header->prev_size = free_block->size;
Packit Service 02e2fd
	}
Packit Service 02e2fd
	else {
Packit Service 02e2fd
		free_block = (struct free_block*)header;
Packit Service 02e2fd
		sljit_insert_free_block(free_block, header->size);
Packit Service 02e2fd
	}
Packit Service 02e2fd
Packit Service 02e2fd
	header = AS_BLOCK_HEADER(free_block, free_block->size);
Packit Service 02e2fd
	if (SLJIT_UNLIKELY(!header->size)) {
Packit Service 02e2fd
		free_block->size += ((struct free_block*)header)->size;
Packit Service 02e2fd
		sljit_remove_free_block((struct free_block*)header);
Packit Service 02e2fd
		header = AS_BLOCK_HEADER(free_block, free_block->size);
Packit Service 02e2fd
		header->prev_size = free_block->size;
Packit Service 02e2fd
	}
Packit Service 02e2fd
Packit Service 02e2fd
	/* The whole chunk is free. */
Packit Service 02e2fd
	if (SLJIT_UNLIKELY(!free_block->header.prev_size && header->size == 1)) {
Packit Service 02e2fd
		/* If this block is freed, we still have (allocated_size / 2) free space. */
Packit Service 02e2fd
		if (total_size - free_block->size > (allocated_size * 3 / 2)) {
Packit Service 02e2fd
			total_size -= free_block->size;
Packit Service 02e2fd
			sljit_remove_free_block(free_block);
Packit Service 02e2fd
			free_chunk(free_block, free_block->size + sizeof(struct block_header));
Packit Service 02e2fd
		}
Packit Service 02e2fd
	}
Packit Service 02e2fd
Packit Service 02e2fd
	allocator_release_lock();
Packit Service 02e2fd
}
Packit Service 02e2fd
Packit Service 02e2fd
SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void)
Packit Service 02e2fd
{
Packit Service 02e2fd
	struct free_block* free_block;
Packit Service 02e2fd
	struct free_block* next_free_block;
Packit Service 02e2fd
Packit Service 02e2fd
	allocator_grab_lock();
Packit Service 02e2fd
Packit Service 02e2fd
	free_block = free_blocks;
Packit Service 02e2fd
	while (free_block) {
Packit Service 02e2fd
		next_free_block = free_block->next;
Packit Service 02e2fd
		if (!free_block->header.prev_size && 
Packit Service 02e2fd
				AS_BLOCK_HEADER(free_block, free_block->size)->size == 1) {
Packit Service 02e2fd
			total_size -= free_block->size;
Packit Service 02e2fd
			sljit_remove_free_block(free_block);
Packit Service 02e2fd
			free_chunk(free_block, free_block->size + sizeof(struct block_header));
Packit Service 02e2fd
		}
Packit Service 02e2fd
		free_block = next_free_block;
Packit Service 02e2fd
	}
Packit Service 02e2fd
Packit Service 02e2fd
	SLJIT_ASSERT((total_size && free_blocks) || (!total_size && !free_blocks));
Packit Service 02e2fd
	allocator_release_lock();
Packit Service 02e2fd
}