From 59c35cc20dc08198b339df4f8b5ea03d2466d181 Mon Sep 17 00:00:00 2001 From: Bogdan Marinescu Date: Wed, 18 Feb 2009 22:13:48 +0000 Subject: [PATCH] Added a very simple allocator to eLua. It's probably the most basic version of a chained blocks allocator. It's slow and it won't handle fragmentation nearly as well as dlmalloc(), but it's much smaller and it doesn't need the extra book-keeping space needed by dlmalloc (about 1KB for each memory space). Recommended only for systems with very low memory (Flash/RAM), and prefferably systems running only precompiled Lua (if you need to compile the code, you might get into stack overflows, and this allocator is much more sensitive to this kind of stuff than dlmalloc()). In fact, this allocator seems to suggest that one should set the stack to at least 4k for the Lua parser to run properly even on small programs. I won't do this just yet, rather I'll keep on trying to move the Lua parser data structures from stack to heap. For now we're OK with the current configuration. The allocator can handle multiple memory spaces. Enable with "allocator=simple" on the scons command line. ...oh yes, also added a newline to the end of elua_adc.c :) (to avoid some annoying warnings) --- SConstruct | 8 +- inc/salloc.h | 14 +++ src/common.c | 23 +++- src/elua_adc.c | 3 +- src/newlib/stubs.c | 22 ++-- src/salloc.c | 282 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 335 insertions(+), 17 deletions(-) create mode 100644 inc/salloc.h create mode 100644 src/salloc.c diff --git a/SConstruct b/SConstruct index cd57525e..0851a829 100644 --- a/SConstruct +++ b/SConstruct @@ -112,9 +112,9 @@ if allocator == '': allocator = 'multiple' else: allocator = 'newlib' -elif allocator not in [ 'newlib', 'multiple' ]: +elif allocator not in [ 'newlib', 'multiple', 'simple' ]: print "Unknown allocator", allocator - print "Allocator can be either 'newlib' or 'multiple'" + print "Allocator can be either 'newlib', 'multiple' or 'simple'" sys.exit( -1 ) @@ -135,6 +135,8 @@ output = 'elua_' + target + '_' + cputype.lower() cdefs = '-DELUA_CPU=%s -DELUA_BOARD=%s -DELUA_PLATFORM=%s -D__BUFSIZ__=128' % ( cputype, boardname, platform.upper() ) if allocator == 'multiple': cdefs = cdefs + " -DUSE_MULTIPLE_ALLOCATOR" +elif allocator == 'simple': + cdefs = cdefs + " -DUSE_SIMPLE_ALLOCATOR" # Lua source files and include path lua_files = """lapi.c lcode.c ldebug.c ldo.c ldump.c lfunc.c lgc.c llex.c lmem.c lobject.c lopcodes.c @@ -155,7 +157,7 @@ cdefs = cdefs + " -DLUA_OPTIMIZE_MEMORY=%d" % ( optram != 0 and 2 or 0 ) local_libs = '' # Application files -app_files = " src/main.c src/romfs.c src/xmodem.c src/shell.c src/term.c src/common.c src/buf.c src/elua_adc.c src/dlmalloc.c " +app_files = " src/main.c src/romfs.c src/xmodem.c src/shell.c src/term.c src/common.c src/buf.c src/elua_adc.c src/dlmalloc.c src/salloc.c " # Newlib related files newlib_files = " src/newlib/devman.c src/newlib/stubs.c src/newlib/genstd.c src/newlib/stdtcp.c" diff --git a/inc/salloc.h b/inc/salloc.h new file mode 100644 index 00000000..3ea7da3e --- /dev/null +++ b/inc/salloc.h @@ -0,0 +1,14 @@ +// A very simple, quite inneficient, yet very small memory allocator + +#ifndef __SALLOC_H__ +#define __SALLOC_H__ + +#include + +void* smalloc( size_t size ); +void sfree( void* ptr ); +void* scalloc( size_t nmemb, size_t size ); +void* srealloc( void* ptr, size_t size ); + +#endif // #ifndef __SALLOC_H__ + diff --git a/src/common.c b/src/common.c index a1f36037..92a08dcb 100644 --- a/src/common.c +++ b/src/common.c @@ -305,20 +305,35 @@ u32 platform_adc_op( unsigned id, int op, u32 data ) // **************************************************************************** // Allocator support +#define MIN_ALIGN 8 +#define MIN_ALIGN_SHIFT 3 + extern char end[]; void* platform_get_first_free_ram( unsigned id ) { void* mstart[] = MEM_START_ADDRESS; - - return id >= sizeof( mstart ) / sizeof( void* ) ? NULL : mstart[ id ]; + u32 p; + + if( id >= sizeof( mstart ) / sizeof( void* ) ) + return NULL; + p = ( u32 )mstart[ id ]; + if( p & ( MIN_ALIGN - 1 ) ) + p = ( ( p >> MIN_ALIGN_SHIFT ) + 1 ) << MIN_ALIGN_SHIFT; + return ( void* )p; } void* platform_get_last_free_ram( unsigned id ) { void* mend[] = MEM_END_ADDRESS; - - return id >= sizeof( mend ) / sizeof( void* ) ? NULL : mend[ id ]; + u32 p; + + if( id >= sizeof( mend ) / sizeof( void* ) ) + return NULL; + p = ( u32 )mend[ id ]; + if( p & ( MIN_ALIGN - 1 ) ) + p = ( ( p >> MIN_ALIGN_SHIFT ) - 1 ) << MIN_ALIGN_SHIFT; + return ( void* )p; } diff --git a/src/elua_adc.c b/src/elua_adc.c index d15ab01f..b87e750c 100644 --- a/src/elua_adc.c +++ b/src/elua_adc.c @@ -156,4 +156,5 @@ void adc_wait_pending( unsigned id ) } -#endif \ No newline at end of file +#endif + diff --git a/src/newlib/stubs.c b/src/newlib/stubs.c index 900bf165..8d2f1cbb 100644 --- a/src/newlib/stubs.c +++ b/src/newlib/stubs.c @@ -12,6 +12,7 @@ #include "platform_conf.h" #include "genstd.h" #include "utils.h" +#include "salloc.h" #ifdef USE_MULTIPLE_ALLOCATOR #include "dlmalloc.h" @@ -344,32 +345,35 @@ struct mallinfo mallinfo() #endif } +#if defined( USE_MULTIPLE_ALLOCATOR ) || defined( USE_SIMPLE_ALLOCATOR ) +// Redirect all allocator calls to our dlmalloc/salloc + #ifdef USE_MULTIPLE_ALLOCATOR -// Redirect all allocator calls to our dlmalloc +#define CNAME( func ) dl##func +#else +#define CNAME( func ) s##func +#endif + void* _malloc_r( struct _reent* r, size_t size ) { - return dlmalloc( size ); + return CNAME( malloc )( size ); } void* _calloc_r( struct _reent* r, size_t nelem, size_t elem_size ) { - return dlcalloc( nelem, elem_size ); + return CNAME( calloc )( nelem, elem_size ); } void _free_r( struct _reent* r, void* ptr ) { - dlfree( ptr ); + CNAME( free )( ptr ); } void* _realloc_r( struct _reent* r, void* ptr, size_t size ) { - return dlrealloc( ptr, size ); + return CNAME( realloc )( ptr, size ); } -void* _memalign_r( struct _reent* r, size_t align, size_t nbytes ) -{ - return dlmemalign( align, nbytes ); -} #endif // #ifdef USE_MULTIPLE_ALLOCATOR // ***************************************************************************** diff --git a/src/salloc.c b/src/salloc.c new file mode 100644 index 00000000..c0091dd6 --- /dev/null +++ b/src/salloc.c @@ -0,0 +1,282 @@ +// A very simple, quite inneficient, yet very small memory allocator +// It can do both fixed block and variable block allocation + +#ifdef USE_SIMPLE_ALLOCATOR + +#include +#include +#include "platform.h" +#include "platform_conf.h" +#include "type.h" + +// Macros for the dynamic size allocator +// Dynamic structure: pointer to next, pointer to prev +// First bit of pointer is 0 if block free, 1 if block taken +// Pointer must be multiplied by DYN_MIN_SIZE to get actual address +// There are two 'guards' (at the beginning and at the end) +#define DYN_SIZE_MULT 8 +#define DYN_SIZE_MULT_SHIFT 3 +#define DYN_HEADER_SIZE 8 +#define DYN_MIN_SPLIT_SIZE 16 + +static u8 s_initialized; + +// **************************************************************************** +// Utility functions for the dynamic memory allocator + +// Get actual size +static size_t s_act_size( size_t size ) +{ + if( size & ( DYN_SIZE_MULT - 1 ) ) + size = ( ( size >> DYN_SIZE_MULT_SHIFT ) + 1 ) << DYN_SIZE_MULT_SHIFT; + return size; +} + +// Get next block +static char* s_get_next_block( char* ptr ) +{ + return ( char* )( ( *( u32* )ptr & 0x7FFFFFFF ) << DYN_SIZE_MULT_SHIFT ); +} + +// Set next block +static void s_set_next_block( char* ptr, char* next ) +{ + u32 *temp = ( u32* )ptr; + + *temp = ( *temp & 0x80000000 ) | ( ( u32 )next >> DYN_SIZE_MULT_SHIFT ); +} + +// Get prev block +static char* s_get_prev_block( char* ptr ) +{ + return ( char* )( ( *( ( u32* )ptr + 1 ) & 0x7FFFFFFF ) << DYN_SIZE_MULT_SHIFT ); +} + +// Set prev block +static void s_set_prev_block( char* ptr, char* prev ) +{ + u32 *temp = ( u32* )ptr + 1; + + *temp = ( *temp & 0x80000000 ) | ( ( u32 )prev >> DYN_SIZE_MULT_SHIFT ); +} + +// Get block size +static size_t s_get_block_size( char* ptr ) +{ + char* next = s_get_next_block( ptr ); + + return next != NULL ? ( size_t )( next - ptr ) : 0; +} + +// Mark block as taken +static void s_mark_block_taken( char* where ) +{ + *( u32* )where |= 0x80000000; +} + +// Mark block as free +static void s_mark_block_free( char* where ) +{ + *( u32* )where &= 0x7FFFFFFF; +} + +// Is the block free? +static int s_is_block_free( char* where ) +{ + return ( *( u32* )where & 0x80000000 ) == 0; +} + +// Create a new block with the given neighbours +static void s_create_new_block( char* where, char* next, char* prev ) +{ + u32* temp = ( u32* )where; + + *temp ++ = ( u32 )next >> DYN_SIZE_MULT_SHIFT; + *temp = ( u32 )prev >> DYN_SIZE_MULT_SHIFT; +} + +// Tries to compact free blocks +static void s_compact_free( char* ptr ) +{ + char *temp1, *temp2; + + s_mark_block_free( ptr ); + // Look for free blocks before and after, concatenate if possible + temp1 = temp2 = ptr; + while( s_is_block_free( temp1 ) ) + temp1 = s_get_prev_block( temp1 ); + temp1 = s_get_next_block( temp1 ); + while( s_is_block_free( temp2 ) ) + temp2 = s_get_next_block( temp2 ); + if( temp1 != ptr || s_get_prev_block( temp2 ) != ptr ) + { + s_set_next_block( temp1, temp2 ); + s_set_prev_block( temp2, temp1 ); + } +} + +// Utility function: find a free block in the dynamic memory part +// Returns pointer to block for success, NULL for error +static void* s_get_free_block( size_t size, void* pstart ) +{ + char *temp, *pblock = NULL, *next; + size_t minsize = ( size_t )~0, bsize; + + if( !size ) + return NULL; + size = s_act_size( size + DYN_HEADER_SIZE ); + temp = s_get_next_block( pstart ); + // Best-fit only for now + while( temp ) + { + if( s_is_block_free( temp ) ) + { + bsize = s_get_block_size( temp ); + if( ( size <= bsize ) && ( bsize < minsize ) ) + { + minsize = bsize; + pblock = temp; + } + } + temp = s_get_next_block( temp ); + } + if( pblock == NULL ) + return NULL; + s_mark_block_taken( pblock ); + if( minsize > size && ( minsize - size ) >= DYN_MIN_SPLIT_SIZE ) + { + temp = pblock + size; + next = s_get_next_block( pblock ); + s_set_prev_block( temp, pblock ); + s_set_next_block( temp, next ); + s_set_prev_block( next, temp ); + s_set_next_block( pblock, temp ); + s_compact_free( temp ); + } + return pblock + DYN_HEADER_SIZE; +} + +// Utility function: free a memory block +static void s_free_block( char* ptr ) +{ + ptr -= DYN_HEADER_SIZE; + s_compact_free( ptr ); +} + +// Get 'real' block size +static size_t s_get_actual_block_size( char* ptr ) +{ + return s_get_block_size( ptr - DYN_HEADER_SIZE ) - DYN_HEADER_SIZE; +} + +// Shrinks the given block to its new size +static void s_shrink_block( char* pblock, size_t size ) +{ + char *temp, *next; + + pblock -= DYN_HEADER_SIZE; + size = s_act_size( size + DYN_HEADER_SIZE ); + if( size >= s_get_block_size( pblock ) || ( s_get_block_size( pblock ) - size ) < DYN_MIN_SPLIT_SIZE ) + return; + temp = pblock + size; + next = s_get_next_block( pblock ); + s_set_prev_block( temp, pblock ); + s_set_next_block( temp, next ); + s_set_prev_block( next, temp ); + s_set_next_block( pblock, temp ); + s_compact_free( temp ); +} + +static void s_init() +{ + unsigned i = 0; + size_t memspace; + char *crt, *g1, *g2, *pstart; + + while( ( pstart = platform_get_first_free_ram( i ) ) != NULL ) + { + memset( pstart, 0, memspace = ( u32 )platform_get_last_free_ram( i ) - ( u32 )pstart ); + g1 = ( char* )pstart; + crt = g1 + DYN_SIZE_MULT; + g2 = g1 + memspace - DYN_SIZE_MULT; + s_create_new_block( g1, crt, NULL ); + s_create_new_block( crt, g2, g1 ); + s_create_new_block( g2, NULL, crt ); + s_mark_block_taken( g1 ); + s_mark_block_taken( g2 ); + s_mark_block_free( crt ); + i ++; + } + s_initialized = 1; +} + +// **************************************************************************** + +void* smalloc( size_t size ) +{ + unsigned i = 0; + void *ptr = NULL, *pstart; + + if( !s_initialized ) + s_init(); + while( ( pstart = platform_get_first_free_ram( i ++ ) ) != NULL ) + if( ( ptr = s_get_free_block( size, pstart ) ) != NULL ) + break; + return ptr; +} + +void sfree( void* ptr ) +{ + if( !ptr || !s_initialized ) + return; + s_free_block( ptr ); +} + +void* scalloc( size_t nmemb, size_t size ) +{ + void* ptr; + + if( !s_initialized ) + s_init(); + if( ( ptr = smalloc( nmemb * size ) ) != NULL ) + memset( ptr, 0, nmemb * size ); + return ptr; +} + +void* srealloc( void* ptr, size_t size ) +{ + void* newptr = NULL; + + if( !s_initialized ) + s_init(); + // Special cases: + // realloc with ptr == NULL -> malloc + // realloc with size == 0 -> free + if( ptr == NULL ) + return smalloc( size ); + else if( size == 0 ) + { + sfree( ptr ); + return NULL; + } + + // Test new size versus the old size + if( s_get_actual_block_size( ptr ) == size ) + return ptr; + else if( size < s_get_actual_block_size( ptr ) ) + { + s_shrink_block( ptr, size ); + return ptr; + } + else + { + if( ( newptr = smalloc( size ) ) == NULL ) + return NULL; + memmove( newptr, ptr, s_get_actual_block_size( ptr ) ); + sfree( ptr ); + } + return newptr; +} + +#endif // #ifdef USE_SIMPLE_ALLOCATOR +