浏览代码

initial gc implementation

Nicolas Cannasse 9 年之前
父节点
当前提交
085b66b4da
共有 4 个文件被更改,包括 601 次插入19 次删除
  1. 567 13
      src/alloc.c
  2. 24 4
      src/hl.h
  3. 1 0
      src/hlc.h
  4. 9 2
      src/main.c

+ 567 - 13
src/alloc.c

@@ -22,12 +22,562 @@
 #include "hl.h"
 #ifdef HL_WIN
 #	include <windows.h>
+#	include <intrin.h>
+static unsigned int __inline TRAILING_ONES( unsigned int x ) {
+	DWORD msb = 0;
+	if( _BitScanForward( &msb, ~x ) )
+		return msb;
+	return 32;
+}
+static unsigned int __inline TRAILING_ZEROES( unsigned int x ) {
+	DWORD msb = 0;
+	if( _BitScanForward( &msb, x ) )
+		return msb;
+	return 32;
+}
 #else
 #	include <sys/types.h>
 #	include <sys/mman.h>
+#	define TRAILING_ONES(x)		(~(x)?__builtin_ctz(~(x)):0)
+#	define TRAILING_ZEROES(x)	(x?__builtin_ctz(x):32)
+#endif
+#	define MZERO(ptr,size)		memset(ptr,0,size)	
+
+// GC
+
+#define	GC_ALIGN_BITS	2
+#define	GC_ALIGN		(1 << GC_ALIGN_BITS)
+#define GC_ALL_PAGES	(GC_PARTITIONS << PAGE_KIND_BITS)
+
+#define GC_PAGE_BITS	16
+#define GC_PAGE_SIZE	(1 << GC_PAGE_BITS)
+
+#ifndef HL_64
+#	define GC_MASK_BITS			16
+#	define GC_LEVEL0_BITS		8
+#	define GC_LEVEL1_BITS		8
+#	define GC_GET_LEVEL1(ptr)	hl_gc_page_map[((unsigned int)(ptr))>>(GC_MASK_BITS+GC_LEVEL1_BITS)]
+#	define GC_GET_PAGE(ptr)		GC_GET_LEVEL1(ptr)[(((unsigned int)(ptr))>>GC_MASK_BITS)&GC_LEVEL1_MASK]
 #endif
+#define GC_LEVEL1_MASK		((1 << GC_LEVEL1_BITS) - 1)
+
+#define PAGE_KIND_BITS		2
+#define PAGE_KIND_MASK		((1 << PAGE_KIND_BITS) - 1)
+
+typedef struct _gc_pheader gc_pheader;
+
+struct _gc_pheader {
+	// const
+	int page_size;
+	int page_kind;
+	int block_size;
+	int max_blocks;
+	int first_block;
+	// mutable
+	int next_block;
+	int free_blocks;
+	unsigned char *sizes;
+	unsigned char *bmp;
+	gc_pheader *next_page;
+	gc_pheader *next_free;
+};
+
+#define GC_PARTITIONS	9
+#define GC_PART_BITS	4
+#define GC_FIXED_PARTS	5
+static const int GC_SBITS[GC_PARTITIONS] = {0,0,0,0,0,		3,6,14,22};
+static const int GC_SIZES[GC_PARTITIONS] = {4,8,12,16,20,	8,64,1<<14,1<<22};
+
+static gc_pheader *gc_pages[GC_ALL_PAGES] = {NULL};
+static gc_pheader *gc_free_pages[GC_ALL_PAGES] = {NULL};
+static gc_pheader *gc_level1_null[1<<GC_LEVEL1_BITS] = {NULL};
+HL_PRIM gc_pheader **hl_gc_page_map[1<<GC_LEVEL0_BITS] = {NULL};
+
+static struct { 
+	int64 total_requested;
+	int64 total_allocated;
+	int64 last_mark;
+	int64 pages_total_memory;
+	int64 debug;
+	int64 allocation_count;
+	int pages_count;
+	int pages_allocated;
+	int mark_bytes;
+} gc_stats = {0};
+
+// -------------------------  ROOTS ----------------------------------------------------------
+
+static void ***gc_roots = NULL;
+static int gc_roots_count = 0;
+static int gc_roots_max = 0;
+
+HL_PRIM void hl_add_root( void **r ) {
+	if( gc_roots_count == gc_roots_max ) {
+		int nroots = gc_roots_max ? (gc_roots_max << 1) : 16;
+		void ***roots = (void***)malloc(sizeof(void*)*nroots);
+		memcpy(roots,gc_roots,sizeof(void*)*gc_roots_count);
+		free(gc_roots);
+		gc_roots = roots;
+		gc_roots_max = nroots;
+	}
+	gc_roots[gc_roots_count++] = r;
+}
+
+HL_PRIM void hl_remove_root( void **v ) {
+	int i;
+	for(i=0;i<gc_roots_count;i++)
+		if( gc_roots[i] == v ) {
+			gc_roots_count--;
+			memcpy(gc_roots + i, gc_roots + (i+1), gc_roots_count - i);
+			break;
+		}
+}
+
+// -------------------------  ALLOCATOR ----------------------------------------------------------
+
+static void *gc_alloc_page_memory( int size );
+static void gc_free_page_memory( void *ptr, int size );
+
+static gc_pheader *gc_alloc_new_page( int block, int size, bool varsize ) {
+	int m, i;
+	unsigned char *base = (unsigned char*)gc_alloc_page_memory(size);
+	gc_pheader *p = (gc_pheader*)base;
+	int start_pos;
+#	ifdef _DEBUG
+	memset(base,0xDD,size);
+#	endif
+	p->page_size = size;
+	p->block_size = block;
+	p->max_blocks = size / block;
+	p->sizes = NULL;
+	p->bmp = NULL;
+	start_pos = sizeof(gc_pheader);
+	if( varsize ) {
+		p->sizes = base + start_pos;
+		start_pos += p->max_blocks;
+		MZERO(p->sizes,p->max_blocks);
+	}
+	m = start_pos % block;
+	if( m ) start_pos += block - m;
+	p->first_block = start_pos / block;
+	p->next_block = p->first_block;
+	p->free_blocks = p->max_blocks - p->first_block;
+	
+	// update stats
+	gc_stats.pages_count++;
+	gc_stats.pages_allocated++;
+	gc_stats.pages_total_memory += size;
+	gc_stats.mark_bytes += (p->max_blocks + 7) >> 3;
+
+	// register page in page map
+	for(i=0;i<size>>GC_MASK_BITS;i++) {
+		void *ptr = (unsigned char*)p + (i<<GC_MASK_BITS);
+		if( GC_GET_LEVEL1(ptr) == gc_level1_null ) {
+			gc_pheader **level = (gc_pheader**)malloc(sizeof(void*) * (1<<GC_LEVEL1_BITS));
+			MZERO(level,sizeof(void*) * (1<<GC_LEVEL1_BITS));
+			GC_GET_LEVEL1(ptr) = level;
+		}
+		GC_GET_PAGE(ptr) = p;
+	}
+	return p;
+}
+
+static void *gc_alloc_fixed( int part, int kind ) {
+	int pid = (part << PAGE_KIND_BITS) | kind;
+	gc_pheader *p = gc_free_pages[pid];
+	unsigned char *ptr;
+	while( p ) {
+		if( p->bmp ) {
+			int next = p->next_block;
+			while( true ) {
+				unsigned int fetch_bits = ((unsigned int*)p->bmp)[next >> 5];
+				int ones = TRAILING_ONES(fetch_bits >> (next&31));
+				next += ones;
+				if( (next&31) == 0 && ones ) {
+					if( next >= p->max_blocks ) {
+						p->next_block = next;
+						break;
+					}
+					continue;
+				}
+				p->next_block = next;
+				if( next >= p->max_blocks )
+					break;
+				goto alloc_fixed;
+			}
+		} else if( p->next_block < p->max_blocks )
+			break;
+		p = p->next_page;
+		gc_free_pages[pid] = p;
+	}
+	if( p == NULL ) {
+		p = gc_alloc_new_page(GC_SIZES[part], GC_PAGE_SIZE, false);
+		p->page_kind = kind;
+		p->next_page = gc_pages[pid];
+		p->next_free = NULL;
+		gc_pages[pid] = p;
+		gc_free_pages[pid] = p;
+	}
+alloc_fixed:
+	ptr = (unsigned char*)p + p->next_block * p->block_size;
+#	ifdef _DEBUG
+	{
+		int i;
+		if( p->next_block < p->first_block || p->next_block >= p->max_blocks )
+			hl_fatal("assert");
+		if( p->bmp && (p->bmp[p->next_block>>3]&(1<<(p->next_block&7))) != 0 )
+			hl_fatal("Alloc on marked bit");
+		for(i=0;i<p->block_size;i++)
+			if( ptr[i] != 0xDD )
+				hl_fatal("assert");
+	}
+#	endif
+	p->next_block++;
+	gc_stats.total_allocated += p->block_size;
+	return ptr;
+}
+
+static void *gc_alloc_var( int part, int size, int kind ) {
+	int pid = (part << PAGE_KIND_BITS) | kind;
+	gc_pheader *p = gc_pages[pid];
+	unsigned char *ptr;
+	int nblocks = size >> GC_SBITS[part];
+loop:
+	while( p ) {
+		if( p->bmp ) {
+			int next, avail = 0;
+			if( p->free_blocks >= nblocks ) {
+				p->next_block = p->first_block;
+				p->free_blocks = 0;
+			}
+			next = p->next_block;
+			if( next + nblocks > p->max_blocks ) {
+				gc_stats.debug++;
+				p = p->next_page;
+				continue;
+			}
+			while( true ) {
+				int fid = next >> 5;
+				unsigned int fetch_bits = ((unsigned int*)p->bmp)[fid];
+				int bits;
+resume:
+				bits = TRAILING_ONES(fetch_bits >> (next&31));
+				if( bits ) {
+					if( avail > p->free_blocks ) p->free_blocks = avail;
+					avail = 0;
+					next += bits - 1;
+					next += p->sizes[next];
+					if( (next>>5) != fid ) {
+						if( next + nblocks > p->max_blocks ) {
+							p->next_block = next;
+							p = p->next_page;
+							goto loop;
+						}
+						continue;
+					}
+					goto resume;
+				}
+				bits = TRAILING_ZEROES( (next & 31) ? (fetch_bits >> (next&31)) | (1<<(32-(next&31))) : fetch_bits );
+				avail += bits;
+				next += bits;
+				if( avail >= nblocks ) {
+					if( next > p->max_blocks ) {
+						avail -= next - p->max_blocks;
+						next = p->max_blocks;
+						if( avail < nblocks ) break;
+					}
+					p->next_block = next - avail;
+					goto alloc_var;
+				}
+				if( next & 31 ) goto resume; 
+			}
+			if( avail > p->free_blocks ) p->free_blocks = avail;
+			p->next_block = next;
+		} else if( p->next_block + nblocks <= p->max_blocks )
+			break;
+		p = p->next_page;
+	}
+	if( p == NULL ) {
+		int psize = GC_PAGE_SIZE;
+		while( psize < size + 1024 )
+			psize <<= 1;
+		p = gc_alloc_new_page(GC_SIZES[part], psize, true);
+		p->page_kind = kind;
+		p->next_page = gc_pages[pid];
+		gc_pages[pid] = p;
+	}
+alloc_var:
+	ptr = (unsigned char*)p + p->next_block * p->block_size;
+#	ifdef _DEBUG
+	{
+		int i;
+		if( p->next_block < p->first_block || p->next_block + nblocks > p->max_blocks )
+			hl_fatal("assert");
+		for(i=0;i<size;i++)
+			if( ptr[i] != 0xDD )
+				hl_fatal("assert");
+	}
+#	endif
+	if( p->bmp ) {
+		int i;
+		int bid = p->next_block;
+		int mark = 1;
+		for(i=0;i<nblocks;i++) {
+#			ifdef _DEBUG
+			if( (p->bmp[bid>>3]&(1<<(bid&7))) != 0 ) hl_fatal("Alloc on marked block");
+#			endif
+			p->bmp[bid>>3] &= ~(1<<(bid&7));
+			bid++;
+		}
+		bid = p->next_block;
+		p->bmp[bid>>3] |= 1<<(bid&7);
+	}
+	if( nblocks > 1 ) MZERO(p->sizes + p->next_block, nblocks);
+	p->sizes[p->next_block] = nblocks;
+	p->next_block += nblocks;
+	gc_stats.total_allocated += size + 1;
+	return ptr;
+}
 
-void hl_global_init() {
+static void *gc_alloc_gen( int size, int flags ) {
+	int m = size & (GC_ALIGN - 1);
+	int p, bestP = 0;
+	gc_stats.allocation_count++;
+	gc_stats.total_requested += size;
+	if( m ) size += GC_ALIGN - m;
+	if( size <= 0 )
+		return NULL;
+	if( size <= GC_SIZES[GC_FIXED_PARTS-1] && (flags & MEM_ALIGN_DOUBLE) == 0 )
+		return gc_alloc_fixed( (size >> GC_ALIGN_BITS) - 1, flags & PAGE_KIND_MASK);
+	for(p=GC_FIXED_PARTS;p<GC_PARTITIONS;p++) {
+		int block = GC_SIZES[p];
+		int m = size & (block - 1);
+		if( m ) size += block - m;
+		if( size < block * 255 )
+			return gc_alloc_var(p, size, flags & PAGE_KIND_MASK);
+	}
+	hl_error("Required memory allocation too big");
+	return NULL;
+}
+
+static void gc_check_mark();
+
+//#define HL_BUMP_ALLOC
+
+#ifdef HL_BUMP_ALLOC
+static unsigned char *alloc_all = NULL;
+static unsigned char *alloc_end = NULL;
+#endif
+
+void *hl_gc_alloc_gen( int size, int flags ) {
+	void *ptr;
+#ifdef HL_BUMP_ALLOC
+	if( !alloc_all ) {
+		int tot = 3<<29;
+		alloc_all = gc_alloc_page_memory(tot);
+		if( !alloc_all ) hl_fatal("Failed to allocate bump memory");
+		alloc_end = alloc_all + tot;
+	}
+	ptr = alloc_all;
+	alloc_all += size;
+	if( alloc_all > alloc_end ) hl_fatal("Out of memory");
+#else
+	gc_check_mark();
+	ptr = gc_alloc_gen(size, flags);
+#	ifdef _DEBUG
+	memset(ptr,0xCD,size);
+#	endif
+#endif
+	return ptr;
+}
+
+// -------------------------  MARKING ----------------------------------------------------------
+
+static float gc_mark_threshold = 0.5;
+static void *gc_stack_top = NULL;
+static int mark_size = 0;
+static unsigned char *mark_data = NULL;
+static void **cur_mark_stack = NULL;
+static void **mark_stack_end = NULL;
+static int mark_stack_size = 0;
+
+#define GC_PUSH_GEN(ptr,page,bid) \
+	if( MEM_HAS_PTR((page)->page_kind) ) { \
+		if( mark_stack == mark_stack_end ) mark_stack = hl_gc_mark_grow(mark_stack); \
+		*mark_stack++ = ptr; \
+		*mark_stack++ = ((unsigned char*)page) + bid; \
+	}
+
+HL_PRIM void **hl_gc_mark_grow( void **stack ) {
+	int nsize = mark_stack_size ? (((mark_stack_size * 3) >> 1) & ~1) : 256;
+	void **nstack = (void**)malloc(sizeof(void**) * nsize);
+	void **base_stack = mark_stack_end - mark_stack_size;
+	cur_mark_stack = stack;
+	memcpy(nstack, base_stack, (unsigned char*)cur_mark_stack - (unsigned char*)base_stack);
+	free(base_stack);
+	mark_stack_size = nsize;
+	mark_stack_end = nstack + nsize;
+	cur_mark_stack = nstack + (cur_mark_stack - base_stack);
+	if( base_stack == NULL ) {
+		*cur_mark_stack++ = 0;
+		*cur_mark_stack++ = 0;
+	}
+	return cur_mark_stack;
+}
+
+static void gc_flush_mark() {
+	register void **mark_stack = cur_mark_stack;
+	while( true ) {
+		unsigned char *page_bid = *--mark_stack;
+		void **block = (void**)*--mark_stack;
+		gc_pheader *page = (gc_pheader*)((int_val)page_bid & ~(GC_PAGE_SIZE - 1));
+		int bid = ((int)(int_val)page_bid) & (GC_PAGE_SIZE - 1);
+		int size, nwords;
+		if( !block ) {
+			mark_stack += 2;
+			break;
+		}
+		size = page->sizes ? page->sizes[bid] * page->block_size : page->block_size;
+		nwords = size / HL_WSIZE;
+		while( nwords-- ) {
+			void *p = *block++;
+			page = GC_GET_PAGE(p);
+			if( !page || ((((unsigned char*)p - (unsigned char*)page))%page->block_size) != 0 ) continue;
+			bid = ((unsigned char*)p - (unsigned char*)page) / page->block_size;
+			if( page->sizes && page->sizes[bid] == 0 ) continue;
+			if( (page->bmp[bid>>3] & (1<<(bid&7))) == 0 ) {
+				page->bmp[bid>>3] |= 1<<(bid&7);
+				GC_PUSH_GEN(p,page,bid);
+			}
+		}
+	}
+	cur_mark_stack = mark_stack;
+}
+
+#ifdef _DEBUG
+static void gc_clear_unmarked_mem() {
+	int i;
+	for(i=0;i<GC_ALL_PAGES;i++) {
+		gc_pheader *p = gc_pages[i];
+		while( p ) {
+			int bid;
+			for(bid=p->first_block;bid<p->max_blocks;bid++) {
+				if( p->sizes && !p->sizes[bid] ) continue;
+				if( (p->bmp[bid>>3] & (1<<(bid&7))) == 0 ) {
+					int size = p->sizes ? p->sizes[bid] * p->block_size : p->block_size;
+					unsigned char *ptr = (unsigned char*)p + bid * p->block_size;
+					if( bid * p->block_size + size > p->page_size ) hl_fatal("invalid block size"); 
+					memset(ptr,0xDD,size);
+					if( p->sizes ) p->sizes[bid] = 0;
+				}
+			}
+			p = p->next_page;
+		}
+	}
+}
+#endif
+
+static void gc_mark() {
+	// prepare mark bits
+	jmp_buf tmp;
+	void **stack_head;
+	void **stack_top = (void**)gc_stack_top;
+	void **mark_stack = cur_mark_stack;
+	int mark_bytes = gc_stats.mark_bytes;
+	int pid, i;
+	unsigned char *mark_cur;
+	setjmp(tmp); // save registers
+	if( mark_bytes > mark_size ) {
+		gc_free_page_memory(mark_data, mark_size);
+		if( mark_size == 0 ) mark_size = GC_PAGE_SIZE;
+		while( mark_size < mark_bytes )
+			mark_size <<= 1;
+		mark_data = gc_alloc_page_memory(mark_size);
+	}
+	mark_cur = mark_data;
+	MZERO(mark_data,mark_bytes);
+	for(pid=0;pid<GC_ALL_PAGES;pid++) {
+		gc_pheader *p = gc_pages[pid];
+		gc_free_pages[pid] = p;
+		while( p ) {
+			p->bmp = mark_cur;
+			p->next_block = p->first_block;
+			p->free_blocks = 0;
+			p->next_free = p->next_page;
+			mark_cur += (p->max_blocks + 7) >> 3;
+			p = p->next_page;
+		}
+	}
+	// push roots
+	for(i=0;i<gc_roots_count;i++) {
+		void *p = *gc_roots[i];
+		gc_pheader *page;
+		if( !p ) continue;
+		page = GC_GET_PAGE(p);
+		// don't check if valid ptr : it's a manual added root, so should be valid
+		int bid = ((unsigned char*)p - (unsigned char*)page) / page->block_size;
+		if( (page->bmp[bid>>3] & (1<<(bid&7))) == 0 ) {
+			page->bmp[bid>>3] |= 1<<(bid&7);
+			GC_PUSH_GEN(p,page,bid);
+		}
+	}
+	// scan stack
+	stack_head = (void**)&stack_head;
+	while( stack_head <= stack_top ) {
+		void *p = *stack_head++;
+		gc_pheader *page = GC_GET_PAGE(p);
+		int bid;
+		if( !page || (((unsigned char*)p - (unsigned char*)page)%page->block_size) != 0 ) continue;
+		bid = ((unsigned char*)p - (unsigned char*)page) / page->block_size;
+		if( page->sizes && !page->sizes[bid] ) continue; // inner pointer
+		if( (page->bmp[bid>>3] & (1<<(bid&7))) == 0 ) {
+			page->bmp[bid>>3] |= 1<<(bid&7);
+			GC_PUSH_GEN(p,page,bid);
+		}
+	}
+	cur_mark_stack = mark_stack;
+	if( mark_stack ) gc_flush_mark();
+#	ifdef _DEBUG
+	gc_clear_unmarked_mem();
+#	endif
+}
+
+HL_API void hl_gc_major() {
+	gc_stats.last_mark = gc_stats.total_allocated;
+	gc_mark();
+}
+
+HL_API bool hl_is_gc_ptr( void *ptr ) {
+	gc_pheader *page = GC_GET_PAGE(ptr);
+	int bid;
+	if( !page ) return false;
+	if( ((unsigned char*)ptr - (unsigned char*)page) % page->block_size != 0 ) return false;
+	bid = ((unsigned char*)ptr - (unsigned char*)page) / page->block_size;
+	if( bid < page->first_block ) return false;
+	if( page->sizes && page->sizes[bid] == 0 ) return false;
+	return true;
+}
+
+static void gc_check_mark() {
+	int64 m = gc_stats.total_allocated - gc_stats.last_mark;
+	if( m > gc_stats.pages_total_memory * gc_mark_threshold ) hl_gc_major();
+}
+
+static void hl_gc_init( void *stack_top ) {
+	int i;
+	gc_stack_top = stack_top;
+	for(i=0;i<1<<GC_LEVEL0_BITS;i++)
+		hl_gc_page_map[i] = gc_level1_null;
+	if( TRAILING_ONES(0x080003FF) != 10 || TRAILING_ONES(0) != 0 || TRAILING_ONES(0xFFFFFFFF) != 32 )
+		hl_fatal("Invalid builtin tl1");
+	if( TRAILING_ZEROES(~0x080003FF) != 10 || TRAILING_ZEROES(0) != 32 || TRAILING_ZEROES(0xFFFFFFFF) != 0 )
+		hl_fatal("Invalid builtin tl0");
+}
+
+// ---- UTILITIES ----------------------
+
+void hl_global_init( void *stack_top ) {
+	hl_gc_init(stack_top);
 }
 
 void hl_cache_free();
@@ -69,7 +619,7 @@ void *hl_malloc( hl_alloc *a, int size ) {
 
 void *hl_zalloc( hl_alloc *a, int size ) {
 	void *p = hl_malloc(a,size);
-	if( p ) memset(p,0,size);
+	if( p ) MZERO(p,size);
 	return p;
 }
 
@@ -107,20 +657,24 @@ void hl_free_executable_memory( void *c, int size ) {
 #endif
 }
 
-void *hl_gc_alloc( int size ) {
+static void *gc_alloc_page_memory( int size ) {
+#ifdef HL_WIN
+	return VirtualAlloc(NULL,size,MEM_RESERVE|MEM_COMMIT,PAGE_READWRITE);
+#else
 	return malloc(size);
+#endif
 }
 
-void *hl_gc_alloc_noptr( int size ) {
-	return (char*)malloc(size);
-}
-
-void *hl_gc_alloc_finalizer( int size ) {
-	return malloc(size);
+static void gc_free_page_memory( void *ptr, int size ) {
+#ifdef HL_WIN
+	VirtualFree(ptr, size, MEM_RELEASE);
+#else
+	free(ptr);
+#endif
 }
 
 vdynamic *hl_alloc_dynamic( hl_type *t ) {
-	vdynamic *d = (vdynamic*) ((t->kind == HENUM || t->kind == HABSTRACT) ? hl_gc_alloc(sizeof(vdynamic)) : hl_gc_alloc_noptr(sizeof(vdynamic)));
+	vdynamic *d = (vdynamic*) (hl_is_ptr(t) ? hl_gc_alloc(sizeof(vdynamic)) : hl_gc_alloc_noptr(sizeof(vdynamic)));
 	d->t = t;
 	d->v.ptr = NULL;
 	return d;
@@ -134,7 +688,7 @@ vdynamic *hl_alloc_obj( hl_type *t ) {
 	size = rt->size;
 	if( size & (HL_WSIZE-1) ) size += HL_WSIZE - (size & (HL_WSIZE-1));
 	o = (vobj*)hl_gc_alloc(size);
-	memset(o,0,size);
+	MZERO(o,size);
 	o->t = t;
 	return (vdynamic*)o;
 }
@@ -159,6 +713,6 @@ vvirtual *hl_alloc_virtual( hl_type *t ) {
 	v->next = NULL;
 	for(i=0;i<t->virt->nfields;i++)
 		fields[i] = (char*)v + t->virt->indexes[i];
-	memset(vdata,0,t->virt->dataSize);
+	MZERO(vdata,t->virt->dataSize);
 	return v;
-}
+}

+ 24 - 4
src/hl.h

@@ -170,6 +170,12 @@ HL_API int uvsprintf( uchar *out, const uchar *fmt, va_list arglist );
 HL_API void uprintf( const uchar *fmt, const uchar *str );
 #endif
 
+#ifdef HL_VCC
+#	define hl_debug_break()	if( IsDebuggerPresent() ) __debugbreak()
+#else
+#	define hl_debug_break() hl_fatal("debugbreak")
+#endif
+
 // ---- TYPES -------------------------------------------
 
 typedef enum {
@@ -459,16 +465,30 @@ HL_API void *hl_wrapper_call( void *value, void **args, vdynamic *ret );
 
 // ----------------------- ALLOC --------------------------------------------------
 
-HL_API void *hl_gc_alloc( int size );
-HL_API void *hl_gc_alloc_noptr( int size );
-HL_API void *hl_gc_alloc_finalizer( int size );
+#define MEM_HAS_PTR(kind)	(!((kind)&2))
+#define MEM_KIND_DYNAMIC	0
+#define MEM_KIND_RAW		1
+#define MEM_KIND_NOPTR		2
+#define MEM_KIND_FINALIZER	3
+#define MEM_ALIGN_DOUBLE	128
+
+HL_API void *hl_gc_alloc_gen( int size, int flags );
+HL_API void hl_add_root( void **ptr );
+HL_API void hl_remove_root( void **ptr );
+HL_API void hl_gc_major();
+HL_API bool hl_is_gc_ptr( void *ptr );
+
+#define hl_gc_alloc_noptr(size)		hl_gc_alloc_gen(size,MEM_KIND_NOPTR)
+#define hl_gc_alloc(size)			hl_gc_alloc_gen(size,MEM_KIND_DYNAMIC)
+#define hl_gc_alloc_raw(size)		hl_gc_alloc_gen(size,MEM_KIND_RAW)
+#define hl_gc_alloc_finalizer(size) hl_gc_alloc_gen(size,MEM_KIND_FINALIZER)
 
 HL_API void hl_alloc_init( hl_alloc *a );
 HL_API void *hl_malloc( hl_alloc *a, int size );
 HL_API void *hl_zalloc( hl_alloc *a, int size );
 HL_API void hl_free( hl_alloc *a );
 
-HL_API void hl_global_init();
+HL_API void hl_global_init( void *stack_top );
 HL_API void hl_global_free();
 
 // ----------------------- BUFFER --------------------------------------------------

+ 1 - 0
src/hlc.h

@@ -135,5 +135,6 @@ HL_API vdynamic *hl_current_exc;
 extern void *hlc_static_call(void *fun, hl_type *t, void **args, vdynamic *out);
 extern void *hlc_get_wrapper(hl_type *t);
 HL_API void hlc_setup(void *sc, void *gw);
+extern void hl_entry_point();
 
 #endif

+ 9 - 2
src/main.c

@@ -25,6 +25,7 @@
 #	include <crtdbg.h>
 #else
 #	define _CrtSetDbgFlag(x)
+#	define _CrtCheckMemory()
 #endif
 
 
@@ -35,10 +36,12 @@ int main(int argc, char *argv[]) {
 #endif
 	hl_trap_ctx ctx;
 	vdynamic *exc;
-	hl_global_init();
+	hl_global_init(&ctx);
 	hlc_setup(hlc_static_call, hlc_get_wrapper);
 	hl_sys_init(argv + 1,argc - 1);
-	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_DELAY_FREE_MEM_DF /*| _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF*/);
+#	ifdef _DEBUG
+	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_DELAY_FREE_MEM_DF /*| _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF*/ );
+#	endif
 	hlc_trap(ctx, exc, on_exception);
 	hl_entry_point();
 	hl_global_free();
@@ -50,6 +53,10 @@ on_exception:
 		uprintf(USTR("Uncaught exception: %s\n"), hl_to_string(exc));
 		for(i=0;i<a->size;i++)
 			uprintf(USTR("Called from %s\n"), hl_aptr(a,uchar*)[i]);
+#		ifdef _DEBUG
+		_CrtCheckMemory();
+#		endif
+		hl_debug_break();
 	}
 	hl_global_free();
 	return 1;