/* GDC -- D front-end for GCC
   Copyright (C) 2004 David Friedman
   
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
 
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
 
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

module gcc.gcgcc;

private import gcc.config;
version (Unix)
    private import std.c.unix;

// from gclinux:

/***********************************
 * Map memory.
 */

void *os_mem_map(uint nbytes)
{   void *p;

    //errno = 0;
    p = mmap(null, nbytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
    return (p == MAP_FAILED) ? null : p;
}

/***********************************
 * Commit memory.
 * Returns:
 *	0	success
 *	!=0	failure
 */

int os_mem_commit(void *base, uint offset, uint nbytes)
{
    return 0;
}


/***********************************
 * Decommit memory.
 * Returns:
 *	0	success
 *	!=0	failure
 */

int os_mem_decommit(void *base, uint offset, uint nbytes)
{
    return 0;
}

/***********************************
 * Unmap memory allocated with os_mem_map().
 * Returns:
 *	0	success
 *	!=0	failure
 */

int os_mem_unmap(void *base, uint nbytes)
{
    return munmap(base, nbytes);
}



/**********************************************
 * Determine "bottom" of stack (actually the top on x86 systems).
 */

version (GNU_MustGuessStackBottom) {

    // TODO: this isn't quite exclusive with GNU_Boehm_GC...

    void * stack_bottom_guess;

    void setStackBottomGuess(void * guess) {
	stack_bottom_guess = guess;
    }
    void * getStackBottomGuess() {
	return stack_bottom_guess;
    }
}

version (GNU_Boehm_GC) {

    extern (C) void * GC_get_stack_base();

    void *os_query_stackBottom() {
	return GC_get_stack_base();
    }
    
} else version (GNU_MustGuessStackBottom) {

    void *os_query_stackBottom()
    {
	return stack_bottom_guess;
    }

} else version (linux) {
    /*
      this causes compilation problems, so just copy from gclinux
    private import internal.gc.gclinux;
    alias internal.gc.gclinux.os_query_stackBottom os_query_StackBottom;
    */
    private import std.c.linux.linuxextern;
    void *os_query_stackBottom()
    {
	return __libc_stack_end;
    }
}

/**********************************************
 * Determine base address and size of static data segment.
 */

version (GNU_Boehm_GC) {
    void os_query_staticdataseg(void **base, uint *nbytes)
    {
	// We can get the start/end, but Boehm GC adds the range explicitly
	*base = null;
	*nbytes = 0;
    }

    private import std.gc;
    
    extern (C) void GC_add_roots_inner(void * b, void * e, int tmp)
    {
	std.gc.addRange(b, e);
    }
    extern (C) void GC_add_roots(void * b, void * e)
    {
	std.gc.addRange(b, e);
    }
    extern (C) void GC_remove_roots(void * b, void * e)
    {
	std.gc.removeRange(b);
    }
    extern (C) void * GC_malloc(size_t size)
    {
	// It doesn't seem like the Boehm GC code we use actually calls
	// this.
	return new ubyte[size];
    }
    extern (C) void GC_init_for_d();
}
else version (darwin)
{
/* Really have to register a function to handle dylibs as in boehm gc */

    extern (C) ubyte * getsectdata(
	char *segname,
	char *sectname,
	uint *size);

    private const char[] SEG_DATA = "__DATA\0";	/* the tradition UNIX data segment */
    private const char[] SECT_DATA = "__data\0";	/* the real initialized data section */

    void os_query_staticdataseg(void **base, uint *nbytes)
    {
	*base = getsectdata(SEG_DATA, SECT_DATA, nbytes);
    }

}
else version(linux)
{
    
    private import std.c.linux.linuxextern;
    void os_query_staticdataseg(void **base, uint *nbytes)
    {
	*base = cast(void *)&__data_start;
	*nbytes = cast(byte *)&_end - cast(byte *)&__data_start;
    }
    
}
else version(GNU_HaveDataStartEnd)
{
    private import std.c.linux.linuxextern;
    void os_query_staticdataseg(void **base, uint *nbytes)
    {
	*base = cast(void *)&__data_start;
	*nbytes = cast(byte *)&_end - cast(byte *)&__data_start;
    }
}
else version(Unix)
{
    private import std.c.unix;
    private static bool fault_occurred;
    private extern (C) void fault_handler(int sig) {
	fault_occurred = true;
    }
    //%% this should be a std.system constants
    // and/or use (void*).sizeof
    // page size should probably be another system constant
    version (GNU_BitsPerWord32) {
	private const size_t word_size = 4;
    } else version (GNU_BitsPerWord64) {
        private const size_t word_size = 8;
    } else {
	static assert(0);
    }

    void os_query_staticdataseg(void **base, uint *nbytes)
    {
	setup_fault_handler();

	ubyte* text_end = (etext_addr + word_size - 1) & ~(word_size-1);
	ubyte* next_page = (text_end + max_page_size - 1)
			      & ~(cast(word)max_page_size - 1);
	for (;;) {
	    volatile {
		int x = *p;
	    }
	}
	
	restore_fault_handler();
    }
}

void gc_init_extra()
{
    version (GNU_Boehm_GC) {
	GC_init_for_d();
    }
}

