/*
 * $Id: jexc_gc.c,v 1.9 2000/12/22 02:51:14 m-hirano Exp $
 * $RWC_Release: Omni-1.6 $
 * $RWC_Copyright:
 *  Omni Compiler Software Version 1.5-1.6
 *  Copyright (C) 2002 PC Cluster Consortium
 *  
 *  This software is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License version
 *  2.1 published by the Free Software Foundation.
 *  
 *  Omni Compiler Software Version 1.0-1.4
 *  Copyright (C) 1999, 2000, 2001.
 *   Tsukuba Research Center, Real World Computing Partnership, Japan.
 *  
 *  Please check the Copyright and License information in the files named
 *  COPYRIGHT and LICENSE under the top  directory of the Omni Compiler
 *  Software release kit.
 *  
 *  
 *  $
 */
#include "jexc_runtime.h"

/*
 * memory allocator & garbage collector
 */
#include "jexc.h"

typedef _omUint32_t word32;

#define MIN_SIZE  16
#define MIN_MASK  0xF
#define MIN_SHIFT 4

#define N_SMALL_MEM 16

#define CHUNK_SIZE 	0x2000000	/* 32M byte */
/*#define CHUNK_SIZE 	0x20000	*/
#define BITMAP_SIZE 	((CHUNK_SIZE/MIN_SIZE)/8)	/* in byte */

typedef struct _mem_chunk {
    struct _mem_chunk *link;
    word32 *bitmap;
    char _pad[2*MIN_SIZE-sizeof(struct _mem_link *)-sizeof(word32 *)];
    char base[MIN_SIZE];
} mem_chunk;

#define IS_MARKED(bitmap,i) (((bitmap)[(i)>>5])&(1 <<((i)&0x1F)))
#define MARK_IT(bitmap,i) ((bitmap)[(i)>>5]) |= (1 <<((i)&0x1F))

static mem_chunk *jexc_mem_chunk_top, *jexc_mem_chunk_tail;

static jexc_mem *jexc_large_mem;
static jexc_mem *jexc_small_mem[N_SMALL_MEM];

static int jexc_largest_mem_size;
static int jexc_mem_used,jexc_mem_total_size;
static int jexc_mem_used_last;	/* used at last GC */
static int jexc_gc_disable = FALSE;

static int jexc_gc_mem_limit;
float jexc_mem_load_factor = 0.8;

static void jexc_malloc_chunk();
static void jexc_mem_mark(jexc_object *p);
static void jexc_add_free(jexc_mem *p,int size);

void jexc_gc_off()
{
    if(jexc_mem_used > jexc_gc_mem_limit &&
       (jexc_mem_used - jexc_mem_used_last) > (CHUNK_SIZE/10))
	jexc_GC();
    jexc_gc_disable = TRUE;
}

void jexc_gc_on()
{
    jexc_gc_disable = FALSE;
}

jexc_mem *jexc_malloc(int size)
{
    jexc_mem *p;
    jexc_mem *q = NULL;
    jexc_mem *pp;
    int s;
    int first_time = TRUE;

    size = (size + MIN_SIZE-1)&~MIN_MASK; /* align size */

retry:
    /* try to take from jexc_small_mem */
    if(size < (N_SMALL_MEM << MIN_SHIFT) && 
       (p = jexc_small_mem[s = (size>>MIN_SHIFT)]) != NULL){
	jexc_small_mem[s] = p->next;
	bzero(p,size);
	p->size = size;
	jexc_mem_used += size;
	return p;
    }

    /* first fit */
    for(p = jexc_large_mem; p != NULL; q = p, p = p->next)
	if(p->size >= size) break;

    if(p == NULL){  /* not found */
	if(jexc_mem_chunk_top == NULL || jexc_gc_disable){ /* first time */
	    jexc_malloc_chunk();
	} else {
	    jexc_GC();	/* call garbage collection */
	    if(first_time && jexc_largest_mem_size >= size &&
	       jexc_mem_used < jexc_gc_mem_limit){
		first_time = FALSE;
		goto retry;
	    }
	    jexc_malloc_chunk();
	}
	if(size > jexc_largest_mem_size) jexc_error("jexc_mem:too large");
	p = jexc_large_mem;
    }

    if(p->size == size){ /* just fit */
	if(p == jexc_large_mem) jexc_large_mem = p->next;
	else q->next = p->next;
    } else {
	pp = (jexc_mem *)(((char *)p)+size);
	pp->size = p->size - size;
	pp->next = p->next;
	if(p == jexc_large_mem) jexc_large_mem = pp;
	else q->next = pp;
#ifdef not
	printf("alloc p=0x%x, size=0x%x,(0x%x)\n",p,size,pp->size);
	/* mem_verify(); */
#endif
    }
    bzero(p,size);
    p->size = size;
    jexc_mem_used += size;
    return p;
}

static void jexc_malloc_chunk()
{
    mem_chunk *mp;
    jexc_mem *p;
    char *tmp;

    mp = (mem_chunk *)malloc(sizeof(mem_chunk) + CHUNK_SIZE + BITMAP_SIZE);
    if(mp == NULL) jexc_error("memory run out");

    mp = (mem_chunk *)((((_omAddrInt_t)mp)+MIN_SIZE-1)&~MIN_MASK); /* align */
    if(jexc_mem_chunk_top == NULL)
	jexc_mem_chunk_top = mp;
    else 
	jexc_mem_chunk_tail->link = mp;
    jexc_mem_chunk_tail = mp;
    tmp = (char *)(mp->base);
    tmp += CHUNK_SIZE;
    mp->bitmap = (word32 *)tmp;
    p = (jexc_mem *)(mp->base);
    p->size = CHUNK_SIZE;
    p->next = jexc_large_mem;
    jexc_large_mem = p;

#ifdef not
    printf("p=0x%x, CHUNK=0x%x, BITMAP=0x%x\n",p,CHUNK_SIZE,BITMAP_SIZE);
#endif
    jexc_largest_mem_size = CHUNK_SIZE;
    jexc_mem_total_size += CHUNK_SIZE;
    jexc_gc_mem_limit = (int)(jexc_mem_load_factor*jexc_mem_total_size);
    /* printf("mem_size=0x%x\n",jexc_mem_total_size); */
}

static void jexc_mem_mark(jexc_object *p)
{
    char *cp;
    jexc_array *ap;
    mem_chunk *mp;
    int i;
    word32 r;
    unsigned int off;

    if(p == NULL) return;	/* check null */

    if((((_omAddrInt_t)p)&MIN_MASK) != 0) return; /* fake as already marked */

    cp = (char *)p;
    for(mp = jexc_mem_chunk_top; mp != NULL; mp = mp->link){
	if(cp >= mp->base && cp < ((char *)mp->bitmap)){
	    off = cp-mp->base;
	    off = off >> MIN_SHIFT;
	    r = IS_MARKED(mp->bitmap,off);
	    if(r) return;	/* already marked */
	    MARK_IT(mp->bitmap,off);
	    switch((_omAddrInt_t)(p->class)){
	    case (_omAddrInt_t)OPAQUE_OBJECT:
	    case (_omAddrInt_t)IARRAY_OBJECT:
		break;
	    case (_omAddrInt_t)ARRAY_OBJECT:
		ap = (jexc_array *)p;
		for(i = 0; i < ap->length; i++) jexc_mem_mark(ap->elem[i]);
		break;
	    default:
		for(i = 0; i < p->class->n_var; i++)
		    jexc_mem_mark(p->n_var_base[i]);
		break;
	    }
	    return;
	}
    }
    /* out of chunk, do nothing */
}

void jexc_GC()
{
    mem_chunk *mp;
    int i,k;
    jexc_class *cp;
    char *base;
    unsigned int off;
    jexc_word *pp;
    jexc_mem *free_p,*p;
    word32 *bitmap;


    /* printf("GC ..."); fflush(stdout); */

    /* clean up bitmap */
    for(mp = jexc_mem_chunk_top; mp != NULL; mp = mp->link){
	bzero(mp->bitmap,BITMAP_SIZE);
    }

    /* 
     * making 
     */
    /* mark class_table */
    for(i = 0; i < n_class; i++){
	cp = jexc_class_table[i]; 
	for(k = 0; k < cp->n_static; k++)
	    jexc_mem_mark(cp->n_static_base[k]);
    }

    /* make string hash table */
    for(i = 0; i < STR_HASH_SIZE; i++)
	jexc_mem_mark((jexc_object *)jexc_string_hash_table[i]);

    /* make object stack */
    for(pp = jexc_stack; pp < Top; pp++)
	jexc_mem_mark(*pp);

    /* reset table and counter */
    jexc_large_mem = NULL;
    for(i = 0; i < N_SMALL_MEM; i++) jexc_small_mem[i] = NULL;
    jexc_largest_mem_size = 0;
    jexc_mem_used = 0;

    /* sweep and reclaim */
    for(mp = jexc_mem_chunk_top; mp != NULL; mp = mp->link){
	base = mp->base;
	bitmap = mp->bitmap;
	free_p = NULL;
	for(off = 0; off < CHUNK_SIZE; ){
	    p = (jexc_mem *)(base + off);
	    if((off & MIN_MASK) != 0) jexc_error("CG:bad offset");
	    if(IS_MARKED(bitmap,off>>MIN_SHIFT)){
		if(free_p != NULL)
		    jexc_add_free(free_p,((char *)p)-((char *)free_p));
		free_p = NULL;
		jexc_mem_used += p->size;
	    } else {
		if(free_p == NULL) free_p = p;	/* head */
	    }
#ifdef not
	    printf("sweep p=0x%x, off=0x%x\n",p,off);
#endif
	    off += p->size;
	}
	if(off != CHUNK_SIZE){
	    printf("CG: off=0x%x CHUNK=0x%x\n",off,CHUNK_SIZE);
	    jexc_error("GC: bad tail");
	}
	if(free_p != NULL)
	    jexc_add_free(free_p,(base+CHUNK_SIZE)-((char *)free_p));
    }

#ifdef not
    /* report status */
    printf("total_mem_size=0x%x\n",jexc_mem_total_size);
    printf("used_mem = 0x%x (%g%%, load_factor=%g)\n",
	   jexc_mem_used,(jexc_mem_used*100.0)/((float)jexc_mem_total_size),
	   jexc_mem_load_factor);
#endif
    jexc_mem_used_last = jexc_mem_used;	/* remember the last value */
    /* printf("...end \n"); fflush(stdout); */
}

static void jexc_add_free(jexc_mem *p,int size)
{
    p->size = size;
    if(jexc_largest_mem_size < size) jexc_largest_mem_size = size;

    size = size >> MIN_SHIFT;
    if(size < N_SMALL_MEM){
	p->next = jexc_small_mem[size];
	jexc_small_mem[size] = p;
    } else {
	p->next = jexc_large_mem;
	jexc_large_mem = p;
    }
}

#ifdef not
mem_verify()
{
    mem_chunk *mp;
    char *base;
    unsigned int off;
    jexc_mem *free_p,*p;

    for(mp = jexc_mem_chunk_top; mp != NULL; mp = mp->link){
	base = mp->base;
	for(off = 0; off < CHUNK_SIZE; ){
	    p = (jexc_mem *)(base + off);
	    printf("  -verify: p=0x%x, off=%x, size=%x\n",p,off,p->size);
	    off += p->size;
	}
    }
}
#endif







