/*
 * cut and paste from gc 4.4 by Hans-J. Boehm and Alan J. Demers
 *
 * Cutter and Paster:  Jeffrey Hsu
 */

/* $XFree86: xc/util/memleak/stackbottom.c,v 3.1 1996/12/31 05:02:27 dawes Exp $ */

#include <signal.h>

typedef char * ptr_t;	/* A generic pointer to which we can add	*/
			/* byte displacements.				*/
			/* Preferably identical to caddr_t, if it 	*/
			/* exists.					*/

#ifndef bool
typedef int bool;
#endif

#define VOLATILE volatile

# ifndef STACK_GROWS_UP
#   define STACK_GROWS_DOWN
# endif

typedef unsigned long word;

#define TRUE 1

#if defined(__alpha) || defined(__alpha__)
    extern __start
#   define HEURISTIC2_LIMIT ((ptr_t)((word)(&__start) & ~(getpagesize()-1)))
#endif

void GC_noop() {}

  /* Some tools to implement HEURISTIC2	*/
#   define MIN_PAGE_SIZE 256	/* Smallest conceivable page size, bytes */
#   include <setjmp.h>
    /* static */ jmp_buf GC_jmp_buf;
    
    /*ARGSUSED*/
    void GC_fault_handler(sig)
    int sig;
    {
        longjmp(GC_jmp_buf, 1);
    }

	typedef void (*handler)(int);

    /* Return the first nonaddressible location > p (up) or 	*/
    /* the smallest location q s.t. [q,p] is addressible (!up).	*/
    ptr_t GC_find_limit(p, up)
    ptr_t p;
    bool up;
    {
        static VOLATILE ptr_t result;
    		/* Needs to be static, since otherwise it may not be	*/
    		/* preserved across the longjmp.  Can safely be 	*/
    		/* static since it's only called once, with the		*/
    		/* allocation lock held.				*/

          static handler old_segv_handler, old_bus_handler;
      		/* See above for static declaration.			*/

    	  old_segv_handler = signal(SIGSEGV, GC_fault_handler);
#	  ifdef SIGBUS
	    old_bus_handler = signal(SIGBUS, GC_fault_handler);
#	  endif
	if (setjmp(GC_jmp_buf) == 0) {
	    result = (ptr_t)(((word)(p))
			      & ~(MIN_PAGE_SIZE-1));
	    for (;;) {
 	        if (up) {
		    result += MIN_PAGE_SIZE;
 	        } else {
		    result -= MIN_PAGE_SIZE;
 	        }
		GC_noop(*result);
	    }
	}
  	  (void) signal(SIGSEGV, old_segv_handler);
#	  ifdef SIGBUS
	    (void) signal(SIGBUS, old_bus_handler);
#	  endif
 	if (!up) {
	    result += MIN_PAGE_SIZE;
 	}
	return(result);
    }

ptr_t GC_get_stack_base()
{
    word dummy;
    static ptr_t result;

    if (!result) {

#	    ifdef STACK_GROWS_DOWN
		result = GC_find_limit((ptr_t)(&dummy), TRUE);
#           	ifdef HEURISTIC2_LIMIT
		    if (result > HEURISTIC2_LIMIT
		        && (ptr_t)(&dummy) < HEURISTIC2_LIMIT) {
		            result = HEURISTIC2_LIMIT;
		    }
#	        endif
#	    else
		result = GC_find_limit((ptr_t)(&dummy), FALSE);
#           	ifdef HEURISTIC2_LIMIT
		    if (result < HEURISTIC2_LIMIT
		        && (ptr_t)(&dummy) > HEURISTIC2_LIMIT) {
		            result = HEURISTIC2_LIMIT;
		    }
#	        endif
#	    endif
    }

    	return(result);
}
