Blame sljit/sljitExecAllocator.c

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