diff options
| author | Akinori Ito <aito@eie.yz.yamagata-u.ac.jp> | 2001-11-09 04:59:17 +0000 | 
|---|---|---|
| committer | Akinori Ito <aito@eie.yz.yamagata-u.ac.jp> | 2001-11-09 04:59:17 +0000 | 
| commit | 6c63633545c254dc085402e0f927a6826d1dd229 (patch) | |
| tree | 0126fb5598304c713ea1276e294da9098b5df3b4 /gc/include/private | |
| parent | Initial revision (diff) | |
| download | w3m-6c63633545c254dc085402e0f927a6826d1dd229.tar.gz w3m-6c63633545c254dc085402e0f927a6826d1dd229.zip | |
Updates from 0.2.1 into 0.2.1-inu-1.5release-0-2-1-inu-1-5
Diffstat (limited to 'gc/include/private')
| -rw-r--r-- | gc/include/private/dbg_mlc.h | 153 | ||||
| -rw-r--r-- | gc/include/private/gc_hdrs.h | 98 | ||||
| -rw-r--r-- | gc/include/private/gc_locks.h | 481 | ||||
| -rw-r--r-- | gc/include/private/gc_pmark.h | 388 | ||||
| -rw-r--r-- | gc/include/private/gc_priv.h | 1433 | ||||
| -rw-r--r-- | gc/include/private/gcconfig.h | 776 | ||||
| -rw-r--r-- | gc/include/private/solaris_threads.h | 34 | ||||
| -rw-r--r-- | gc/include/private/specific.h | 83 | 
8 files changed, 2713 insertions, 733 deletions
| diff --git a/gc/include/private/dbg_mlc.h b/gc/include/private/dbg_mlc.h new file mode 100644 index 0000000..6f5b3c8 --- /dev/null +++ b/gc/include/private/dbg_mlc.h @@ -0,0 +1,153 @@ +/*  + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation.  All rights reserved. + * Copyright (c) 1997 by Silicon Graphics.  All rights reserved. + * Copyright (c) 1999 by Hewlett-Packard Company.  All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose,  provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* + * This is mostly an internal header file.  Typical clients should + * not use it.  Clients that define their own object kinds with + * debugging allocators will probably want to include this, however. + * No attempt is made to keep the namespace clean.  This should not be + * included from header files that are frequently included by clients. + */ + +#ifndef _DBG_MLC_H + +#define _DBG_MLC_H + +# define I_HIDE_POINTERS +# include "gc_priv.h" +# ifdef KEEP_BACK_PTRS +#   include "gc_backptr.h" +# endif + +#ifndef HIDE_POINTER +  /* Gc.h was previously included, and hence the I_HIDE_POINTERS	*/ +  /* definition had no effect.  Repeat the gc.h definitions here to	*/ +  /* get them anyway.							*/ +    typedef GC_word GC_hidden_pointer; +#   define HIDE_POINTER(p) (~(GC_hidden_pointer)(p)) +#   define REVEAL_POINTER(p) ((GC_PTR)(HIDE_POINTER(p))) +#endif /* HIDE_POINTER */ + +# define START_FLAG ((word)0xfedcedcb) +# define END_FLAG ((word)0xbcdecdef) +	/* Stored both one past the end of user object, and one before	*/ +	/* the end of the object as seen by the allocator.		*/ + +# if defined(KEEP_BACK_PTRS) || defined(PRINT_BLACK_LIST) +    /* Pointer "source"s that aren't real locations.	*/ +    /* Used in oh_back_ptr fields and as "source"	*/ +    /* argument to some marking functions.		*/ +#	define NOT_MARKED (ptr_t)(0) +#	define MARKED_FOR_FINALIZATION (ptr_t)(2) +	    /* Object was marked because it is finalizable.	*/ +#	define MARKED_FROM_REGISTER (ptr_t)(4) +	    /* Object was marked from a rgister.  Hence the	*/ +	    /* source of the reference doesn't have an address.	*/ +# endif /* KEEP_BACK_PTRS || PRINT_BLACK_LIST */ + +/* Object header */ +typedef struct { +#   ifdef KEEP_BACK_PTRS +	GC_hidden_pointer oh_back_ptr; +	    /* We make sure that we only store even valued	*/ +	    /* pointers here, so that the hidden version has	*/ +	    /* the least significant bit set.  We never		*/ +	    /* overwrite a value with the least significant	*/ +	    /* bit clear, thus ensuring that we never overwrite	*/ +	    /* a free list link field.				*/ + 	    /* Note that blocks dropped by black-listing will	*/ + 	    /* also have the lsb clear once debugging has	*/ + 	    /* started.						*/ +	    /* The following are special back pointer values.	*/ +	    /* Note that the "hidden" (i.e. bitwise 		*/ +	    /* complemented version) of these is actually 	*/ +	    /* stored.						*/ +#       if ALIGNMENT == 1 +	  /* Fudge back pointer to be even.  */ +#	  define HIDE_BACK_PTR(p) HIDE_POINTER(~1 & (GC_word)(p)) +#	else +#	  define HIDE_BACK_PTR(p) HIDE_POINTER(p) +#	endif +#	ifdef ALIGN_DOUBLE +	  word oh_dummy; +#	endif +#   endif +    GC_CONST char * oh_string;	/* object descriptor string	*/ +    word oh_int;		/* object descriptor integers	*/ +#   ifdef NEED_CALLINFO +      struct callinfo oh_ci[NFRAMES]; +#   endif +#   ifndef SHORT_DBG_HDRS +      word oh_sz;			/* Original malloc arg.		*/ +      word oh_sf;			/* start flag */ +#   endif /* SHORT_DBG_HDRS */ +} oh; +/* The size of the above structure is assumed not to dealign things,	*/ +/* and to be a multiple of the word length.				*/ + +#ifdef SHORT_DBG_HDRS +#   define DEBUG_BYTES (sizeof (oh)) +#else +    /* Add space for END_FLAG, but use any extra space that was already	*/ +    /* added to catch off-the-end pointers.				*/ +#   define DEBUG_BYTES (sizeof (oh) + sizeof (word) - EXTRA_BYTES) +#endif +#define USR_PTR_FROM_BASE(p) ((ptr_t)(p) + sizeof(oh)) + +/* Round bytes to words without adding extra byte at end.	*/ +#define SIMPLE_ROUNDED_UP_WORDS(n) BYTES_TO_WORDS((n) + WORDS_TO_BYTES(1) - 1) + +#ifdef SAVE_CALL_CHAIN +#   define ADD_CALL_CHAIN(base, ra) GC_save_callers(((oh *)(base)) -> oh_ci) +#   define PRINT_CALL_CHAIN(base) GC_print_callers(((oh *)(base)) -> oh_ci) +#else +# ifdef GC_ADD_CALLER +#   define ADD_CALL_CHAIN(base, ra) ((oh *)(base)) -> oh_ci[0].ci_pc = (ra) +#   define PRINT_CALL_CHAIN(base) GC_print_callers(((oh *)(base)) -> oh_ci) +# else +#   define ADD_CALL_CHAIN(base, ra) +#   define PRINT_CALL_CHAIN(base) +# endif +#endif + +# ifdef GC_ADD_CALLER +#   define OPT_RA ra, +# else +#   define OPT_RA +# endif + + +/* Check whether object with base pointer p has debugging info	*/  +/* p is assumed to point to a legitimate object in our part	*/ +/* of the heap.							*/ +#ifdef SHORT_DBG_HDRS +# define GC_has_other_debug_info(p) TRUE +#else +  GC_bool GC_has_other_debug_info(/* p */); +#endif + +#ifdef KEEP_BACK_PTRS +# define GC_HAS_DEBUG_INFO(p) \ +	((((oh *)p)->oh_back_ptr & 1) && GC_has_other_debug_info(p)) +#else +# define GC_HAS_DEBUG_INFO(p) GC_has_other_debug_info(p) +#endif + +/* Store debugging info into p.  Return displaced pointer. */ +/* Assumes we don't hold allocation lock.		   */ +ptr_t GC_store_debug_info(/* p, sz, string, integer */); + +#endif /* _DBG_MLC_H */ diff --git a/gc/include/private/gc_hdrs.h b/gc/include/private/gc_hdrs.h index 60dc2ad..dd61545 100644 --- a/gc/include/private/gc_hdrs.h +++ b/gc/include/private/gc_hdrs.h @@ -24,6 +24,15 @@ typedef struct hblkhdr hdr;   * The 2 level tree data structure that is used to find block headers.   * If there are more than 32 bits in a pointer, the top level is a hash   * table. + * + * This defines HDR, GET_HDR, and SET_HDR, the main macros used to + * retrieve and set object headers. + * + * Since 5.0 alpha 5, we can also take advantage of a header lookup + * cache.  This is a locally declared direct mapped cache, used inside + * the marker.  The HC_GET_HDR macro uses and maintains this + * cache.  Assuming we get reasonable hit rates, this shaves a few + * memory references from each pointer validation.   */  # if CPP_WORDSZ > 32 @@ -45,6 +54,95 @@ typedef struct hblkhdr hdr;  # define TOP_SZ (1 << LOG_TOP_SZ)  # define BOTTOM_SZ (1 << LOG_BOTTOM_SZ) +#ifndef SMALL_CONFIG +# define USE_HDR_CACHE +#endif + +/* #define COUNT_HDR_CACHE_HITS  */ + +extern hdr * GC_invalid_header; /* header for an imaginary block 	*/ +				/* containing no objects.		*/ + + +/* Check whether p and corresponding hhdr point to long or invalid	*/ +/* object.  If so, advance hhdr	to					*/ +/* beginning of	block, or set hhdr to GC_invalid_header.		*/ +#define ADVANCE(p, hhdr, source) \ +	    { \ +	      hdr * new_hdr = GC_invalid_header; \ +              p = GC_FIND_START(p, hhdr, &new_hdr, (word)source); \ +	      hhdr = new_hdr; \ +    	    } + +#ifdef USE_HDR_CACHE + +# ifdef COUNT_HDR_CACHE_HITS +    extern word GC_hdr_cache_hits; +    extern word GC_hdr_cache_misses; +#   define HC_HIT() ++GC_hdr_cache_hits +#   define HC_MISS() ++GC_hdr_cache_misses +# else +#   define HC_HIT() +#   define HC_MISS() +# endif + +  typedef struct hce { +    word block_addr;  	/* right shifted by LOG_HBLKSIZE */ +    hdr * hce_hdr; +  } hdr_cache_entry; + +# define HDR_CACHE_SIZE 8  /* power of 2 */ + +# define DECLARE_HDR_CACHE \ +	hdr_cache_entry hdr_cache[HDR_CACHE_SIZE] + +# define INIT_HDR_CACHE BZERO(hdr_cache, sizeof(hdr_cache)); + +# define HCE(h) hdr_cache + (((word)(h) >> LOG_HBLKSIZE) & (HDR_CACHE_SIZE-1)) + +# define HCE_VALID_FOR(hce,h) ((hce) -> block_addr == \ +				((word)(h) >> LOG_HBLKSIZE)) + +# define HCE_HDR(h) ((hce) -> hce_hdr) + + +/* Analogous to GET_HDR, except that in the case of large objects, it	*/ +/* Returns the header for the object beginning, and updates p.		*/ +/* Returns &GC_bad_header instead of 0.  All of this saves a branch	*/ +/* in the fast path.							*/ +# define HC_GET_HDR(p, hhdr, source) \ +	{ \ +	  hdr_cache_entry * hce = HCE(p); \ +	  if (HCE_VALID_FOR(hce, p)) { \ +	    HC_HIT(); \ +	    hhdr = hce -> hce_hdr; \ +	  } else { \ +	    HC_MISS(); \ +	    GET_HDR(p, hhdr); \ +            if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { \ +	      ADVANCE(p, hhdr, source); \ +	    } else { \ +	      hce -> block_addr = (word)(p) >> LOG_HBLKSIZE; \ +	      hce -> hce_hdr = hhdr; \ +	    } \ +	  } \ +	} + +#else /* !USE_HDR_CACHE */ + +# define DECLARE_HDR_CACHE + +# define INIT_HDR_CACHE + +# define HC_GET_HDR(p, hhdr, source) \ +	{ \ +	  GET_HDR(p, hhdr); \ +          if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { \ +	    ADVANCE(p, hhdr, source); \ +	  } \ +	} +#endif +  typedef struct bi {      hdr * index[BOTTOM_SZ];  	/* diff --git a/gc/include/private/gc_locks.h b/gc/include/private/gc_locks.h new file mode 100644 index 0000000..eed9f10 --- /dev/null +++ b/gc/include/private/gc_locks.h @@ -0,0 +1,481 @@ +/*  + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics.  All rights reserved. + * Copyright (c) 1999 by Hewlett-Packard Company. All rights reserved. + * + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose,  provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifndef GC_LOCKS_H +#define GC_LOCKS_H + +/* + * Mutual exclusion between allocator/collector routines. + * Needed if there is more than one allocator thread. + * FASTLOCK() is assumed to try to acquire the lock in a cheap and + * dirty way that is acceptable for a few instructions, e.g. by + * inhibiting preemption.  This is assumed to have succeeded only + * if a subsequent call to FASTLOCK_SUCCEEDED() returns TRUE. + * FASTUNLOCK() is called whether or not FASTLOCK_SUCCEEDED(). + * If signals cannot be tolerated with the FASTLOCK held, then + * FASTLOCK should disable signals.  The code executed under + * FASTLOCK is otherwise immune to interruption, provided it is + * not restarted. + * DCL_LOCK_STATE declares any local variables needed by LOCK and UNLOCK + * and/or DISABLE_SIGNALS and ENABLE_SIGNALS and/or FASTLOCK. + * (There is currently no equivalent for FASTLOCK.) + * + * In the PARALLEL_MARK case, we also need to define a number of + * other inline finctions here: + *   GC_bool GC_compare_and_exchange( volatile GC_word *addr, + *   				      GC_word old, GC_word new ) + *   GC_word GC_atomic_add( volatile GC_word *addr, GC_word how_much ) + *   void GC_memory_barrier( ) + *    + */   +# ifdef THREADS +   void GC_noop1 GC_PROTO((word)); +#  ifdef PCR_OBSOLETE	/* Faster, but broken with multiple lwp's	*/ +#    include  "th/PCR_Th.h" +#    include  "th/PCR_ThCrSec.h" +     extern struct PCR_Th_MLRep GC_allocate_ml; +#    define DCL_LOCK_STATE  PCR_sigset_t GC_old_sig_mask +#    define LOCK() PCR_Th_ML_Acquire(&GC_allocate_ml)  +#    define UNLOCK() PCR_Th_ML_Release(&GC_allocate_ml) +#    define UNLOCK() PCR_Th_ML_Release(&GC_allocate_ml) +#    define FASTLOCK() PCR_ThCrSec_EnterSys() +     /* Here we cheat (a lot): */ +#        define FASTLOCK_SUCCEEDED() (*(int *)(&GC_allocate_ml) == 0) +		/* TRUE if nobody currently holds the lock */ +#    define FASTUNLOCK() PCR_ThCrSec_ExitSys() +#  endif +#  ifdef PCR +#    include <base/PCR_Base.h> +#    include <th/PCR_Th.h> +     extern PCR_Th_ML GC_allocate_ml; +#    define DCL_LOCK_STATE \ +	 PCR_ERes GC_fastLockRes; PCR_sigset_t GC_old_sig_mask +#    define LOCK() PCR_Th_ML_Acquire(&GC_allocate_ml) +#    define UNLOCK() PCR_Th_ML_Release(&GC_allocate_ml) +#    define FASTLOCK() (GC_fastLockRes = PCR_Th_ML_Try(&GC_allocate_ml)) +#    define FASTLOCK_SUCCEEDED() (GC_fastLockRes == PCR_ERes_okay) +#    define FASTUNLOCK()  {\ +        if( FASTLOCK_SUCCEEDED() ) PCR_Th_ML_Release(&GC_allocate_ml); } +#  endif +#  ifdef SRC_M3 +     extern GC_word RT0u__inCritical; +#    define LOCK() RT0u__inCritical++ +#    define UNLOCK() RT0u__inCritical-- +#  endif +#  ifdef SOLARIS_THREADS +#    include <thread.h> +#    include <signal.h> +     extern mutex_t GC_allocate_ml; +#    define LOCK() mutex_lock(&GC_allocate_ml); +#    define UNLOCK() mutex_unlock(&GC_allocate_ml); +#  endif + +/* Try to define GC_TEST_AND_SET and a matching GC_CLEAR for spin lock	*/ +/* acquisition and release.  We need this for correct operation of the	*/ +/* incremental GC.							*/ +#  ifdef __GNUC__ +#    if defined(I386) +       inline static int GC_test_and_set(volatile unsigned int *addr) { +	  int oldval; +	  /* Note: the "xchg" instruction does not need a "lock" prefix */ +	  __asm__ __volatile__("xchgl %0, %1" +		: "=r"(oldval), "=m"(*(addr)) +		: "0"(1), "m"(*(addr)) : "memory"); +	  return oldval; +       } +#      define GC_TEST_AND_SET_DEFINED +#    endif +#    if defined(IA64) +       inline static int GC_test_and_set(volatile unsigned int *addr) { +	  long oldval, n = 1; +	  __asm__ __volatile__("xchg4 %0=%1,%2" +		: "=r"(oldval), "=m"(*addr) +		: "r"(n), "1"(*addr) : "memory"); +	  return oldval; +       } +#      define GC_TEST_AND_SET_DEFINED +       /* Should this handle post-increment addressing?? */ +       inline static void GC_clear(volatile unsigned int *addr) { +	 __asm__ __volatile__("st4.rel %0=r0" : "=m" (*addr) : : "memory"); +       } +#      define GC_CLEAR_DEFINED +#    endif +#    ifdef SPARC +       inline static int GC_test_and_set(volatile unsigned int *addr) { +	 int oldval; + +	 __asm__ __volatile__("ldstub %1,%0" +	 : "=r"(oldval), "=m"(*addr) +	 : "m"(*addr) : "memory"); +	 return oldval; +       } +#      define GC_TEST_AND_SET_DEFINED +#    endif +#    ifdef M68K +       /* Contributed by Tony Mantler.  I'm not sure how well it was	*/ +       /* tested.							*/ +       inline static int GC_test_and_set(volatile unsigned int *addr) { +          char oldval; /* this must be no longer than 8 bits */ + +          /* The return value is semi-phony. */ +          /* 'tas' sets bit 7 while the return */ +          /* value pretends bit 0 was set */ +          __asm__ __volatile__( +                 "tas %1@; sne %0; negb %0" +                 : "=d" (oldval) +                 : "a" (addr) : "memory"); +          return oldval; +       } +#      define GC_TEST_AND_SET_DEFINED +#    endif +#    if defined(POWERPC) +        inline static int GC_test_and_set(volatile unsigned int *addr) { +          int oldval; +          int temp = 1; // locked value + +          __asm__ __volatile__( +               "1:\tlwarx %0,0,%3\n"   // load and reserve +               "\tcmpwi %0, 0\n"       // if load is +               "\tbne 2f\n"            //   non-zero, return already set +               "\tstwcx. %2,0,%1\n"    // else store conditional +               "\tbne- 1b\n"           // retry if lost reservation +               "2:\t\n"                // oldval is zero if we set +              : "=&r"(oldval), "=p"(addr) +              : "r"(temp), "1"(addr) +              : "memory"); +          return (int)oldval; +        } +#       define GC_TEST_AND_SET_DEFINED +        inline static void GC_clear(volatile unsigned int *addr) { +	  __asm__ __volatile__("eieio" ::: "memory"); +          *(addr) = 0; +        } +#       define GC_CLEAR_DEFINED +#    endif +#    if defined(ALPHA)  +        inline static int GC_test_and_set(volatile unsigned int * addr) +        { +          unsigned long oldvalue; +          unsigned long temp; + +          __asm__ __volatile__( +                             "1:     ldl_l %0,%1\n" +                             "       and %0,%3,%2\n" +                             "       bne %2,2f\n" +                             "       xor %0,%3,%0\n" +                             "       stl_c %0,%1\n" +                             "       beq %0,3f\n" +                             "       mb\n" +                             "2:\n" +                             ".section .text2,\"ax\"\n" +                             "3:     br 1b\n" +                             ".previous" +                             :"=&r" (temp), "=m" (*addr), "=&r" (oldvalue) +                             :"Ir" (1), "m" (*addr) +			     :"memory"); + +          return oldvalue; +        } +#       define GC_TEST_AND_SET_DEFINED +        /* Should probably also define GC_clear, since it needs	*/ +        /* a memory barrier ??					*/ +#    endif /* ALPHA */ +#    ifdef ARM32 +        inline static int GC_test_and_set(volatile unsigned int *addr) { +          int oldval; +          /* SWP on ARM is very similar to XCHG on x86.  Doesn't lock the +           * bus because there are no SMP ARM machines.  If/when there are, +           * this code will likely need to be updated. */ +          /* See linuxthreads/sysdeps/arm/pt-machine.h in glibc-2.1 */ +          __asm__ __volatile__("swp %0, %1, [%2]" +      		  	     : "=r"(oldval) +      			     : "r"(1), "r"(addr) +			     : "memory"); +          return oldval; +        } +#       define GC_TEST_AND_SET_DEFINED +#    endif /* ARM32 */ +#  endif /* __GNUC__ */ +#  if (defined(ALPHA) && !defined(__GNUC__)) +#    define GC_test_and_set(addr) __cxx_test_and_set_atomic(addr, 1) +#    define GC_TEST_AND_SET_DEFINED +#  endif +#  if defined(MSWIN32) +#    define GC_test_and_set(addr) InterlockedExchange((LPLONG)addr,1) +#    define GC_TEST_AND_SET_DEFINED +#  endif +#  ifdef MIPS +#    if __mips < 3 || !(defined (_ABIN32) || defined(_ABI64)) \ +	|| !defined(_COMPILER_VERSION) || _COMPILER_VERSION < 700 +#        define GC_test_and_set(addr, v) test_and_set(addr,v) +#    else +#	 define GC_test_and_set(addr, v) __test_and_set(addr,v) +#	 define GC_clear(addr) __lock_release(addr); +#	 define GC_CLEAR_DEFINED +#    endif +#    define GC_TEST_AND_SET_DEFINED +#  endif /* MIPS */ +#  if 0 /* defined(HP_PA) */ +     /* The official recommendation seems to be to not use ldcw from	*/ +     /* user mode.  Since multithreaded incremental collection doesn't	*/ +     /* work anyway on HP_PA, this shouldn't be a major loss.		*/ + +     /* "set" means 0 and "clear" means 1 here.		*/ +#    define GC_test_and_set(addr) !GC_test_and_clear(addr); +#    define GC_TEST_AND_SET_DEFINED +#    define GC_clear(addr) GC_noop1((word)(addr)); *(volatile unsigned int *)addr = 1; +	/* The above needs a memory barrier! */ +#    define GC_CLEAR_DEFINED +#  endif +#  if defined(GC_TEST_AND_SET_DEFINED) && !defined(GC_CLEAR_DEFINED) +#    ifdef __GNUC__ +       inline static void GC_clear(volatile unsigned int *addr) { +         /* Try to discourage gcc from moving anything past this. */ +         __asm__ __volatile__(" " : : : "memory"); +         *(addr) = 0; +       } +#    else +	    /* The function call in the following should prevent the	*/ +	    /* compiler from moving assignments to below the UNLOCK.	*/ +#      define GC_clear(addr) GC_noop1((word)(addr)); \ +			     *((volatile unsigned int *)(addr)) = 0; +#    endif +#    define GC_CLEAR_DEFINED +#  endif /* !GC_CLEAR_DEFINED */ + +#  if !defined(GC_TEST_AND_SET_DEFINED) +#    define USE_PTHREAD_LOCKS +#  endif + +#  if defined(LINUX_THREADS) || defined(OSF1_THREADS) \ +      || defined(HPUX_THREADS) +#    define NO_THREAD (pthread_t)(-1) +#    include <pthread.h> +#    if defined(PARALLEL_MARK)  +      /* We need compare-and-swap to update mark bits, where it's	*/ +      /* performance critical.  If USE_MARK_BYTES is defined, it is	*/ +      /* no longer needed for this purpose.  However we use it in	*/ +      /* either case to implement atomic fetch-and-add, though that's	*/ +      /* less performance critical, and could perhaps be done with	*/ +      /* a lock.							*/ +#     if defined(GENERIC_COMPARE_AND_SWAP) +	/* Probably not useful, except for debugging.	*/ +	/* We do use GENERIC_COMPARE_AND_SWAP on PA_RISC, but we 	*/ +	/* minimize its use.						*/ +	extern pthread_mutex_t GC_compare_and_swap_lock; + +	/* Note that if GC_word updates are not atomic, a concurrent 	*/ +	/* reader should acquire GC_compare_and_swap_lock.  On 		*/ +	/* currently supported platforms, such updates are atomic.	*/ +	extern GC_bool GC_compare_and_exchange(volatile GC_word *addr, +					       GC_word old, GC_word new_val); +#     endif /* GENERIC_COMPARE_AND_SWAP */ +#     if defined(I386) +#      if !defined(GENERIC_COMPARE_AND_SWAP) +         /* Returns TRUE if the comparison succeeded. */ +         inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr, +		  				       GC_word old, +						       GC_word new_val)  +         { +	   char result; +	   __asm__ __volatile__("lock; cmpxchgl %2, %0; setz %1" +	    	: "=m"(*(addr)), "=r"(result) +		: "r" (new_val), "0"(*(addr)), "a"(old) : "memory"); +	   return (GC_bool) result; +         } +#      endif /* !GENERIC_COMPARE_AND_SWAP */ +       inline static void GC_memory_write_barrier() +       { +	 /* We believe the processor ensures at least processor	*/ +	 /* consistent ordering.  Thus a compiler barrier	*/ +	 /* should suffice.					*/ +         __asm__ __volatile__("" : : : "memory"); +       } +#     endif /* I386 */ +#     if defined(IA64) +#      if !defined(GENERIC_COMPARE_AND_SWAP) +         inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr, +						       GC_word old, GC_word new_val)  +	 { +	  unsigned long oldval; +	  __asm__ __volatile__("mov ar.ccv=%4 ;; cmpxchg8.rel %0=%1,%2,ar.ccv" +		: "=r"(oldval), "=m"(*addr) +		: "r"(new_val), "1"(*addr), "r"(old) : "memory"); +	  return (oldval == old); +         } +#      endif /* !GENERIC_COMPARE_AND_SWAP */ +#      if 0 +	/* Shouldn't be needed; we use volatile stores instead. */ +        inline static void GC_memory_write_barrier() +        { +          __asm__ __volatile__("mf" : : : "memory"); +        } +#      endif /* 0 */ +#     endif /* IA64 */ +#     if !defined(GENERIC_COMPARE_AND_SWAP) +        /* Returns the original value of *addr.	*/ +        inline static GC_word GC_atomic_add(volatile GC_word *addr, +					    GC_word how_much) +        { +	  GC_word old; +	  do { +	    old = *addr; +	  } while (!GC_compare_and_exchange(addr, old, old+how_much)); +          return old; +        } +#     else /* GENERIC_COMPARE_AND_SWAP */ +	/* So long as a GC_word can be atomically updated, it should	*/ +	/* be OK to read *addr without a lock.				*/ +	extern GC_word GC_atomic_add(volatile GC_word *addr, GC_word how_much); +#     endif /* GENERIC_COMPARE_AND_SWAP */ + +#    endif /* PARALLEL_MARK */ + +#    if !defined(THREAD_LOCAL_ALLOC) && !defined(USE_PTHREAD_LOCKS) +      /* In the THREAD_LOCAL_ALLOC case, the allocation lock tends to	*/ +      /* be held for long periods, if it is held at all.  Thus spinning	*/ +      /* and sleeping for fixed periods are likely to result in 	*/ +      /* significant wasted time.  We thus rely mostly on queued locks. */ +#     define USE_SPIN_LOCK +      extern volatile unsigned int GC_allocate_lock; +      extern void GC_lock(void); +	/* Allocation lock holder.  Only set if acquired by client through */ +	/* GC_call_with_alloc_lock.					   */ +#     ifdef GC_ASSERTIONS +#        define LOCK() \ +		{ if (GC_test_and_set(&GC_allocate_lock)) GC_lock(); \ +		  SET_LOCK_HOLDER(); } +#        define UNLOCK() \ +		{ GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \ +	          GC_clear(&GC_allocate_lock); } +#     else +#        define LOCK() \ +		{ if (GC_test_and_set(&GC_allocate_lock)) GC_lock(); } +#        define UNLOCK() \ +		GC_clear(&GC_allocate_lock) +#     endif /* !GC_ASSERTIONS */ +#     if 0 +	/* Another alternative for OSF1 might be:		*/ +#       include <sys/mman.h> +        extern msemaphore GC_allocate_semaphore; +#       define LOCK() { if (msem_lock(&GC_allocate_semaphore, MSEM_IF_NOWAIT) \ + 			    != 0) GC_lock(); else GC_allocate_lock = 1; } +        /* The following is INCORRECT, since the memory model is too weak. */ +	/* Is this true?  Presumably msem_unlock has the right semantics?  */ +	/*		- HB						   */ +#       define UNLOCK() { GC_allocate_lock = 0; \ +                          msem_unlock(&GC_allocate_semaphore, 0); } +#     endif /* 0 */ +#    else /* THREAD_LOCAL_ALLOC  || USE_PTHREAD_LOCKS */ +#      ifndef USE_PTHREAD_LOCKS +#        define USE_PTHREAD_LOCKS +#      endif +#    endif /* THREAD_LOCAL_ALLOC */ +#   ifdef USE_PTHREAD_LOCKS +#      include <pthread.h> +       extern pthread_mutex_t GC_allocate_ml; +#      ifdef GC_ASSERTIONS +#        define LOCK() \ +		{ GC_lock(); \ +		  SET_LOCK_HOLDER(); } +#        define UNLOCK() \ +		{ GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \ +	          pthread_mutex_unlock(&GC_allocate_ml); } +#      else /* !GC_ASSERTIONS */ +#        define LOCK() \ +	   { if (0 != pthread_mutex_trylock(&GC_allocate_ml)) GC_lock(); } +#        define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml) +#      endif /* !GC_ASSERTIONS */ +#   endif /* USE_PTHREAD_LOCKS */ +#   define SET_LOCK_HOLDER() GC_lock_holder = pthread_self() +#   define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD +#   define I_HOLD_LOCK() (pthread_equal(GC_lock_holder, pthread_self())) +    extern VOLATILE GC_bool GC_collecting; +#   define ENTER_GC() GC_collecting = 1; +#   define EXIT_GC() GC_collecting = 0; +    extern void GC_lock(void); +    extern pthread_t GC_lock_holder; +#   ifdef GC_ASSERTIONS +      extern pthread_t GC_mark_lock_holder; +#   endif +#  endif /* LINUX_THREADS || OSF1_THREADS  || HPUX_THREADS */ +#  if defined(IRIX_THREADS) +#    include <pthread.h> +     /* This probably should never be included, but I can't test	*/ +     /* on Irix anymore.						*/ +#    include <mutex.h> + +     extern unsigned long GC_allocate_lock; +	/* This is not a mutex because mutexes that obey the (optional) 	*/ +	/* POSIX scheduling rules are subject to convoys in high contention	*/ +	/* applications.  This is basically a spin lock.			*/ +     extern pthread_t GC_lock_holder; +     extern void GC_lock(void); +	/* Allocation lock holder.  Only set if acquired by client through */ +	/* GC_call_with_alloc_lock.					   */ +#    define SET_LOCK_HOLDER() GC_lock_holder = pthread_self() +#    define NO_THREAD (pthread_t)(-1) +#    define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD +#    define I_HOLD_LOCK() (pthread_equal(GC_lock_holder, pthread_self())) +#    define LOCK() { if (GC_test_and_set(&GC_allocate_lock, 1)) GC_lock(); } +#    define UNLOCK() GC_clear(&GC_allocate_lock); +     extern VOLATILE GC_bool GC_collecting; +#    define ENTER_GC() \ +		{ \ +		    GC_collecting = 1; \ +		} +#    define EXIT_GC() GC_collecting = 0; +#  endif /* IRIX_THREADS */ +#  ifdef WIN32_THREADS +#    include <windows.h> +     GC_API CRITICAL_SECTION GC_allocate_ml; +#    define LOCK() EnterCriticalSection(&GC_allocate_ml); +#    define UNLOCK() LeaveCriticalSection(&GC_allocate_ml); +#  endif +#  ifndef SET_LOCK_HOLDER +#      define SET_LOCK_HOLDER() +#      define UNSET_LOCK_HOLDER() +#      define I_HOLD_LOCK() FALSE +		/* Used on platforms were locks can be reacquired,	*/ +		/* so it doesn't matter if we lie.			*/ +#  endif +# else /* !THREADS */ +#    define LOCK() +#    define UNLOCK() +# endif /* !THREADS */ +# ifndef SET_LOCK_HOLDER +#   define SET_LOCK_HOLDER() +#   define UNSET_LOCK_HOLDER() +#   define I_HOLD_LOCK() FALSE +		/* Used on platforms were locks can be reacquired,	*/ +		/* so it doesn't matter if we lie.			*/ +# endif +# ifndef ENTER_GC +#   define ENTER_GC() +#   define EXIT_GC() +# endif + +# ifndef DCL_LOCK_STATE +#   define DCL_LOCK_STATE +# endif +# ifndef FASTLOCK +#   define FASTLOCK() LOCK() +#   define FASTLOCK_SUCCEEDED() TRUE +#   define FASTUNLOCK() UNLOCK() +# endif + +#endif /* GC_LOCKS_H */ diff --git a/gc/include/private/gc_pmark.h b/gc/include/private/gc_pmark.h new file mode 100644 index 0000000..cf70706 --- /dev/null +++ b/gc/include/private/gc_pmark.h @@ -0,0 +1,388 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved. + * Copyright (c) 2001 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose,  provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ + +/* Private declarations of GC marker data structures and macros */ + +/* + * Declarations of mark stack.  Needed by marker and client supplied mark + * routines.  Transitively include gc_priv.h. + * (Note that gc_priv.h should not be included before this, since this + * includes dbg_mlc.h, which wants to include gc_priv.h AFTER defining + * I_HIDE_POINTERS.) + */ +#ifndef GC_PMARK_H +# define GC_PMARK_H + +# if defined(KEEP_BACK_PTRS) || defined(PRINT_BLACK_LIST) +#   include "dbg_mlc.h" +# endif +# ifndef GC_MARK_H +#   include "../gc_mark.h" +# endif +# ifndef GC_PRIVATE_H +#   include "gc_priv.h" +# endif + +/* The real declarations of the following is in gc_priv.h, so that	*/ +/* we can avoid scanning the following table.				*/ +/* +extern mark_proc GC_mark_procs[MAX_MARK_PROCS]; +*/ + +/* + * Mark descriptor stuff that should remain private for now, mostly + * because it's hard to export WORDSZ without including gcconfig.h. + */ +# define BITMAP_BITS (WORDSZ - GC_DS_TAG_BITS) +# define PROC(descr) \ +	(GC_mark_procs[((descr) >> GC_DS_TAG_BITS) & (GC_MAX_MARK_PROCS-1)]) +# define ENV(descr) \ +	((descr) >> (GC_DS_TAG_BITS + GC_LOG_MAX_MARK_PROCS)) +# define MAX_ENV \ +  	(((word)1 << (WORDSZ - GC_DS_TAG_BITS - GC_LOG_MAX_MARK_PROCS)) - 1) + + +extern word GC_n_mark_procs; + +/* Number of mark stack entries to discard on overflow.	*/ +#define GC_MARK_STACK_DISCARDS (INITIAL_MARK_STACK_SIZE/8) + +typedef struct GC_ms_entry { +    GC_word * mse_start;   /* First word of object */ +    GC_word mse_descr;	/* Descriptor; low order two bits are tags,	*/ +    			/* identifying the upper 30 bits as one of the	*/ +    			/* following:					*/ +} mse; + +extern word GC_mark_stack_size; + +extern mse * GC_mark_stack_limit; + +#ifdef PARALLEL_MARK +  extern mse * VOLATILE GC_mark_stack_top; +#else +  extern mse * GC_mark_stack_top; +#endif + +extern mse * GC_mark_stack; + +#ifdef PARALLEL_MARK +    /* +     * Allow multiple threads to participate in the marking process. +     * This works roughly as follows: +     *  The main mark stack never shrinks, but it can grow. +     * +     *	The initiating threads holds the GC lock, and sets GC_help_wanted. +     *   +     *  Other threads: +     *     1) update helper_count (while holding mark_lock.) +     *	   2) allocate a local mark stack +     *     repeatedly: +     *		3) Steal a global mark stack entry by atomically replacing +     *		   its descriptor with 0. +     *		4) Copy it to the local stack. +     *	        5) Mark on the local stack until it is empty, or +     *		   it may be profitable to copy it back. +     *	        6) If necessary, copy local stack to global one, +     *		   holding mark lock. +     *    7) Stop when the global mark stack is empty. +     *    8) decrement helper_count (holding mark_lock). +     * +     * This is an experiment to see if we can do something along the lines +     * of the University of Tokyo SGC in a less intrusive, though probably +     * also less performant, way. +     */ +    void GC_do_parallel_mark(); +		/* inititate parallel marking.	*/ + +    extern GC_bool GC_help_wanted;	/* Protected by mark lock	*/ +    extern unsigned GC_helper_count;	/* Number of running helpers.	*/ +					/* Protected by mark lock	*/ +    extern unsigned GC_active_count;	/* Number of active helpers.	*/ +					/* Protected by mark lock	*/ +					/* May increase and decrease	*/ +					/* within each mark cycle.  But	*/ +					/* once it returns to 0, it	*/ +					/* stays zero for the cycle.	*/ +    /* GC_mark_stack_top is also protected by mark lock.	*/ +    extern mse * VOLATILE GC_first_nonempty; +					/* Lowest entry on mark stack	*/ +					/* that may be nonempty.	*/ +					/* Updated only by initiating 	*/ +					/* thread.			*/ +    /* +     * GC_notify_all_marker() is used when GC_help_wanted is first set, +     * when the last helper becomes inactive, +     * when something is added to the global mark stack, and just after +     * GC_mark_no is incremented. +     * This could be split into multiple CVs (and probably should be to +     * scale to really large numbers of processors.) +     */ +#endif /* PARALLEL_MARK */ + +/* Return a pointer to within 1st page of object.  	*/ +/* Set *new_hdr_p to corr. hdr.				*/ +#ifdef __STDC__ +# ifdef PRINT_BLACK_LIST +    ptr_t GC_find_start(ptr_t current, hdr *hhdr, hdr **new_hdr_p, word source); +# else +    ptr_t GC_find_start(ptr_t current, hdr *hhdr, hdr **new_hdr_p); +# endif +#else +  ptr_t GC_find_start(); +#endif + +mse * GC_signal_mark_stack_overflow(); + +# ifdef GATHERSTATS +#   define ADD_TO_ATOMIC(sz) GC_atomic_in_use += (sz) +#   define ADD_TO_COMPOSITE(sz) GC_composite_in_use += (sz) +# else +#   define ADD_TO_ATOMIC(sz) +#   define ADD_TO_COMPOSITE(sz) +# endif + +/* Push the object obj with corresponding heap block header hhdr onto 	*/ +/* the mark stack.							*/ +# define PUSH_OBJ(obj, hhdr, mark_stack_top, mark_stack_limit) \ +{ \ +    register word _descr = (hhdr) -> hb_descr; \ +        \ +    if (_descr == 0) { \ +    	ADD_TO_ATOMIC((hhdr) -> hb_sz); \ +    } else { \ +        ADD_TO_COMPOSITE((hhdr) -> hb_sz); \ +        mark_stack_top++; \ +        if (mark_stack_top >= mark_stack_limit) { \ +          mark_stack_top = GC_signal_mark_stack_overflow(mark_stack_top); \ +        } \ +        mark_stack_top -> mse_start = (obj); \ +        mark_stack_top -> mse_descr = _descr; \ +    } \ +} + +#ifdef PRINT_BLACK_LIST +#   define GC_FIND_START(current, hhdr, new_hdr_p, source) \ +	GC_find_start(current, hhdr, new_hdr_p, source) +#else +#   define GC_FIND_START(current, hhdr, new_hdr_p, source) \ +	GC_find_start(current, hhdr, new_hdr_p) +#endif + +/* Push the contents of current onto the mark stack if it is a valid	*/ +/* ptr to a currently unmarked object.  Mark it.			*/ +/* If we assumed a standard-conforming compiler, we could probably	*/ +/* generate the exit_label transparently.				*/ +# define PUSH_CONTENTS(current, mark_stack_top, mark_stack_limit, \ +		       source, exit_label) \ +{ \ +    hdr * my_hhdr; \ +    ptr_t my_current = current; \ + \ +    GET_HDR(my_current, my_hhdr); \ +    if (IS_FORWARDING_ADDR_OR_NIL(my_hhdr)) { \ +	 hdr * new_hdr = GC_invalid_header; \ +         my_current = GC_FIND_START(my_current, my_hhdr, \ +			 	    &new_hdr, (word)source); \ +         my_hhdr = new_hdr; \ +    } \ +    PUSH_CONTENTS_HDR(my_current, mark_stack_top, mark_stack_limit, \ +		  source, exit_label, my_hhdr);	\ +exit_label: ; \ +} + +/* As above, but use header cache for header lookup.	*/ +# define HC_PUSH_CONTENTS(current, mark_stack_top, mark_stack_limit, \ +		       source, exit_label) \ +{ \ +    hdr * my_hhdr; \ +    ptr_t my_current = current; \ + \ +    HC_GET_HDR(my_current, my_hhdr, source); \ +    PUSH_CONTENTS_HDR(my_current, mark_stack_top, mark_stack_limit, \ +		  source, exit_label, my_hhdr);	\ +exit_label: ; \ +} + +/* Set mark bit, exit if it was already set.	*/ + +# ifdef USE_MARK_BYTES +    /* Unlike the mark bit case, there is a race here, and we may set	*/ +    /* the bit twice in the concurrent case.  This can result in the	*/ +    /* object being pushed twice.  But that's only a performance issue.	*/ +#   define SET_MARK_BIT_EXIT_IF_SET(hhdr,displ,exit_label) \ +    { \ +        register VOLATILE char * mark_byte_addr = \ +				hhdr -> hb_marks + ((displ) >> 1); \ +        register char mark_byte = *mark_byte_addr; \ +          \ +	if (mark_byte) goto exit_label; \ +	*mark_byte_addr = 1;  \ +    }  +# else +#   define SET_MARK_BIT_EXIT_IF_SET(hhdr,displ,exit_label) \ +    { \ +        register word * mark_word_addr = hhdr -> hb_marks + divWORDSZ(displ); \ +        register word mark_word = *mark_word_addr; \ +          \ +        OR_WORD_EXIT_IF_SET(mark_word_addr, (word)1 << modWORDSZ(displ), \ +			    exit_label); \ +    }  +# endif /* USE_MARK_BYTES */ + +/* If the mark bit corresponding to current is not set, set it, and 	*/ +/* push the contents of the object on the mark stack.  For a small 	*/ +/* object we assume that current is the (possibly interior) pointer	*/ +/* to the object.  For large objects we assume that current points	*/ +/* to somewhere inside the first page of the object.  If		*/ +/* GC_all_interior_pointers is set, it may have been previously 	*/ +/* adjusted to make that true.						*/ +# define PUSH_CONTENTS_HDR(current, mark_stack_top, mark_stack_limit, \ +		           source, exit_label, hhdr) \ +{ \ +    int displ;  /* Displacement in block; first bytes, then words */ \ +    int map_entry; \ +    \ +    displ = HBLKDISPL(current); \ +    map_entry = MAP_ENTRY((hhdr -> hb_map), displ); \ +    displ = BYTES_TO_WORDS(displ); \ +    if (map_entry > CPP_MAX_OFFSET) { \ +	if (map_entry == OFFSET_TOO_BIG) { \ +	  map_entry = displ % (hhdr -> hb_sz); \ +	  displ -= map_entry; \ +	  if (displ + (hhdr -> hb_sz) > BYTES_TO_WORDS(HBLKSIZE)) { \ +	    GC_ADD_TO_BLACK_LIST_NORMAL((word)current, source); \ +	    goto exit_label; \ +	  } \ +	} else { \ +          GC_ADD_TO_BLACK_LIST_NORMAL((word)current, source); goto exit_label; \ +	} \ +    } else { \ +        displ -= map_entry; \ +    } \ +    GC_ASSERT(displ >= 0 && displ < MARK_BITS_PER_HBLK); \ +    SET_MARK_BIT_EXIT_IF_SET(hhdr, displ, exit_label); \ +    GC_STORE_BACK_PTR((ptr_t)source, (ptr_t)HBLKPTR(current) \ +				      + WORDS_TO_BYTES(displ)); \ +    PUSH_OBJ(((word *)(HBLKPTR(current)) + displ), hhdr, \ +    	     mark_stack_top, mark_stack_limit) \ +} + +#if defined(PRINT_BLACK_LIST) || defined(KEEP_BACK_PTRS) +#   define PUSH_ONE_CHECKED_STACK(p, source) \ +	GC_mark_and_push_stack(p, (ptr_t)(source)) +#else +#   define PUSH_ONE_CHECKED_STACK(p, source) \ +	GC_mark_and_push_stack(p) +#endif + +/* + * Push a single value onto mark stack. Mark from the object pointed to by p. + * P is considered valid even if it is an interior pointer. + * Previously marked objects are not pushed.  Hence we make progress even + * if the mark stack overflows. + */ +# define GC_PUSH_ONE_STACK(p, source) \ +    if ((ptr_t)(p) >= (ptr_t)GC_least_plausible_heap_addr 	\ +	 && (ptr_t)(p) < (ptr_t)GC_greatest_plausible_heap_addr) {	\ +	 PUSH_ONE_CHECKED_STACK(p, source);	\ +    } + +/* + * As above, but interior pointer recognition as for + * normal for heap pointers. + */ +# define GC_PUSH_ONE_HEAP(p,source) \ +    if ((ptr_t)(p) >= (ptr_t)GC_least_plausible_heap_addr 	\ +	 && (ptr_t)(p) < (ptr_t)GC_greatest_plausible_heap_addr) {	\ +	    GC_mark_stack_top = GC_mark_and_push( \ +			    (GC_PTR)(p), GC_mark_stack_top, \ +			    GC_mark_stack_limit, (GC_PTR *)(source)); \ +    } + +/* Mark starting at mark stack entry top (incl.) down to	*/ +/* mark stack entry bottom (incl.).  Stop after performing	*/ +/* about one page worth of work.  Return the new mark stack	*/ +/* top entry.							*/ +mse * GC_mark_from GC_PROTO((mse * top, mse * bottom, mse *limit)); + +#define MARK_FROM_MARK_STACK() \ +	GC_mark_stack_top = GC_mark_from(GC_mark_stack_top, \ +					 GC_mark_stack, \ +					 GC_mark_stack + GC_mark_stack_size); + +/* + * Mark from one finalizable object using the specified + * mark proc. May not mark the object pointed to by  + * real_ptr. That is the job of the caller, if appropriate + */ +# define GC_MARK_FO(real_ptr, mark_proc) \ +{ \ +    (*(mark_proc))(real_ptr); \ +    while (!GC_mark_stack_empty()) MARK_FROM_MARK_STACK(); \ +    if (GC_mark_state != MS_NONE) { \ +        GC_set_mark_bit(real_ptr); \ +        while (!GC_mark_some((ptr_t)0)) {} \ +    } \ +} + +extern GC_bool GC_mark_stack_too_small; +				/* We need a larger mark stack.  May be	*/ +				/* set by client supplied mark routines.*/ + +typedef int mark_state_t;	/* Current state of marking, as follows:*/ +				/* Used to remember where we are during */ +				/* concurrent marking.			*/ + +				/* We say something is dirty if it was	*/ +				/* written since the last time we	*/ +				/* retrieved dirty bits.  We say it's 	*/ +				/* grungy if it was marked dirty in the	*/ +				/* last set of bits we retrieved.	*/ +				 +				/* Invariant I: all roots and marked	*/ +				/* objects p are either dirty, or point */ +				/* to objects q that are either marked 	*/ +				/* or a pointer to q appears in a range	*/ +				/* on the mark stack.			*/ + +# define MS_NONE 0		/* No marking in progress. I holds.	*/ +				/* Mark stack is empty.			*/ + +# define MS_PUSH_RESCUERS 1	/* Rescuing objects are currently 	*/ +				/* being pushed.  I holds, except	*/ +				/* that grungy roots may point to 	*/ +				/* unmarked objects, as may marked	*/ +				/* grungy objects above scan_ptr.	*/ + +# define MS_PUSH_UNCOLLECTABLE 2 +				/* I holds, except that marked 		*/ +				/* uncollectable objects above scan_ptr */ +				/* may point to unmarked objects.	*/ +				/* Roots may point to unmarked objects	*/ + +# define MS_ROOTS_PUSHED 3	/* I holds, mark stack may be nonempty  */ + +# define MS_PARTIALLY_INVALID 4	/* I may not hold, e.g. because of M.S. */ +				/* overflow.  However marked heap	*/ +				/* objects below scan_ptr point to	*/ +				/* marked or stacked objects.		*/ + +# define MS_INVALID 5		/* I may not hold.			*/ + +extern mark_state_t GC_mark_state; + +#endif  /* GC_PMARK_H */ + diff --git a/gc/include/private/gc_priv.h b/gc/include/private/gc_priv.h index 5ce52a7..5135e3e 100644 --- a/gc/include/private/gc_priv.h +++ b/gc/include/private/gc_priv.h @@ -1,6 +1,9 @@  /*    * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers   * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics.  All rights reserved. + * Copyright (c) 1999-2001 by Hewlett-Packard Company. All rights reserved. + *   *   * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED   * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK. @@ -11,7 +14,6 @@   * provided the above notices are retained, and a notice that the code was   * modified is included with the above copyright notice.   */ -/* Boehm, February 16, 1996 2:30 pm PST */  # ifndef GC_PRIVATE_H @@ -38,16 +40,16 @@  #   include "gc.h"  # endif -typedef GC_word word; -typedef GC_signed_word signed_word; +# ifndef GC_MARK_H +#   include "../gc_mark.h" +# endif -# ifndef CONFIG_H +# ifndef GCCONFIG_H  #   include "gcconfig.h"  # endif -# ifndef HEADERS_H -#   include "gc_hdrs.h" -# endif +typedef GC_word word; +typedef GC_signed_word signed_word;  typedef int GC_bool;  # define TRUE 1 @@ -58,27 +60,54 @@ typedef char * ptr_t;	/* A generic pointer to which we can add	*/  			/* Preferably identical to caddr_t, if it 	*/  			/* exists.					*/ +# ifndef HEADERS_H +#   include "gc_hdrs.h" +# endif +  #if defined(__STDC__)  #   include <stdlib.h>  #   if !(defined( sony_news ) )  #       include <stddef.h>  #   endif  #   define VOLATILE volatile -#   define CONST const  #else  #   ifdef MSWIN32  #   	include <stdlib.h>  #   endif  #   define VOLATILE -#   define CONST  #endif -#if 0 /* was once defined for AMIGA */ +#if 0 /* defined(__GNUC__) doesn't work yet */ +# define EXPECT(expr, outcome) __builtin_expect(expr,outcome) +  /* Equivalent to (expr), but predict that usually (expr)==outcome. */ +#else +# define EXPECT(expr, outcome) (expr) +#endif /* __GNUC__ */ + +# ifndef GC_LOCKS_H +#   include "gc_locks.h" +# endif + +# ifdef STACK_GROWS_DOWN +#   define COOLER_THAN > +#   define HOTTER_THAN < +#   define MAKE_COOLER(x,y) if ((word)(x)+(y) > (word)(x)) {(x) += (y);} \ +			    else {(x) = (word)ONES;} +#   define MAKE_HOTTER(x,y) (x) -= (y) +# else +#   define COOLER_THAN < +#   define HOTTER_THAN > +#   define MAKE_COOLER(x,y) if ((word)(x)-(y) < (word)(x)) {(x) -= (y);} else {(x) = 0;} +#   define MAKE_HOTTER(x,y) (x) += (y) +# endif + +#if defined(AMIGA) && defined(__SASC)  #   define GC_FAR __far  #else  #   define GC_FAR  #endif +  /*********************************/  /*                               */  /* Definitions for conservative  */ @@ -92,11 +121,9 @@ typedef char * ptr_t;	/* A generic pointer to which we can add	*/  /*                               */  /*********************************/ -#define STUBBORN_ALLOC	/* Define stubborn allocation primitives	*/ -#if defined(SRC_M3) || defined(SMALL_CONFIG) -# undef STUBBORN_ALLOC -#endif - +/* #define STUBBORN_ALLOC */ +		    /* Enable stubborm allocation, and thus a limited	*/ +		    /* form of incremental collection w/o dirty bits.	*/  /* #define ALL_INTERIOR_POINTERS */  		    /* Forces all pointers into the interior of an 	*/ @@ -123,6 +150,8 @@ typedef char * ptr_t;	/* A generic pointer to which we can add	*/  		    /*    touched.					*/  		    /* If you can easily avoid using this option, do.	*/  		    /* If not, try to keep individual objects small.	*/ +		    /* This is now really controlled at startup,	*/ +		    /* through GC_all_interior_pointers.		*/  #define PRINTSTATS  /* Print garbage collection statistics          	*/  		    /* For less verbose output, undefine in reclaim.c 	*/ @@ -153,12 +182,12 @@ typedef char * ptr_t;	/* A generic pointer to which we can add	*/  #   define GATHERSTATS  #endif -#ifdef FINALIZE_ON_DEMAND -#   define GC_INVOKE_FINALIZERS() -#else -#   define GC_INVOKE_FINALIZERS() (void)GC_invoke_finalizers() +#if defined(PRINTSTATS) || !defined(SMALL_CONFIG) +#   define CONDPRINT  /* Print some things if GC_print_stats is set */  #endif +#define GC_INVOKE_FINALIZERS() GC_notify_or_invoke_finalizers() +  #define MERGE_SIZES /* Round up some object sizes, so that fewer distinct */  		    /* free lists are actually maintained.  This applies  */  		    /* only to the top level routines in misc.c, not to   */ @@ -170,13 +199,10 @@ typedef char * ptr_t;	/* A generic pointer to which we can add	*/  		    /* May save significant amounts of space for obj_map  */  		    /* entries.						  */ -#ifndef OLD_BLOCK_ALLOC -   /* Macros controlling large block allocation strategy.	*/ -#  define EXACT_FIRST  	/* Make a complete pass through the large object */ -			/* free list before splitting a block		 */ -#  define PRESERVE_LAST /* Do not divide last allocated heap segment	 */ -			/* unless we would otherwise need to expand the	 */ -			/* heap.					 */ +#if defined(USE_MARK_BYTES) && !defined(ALIGN_DOUBLE) +#  define ALIGN_DOUBLE +   /* We use one byte for every 2 words, which doesn't allow for	*/ +   /* odd numbered words to have mark bits.				*/  #endif  /* ALIGN_DOUBLE requires MERGE_SIZES at present. */ @@ -184,15 +210,17 @@ typedef char * ptr_t;	/* A generic pointer to which we can add	*/  #   define MERGE_SIZES  # endif -#if defined(ALL_INTERIOR_POINTERS) && !defined(DONT_ADD_BYTE_AT_END) -# define ADD_BYTE_AT_END +#if !defined(DONT_ADD_BYTE_AT_END) +# define EXTRA_BYTES GC_all_interior_pointers +#else +# define EXTRA_BYTES 0  #endif  # ifndef LARGE_CONFIG -#   define MINHINCR 16	/* Minimum heap increment, in blocks of HBLKSIZE  */ -			/* Must be multiple of largest page size.	  */ -#   define MAXHINCR 512	/* Maximum heap increment, in blocks              */ +#   define MINHINCR 16	 /* Minimum heap increment, in blocks of HBLKSIZE  */ +			 /* Must be multiple of largest page size.	   */ +#   define MAXHINCR 2048 /* Maximum heap increment, in blocks              */  # else  #   define MINHINCR 64  #   define MAXHINCR 4096 @@ -225,17 +253,22 @@ typedef char * ptr_t;	/* A generic pointer to which we can add	*/   * Number of frames and arguments to save in objects allocated by   * debugging allocator.   */ -#   define NFRAMES 6	/* Number of frames to save. Even for		*/ +#   ifndef SAVE_CALL_COUNT +#     define NFRAMES 6	/* Number of frames to save. Even for		*/  			/* alignment reasons.				*/ +#   else +#     define NFRAMES ((SAVE_CALL_COUNT + 1) & ~1) +#   endif  #   define NARGS 2	/* Mumber of arguments to save for each call.	*/  #   define NEED_CALLINFO  /* Fill in the pc and argument information for up to NFRAMES of my	*/  /* callers.  Ignore my frame and my callers frame.			*/ -void GC_save_callers (/* struct callinfo info[NFRAMES] */); - -void GC_print_callers (/* struct callinfo info[NFRAMES] */); +struct callinfo; +void GC_save_callers GC_PROTO((struct callinfo info[NFRAMES])); +   +void GC_print_callers GC_PROTO((struct callinfo info[NFRAMES]));  #else @@ -249,7 +282,7 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */);  #ifdef NEED_CALLINFO      struct callinfo { -	word ci_pc; +	word ci_pc;  	/* Caller, not callee, pc	*/  #	if NARGS > 0  	    word ci_arg[NARGS];	/* bit-wise complement to avoid retention */  #	endif @@ -278,6 +311,13 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */);  #   define MS_TIME_DIFF(a,b) ((double) (a.tv_sec - b.tv_sec) * 1000.0 \                                 + (double) (a.tv_usec - b.tv_usec) / 1000.0)  #else /* !BSD_TIME */ +# if defined(MSWIN32) || defined(MSWINCE) +#   include <windows.h> +#   include <winbase.h> +#   define CLOCK_TYPE DWORD +#   define GET_TIME(x) x = GetTickCount() +#   define MS_TIME_DIFF(a,b) ((long)((a)-(b))) +# else /* !MSWIN32, !MSWINCE, !BSD_TIME */  #   include <time.h>  #   if !defined(__STDC__) && defined(SPARC) && defined(SUNOS4)        clock_t clock();	/* Not in time.h, where it belongs	*/ @@ -303,6 +343,7 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */);  #   define GET_TIME(x) x = clock()  #   define MS_TIME_DIFF(a,b) ((unsigned long) \  		(1000.0*(double)((a)-(b))/(double)CLOCKS_PER_SEC)) +# endif /* !MSWIN32 */  #endif /* !BSD_TIME */  /* We use bzero and bcopy internally.  They may not be available.	*/ @@ -322,6 +363,9 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */);  #   include <string.h>  #   define BCOPY_EXISTS  # endif +# if defined(MACOSX) +#   define BCOPY_EXISTS +# endif  # ifndef BCOPY_EXISTS  #   include <string.h> @@ -339,6 +383,7 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */);  /* GET_MEM is currently not assumed to retrieve 0 filled space, */  /* though we should perhaps take advantage of the case in which */  /* does.							*/ +struct hblk;	/* See below.	*/  # ifdef PCR      char * real_malloc();  #   define GET_MEM(bytes) HBLKPTR(real_malloc((size_t)bytes + GC_page_size) \ @@ -350,7 +395,8 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */);  				    + GC_page_size) \                                      + GC_page_size-1)  #   else -#     if defined(AMIGA) || defined(NEXT) || defined(MACOSX) || defined(DOS4GW) +#     if defined(NEXT) || defined(MACOSX) || defined(DOS4GW) || \ +	 (defined(AMIGA) && !defined(GC_AMIGA_FASTALLOC))  #       define GET_MEM(bytes) HBLKPTR((size_t) \  				      calloc(1, (size_t)bytes + GC_page_size) \                                        + GC_page_size-1) @@ -371,198 +417,26 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */);  			NewPtrClear(bytes + GC_page_size) + GC_page_size-1)  #	    endif  #	  else -              extern ptr_t GC_unix_get_mem(); -#             define GET_MEM(bytes) (struct hblk *)GC_unix_get_mem(bytes) +#	    ifdef MSWINCE +	      extern ptr_t GC_wince_get_mem(); +#	      define GET_MEM(bytes) (struct hblk *)GC_wince_get_mem(bytes) +#	    else +#	      if defined(AMIGA) && defined(GC_AMIGA_FASTALLOC) +	        extern void *GC_amiga_get_mem(size_t size); +		define GET_MEM(bytes) HBLKPTR((size_t) \ +                  GC_amiga_get_mem((size_t)bytes + GC_page_size) \ +		  + GC_page_size-1) +#	      else +                extern ptr_t GC_unix_get_mem(); +#               define GET_MEM(bytes) (struct hblk *)GC_unix_get_mem(bytes) +#	      endif +#	    endif  #	  endif  #	endif  #     endif  #   endif  # endif -/* - * Mutual exclusion between allocator/collector routines. - * Needed if there is more than one allocator thread. - * FASTLOCK() is assumed to try to acquire the lock in a cheap and - * dirty way that is acceptable for a few instructions, e.g. by - * inhibiting preemption.  This is assumed to have succeeded only - * if a subsequent call to FASTLOCK_SUCCEEDED() returns TRUE. - * FASTUNLOCK() is called whether or not FASTLOCK_SUCCEEDED(). - * If signals cannot be tolerated with the FASTLOCK held, then - * FASTLOCK should disable signals.  The code executed under - * FASTLOCK is otherwise immune to interruption, provided it is - * not restarted. - * DCL_LOCK_STATE declares any local variables needed by LOCK and UNLOCK - * and/or DISABLE_SIGNALS and ENABLE_SIGNALS and/or FASTLOCK. - * (There is currently no equivalent for FASTLOCK.) - */   -# ifdef THREADS -#  ifdef PCR_OBSOLETE	/* Faster, but broken with multiple lwp's	*/ -#    include  "th/PCR_Th.h" -#    include  "th/PCR_ThCrSec.h" -     extern struct PCR_Th_MLRep GC_allocate_ml; -#    define DCL_LOCK_STATE  PCR_sigset_t GC_old_sig_mask -#    define LOCK() PCR_Th_ML_Acquire(&GC_allocate_ml)  -#    define UNLOCK() PCR_Th_ML_Release(&GC_allocate_ml) -#    define FASTLOCK() PCR_ThCrSec_EnterSys() -     /* Here we cheat (a lot): */ -#        define FASTLOCK_SUCCEEDED() (*(int *)(&GC_allocate_ml) == 0) -		/* TRUE if nobody currently holds the lock */ -#    define FASTUNLOCK() PCR_ThCrSec_ExitSys() -#  endif -#  ifdef PCR -#    include <base/PCR_Base.h> -#    include <th/PCR_Th.h> -     extern PCR_Th_ML GC_allocate_ml; -#    define DCL_LOCK_STATE \ -	 PCR_ERes GC_fastLockRes; PCR_sigset_t GC_old_sig_mask -#    define LOCK() PCR_Th_ML_Acquire(&GC_allocate_ml) -#    define UNLOCK() PCR_Th_ML_Release(&GC_allocate_ml) -#    define FASTLOCK() (GC_fastLockRes = PCR_Th_ML_Try(&GC_allocate_ml)) -#    define FASTLOCK_SUCCEEDED() (GC_fastLockRes == PCR_ERes_okay) -#    define FASTUNLOCK()  {\ -        if( FASTLOCK_SUCCEEDED() ) PCR_Th_ML_Release(&GC_allocate_ml); } -#  endif -#  ifdef SRC_M3 -     extern word RT0u__inCritical; -#    define LOCK() RT0u__inCritical++ -#    define UNLOCK() RT0u__inCritical-- -#  endif -#  ifdef SOLARIS_THREADS -#    include <thread.h> -#    include <signal.h> -     extern mutex_t GC_allocate_ml; -#    define LOCK() mutex_lock(&GC_allocate_ml); -#    define UNLOCK() mutex_unlock(&GC_allocate_ml); -#  endif -#  ifdef LINUX_THREADS -#    include <pthread.h> -#    ifdef __i386__ -       inline static int GC_test_and_set(volatile unsigned int *addr) { -	  int oldval; -	  /* Note: the "xchg" instruction does not need a "lock" prefix */ -	  __asm__ __volatile__("xchgl %0, %1" -		: "=r"(oldval), "=m"(*(addr)) -		: "0"(1), "m"(*(addr))); -	  return oldval; -       } -#    else -       -- > Need implementation of GC_test_and_set() -#    endif -#    define GC_clear(addr) (*(addr) = 0) - -     extern volatile unsigned int GC_allocate_lock; -	/* This is not a mutex because mutexes that obey the (optional)     */ -	/* POSIX scheduling rules are subject to convoys in high contention */ -	/* applications.  This is basically a spin lock.		    */ -     extern pthread_t GC_lock_holder; -     extern void GC_lock(void); -	/* Allocation lock holder.  Only set if acquired by client through */ -	/* GC_call_with_alloc_lock.					   */ -#    define SET_LOCK_HOLDER() GC_lock_holder = pthread_self() -#    define NO_THREAD (pthread_t)(-1) -#    define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD -#    define I_HOLD_LOCK() (pthread_equal(GC_lock_holder, pthread_self())) -#    ifdef UNDEFINED -#    	define LOCK() pthread_mutex_lock(&GC_allocate_ml) -#    	define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml) -#    else -#	define LOCK() \ -		{ if (GC_test_and_set(&GC_allocate_lock)) GC_lock(); } -#	define UNLOCK() \ -		GC_clear(&GC_allocate_lock) -#    endif -     extern GC_bool GC_collecting; -#    define ENTER_GC() \ -		{ \ -		    GC_collecting = 1; \ -		} -#    define EXIT_GC() GC_collecting = 0; -#  endif /* LINUX_THREADS */ -#  if defined(IRIX_THREADS) || defined(IRIX_JDK_THREADS) -#    include <pthread.h> -#    include <mutex.h> - -#    if __mips < 3 || !(defined (_ABIN32) || defined(_ABI64)) \ -	|| !defined(_COMPILER_VERSION) || _COMPILER_VERSION < 700 -#        define GC_test_and_set(addr, v) test_and_set(addr,v) -#    else -#	 define GC_test_and_set(addr, v) __test_and_set(addr,v) -#    endif -     extern unsigned long GC_allocate_lock; -	/* This is not a mutex because mutexes that obey the (optional) 	*/ -	/* POSIX scheduling rules are subject to convoys in high contention	*/ -	/* applications.  This is basically a spin lock.			*/ -     extern pthread_t GC_lock_holder; -     extern void GC_lock(void); -	/* Allocation lock holder.  Only set if acquired by client through */ -	/* GC_call_with_alloc_lock.					   */ -#    define SET_LOCK_HOLDER() GC_lock_holder = pthread_self() -#    define NO_THREAD (pthread_t)(-1) -#    define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD -#    define I_HOLD_LOCK() (pthread_equal(GC_lock_holder, pthread_self())) -#    ifdef UNDEFINED -#    	define LOCK() pthread_mutex_lock(&GC_allocate_ml) -#    	define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml) -#    else -#	define LOCK() { if (GC_test_and_set(&GC_allocate_lock, 1)) GC_lock(); } -#       if __mips >= 3 && (defined (_ABIN32) || defined(_ABI64)) \ -	   && defined(_COMPILER_VERSION) && _COMPILER_VERSION >= 700 -#	    define UNLOCK() __lock_release(&GC_allocate_lock) -#	else -	    /* The function call in the following should prevent the	*/ -	    /* compiler from moving assignments to below the UNLOCK.	*/ -	    /* This is probably not necessary for ucode or gcc 2.8.	*/ -	    /* It may be necessary for Ragnarok and future gcc		*/ -	    /* versions.						*/ -#           define UNLOCK() { GC_noop1(&GC_allocate_lock); \ -			*(volatile unsigned long *)(&GC_allocate_lock) = 0; } -#	endif -#    endif -     extern GC_bool GC_collecting; -#    define ENTER_GC() \ -		{ \ -		    GC_collecting = 1; \ -		} -#    define EXIT_GC() GC_collecting = 0; -#  endif /* IRIX_THREADS || IRIX_JDK_THREADS */ -#  ifdef WIN32_THREADS -#    include <windows.h> -     GC_API CRITICAL_SECTION GC_allocate_ml; -#    define LOCK() EnterCriticalSection(&GC_allocate_ml); -#    define UNLOCK() LeaveCriticalSection(&GC_allocate_ml); -#  endif -#  ifndef SET_LOCK_HOLDER -#      define SET_LOCK_HOLDER() -#      define UNSET_LOCK_HOLDER() -#      define I_HOLD_LOCK() FALSE -		/* Used on platforms were locks can be reacquired,	*/ -		/* so it doesn't matter if we lie.			*/ -#  endif -# else -#    define LOCK() -#    define UNLOCK() -# endif -# ifndef SET_LOCK_HOLDER -#   define SET_LOCK_HOLDER() -#   define UNSET_LOCK_HOLDER() -#   define I_HOLD_LOCK() FALSE -		/* Used on platforms were locks can be reacquired,	*/ -		/* so it doesn't matter if we lie.			*/ -# endif -# ifndef ENTER_GC -#   define ENTER_GC() -#   define EXIT_GC() -# endif - -# ifndef DCL_LOCK_STATE -#   define DCL_LOCK_STATE -# endif -# ifndef FASTLOCK -#   define FASTLOCK() LOCK() -#   define FASTLOCK_SUCCEEDED() TRUE -#   define FASTUNLOCK() UNLOCK() -# endif -  /* Delay any interrupts or signals that may abort this thread.  Data	*/  /* structures are in a consistent state outside this pair of calls.	*/  /* ANSI C allows both to be empty (though the standard isn't very	*/ @@ -576,9 +450,9 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */);  		PCR_Th_SetSigMask(&GC_old_sig_mask, NIL)  # else  #   if defined(SRC_M3) || defined(AMIGA) || defined(SOLARIS_THREADS) \ -	|| defined(MSWIN32) || defined(MACOS) || defined(DJGPP) \ -	|| defined(NO_SIGNALS) || defined(IRIX_THREADS) \ -	|| defined(IRIX_JDK_THREADS) || defined(LINUX_THREADS)  +	|| defined(MSWIN32) || defined(MSWINCE) || defined(MACOS) \ +	|| defined(DJGPP) || defined(NO_SIGNALS) || defined(IRIX_THREADS) \ +	|| defined(LINUX_THREADS)   			/* Also useful for debugging.		*/  	/* Should probably use thr_sigsetmask for SOLARIS_THREADS. */  #     define DISABLE_SIGNALS() @@ -607,7 +481,7 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */);  # else  #   if defined(SOLARIS_THREADS) || defined(WIN32_THREADS) \  	|| defined(IRIX_THREADS) || defined(LINUX_THREADS) \ -	|| defined(IRIX_JDK_THREADS) +	|| defined(HPUX_THREADS)        void GC_stop_world();        void GC_start_world();  #     define STOP_WORLD() GC_stop_world() @@ -641,6 +515,13 @@ void GC_print_callers (/* struct callinfo info[NFRAMES] */);  # define WARN(msg,arg) (*GC_current_warn_proc)(msg, (GC_word)(arg))  extern GC_warn_proc GC_current_warn_proc; +/* Get environment entry */ +#if !defined(NO_GETENV) +#   define GETENV(name) getenv(name) +#else +#   define GETENV(name) 0 +#endif +  /*********************************/  /*                               */  /* Word-size-dependent defines   */ @@ -670,7 +551,7 @@ extern GC_warn_proc GC_current_warn_proc;  #define WORDSZ ((word)CPP_WORDSZ)  #define SIGNB  ((word)1 << (WORDSZ-1))  #define BYTES_PER_WORD      ((word)(sizeof (word))) -#define ONES                ((word)(-1)) +#define ONES                ((word)(signed_word)(-1))  #define divWORDSZ(n) ((n) >> LOGWL)	   /* divide n by size of word      */  /*********************/ @@ -723,6 +604,8 @@ extern GC_warn_proc GC_current_warn_proc;  /*  max size objects supported by freelist (larger objects may be   */  /*  allocated, but less efficiently)                                */ +#define CPP_MAXOBJBYTES (CPP_HBLKSIZE/2) +#define MAXOBJBYTES ((word)CPP_MAXOBJBYTES)  #define CPP_MAXOBJSZ    BYTES_TO_WORDS(CPP_HBLKSIZE/2)  #define MAXOBJSZ ((word)CPP_MAXOBJSZ) @@ -743,25 +626,28 @@ extern GC_warn_proc GC_current_warn_proc;  # define HBLKDISPL(objptr) (((word) (objptr)) & (HBLKSIZE-1))  /* Round up byte allocation requests to integral number of words, etc. */ -# ifdef ADD_BYTE_AT_END -#   define ROUNDED_UP_WORDS(n) BYTES_TO_WORDS((n) + WORDS_TO_BYTES(1)) -#   ifdef ALIGN_DOUBLE -#       define ALIGNED_WORDS(n) (BYTES_TO_WORDS((n) + WORDS_TO_BYTES(2)) & ~1) -#   else -#       define ALIGNED_WORDS(n) ROUNDED_UP_WORDS(n) -#   endif -#   define SMALL_OBJ(bytes) ((bytes) < WORDS_TO_BYTES(MAXOBJSZ)) -#   define ADD_SLOP(bytes) ((bytes)+1) -# else -#   define ROUNDED_UP_WORDS(n) BYTES_TO_WORDS((n) + (WORDS_TO_BYTES(1) - 1)) -#   ifdef ALIGN_DOUBLE +# define ROUNDED_UP_WORDS(n) \ +	BYTES_TO_WORDS((n) + (WORDS_TO_BYTES(1) - 1 + EXTRA_BYTES)) +# ifdef ALIGN_DOUBLE  #       define ALIGNED_WORDS(n) \ -			(BYTES_TO_WORDS((n) + WORDS_TO_BYTES(2) - 1) & ~1) -#   else +	    (BYTES_TO_WORDS((n) + WORDS_TO_BYTES(2) - 1 + EXTRA_BYTES) & ~1) +# else  #       define ALIGNED_WORDS(n) ROUNDED_UP_WORDS(n) +# endif +# define SMALL_OBJ(bytes) ((bytes) < (MAXOBJBYTES - EXTRA_BYTES)) +# define ADD_SLOP(bytes) ((bytes) + EXTRA_BYTES) +# ifndef MIN_WORDS +    /* MIN_WORDS is the size of the smallest allocated object.	*/ +    /* 1 and 2 are the only valid values.			*/ +    /* 2 must be used if:					*/ +    /* - GC_gcj_malloc can be used for objects of requested 	*/ +    /*   size  smaller than 2 words, or				*/ +    /* - USE_MARK_BYTES is defined.				*/ +#   if defined(USE_MARK_BYTES) || defined(GC_GCJ_SUPPORT) +#     define MIN_WORDS 2   	/* Smallest allocated object.	*/ +#   else +#     define MIN_WORDS 1  #   endif -#   define SMALL_OBJ(bytes) ((bytes) <= WORDS_TO_BYTES(MAXOBJSZ)) -#   define ADD_SLOP(bytes) (bytes)  # endif @@ -772,11 +658,19 @@ extern GC_warn_proc GC_current_warn_proc;   */  # ifdef LARGE_CONFIG -#   define LOG_PHT_ENTRIES  17 +#   define LOG_PHT_ENTRIES  19  /* Collisions likely at 512K blocks,	*/ +				/* which is >= 2GB.  Each table takes	*/ +				/* 64KB.				*/  # else -#   define LOG_PHT_ENTRIES  14	/* Collisions are likely if heap grows	*/ -				/* to more than 16K hblks = 64MB.	*/ -				/* Each hash table occupies 2K bytes.   */ +#   ifdef SMALL_CONFIG +#     define LOG_PHT_ENTRIES  14 /* Collisions are likely if heap grows	*/ +				 /* to more than 16K hblks = 64MB.	*/ +				 /* Each hash table occupies 2K bytes.   */ +#   else /* default "medium" configuration */ +#     define LOG_PHT_ENTRIES  16 /* Collisions are likely if heap grows	*/ +				 /* to more than 16K hblks >= 256MB.	*/ +				 /* Each hash table occupies 8K bytes.  */ +#   endif  # endif  # define PHT_ENTRIES ((word)1 << LOG_PHT_ENTRIES)  # define PHT_SIZE (PHT_ENTRIES >> LOGWL) @@ -790,6 +684,10 @@ typedef word page_hash_table[PHT_SIZE];  		(bl)[divWORDSZ(index)] |= (word)1 << modWORDSZ(index)  # define clear_pht_entry_from_index(bl, index) \  		(bl)[divWORDSZ(index)] &= ~((word)1 << modWORDSZ(index)) +/* And a dumb but thread-safe version of set_pht_entry_from_index.	*/ +/* This sets (many) extra bits.						*/ +# define set_pht_entry_from_index_safe(bl, index) \ +		(bl)[divWORDSZ(index)] = ONES @@ -802,20 +700,28 @@ typedef word page_hash_table[PHT_SIZE];  /*  heap block header */  #define HBLKMASK   (HBLKSIZE-1) -#define BITS_PER_HBLK (HBLKSIZE * 8) +#define BITS_PER_HBLK (CPP_HBLKSIZE * 8)  #define MARK_BITS_PER_HBLK (BITS_PER_HBLK/CPP_WORDSZ)  	   /* upper bound                                    */ -	   /* We allocate 1 bit/word.  Only the first word   */ +	   /* We allocate 1 bit/word, unless USE_MARK_BYTES  */ +	   /* is defined.  Only the first word   	     */  	   /* in each object is actually marked.             */ -# ifdef ALIGN_DOUBLE -#   define MARK_BITS_SZ (((MARK_BITS_PER_HBLK + 2*CPP_WORDSZ - 1) \ -			  / (2*CPP_WORDSZ))*2) +# ifdef USE_MARK_BYTES +#   define MARK_BITS_SZ (MARK_BITS_PER_HBLK/2) +	/* Unlike the other case, this is in units of bytes.		*/ +	/* We actually allocate only every second mark bit, since we	*/ +	/* force all objects to be doubleword aligned.			*/ +	/* However, each mark bit is allocated as a byte.		*/  # else -#   define MARK_BITS_SZ ((MARK_BITS_PER_HBLK + CPP_WORDSZ - 1)/CPP_WORDSZ) +#   define MARK_BITS_SZ (MARK_BITS_PER_HBLK/CPP_WORDSZ)  # endif -	   /* Upper bound on number of mark words per heap block  */ + +/* We maintain layout maps for heap blocks containing objects of a given */ +/* size.  Each entry in this map describes a byte offset and has the	 */ +/* following type.							 */ +typedef unsigned char map_entry_type;  struct hblkhdr {      word hb_sz;  /* If in use, size in words, of objects in the block. */ @@ -826,7 +732,8 @@ struct hblkhdr {      struct hblk * hb_prev;	/* Backwards link for free list.	*/      word hb_descr;   		/* object descriptor for marking.  See	*/      				/* mark.h.				*/ -    char* hb_map;	/* A pointer to a pointer validity map of the block. */ +    map_entry_type * hb_map;	 +    			/* A pointer to a pointer validity map of the block. */      		      	/* See GC_obj_map.				     */      		     	/* Valid for all blocks with headers.		     */      		     	/* Free blocks point to GC_invalid_map.		     */ @@ -848,51 +755,49 @@ struct hblkhdr {      				/* Value of GC_gc_no when block was	*/      				/* last allocated or swept. May wrap.   */  				/* For a free block, this is maintained */ -				/* unly for USE_MUNMAP, and indicates	*/ +				/* only for USE_MUNMAP, and indicates	*/  				/* when the header was allocated, or	*/  				/* when the size of the block last	*/  				/* changed.				*/ -    word hb_marks[MARK_BITS_SZ]; +#   ifdef USE_MARK_BYTES +      union { +        char _hb_marks[MARK_BITS_SZ]; +			    /* The i'th byte is 1 if the object 	*/ +			    /* starting at word 2i is marked, 0 o.w.	*/ +	word dummy;	/* Force word alignment of mark bytes. */ +      } _mark_byte_union; +#     define hb_marks _mark_byte_union._hb_marks +#   else +      word hb_marks[MARK_BITS_SZ];  			    /* Bit i in the array refers to the             */  			    /* object starting at the ith word (header      */  			    /* INCLUDED) in the heap block.                 */  			    /* The lsb of word 0 is numbered 0.		    */ +			    /* Unused bits are invalid, and are 	    */ +			    /* occasionally set, e.g for uncollectable	    */ +			    /* objects.					    */ +#   endif /* !USE_MARK_BYTES */  };  /*  heap block body */ -# define DISCARD_WORDS 0 -	/* Number of words to be dropped at the beginning of each block	*/ -	/* Must be a multiple of WORDSZ.  May reasonably be nonzero	*/ -	/* on machines that don't guarantee longword alignment of	*/ -	/* pointers, so that the number of false hits is minimized.	*/ -	/* 0 and WORDSZ are probably the only reasonable values.	*/ - -# define BODY_SZ ((HBLKSIZE-WORDS_TO_BYTES(DISCARD_WORDS))/sizeof(word)) +# define BODY_SZ (HBLKSIZE/sizeof(word))  struct hblk { -#   if (DISCARD_WORDS != 0) +#   if 0  /* DISCARDWORDS no longer supported */          word garbage[DISCARD_WORDS];  #   endif      word hb_body[BODY_SZ];  }; -# define HDR_WORDS ((word)DISCARD_WORDS) -# define HDR_BYTES ((word)WORDS_TO_BYTES(DISCARD_WORDS)) -  # define OBJ_SZ_TO_BLOCKS(sz) \ -    divHBLKSZ(HDR_BYTES + WORDS_TO_BYTES(sz) + HBLKSIZE-1) +    divHBLKSZ(WORDS_TO_BYTES(sz) + HBLKSIZE-1)      /* Size of block (in units of HBLKSIZE) needed to hold objects of	*/      /* given sz (in words).						*/  /* Object free list link */  # define obj_link(p) (*(ptr_t *)(p)) -/* The type of mark procedures.  This really belongs in gc_mark.h.	*/ -/* But we put it here, so that we can avoid scanning the mark proc	*/ -/* table.								*/ -typedef struct ms_entry * (*mark_proc)(/* word * addr, mark_stack_ptr, -					  mark_stack_limit, env */);  # define LOG_MAX_MARK_PROCS 6  # define MAX_MARK_PROCS (1 << LOG_MAX_MARK_PROCS) @@ -906,12 +811,12 @@ typedef struct ms_entry * (*mark_proc)(/* word * addr, mark_stack_ptr,  #   ifdef PCR  #     define MAX_ROOT_SETS 1024  #   else -#     ifdef MSWIN32 -#	define MAX_ROOT_SETS 512 +#     if defined(MSWIN32) || defined(MSWINCE) +#	define MAX_ROOT_SETS 1024  	    /* Under NT, we add only written pages, which can result 	*/  	    /* in many small root sets.					*/  #     else -#       define MAX_ROOT_SETS 64 +#       define MAX_ROOT_SETS 256  #     endif  #   endif  # endif @@ -934,14 +839,14 @@ struct exclusion {  struct roots {  	ptr_t r_start;  	ptr_t r_end; -#	ifndef MSWIN32 +#	if !defined(MSWIN32) && !defined(MSWINCE)  	  struct roots * r_next;  #	endif  	GC_bool r_tmp;  	  	/* Delete before registering new dynamic libraries */  }; -#ifndef MSWIN32 +#if !defined(MSWIN32) && !defined(MSWINCE)      /* Size of hash table index to roots.	*/  #   define LOG_RT_SIZE 6  #   define RT_SIZE (1 << LOG_RT_SIZE) /* Power of 2, may be != MAX_ROOT_SETS */ @@ -969,16 +874,28 @@ struct roots {  struct _GC_arrays {    word _heapsize;    word _max_heapsize; +  word _requested_heapsize;	/* Heap size due to explicit expansion */    ptr_t _last_heap_addr;    ptr_t _prev_heap_addr;    word _large_free_bytes;  	/* Total bytes contained in blocks on large object free */  	/* list.						*/ +  word _large_allocd_bytes; +  	/* Total number of bytes in allocated large objects blocks.	*/ +  	/* For the purposes of this counter and the next one only, a 	*/ +  	/* large object is one that occupies a block of at least	*/ +  	/* 2*HBLKSIZE.							*/ +  word _max_large_allocd_bytes; +  	/* Maximum number of bytes that were ever allocated in		*/ +  	/* large object blocks.  This is used to help decide when it	*/ +  	/* is safe to split up a large block.				*/    word _words_allocd_before_gc;  		/* Number of words allocated before this	*/  		/* collection cycle.				*/ -  word _words_allocd; +# ifndef SEPARATE_GLOBALS +    word _words_allocd;    	/* Number of words allocated during this collection cycle */ +# endif    word _words_wasted;    	/* Number of words wasted due to internal fragmentation	*/    	/* in large objects, or due to dropping blacklisted     */ @@ -993,14 +910,21 @@ struct _GC_arrays {    word _mem_freed;    	/* Number of explicitly deallocated words of memory	*/    	/* since last collection.				*/ -  mark_proc _mark_procs[MAX_MARK_PROCS]; +  ptr_t _scratch_end_ptr; +  ptr_t _scratch_last_end_ptr; +	/* Used by headers.c, and can easily appear to point to	*/ +	/* heap.						*/ +  GC_mark_proc _mark_procs[MAX_MARK_PROCS];    	/* Table of user-defined mark procedures.  There is	*/  	/* a small number of these, which can be referenced	*/  	/* by DS_PROC mark descriptors.  See gc_mark.h.		*/ -  ptr_t _objfreelist[MAXOBJSZ+1]; + +# ifndef SEPARATE_GLOBALS +    ptr_t _objfreelist[MAXOBJSZ+1];  			  /* free list for objects */ -  ptr_t _aobjfreelist[MAXOBJSZ+1]; +    ptr_t _aobjfreelist[MAXOBJSZ+1];  			  /* free list for atomic objs 	*/ +# endif    ptr_t _uobjfreelist[MAXOBJSZ+1];  			  /* uncollectable but traced objs 	*/ @@ -1033,41 +957,28 @@ struct _GC_arrays {      ptr_t _sobjfreelist[MAXOBJSZ+1];  # endif    			  /* free list for immutable objects	*/ -  ptr_t _obj_map[MAXOBJSZ+1]; +  map_entry_type * _obj_map[MAXOBJSZ+1];                         /* If not NIL, then a pointer to a map of valid  */      		       /* object addresses. _obj_map[sz][i] is j if the	*/      		       /* address block_start+i is a valid pointer      */ -    		       /* to an object at				*/ -    		       /* block_start+i&~3 - WORDS_TO_BYTES(j).		*/ -    		       /* (If ALL_INTERIOR_POINTERS is defined, then	*/ -    		       /* instead ((short *)(hb_map[sz])[i] is j if	*/ -    		       /* block_start+WORDS_TO_BYTES(i) is in the	*/ -    		       /* interior of an object starting at		*/ -    		       /* block_start+WORDS_TO_BYTES(i-j)).		*/ -    		       /* It is OBJ_INVALID if				*/ -    		       /* block_start+WORDS_TO_BYTES(i) is not		*/ -    		       /* valid as a pointer to an object.              */ -    		       /* We assume all values of j <= OBJ_INVALID.	*/ -    		       /* The zeroth entry corresponds to large objects.*/ -#   ifdef ALL_INTERIOR_POINTERS -#	define map_entry_type short -#       define OBJ_INVALID 0x7fff -#	define MAP_ENTRY(map, bytes) \ -		(((map_entry_type *)(map))[BYTES_TO_WORDS(bytes)]) -#	define MAP_ENTRIES BYTES_TO_WORDS(HBLKSIZE) -#	define MAP_SIZE (MAP_ENTRIES * sizeof(map_entry_type)) -#	define OFFSET_VALID(displ) TRUE -#	define CPP_MAX_OFFSET (HBLKSIZE - HDR_BYTES - 1) -#	define MAX_OFFSET ((word)CPP_MAX_OFFSET) -#   else -#	define map_entry_type char -#       define OBJ_INVALID 0x7f -#	define MAP_ENTRY(map, bytes) \ -		(map)[bytes] -#	define MAP_ENTRIES HBLKSIZE -#	define MAP_SIZE MAP_ENTRIES -#	define CPP_MAX_OFFSET (WORDS_TO_BYTES(OBJ_INVALID) - 1)	 -#	define MAX_OFFSET ((word)CPP_MAX_OFFSET) +    		       /* to an object at block_start +			*/ + 		       /* WORDS_TO_BYTES(BYTES_TO_WORDS(i) - j)		*/ +  		       /* I.e. j is a word displacement from the	*/ +  		       /* object beginning.				*/ +  		       /* The entry is OBJ_INVALID if the corresponding	*/ +  		       /* address is not a valid pointer.  It is 	*/ +  		       /* OFFSET_TOO_BIG if the value j would be too 	*/ +  		       /* large to fit in the entry.  (Note that the	*/ +  		       /* size of these entries matters, both for 	*/ +  		       /* space consumption and for cache utilization.	*/ +#   define OFFSET_TOO_BIG 0xfe +#   define OBJ_INVALID 0xff +#   define MAP_ENTRY(map, bytes) (map)[bytes] +#   define MAP_ENTRIES HBLKSIZE +#   define MAP_SIZE MAP_ENTRIES +#   define CPP_MAX_OFFSET (OFFSET_TOO_BIG - 1)	 +#   define MAX_OFFSET ((word)CPP_MAX_OFFSET) +    /* The following are used only if GC_all_interior_ptrs != 0 */  # 	define VALID_OFFSET_SZ \  	  (CPP_MAX_OFFSET > WORDS_TO_BYTES(CPP_MAXOBJSZ)? \  	   CPP_MAX_OFFSET+1 \ @@ -1075,11 +986,11 @@ struct _GC_arrays {    	char _valid_offsets[VALID_OFFSET_SZ];  				/* GC_valid_offsets[i] == TRUE ==> i 	*/  				/* is registered as a displacement.	*/ -#	define OFFSET_VALID(displ) GC_valid_offsets[displ] +#	define OFFSET_VALID(displ) \ +	  (GC_all_interior_pointers || GC_valid_offsets[displ])    	char _modws_valid_offsets[sizeof(word)];  				/* GC_valid_offsets[i] ==>		  */  				/* GC_modws_valid_offsets[i%sizeof(word)] */ -#   endif  # ifdef STUBBORN_ALLOC      page_hash_table _changed_pages;          /* Stubborn object pages that were changes since last call to	*/ @@ -1106,17 +1017,25 @@ struct _GC_arrays {  #     define MAX_HEAP_SECTS 768		/* Separately added heap sections. */  #   endif  # else -#   define MAX_HEAP_SECTS 256 +#   ifdef SMALL_CONFIG +#     define MAX_HEAP_SECTS 128		/* Roughly 1GB			*/ +#   else +#     define MAX_HEAP_SECTS 384		/* Roughly 3GB			*/ +#   endif  # endif    struct HeapSect {        ptr_t hs_start; word hs_bytes;    } _heap_sects[MAX_HEAP_SECTS]; -# ifdef MSWIN32 +# if defined(MSWIN32) || defined(MSWINCE)      ptr_t _heap_bases[MAX_HEAP_SECTS];      		/* Start address of memory regions obtained from kernel. */  # endif +# ifdef MSWINCE +    word _heap_lengths[MAX_HEAP_SECTS]; +    		/* Commited lengths of memory regions obtained from kernel. */ +# endif    struct roots _static_roots[MAX_ROOT_SETS]; -# ifndef MSWIN32 +# if !defined(MSWIN32) && !defined(MSWINCE)      struct roots * _root_index[RT_SIZE];  # endif    struct exclusion _excl_table[MAX_EXCLUSIONS]; @@ -1135,8 +1054,11 @@ struct _GC_arrays {  GC_API GC_FAR struct _GC_arrays GC_arrays;  -# define GC_objfreelist GC_arrays._objfreelist -# define GC_aobjfreelist GC_arrays._aobjfreelist +# ifndef SEPARATE_GLOBALS +#   define GC_objfreelist GC_arrays._objfreelist +#   define GC_aobjfreelist GC_arrays._aobjfreelist +#   define GC_words_allocd GC_arrays._words_allocd +# endif  # define GC_uobjfreelist GC_arrays._uobjfreelist  # ifdef ATOMIC_UNCOLLECTABLE  #   define GC_auobjfreelist GC_arrays._auobjfreelist @@ -1151,24 +1073,31 @@ GC_API GC_FAR struct _GC_arrays GC_arrays;  # define GC_obj_map GC_arrays._obj_map  # define GC_last_heap_addr GC_arrays._last_heap_addr  # define GC_prev_heap_addr GC_arrays._prev_heap_addr -# define GC_words_allocd GC_arrays._words_allocd  # define GC_words_wasted GC_arrays._words_wasted  # define GC_large_free_bytes GC_arrays._large_free_bytes +# define GC_large_allocd_bytes GC_arrays._large_allocd_bytes +# define GC_max_large_allocd_bytes GC_arrays._max_large_allocd_bytes  # define GC_words_finalized GC_arrays._words_finalized  # define GC_non_gc_bytes_at_gc GC_arrays._non_gc_bytes_at_gc  # define GC_mem_freed GC_arrays._mem_freed +# define GC_scratch_end_ptr GC_arrays._scratch_end_ptr +# define GC_scratch_last_end_ptr GC_arrays._scratch_last_end_ptr  # define GC_mark_procs GC_arrays._mark_procs  # define GC_heapsize GC_arrays._heapsize  # define GC_max_heapsize GC_arrays._max_heapsize +# define GC_requested_heapsize GC_arrays._requested_heapsize  # define GC_words_allocd_before_gc GC_arrays._words_allocd_before_gc  # define GC_heap_sects GC_arrays._heap_sects  # define GC_last_stack GC_arrays._last_stack  # ifdef USE_MUNMAP  #   define GC_unmapped_bytes GC_arrays._unmapped_bytes  # endif -# ifdef MSWIN32 +# if defined(MSWIN32) || defined(MSWINCE)  #   define GC_heap_bases GC_arrays._heap_bases  # endif +# ifdef MSWINCE +#   define GC_heap_lengths GC_arrays._heap_lengths +# endif  # define GC_static_roots GC_arrays._static_roots  # define GC_root_index GC_arrays._root_index  # define GC_excl_table GC_arrays._excl_table @@ -1194,6 +1123,8 @@ GC_API GC_FAR struct _GC_arrays GC_arrays;  # define beginGC_arrays ((ptr_t)(&GC_arrays))  # define endGC_arrays (((ptr_t)(&GC_arrays)) + (sizeof GC_arrays)) +#define USED_HEAP_SIZE (GC_heapsize - GC_large_free_bytes) +  /* Object kinds: */  # define MAXOBJKINDS 16 @@ -1213,10 +1144,27 @@ extern struct obj_kind {     GC_bool ok_init;   /* Clear objects before putting them on the free list. */  } GC_obj_kinds[MAXOBJKINDS]; -# define endGC_obj_kinds (((ptr_t)(&GC_obj_kinds)) + (sizeof GC_obj_kinds)) +# define beginGC_obj_kinds ((ptr_t)(&GC_obj_kinds)) +# define endGC_obj_kinds (beginGC_obj_kinds + (sizeof GC_obj_kinds)) -# define end_gc_area ((ptr_t)endGC_arrays == (ptr_t)(&GC_obj_kinds) ? \ -			endGC_obj_kinds : endGC_arrays) +/* Variables that used to be in GC_arrays, but need to be accessed by 	*/ +/* inline allocation code.  If they were in GC_arrays, the inlined 	*/ +/* allocation code would include GC_arrays offsets (as it did), which	*/ +/* introduce maintenance problems.					*/ + +#ifdef SEPARATE_GLOBALS +  word GC_words_allocd; +  	/* Number of words allocated during this collection cycle */ +  ptr_t GC_objfreelist[MAXOBJSZ+1]; +			  /* free list for NORMAL objects */ +# define beginGC_objfreelist ((ptr_t)(&GC_objfreelist)) +# define endGC_objfreelist (beginGC_objfreelist + sizeof(GC_objfreelist)) + +  ptr_t GC_aobjfreelist[MAXOBJSZ+1]; +			  /* free list for atomic (PTRFREE) objs 	*/ +# define beginGC_aobjfreelist ((ptr_t)(&GC_aobjfreelist)) +# define endGC_aobjfreelist (beginGC_aobjfreelist + sizeof(GC_aobjfreelist)) +#endif  /* Predefined kinds: */  # define PTRFREE 0 @@ -1240,8 +1188,10 @@ extern word GC_n_heap_sects;	/* Number of separately added heap	*/  extern word GC_page_size; -# ifdef MSWIN32 -extern word GC_n_heap_bases;	/* See GC_heap_bases.	*/ +# if defined(MSWIN32) || defined(MSWINCE) +  struct _SYSTEM_INFO; +  extern struct _SYSTEM_INFO GC_sysinfo; +  extern word GC_n_heap_bases;	/* See GC_heap_bases.	*/  # endif  extern word GC_total_stack_black_listed; @@ -1254,7 +1204,7 @@ extern word GC_black_list_spacing;  			/* "stack-blacklisted", i.e. that are 		*/  			/* problematic in the interior of an object.	*/ -extern char * GC_invalid_map; +extern map_entry_type * GC_invalid_map;  			/* Pointer to the nowhere valid hblk map */  			/* Blocks pointing to this map are free. */ @@ -1273,7 +1223,7 @@ extern GC_bool GC_objects_are_marked;	/* There are marked objects in  */    extern GC_bool GC_incremental;  			/* Using incremental/generational collection. */  #else -# define GC_incremental TRUE +# define GC_incremental FALSE  			/* Hopefully allow optimizer to remove some code. */  #endif @@ -1286,10 +1236,6 @@ extern word GC_root_size;	/* Total size of registered root sections */  extern GC_bool GC_debugging_started;	/* GC_debug_malloc has been called. */  -extern ptr_t GC_least_plausible_heap_addr; -extern ptr_t GC_greatest_plausible_heap_addr; -			/* Bounds on the heap.  Guaranteed valid	*/ -			/* Likely to include future heap expansion.	*/  /* Operations */  # ifndef abs @@ -1302,6 +1248,33 @@ extern ptr_t GC_greatest_plausible_heap_addr;  /*  with it. Only those corresponding to the beginning of an */  /*  object are used.                                         */ +/* Set mark bit correctly, even if mark bits may be concurrently 	*/ +/* accessed.								*/ +#ifdef PARALLEL_MARK +# define OR_WORD(addr, bits) \ +	{ word old; \ +	  do { \ +	    old = *((volatile word *)addr); \ +	  } while (!GC_compare_and_exchange((addr), old, old | (bits))); \ +	} +# define OR_WORD_EXIT_IF_SET(addr, bits, exit_label) \ +	{ word old; \ +	  word my_bits = (bits); \ +	  do { \ +	    old = *((volatile word *)addr); \ +	    if (old & my_bits) goto exit_label; \ +	  } while (!GC_compare_and_exchange((addr), old, old | my_bits)); \ +	} +#else +# define OR_WORD(addr, bits) *(addr) |= (bits) +# define OR_WORD_EXIT_IF_SET(addr, bits, exit_label) \ +	{ \ +	  word old = *(addr); \ +	  word my_bits = (bits); \ +	  if (old & my_bits) goto exit_label; \ +	  *(addr) = (old | my_bits); \ +	} +#endif  /* Mark bit operations */ @@ -1313,75 +1286,87 @@ extern ptr_t GC_greatest_plausible_heap_addr;   * relative to the beginning of the block, including unused words)   */ +#ifdef USE_MARK_BYTES +# define mark_bit_from_hdr(hhdr,n) ((hhdr)->hb_marks[(n) >> 1]) +# define set_mark_bit_from_hdr(hhdr,n) ((hhdr)->hb_marks[(n)>>1]) = 1 +# define clear_mark_bit_from_hdr(hhdr,n) ((hhdr)->hb_marks[(n)>>1]) = 0 +#else /* !USE_MARK_BYTES */  # define mark_bit_from_hdr(hhdr,n) (((hhdr)->hb_marks[divWORDSZ(n)] \  			    >> (modWORDSZ(n))) & (word)1) -# define set_mark_bit_from_hdr(hhdr,n) (hhdr)->hb_marks[divWORDSZ(n)] \ -				|= (word)1 << modWORDSZ(n) - +# define set_mark_bit_from_hdr(hhdr,n) \ +			    OR_WORD((hhdr)->hb_marks+divWORDSZ(n), \ +				    (word)1 << modWORDSZ(n))  # define clear_mark_bit_from_hdr(hhdr,n) (hhdr)->hb_marks[divWORDSZ(n)] \  				&= ~((word)1 << modWORDSZ(n)) +#endif /* !USE_MARK_BYTES */  /* Important internal collector routines */ -ptr_t GC_approx_sp(); - -GC_bool GC_should_collect(); -#ifdef PRESERVE_LAST -    GC_bool GC_in_last_heap_sect(/* ptr_t */); -	/* In last added heap section?  If so, avoid breaking up.	*/ -#endif -void GC_apply_to_all_blocks(/*fn, client_data*/); -			/* Invoke fn(hbp, client_data) for each 	*/ -			/* allocated heap block.			*/ -struct hblk * GC_next_used_block(/* struct hblk * h */); -			/* Return first in-use block >= h	*/ -struct hblk * GC_prev_block(/* struct hblk * h */); -			/* Return last block <= h.  Returned block	*/ -			/* is managed by GC, but may or may not be in	*/ +ptr_t GC_approx_sp GC_PROTO((void)); +   +GC_bool GC_should_collect GC_PROTO((void)); +   +void GC_apply_to_all_blocks GC_PROTO(( \ +    void (*fn) GC_PROTO((struct hblk *h, word client_data)), \ +    word client_data)); +  			/* Invoke fn(hbp, client_data) for each 	*/ +  			/* allocated heap block.			*/ +struct hblk * GC_next_used_block GC_PROTO((struct hblk * h)); +  			/* Return first in-use block >= h	*/ +struct hblk * GC_prev_block GC_PROTO((struct hblk * h)); +  			/* Return last block <= h.  Returned block	*/ +  			/* is managed by GC, but may or may not be in	*/  			/* use.						*/ -void GC_mark_init(); -void GC_clear_marks();	/* Clear mark bits for all heap objects. */ -void GC_invalidate_mark_state();	/* Tell the marker that	marked 	   */ -					/* objects may point to	unmarked   */ -					/* ones, and roots may point to	   */ -					/* unmarked objects.		   */ -					/* Reset mark stack.		   */ -void GC_mark_from_mark_stack(); /* Mark from everything on the mark stack. */ -				/* Return after about one pages worth of   */ -				/* work.				   */ -GC_bool GC_mark_stack_empty(); -GC_bool GC_mark_some(/* cold_gc_frame */); -			/* Perform about one pages worth of marking	*/ -			/* work of whatever kind is needed.  Returns	*/ -			/* quickly if no collection is in progress.	*/ -			/* Return TRUE if mark phase finished.		*/ -void GC_initiate_gc();		/* initiate collection.			*/ -				/* If the mark state is invalid, this	*/ -				/* becomes full colleection.  Otherwise */ -				/* it's partial.			*/ -void GC_push_all(/*b,t*/);	/* Push everything in a range 		*/ -				/* onto mark stack.			*/ -void GC_push_dirty(/*b,t*/);      /* Push all possibly changed	 	*/ -				  /* subintervals of [b,t) onto		*/ -				  /* mark stack.			*/ +void GC_mark_init GC_PROTO((void)); +void GC_clear_marks GC_PROTO((void));	/* Clear mark bits for all heap objects. */ +void GC_invalidate_mark_state GC_PROTO((void)); +					/* Tell the marker that	marked 	   */ +  					/* objects may point to	unmarked   */ +  					/* ones, and roots may point to	   */ +  					/* unmarked objects.		   */ +  					/* Reset mark stack.		   */ +GC_bool GC_mark_stack_empty GC_PROTO((void)); +GC_bool GC_mark_some GC_PROTO((ptr_t cold_gc_frame)); +  			/* Perform about one pages worth of marking	*/ +  			/* work of whatever kind is needed.  Returns	*/ +  			/* quickly if no collection is in progress.	*/ +  			/* Return TRUE if mark phase finished.		*/ +void GC_initiate_gc GC_PROTO((void)); +				/* initiate collection.			*/ +  				/* If the mark state is invalid, this	*/ +  				/* becomes full colleection.  Otherwise */ +  				/* it's partial.			*/ +void GC_push_all GC_PROTO((ptr_t bottom, ptr_t top)); +				/* Push everything in a range 		*/ +  				/* onto mark stack.			*/ +void GC_push_selected GC_PROTO(( \ +    ptr_t bottom, \ +    ptr_t top, \ +    int (*dirty_fn) GC_PROTO((struct hblk *h)), \ +    void (*push_fn) GC_PROTO((ptr_t bottom, ptr_t top)) )); +				  /* Push all pages h in [b,t) s.t. 	*/ +				  /* select_fn(h) != 0 onto mark stack. */  #ifndef SMALL_CONFIG -  void GC_push_conditional(/* ptr_t b, ptr_t t, GC_bool all*/); +  void GC_push_conditional GC_PROTO((ptr_t b, ptr_t t, GC_bool all));  #else  # define GC_push_conditional(b, t, all) GC_push_all(b, t)  #endif                                  /* Do either of the above, depending	*/  				/* on the third arg.			*/ -void GC_push_all_stack(/*b,t*/);    /* As above, but consider		*/ +void GC_push_all_stack GC_PROTO((ptr_t b, ptr_t t)); +				    /* As above, but consider		*/  				    /*  interior pointers as valid  	*/ -void GC_push_all_eager(/*b,t*/);    /* Same as GC_push_all_stack, but   */ +void GC_push_all_eager GC_PROTO((ptr_t b, ptr_t t)); +				    /* Same as GC_push_all_stack, but   */  				    /* ensures that stack is scanned	*/  				    /* immediately, not just scheduled  */  				    /* for scanning.			*/  #ifndef THREADS -  void GC_push_all_stack_partially_eager(/* bottom, top, cold_gc_frame */); +  void GC_push_all_stack_partially_eager GC_PROTO(( \ +      ptr_t bottom, ptr_t top, ptr_t cold_gc_frame ));  			/* Similar to GC_push_all_eager, but only the	*/  			/* part hotter than cold_gc_frame is scanned	*/ -			/* immediately.  Needed to endure that callee-	*/ +			/* immediately.  Needed to ensure that callee-	*/  			/* save registers are not missed.		*/  #else    /* In the threads case, we push part of the current thread stack	*/ @@ -1390,163 +1375,240 @@ void GC_push_all_eager(/*b,t*/);    /* Same as GC_push_all_stack, but   */    /* stacks are scheduled for scanning in *GC_push_other_roots, which	*/    /* is thread-package-specific.					*/  #endif -void GC_push_current_stack(/* ptr_t cold_gc_frame */); -			/* Push enough of the current stack eagerly to	*/ -			/* ensure that callee-save registers saved in	*/ -			/* GC frames are scanned.			*/ -			/* In the non-threads case, schedule entire	*/ -			/* stack for scanning.				*/ -void GC_push_roots(/* GC_bool all, ptr_t cold_gc_frame */); -			/* Push all or dirty roots.	*/ -extern void (*GC_push_other_roots)(); -			/* Push system or application specific roots	*/ -			/* onto the mark stack.  In some environments	*/ -			/* (e.g. threads environments) this is		*/ -			/* predfined to be non-zero.  A client supplied */ -			/* replacement should also call the original	*/ -			/* function.					*/ -extern void (*GC_start_call_back)(/* void */); -			/* Called at start of full collections.		*/ -			/* Not called if 0.  Called with allocation 	*/ -			/* lock held.					*/ -			/* 0 by default.				*/ -void GC_push_regs();	/* Push register contents onto mark stack.	*/ -void GC_remark();	/* Mark from all marked objects.  Used	*/ -		 	/* only if we had to drop something.	*/ -# if defined(MSWIN32) -  void __cdecl GC_push_one(); +void GC_push_current_stack GC_PROTO((ptr_t cold_gc_frame)); +  			/* Push enough of the current stack eagerly to	*/ +  			/* ensure that callee-save registers saved in	*/ +  			/* GC frames are scanned.			*/ +  			/* In the non-threads case, schedule entire	*/ +  			/* stack for scanning.				*/ +void GC_push_roots GC_PROTO((GC_bool all, ptr_t cold_gc_frame)); +  			/* Push all or dirty roots.	*/ +extern void (*GC_push_other_roots) GC_PROTO((void)); +  			/* Push system or application specific roots	*/ +  			/* onto the mark stack.  In some environments	*/ +  			/* (e.g. threads environments) this is		*/ +  			/* predfined to be non-zero.  A client supplied */ +  			/* replacement should also call the original	*/ +  			/* function.					*/ +extern void GC_push_gc_structures GC_PROTO((void)); +			/* Push GC internal roots.  These are normally	*/ +			/* included in the static data segment, and 	*/ +			/* Thus implicitly pushed.  But we must do this	*/ +			/* explicitly if normal root processing is 	*/ +			/* disabled.  Calls the following:		*/ +	extern void GC_push_finalizer_structures GC_PROTO((void)); +	extern void GC_push_stubborn_structures GC_PROTO((void)); +#	ifdef THREADS +	  extern void GC_push_thread_structures GC_PROTO((void)); +#	endif +extern void (*GC_start_call_back) GC_PROTO((void)); +  			/* Called at start of full collections.		*/ +  			/* Not called if 0.  Called with allocation 	*/ +  			/* lock held.					*/ +  			/* 0 by default.				*/ +# if defined(USE_GENERIC_PUSH_REGS) +  void GC_generic_push_regs GC_PROTO((ptr_t cold_gc_frame)); +# else +  void GC_push_regs GC_PROTO((void)); +# endif +			/* Push register contents onto mark stack.	*/ +  			/* If NURSERY is defined, the default push	*/ +  			/* action can be overridden with GC_push_proc	*/ + +# ifdef NURSERY +    extern void (*GC_push_proc)(ptr_t); +# endif +# if defined(MSWIN32) || defined(MSWINCE) +  void __cdecl GC_push_one GC_PROTO((word p));  # else -  void GC_push_one(/*p*/);    /* If p points to an object, mark it    */ +  void GC_push_one GC_PROTO((word p)); +			      /* If p points to an object, mark it    */                                /* and push contents on the mark stack  */ +  			      /* Pointer recognition test always      */ +  			      /* accepts interior pointers, i.e. this */ +  			      /* is appropriate for pointers found on */ +  			      /* stack.				      */  # endif -void GC_push_one_checked(/*p*/); /* Ditto, omits plausibility test	*/ -void GC_push_marked(/* struct hblk h, hdr * hhdr */); +# if defined(PRINT_BLACK_LIST) || defined(KEEP_BACK_PTRS) +  void GC_mark_and_push_stack GC_PROTO((word p, ptr_t source)); +				/* Ditto, omits plausibility test	*/ +# else +  void GC_mark_and_push_stack GC_PROTO((word p)); +# endif +void GC_push_marked GC_PROTO((struct hblk * h, hdr * hhdr));  		/* Push contents of all marked objects in h onto	*/  		/* mark stack.						*/  #ifdef SMALL_CONFIG  # define GC_push_next_marked_dirty(h) GC_push_next_marked(h)  #else -  struct hblk * GC_push_next_marked_dirty(/* h */); +  struct hblk * GC_push_next_marked_dirty GC_PROTO((struct hblk * h));  		/* Invoke GC_push_marked on next dirty block above h.	*/  		/* Return a pointer just past the end of this block.	*/  #endif /* !SMALL_CONFIG */ -struct hblk * GC_push_next_marked(/* h */); -		/* Ditto, but also mark from clean pages.	*/ -struct hblk * GC_push_next_marked_uncollectable(/* h */); -		/* Ditto, but mark only from uncollectable pages.	*/ -GC_bool GC_stopped_mark(); /* Stop world and mark from all roots	*/ -			/* and rescuers.			*/ -void GC_clear_hdr_marks(/* hhdr */);  /* Clear the mark bits in a header */ -void GC_set_hdr_marks(/* hhdr */);  /* Set the mark bits in a header */ -void GC_add_roots_inner(); -GC_bool GC_is_static_root(/* ptr_t p */); -		/* Is the address p in one of the registered static	*/ +struct hblk * GC_push_next_marked GC_PROTO((struct hblk * h)); +  		/* Ditto, but also mark from clean pages.	*/ +struct hblk * GC_push_next_marked_uncollectable GC_PROTO((struct hblk * h)); +  		/* Ditto, but mark only from uncollectable pages.	*/ +GC_bool GC_stopped_mark GC_PROTO((GC_stop_func stop_func)); + 			/* Stop world and mark from all roots	*/ +  			/* and rescuers.			*/ +void GC_clear_hdr_marks GC_PROTO((hdr * hhdr)); +				    /* Clear the mark bits in a header */ +void GC_set_hdr_marks GC_PROTO((hdr * hhdr)); + 				    /* Set the mark bits in a header */ +void GC_set_fl_marks GC_PROTO((ptr_t p)); +				    /* Set all mark bits associated with */ +				    /* a free list.			 */ +void GC_add_roots_inner GC_PROTO((char * b, char * e, GC_bool tmp)); +GC_bool GC_is_static_root GC_PROTO((ptr_t p)); +  		/* Is the address p in one of the registered static	*/ +  		/* root sections?					*/ +# if defined(MSWIN32) || defined(_WIN32_WCE_EMULATION) +GC_bool GC_is_tmp_root GC_PROTO((ptr_t p)); +		/* Is the address p in one of the temporary static	*/  		/* root sections?					*/ -void GC_register_dynamic_libraries(); -		/* Add dynamic library data sections to the root set. */ - +# endif +void GC_register_dynamic_libraries GC_PROTO((void)); +  		/* Add dynamic library data sections to the root set. */ +    /* Machine dependent startup routines */ -ptr_t GC_get_stack_base(); -void GC_register_data_segments(); - +ptr_t GC_get_stack_base GC_PROTO((void));	/* Cold end of stack */ +#ifdef IA64 +  ptr_t GC_get_register_stack_base GC_PROTO((void)); +  					/* Cold end of register stack.	*/ +#endif +void GC_register_data_segments GC_PROTO((void)); +    /* Black listing: */ -void GC_bl_init(); 	 -# ifndef ALL_INTERIOR_POINTERS -    void GC_add_to_black_list_normal(/* bits, maybe source */); +void GC_bl_init GC_PROTO((void)); +# ifdef PRINT_BLACK_LIST +      void GC_add_to_black_list_normal GC_PROTO((word p, ptr_t source));  			/* Register bits as a possible future false	*/  			/* reference from the heap or static data	*/ -#   ifdef PRINT_BLACK_LIST  #     define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \ -			GC_add_to_black_list_normal(bits, source) -#   else -#     define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \ -			GC_add_to_black_list_normal(bits) -#   endif +      		if (GC_all_interior_pointers) { \ +		  GC_add_to_black_list_stack(bits, (ptr_t)(source)); \ +		} else { \ +  		  GC_add_to_black_list_normal(bits, (ptr_t)(source)); \ +		}  # else -#   ifdef PRINT_BLACK_LIST -#     define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \ -			GC_add_to_black_list_stack(bits, source) -#   else +      void GC_add_to_black_list_normal GC_PROTO((word p));  #     define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \ -			GC_add_to_black_list_stack(bits) -#   endif +      		if (GC_all_interior_pointers) { \ +		  GC_add_to_black_list_stack(bits); \ +		} else { \ +  		  GC_add_to_black_list_normal(bits); \ +		}  # endif -void GC_add_to_black_list_stack(/* bits, maybe source */); -struct hblk * GC_is_black_listed(/* h, len */); -			/* If there are likely to be false references	*/ -			/* to a block starting at h of the indicated    */ -			/* length, then return the next plausible	*/ -			/* starting location for h that might avoid	*/ -			/* these false references.			*/ -void GC_promote_black_lists(); -			/* Declare an end to a black listing phase.	*/ -void GC_unpromote_black_lists(); -			/* Approximately undo the effect of the above.	*/ -			/* This actually loses some information, but	*/ -			/* only in a reasonably safe way.		*/ -word GC_number_stack_black_listed(/*struct hblk *start, struct hblk *endp1 */); -			/* Return the number of (stack) blacklisted	*/ -			/* blocks in the range for statistical		*/ -			/* purposes.					*/ -		 	 -ptr_t GC_scratch_alloc(/*bytes*/); -				/* GC internal memory allocation for	*/ -				/* small objects.  Deallocation is not  */ -				/* possible.				*/ -	 +# ifdef PRINT_BLACK_LIST +    void GC_add_to_black_list_stack GC_PROTO((word p, ptr_t source)); +# else +    void GC_add_to_black_list_stack GC_PROTO((word p)); +# endif +struct hblk * GC_is_black_listed GC_PROTO((struct hblk * h, word len)); +  			/* If there are likely to be false references	*/ +  			/* to a block starting at h of the indicated    */ +  			/* length, then return the next plausible	*/ +  			/* starting location for h that might avoid	*/ +  			/* these false references.			*/ +void GC_promote_black_lists GC_PROTO((void)); +  			/* Declare an end to a black listing phase.	*/ +void GC_unpromote_black_lists GC_PROTO((void)); +  			/* Approximately undo the effect of the above.	*/ +  			/* This actually loses some information, but	*/ +  			/* only in a reasonably safe way.		*/ +word GC_number_stack_black_listed GC_PROTO(( \ +	struct hblk *start, struct hblk *endp1)); +  			/* Return the number of (stack) blacklisted	*/ +  			/* blocks in the range for statistical		*/ +  			/* purposes.					*/ +  		 	 +ptr_t GC_scratch_alloc GC_PROTO((word bytes)); +  				/* GC internal memory allocation for	*/ +  				/* small objects.  Deallocation is not  */ +  				/* possible.				*/ +  	  /* Heap block layout maps: */			 -void GC_invalidate_map(/* hdr */); -				/* Remove the object map associated	*/ -				/* with the block.  This identifies	*/ -				/* the block as invalid to the mark	*/ -				/* routines.				*/ -GC_bool GC_add_map_entry(/*sz*/); -				/* Add a heap block map for objects of	*/ -				/* size sz to obj_map.			*/ -				/* Return FALSE on failure.		*/ -void GC_register_displacement_inner(/*offset*/); -				/* Version of GC_register_displacement	*/ -				/* that assumes lock is already held	*/ -				/* and signals are already disabled.	*/ - +void GC_invalidate_map GC_PROTO((hdr * hhdr)); +  				/* Remove the object map associated	*/ +  				/* with the block.  This identifies	*/ +  				/* the block as invalid to the mark	*/ +  				/* routines.				*/ +GC_bool GC_add_map_entry GC_PROTO((word sz)); +  				/* Add a heap block map for objects of	*/ +  				/* size sz to obj_map.			*/ +  				/* Return FALSE on failure.		*/ +void GC_register_displacement_inner GC_PROTO((word offset)); +  				/* Version of GC_register_displacement	*/ +  				/* that assumes lock is already held	*/ +  				/* and signals are already disabled.	*/ +    /*  hblk allocation: */		 -void GC_new_hblk(/*size_in_words, kind*/); -				/* Allocate a new heap block, and build */ -				/* a free list in it.			*/				 -struct hblk * GC_allochblk(/*size_in_words, kind*/); -				/* Allocate a heap block, clear it if	*/ -				/* for composite objects, inform	*/ +void GC_new_hblk GC_PROTO((word size_in_words, int kind)); +  				/* Allocate a new heap block, and build */ +  				/* a free list in it.			*/				 + +ptr_t GC_build_fl GC_PROTO((struct hblk *h, word sz, +			   GC_bool clear,  ptr_t list)); +				/* Build a free list for objects of 	*/ +				/* size sz in block h.  Append list to	*/ +				/* end of the free lists.  Possibly	*/ +				/* clear objects on the list.  Normally	*/ +				/* called by GC_new_hblk, but also	*/ +				/* called explicitly without GC lock.	*/ + +struct hblk * GC_allochblk GC_PROTO(( \ +	word size_in_words, int kind, unsigned flags)); +				/* Allocate a heap block, inform	*/  				/* the marker that block is valid	*/  				/* for objects of indicated size.	*/ -				/* sz < 0 ==> atomic.			*/  -void GC_freehblk();		/* Deallocate a heap block and mark it  */ -				/* as invalid.				*/ -				 + +ptr_t GC_alloc_large GC_PROTO((word lw, int k, unsigned flags)); +			/* Allocate a large block of size lw words.	*/ +			/* The block is not cleared.			*/ +			/* Flags is 0 or IGNORE_OFF_PAGE.		*/ +			/* Calls GC_allchblk to do the actual 		*/ +			/* allocation, but also triggers GC and/or	*/ +			/* heap expansion as appropriate.		*/ +			/* Does not update GC_words_allocd, but does	*/ +			/* other accounting.				*/ + +ptr_t GC_alloc_large_and_clear GC_PROTO((word lw, int k, unsigned flags)); +			/* As above, but clear block if appropriate	*/ +			/* for kind k.					*/ + +void GC_freehblk GC_PROTO((struct hblk * p)); +				/* Deallocate a heap block and mark it  */ +  				/* as invalid.				*/ +  				  /*  Misc GC: */ -void GC_init_inner(); -GC_bool GC_expand_hp_inner(); -void GC_start_reclaim(/*abort_if_found*/); -				/* Restore unmarked objects to free	*/ -				/* lists, or (if abort_if_found is	*/ -				/* TRUE) report them.			*/ -				/* Sweeping of small object pages is	*/ -				/* largely deferred.			*/ -void GC_continue_reclaim(/*size, kind*/); -				/* Sweep pages of the given size and	*/ -				/* kind, as long as possible, and	*/ -				/* as long as the corr. free list is    */ -				/* empty.				*/ -void GC_reclaim_or_delete_all(); -				/* Arrange for all reclaim lists to be	*/ -				/* empty.  Judiciously choose between	*/ -				/* sweeping and discarding each page.	*/ -GC_bool GC_reclaim_all(/* GC_stop_func f*/); -				/* Reclaim all blocks.  Abort (in a	*/ -				/* consistent state) if f returns TRUE. */ -GC_bool GC_block_empty(/* hhdr */); /* Block completely unmarked? 	*/ -GC_bool GC_never_stop_func();	/* Returns FALSE.		*/ -GC_bool GC_try_to_collect_inner(/* GC_stop_func f */); +void GC_init_inner GC_PROTO((void)); +GC_bool GC_expand_hp_inner GC_PROTO((word n)); +void GC_start_reclaim GC_PROTO((int abort_if_found)); +  				/* Restore unmarked objects to free	*/ +  				/* lists, or (if abort_if_found is	*/ +  				/* TRUE) report them.			*/ +  				/* Sweeping of small object pages is	*/ +  				/* largely deferred.			*/ +void GC_continue_reclaim GC_PROTO((word sz, int kind)); +  				/* Sweep pages of the given size and	*/ +  				/* kind, as long as possible, and	*/ +  				/* as long as the corr. free list is    */ +  				/* empty.				*/ +void GC_reclaim_or_delete_all GC_PROTO((void)); +  				/* Arrange for all reclaim lists to be	*/ +  				/* empty.  Judiciously choose between	*/ +  				/* sweeping and discarding each page.	*/ +GC_bool GC_reclaim_all GC_PROTO((GC_stop_func stop_func, GC_bool ignore_old)); +  				/* Reclaim all blocks.  Abort (in a	*/ +  				/* consistent state) if f returns TRUE. */ +GC_bool GC_block_empty GC_PROTO((hdr * hhdr)); + 				/* Block completely unmarked? 	*/ +GC_bool GC_never_stop_func GC_PROTO((void)); +				/* Returns FALSE.		*/ +GC_bool GC_try_to_collect_inner GC_PROTO((GC_stop_func f)); +  				/* Collect; caller must have acquired	*/  				/* lock and disabled signals.		*/  				/* Collection is aborted if f returns	*/ @@ -1554,87 +1616,133 @@ GC_bool GC_try_to_collect_inner(/* GC_stop_func f */);  				/* successfully.			*/  # define GC_gcollect_inner() \  	(void) GC_try_to_collect_inner(GC_never_stop_func) -void GC_finish_collection();	/* Finish collection.  Mark bits are	*/ -				/* consistent and lock is still held.	*/ -GC_bool GC_collect_or_expand(/* needed_blocks */); -				/* Collect or expand heap in an attempt */ -				/* make the indicated number of free	*/ -				/* blocks available.  Should be called	*/ -				/* until the blocks are available or	*/ -				/* until it fails by returning FALSE.	*/ -GC_API void GC_init();		/* Initialize collector.		*/ -void GC_collect_a_little_inner(/* int n */); -				/* Do n units worth of garbage 		*/ -				/* collection work, if appropriate.	*/ -				/* A unit is an amount appropriate for  */ -				/* HBLKSIZE bytes of allocation.	*/ -ptr_t GC_generic_malloc(/* bytes, kind */); -				/* Allocate an object of the given	*/ -				/* kind.  By default, there are only	*/ -				/* a few kinds: composite(pointerfree), */ +void GC_finish_collection GC_PROTO((void)); + 				/* Finish collection.  Mark bits are	*/ +  				/* consistent and lock is still held.	*/ +GC_bool GC_collect_or_expand GC_PROTO(( \ +	word needed_blocks, GC_bool ignore_off_page)); +  				/* Collect or expand heap in an attempt */ +  				/* make the indicated number of free	*/ +  				/* blocks available.  Should be called	*/ +  				/* until the blocks are available or	*/ +  				/* until it fails by returning FALSE.	*/ +GC_API void GC_init GC_PROTO((void)); /* Initialize collector.		*/ + +#if defined(MSWIN32) || defined(MSWINCE) +  void GC_deinit GC_PROTO((void)); +                                /* Free any resources allocated by      */ +                                /* GC_init                              */ +#endif + +void GC_collect_a_little_inner GC_PROTO((int n)); +  				/* Do n units worth of garbage 		*/ +  				/* collection work, if appropriate.	*/ +  				/* A unit is an amount appropriate for  */ +  				/* HBLKSIZE bytes of allocation.	*/ +ptr_t GC_generic_malloc GC_PROTO((word lb, int k)); +  				/* Allocate an object of the given	*/ +  				/* kind.  By default, there are only	*/ +  				/* a few kinds: composite(pointerfree), */  				/* atomic, uncollectable, etc.		*/  				/* We claim it's possible for clever	*/  				/* client code that understands GC	*/  				/* internals to add more, e.g. to	*/  				/* communicate object layout info	*/  				/* to the collector.			*/ -ptr_t GC_generic_malloc_ignore_off_page(/* bytes, kind */); -				/* As above, but pointers past the 	*/ -				/* first page of the resulting object	*/ -				/* are ignored.				*/ -ptr_t GC_generic_malloc_inner(/* bytes, kind */); -				/* Ditto, but I already hold lock, etc.	*/ -ptr_t GC_generic_malloc_words_small GC_PROTO((size_t words, int kind)); -				/* As above, but size in units of words */ -				/* Bypasses MERGE_SIZES.  Assumes	*/ -				/* words <= MAXOBJSZ.			*/ -ptr_t GC_generic_malloc_inner_ignore_off_page(/* bytes, kind */); -				/* Allocate an object, where		*/ -				/* the client guarantees that there	*/ -				/* will always be a pointer to the 	*/ -				/* beginning of the object while the	*/ -				/* object is live.			*/ -ptr_t GC_allocobj(/* sz_inn_words, kind */); -				/* Make the indicated 			*/ -				/* free list nonempty, and return its	*/ -				/* head.				*/ - -void GC_init_headers(); -GC_bool GC_install_header(/*h*/); -				/* Install a header for block h.	*/ -				/* Return FALSE on failure.		*/ -GC_bool GC_install_counts(/*h, sz*/); -				/* Set up forwarding counts for block	*/ -				/* h of size sz.			*/ -				/* Return FALSE on failure.		*/ -void GC_remove_header(/*h*/); -				/* Remove the header for block h.	*/ -void GC_remove_counts(/*h, sz*/); -				/* Remove forwarding counts for h.	*/ -hdr * GC_find_header(/*p*/);	/* Debugging only.			*/ - -void GC_finalize();	/* Perform all indicated finalization actions	*/ -			/* on unmarked objects.				*/ -			/* Unreachable finalizable objects are enqueued	*/ -			/* for processing by GC_invoke_finalizers.	*/ -			/* Invoked with lock.				*/ -			 -void GC_add_to_heap(/*p, bytes*/); -			/* Add a HBLKSIZE aligned chunk to the heap.	*/ - -void GC_print_obj(/* ptr_t p */); -			/* P points to somewhere inside an object with	*/ -			/* debugging info.  Print a human readable	*/ -			/* description of the object to stderr.		*/ -extern void (*GC_check_heap)(); -			/* Check that all objects in the heap with 	*/ -			/* debugging info are intact.  Print 		*/ -			/* descriptions of any that are not.		*/ -extern void (*GC_print_heap_obj)(/* ptr_t p */); -			/* If possible print s followed by a more	*/ -			/* detailed description of the object 		*/ -			/* referred to by p.				*/ -			 +ptr_t GC_generic_malloc_ignore_off_page GC_PROTO((size_t b, int k)); +  				/* As above, but pointers past the 	*/ +  				/* first page of the resulting object	*/ +  				/* are ignored.				*/ +ptr_t GC_generic_malloc_inner GC_PROTO((word lb, int k)); +  				/* Ditto, but I already hold lock, etc.	*/ +ptr_t GC_generic_malloc_words_small GC_PROTO((size_t lw, int k)); +  				/* As above, but size in units of words */ +  				/* Bypasses MERGE_SIZES.  Assumes	*/ +  				/* words <= MAXOBJSZ.			*/ +ptr_t GC_generic_malloc_inner_ignore_off_page GC_PROTO((size_t lb, int k)); +  				/* Allocate an object, where		*/ +  				/* the client guarantees that there	*/ +  				/* will always be a pointer to the 	*/ +  				/* beginning of the object while the	*/ +  				/* object is live.			*/ +ptr_t GC_allocobj GC_PROTO((word sz, int kind)); +  				/* Make the indicated 			*/ +  				/* free list nonempty, and return its	*/ +  				/* head.				*/ +   +void GC_init_headers GC_PROTO((void)); +struct hblkhdr * GC_install_header GC_PROTO((struct hblk *h)); +  				/* Install a header for block h.	*/ +  				/* Return 0 on failure, or the header	*/ +  				/* otherwise.				*/ +GC_bool GC_install_counts GC_PROTO((struct hblk * h, word sz)); +  				/* Set up forwarding counts for block	*/ +  				/* h of size sz.			*/ +  				/* Return FALSE on failure.		*/ +void GC_remove_header GC_PROTO((struct hblk * h)); +  				/* Remove the header for block h.	*/ +void GC_remove_counts GC_PROTO((struct hblk * h, word sz)); +  				/* Remove forwarding counts for h.	*/ +hdr * GC_find_header GC_PROTO((ptr_t h)); /* Debugging only.		*/ +   +void GC_finalize GC_PROTO((void)); + 			/* Perform all indicated finalization actions	*/ +  			/* on unmarked objects.				*/ +  			/* Unreachable finalizable objects are enqueued	*/ +  			/* for processing by GC_invoke_finalizers.	*/ +  			/* Invoked with lock.				*/ + +void GC_notify_or_invoke_finalizers GC_PROTO((void)); +			/* If GC_finalize_on_demand is not set, invoke	*/ +			/* eligible finalizers. Otherwise:		*/ +			/* Call *GC_finalizer_notifier if there are	*/ +			/* finalizers to be run, and we haven't called	*/ +			/* this procedure yet this GC cycle.		*/ +  			 +void GC_add_to_heap GC_PROTO((struct hblk *p, word bytes)); +  			/* Add a HBLKSIZE aligned chunk to the heap.	*/ +   +void GC_print_obj GC_PROTO((ptr_t p)); +  			/* P points to somewhere inside an object with	*/ +  			/* debugging info.  Print a human readable	*/ +  			/* description of the object to stderr.		*/ +extern void (*GC_check_heap) GC_PROTO((void)); +  			/* Check that all objects in the heap with 	*/ +  			/* debugging info are intact.  Print 		*/ +  			/* descriptions of any that are not.		*/ +extern void (*GC_print_heap_obj) GC_PROTO((ptr_t p)); +  			/* If possible print s followed by a more	*/ +  			/* detailed description of the object 		*/ +  			/* referred to by p.				*/ + +extern GC_bool GC_print_stats;	/* Produce at least some logging output	*/ +				/* Set from environment variable.	*/ + +/* Macros used for collector internal allocation.	*/ +/* These assume the collector lock is held.		*/ +#ifdef DBG_HDRS_ALL +    extern GC_PTR GC_debug_generic_malloc_inner(size_t lb, int k); +    extern GC_PTR GC_debug_generic_malloc_inner_ignore_off_page(size_t lb, +								int k); +#   define GC_INTERNAL_MALLOC GC_debug_generic_malloc_inner +#   define GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE \ +		 GC_debug_generic_malloc_inner_ignore_off_page +#   ifdef THREADS +#       define GC_INTERNAL_FREE GC_debug_free_inner +#   else +#       define GC_INTERNAL_FREE GC_debug_free +#   endif +#else +#   define GC_INTERNAL_MALLOC GC_generic_malloc_inner +#   define GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE \ +		 GC_generic_malloc_inner_ignore_off_page +#   ifdef THREADS +#       define GC_INTERNAL_FREE GC_free_inner +#   else +#       define GC_INTERNAL_FREE GC_free +#   endif +#endif +  /* Memory unmapping: */  #ifdef USE_MUNMAP    void GC_unmap_old(void); @@ -1646,35 +1754,38 @@ extern void (*GC_print_heap_obj)(/* ptr_t p */);  /* Virtual dirty bit implementation:		*/  /* Each implementation exports the following:	*/ -void GC_read_dirty();	/* Retrieve dirty bits.	*/ -GC_bool GC_page_was_dirty(/* struct hblk * h  */); -			/* Read retrieved dirty bits.	*/ -GC_bool GC_page_was_ever_dirty(/* struct hblk * h  */); -			/* Could the page contain valid heap pointers?	*/ -void GC_is_fresh(/* struct hblk * h, word number_of_blocks  */); -			/* Assert the region currently contains no	*/ -			/* valid pointers.				*/ -void GC_write_hint(/* struct hblk * h  */); -			/* h is about to be written.	*/ -void GC_dirty_init(); - +void GC_read_dirty GC_PROTO((void)); +			/* Retrieve dirty bits.	*/ +GC_bool GC_page_was_dirty GC_PROTO((struct hblk *h)); +  			/* Read retrieved dirty bits.	*/ +GC_bool GC_page_was_ever_dirty GC_PROTO((struct hblk *h)); +  			/* Could the page contain valid heap pointers?	*/ +void GC_is_fresh GC_PROTO((struct hblk *h, word n)); +  			/* Assert the region currently contains no	*/ +  			/* valid pointers.				*/ +void GC_write_hint GC_PROTO((struct hblk *h)); +  			/* h is about to be written.	*/ +void GC_dirty_init GC_PROTO((void)); +    /* Slow/general mark bit manipulation: */ -GC_API GC_bool GC_is_marked(); -void GC_clear_mark_bit(); -void GC_set_mark_bit(); - +GC_API GC_bool GC_is_marked GC_PROTO((ptr_t p)); +void GC_clear_mark_bit GC_PROTO((ptr_t p)); +void GC_set_mark_bit GC_PROTO((ptr_t p)); +    /* Stubborn objects: */ -void GC_read_changed();	/* Analogous to GC_read_dirty */ -GC_bool GC_page_was_changed(/* h */);	/* Analogous to GC_page_was_dirty */ -void GC_clean_changing_list();	/* Collect obsolete changing list entries */ -void GC_stubborn_init(); - +void GC_read_changed GC_PROTO((void));	/* Analogous to GC_read_dirty */ +GC_bool GC_page_was_changed GC_PROTO((struct hblk * h)); + 				/* Analogous to GC_page_was_dirty */ +void GC_clean_changing_list GC_PROTO((void)); + 				/* Collect obsolete changing list entries */ +void GC_stubborn_init GC_PROTO((void)); +    /* Debugging print routines: */ -void GC_print_block_list(); -void GC_print_hblkfreelist(); -void GC_print_heap_sects(); -void GC_print_static_roots(); -void GC_dump(); +void GC_print_block_list GC_PROTO((void)); +void GC_print_hblkfreelist GC_PROTO((void)); +void GC_print_heap_sects GC_PROTO((void)); +void GC_print_static_roots GC_PROTO((void)); +void GC_dump GC_PROTO((void));  #ifdef KEEP_BACK_PTRS     void GC_store_back_pointer(ptr_t source, ptr_t dest); @@ -1688,15 +1799,19 @@ void GC_dump();  /* Make arguments appear live to compiler */  # ifdef __WATCOMC__ -  void GC_noop(void*, ...); +    void GC_noop(void*, ...);  # else -  GC_API void GC_noop(); +#   ifdef __DMC__ +      GC_API void GC_noop(...); +#   else +      GC_API void GC_noop(); +#   endif  # endif -void GC_noop1(/* word arg */); +void GC_noop1 GC_PROTO((word));  /* Logging and diagnostic output: 	*/ -GC_API void GC_printf GC_PROTO((char * format, long, long, long, long, long, long)); +GC_API void GC_printf GC_PROTO((GC_CONST char * format, long, long, long, long, long, long));  			/* A version of printf that doesn't allocate,	*/  			/* is restricted to long arguments, and		*/  			/* (unfortunately) doesn't use varargs for	*/ @@ -1715,7 +1830,7 @@ GC_API void GC_printf GC_PROTO((char * format, long, long, long, long, long, lon  # define GC_printf6(f,a,b,c,d,e,g) GC_printf(f, (long)a, (long)b, (long)c, \  						(long)d, (long)e, (long)g) -void GC_err_printf(/* format, a, b, c, d, e, f */); +GC_API void GC_err_printf GC_PROTO((GC_CONST char * format, long, long, long, long, long, long));  # define GC_err_printf0(f) GC_err_puts(f)  # define GC_err_printf1(f,a) GC_err_printf(f, (long)a, 0l, 0l, 0l, 0l, 0l)  # define GC_err_printf2(f,a,b) GC_err_printf(f, (long)a, (long)b, 0l, 0l, 0l, 0l) @@ -1731,18 +1846,86 @@ void GC_err_printf(/* format, a, b, c, d, e, f */);  							(long)e, (long)g)  			/* Ditto, writes to stderr.			*/ -void GC_err_puts(/* char *s */); +void GC_err_puts GC_PROTO((GC_CONST char *s));  			/* Write s to stderr, don't buffer, don't add	*/  			/* newlines, don't ...				*/ +#if defined(LINUX) && !defined(SMALL_CONFIG) +  void GC_err_write GC_PROTO((GC_CONST char *buf, size_t len)); + 			/* Write buf to stderr, don't buffer, don't add	*/ +  			/* newlines, don't ...				*/ +#endif -#   ifdef GC_ASSERTIONS + +# ifdef GC_ASSERTIONS  #	define GC_ASSERT(expr) if(!(expr)) {\  		GC_err_printf2("Assertion failure: %s:%ld\n", \  				__FILE__, (unsigned long)__LINE__); \  		ABORT("assertion failure"); } -#   else  +# else   #	define GC_ASSERT(expr) +# endif + +# if defined(PARALLEL_MARK) || defined(THREAD_LOCAL_ALLOC) +    /* We need additional synchronization facilities from the thread	*/ +    /* support.  We believe these are less performance critical		*/ +    /* than the main garbage collector lock; standard pthreads-based	*/ +    /* implementations should be sufficient.				*/ + +    /* The mark lock and condition variable.  If the GC lock is also 	*/ +    /* acquired, the GC lock must be acquired first.  The mark lock is	*/ +    /* used to both protect some variables used by the parallel		*/ +    /* marker, and to protect GC_fl_builder_count, below.		*/ +    /* GC_notify_all_marker() is called when				*/  +    /* the state of the parallel marker changes				*/ +    /* in some significant way (see gc_mark.h for details).  The	*/ +    /* latter set of events includes incrementing GC_mark_no.		*/ +    /* GC_notify_all_builder() is called when GC_fl_builder_count	*/ +    /* reaches 0.							*/ + +     extern void GC_acquire_mark_lock(); +     extern void GC_release_mark_lock(); +     extern void GC_notify_all_builder(); +     /* extern void GC_wait_builder(); */ +     extern void GC_wait_for_reclaim(); + +     extern word GC_fl_builder_count;	/* Protected by mark lock.	*/ +# endif /* PARALLEL_MARK || THREAD_LOCAL_ALLOC */ +# ifdef PARALLEL_MARK +     extern void GC_notify_all_marker(); +     extern void GC_wait_marker(); +     extern word GC_mark_no;		/* Protected by mark lock.	*/ + +     extern void GC_help_marker(word my_mark_no); +		/* Try to help out parallel marker for mark cycle 	*/ +		/* my_mark_no.  Returns if the mark cycle finishes or	*/ +		/* was already done, or there was nothing to do for	*/ +		/* some other reason.					*/ +# endif /* PARALLEL_MARK */ + +# if defined(LINUX_THREADS) || defined(IRIX_THREADS) \ +     || defined(HPUX_THREADS) || defined(OSF1_THREADS) +  /* We define the thread suspension signal here, so that we can refer	*/ +  /* to it in the dirty bit implementation, if necessary.  Ideally we	*/ +  /* would allocate a (real-time ?) signal using the standard mechanism.*/ +  /* unfortunately, there is no standard mechanism.  (There is one 	*/ +  /* in Linux glibc, but it's not exported.)  Thus we continue to use	*/ +  /* the same hard-coded signals we've always used.			*/ +#  if !defined(SIG_SUSPEND) +#   if defined(LINUX_THREADS) +#    if defined(SPARC) && !defined(SIGPWR) +       /* SPARC/Linux doesn't properly define SIGPWR in <signal.h>. +        * It is aliased to SIGLOST in asm/signal.h, though.		*/ +#      define SIG_SUSPEND SIGLOST +#    else +       /* Linuxthreads uses SIGUSR1 and SIGUSR2.			*/ +#      define SIG_SUSPEND SIGPWR +#    endif +#   else  /* !LINUX_THREADS */ +#    define SIG_SUSPEND _SIGRTMIN + 6  #   endif +#  endif /* !SIG_SUSPEND */ +   +# endif  # endif /* GC_PRIVATE_H */ diff --git a/gc/include/private/gcconfig.h b/gc/include/private/gcconfig.h index c9017d3..a0d12c8 100644 --- a/gc/include/private/gcconfig.h +++ b/gc/include/private/gcconfig.h @@ -2,6 +2,7 @@   * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers   * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.   * Copyright (c) 1996 by Silicon Graphics.  All rights reserved. + * Copyright (c) 2000 by Hewlett-Packard Company.  All rights reserved.   *   * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED   * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK. @@ -13,9 +14,9 @@   * modified is included with the above copyright notice.   */ -#ifndef CONFIG_H +#ifndef GCCONFIG_H -# define CONFIG_H +# define GCCONFIG_H  /* Machine dependent parameters.  Some tuning parameters can be found	*/  /* near the top of gc_private.h.					*/ @@ -27,6 +28,11 @@  #    define LINUX  # endif +/* And one for NetBSD: */ +# if defined(__NetBSD__) +#    define NETBSD +# endif +  /* Determine the machine type: */  # if defined(sun) && defined(mc68000)  #    define M68K @@ -50,7 +56,14 @@  # endif  # if defined(__NetBSD__) && defined(m68k)  #    define M68K -#    define NETBSD +#    define mach_type_known +# endif +# if defined(__NetBSD__) && defined(__powerpc__) +#    define POWERPC +#    define mach_type_known +# endif +# if defined(__NetBSD__) && defined(__arm32__) +#    define ARM32  #    define mach_type_known  # endif  # if defined(vax) @@ -64,23 +77,29 @@  # endif  # if defined(mips) || defined(__mips)  #    define MIPS -#    if defined(ultrix) || defined(__ultrix) || defined(__NetBSD__) -#	define ULTRIX -#    else -#	if defined(_SYSTYPE_SVR4) || defined(SYSTYPE_SVR4) || defined(__SYSTYPE_SVR4__) -#	  define IRIX5   /* or IRIX 6.X */ -#	else -#	  define RISCOS  /* or IRIX 4.X */ -#	endif +#    if !defined(LINUX) +#      if defined(ultrix) || defined(__ultrix) || defined(__NetBSD__) +#	 define ULTRIX +#      else +#	 if defined(_SYSTYPE_SVR4) || defined(SYSTYPE_SVR4) \ +	    || defined(__SYSTYPE_SVR4__) +#	   define IRIX5   /* or IRIX 6.X */ +#	 else +#	   define RISCOS  /* or IRIX 4.X */ +#	 endif +#      endif +#    endif /* !LINUX */ +#    if defined(__NetBSD__) && defined(__MIPSEL__) +#      undef ULTRIX  #    endif  #    define mach_type_known  # endif -# if defined(sequent) && defined(i386) +# if defined(sequent) && (defined(i386) || defined(__i386__))  #    define I386  #    define SEQUENT  #    define mach_type_known  # endif -# if defined(sun) && defined(i386) +# if defined(sun) && (defined(i386) || defined(__i386__))  #    define I386  #    define SUNOS5  #    define mach_type_known @@ -106,7 +125,7 @@  #   define mach_type_known  # endif  # if defined(sparc) && defined(unix) && !defined(sun) && !defined(linux) \ -     && !defined(__OpenBSD__) +     && !defined(__OpenBSD__) && !(__NetBSD__)  #   define SPARC  #   define DRSNX  #   define mach_type_known @@ -115,6 +134,10 @@  #   define RS6000  #   define mach_type_known  # endif +# if defined(__NetBSD__) && defined(__sparc__) +#   define SPARC +#   define mach_type_known +# endif  # if defined(_M_XENIX) && defined(_M_SYSV) && defined(_M_I386)  	/* The above test may need refinement	*/  #   define I386 @@ -130,16 +153,28 @@  #   define SYSV  #   define mach_type_known  # endif -# if defined(_PA_RISC1_0) || defined(_PA_RISC1_1) \ +# if defined(_PA_RISC1_0) || defined(_PA_RISC1_1) || defined(_PA_RISC2_0) \       || defined(hppa) || defined(__hppa__)  #   define HP_PA +#   ifndef LINUX +#     define HPUX +#   endif  #   define mach_type_known  # endif +# if defined(__BEOS__) && defined(_X86_) +#    define I386 +#    define BEOS +#    define mach_type_known +# endif  # if defined(LINUX) && (defined(i386) || defined(__i386__))  #    define I386  #    define mach_type_known  # endif -# if defined(LINUX) && defined(powerpc) +# if defined(LINUX) && (defined(__ia64__) || defined(__ia64)) +#    define IA64 +#    define mach_type_known +# endif +# if defined(LINUX) && (defined(powerpc) || defined(__powerpc__))  #    define POWERPC  #    define mach_type_known  # endif @@ -147,13 +182,21 @@  #    define M68K  #    define mach_type_known  # endif -# if defined(LINUX) && defined(sparc) +# if defined(LINUX) && (defined(sparc) || defined(__sparc__))  #    define SPARC  #    define mach_type_known  # endif +# if defined(LINUX) && defined(__arm__) +#    define ARM32 +#    define mach_type_known +# endif +# if defined(LINUX) && defined(__sh__) +#    define SH +#    define mach_type_known +# endif  # if defined(__alpha) || defined(__alpha__)  #   define ALPHA -#   if !defined(LINUX) +#   if !defined(LINUX) && !defined(NETBSD)  #     define OSF1	/* a.k.a Digital Unix */  #   endif  #   define mach_type_known @@ -175,37 +218,42 @@  #   define MACOS  #   define mach_type_known  # endif -# if defined(macosx) +# if defined(macosx) || \ +     defined(__APPLE__) && defined(__MACH__) && defined(__ppc__)  #    define MACOSX  #    define POWERPC  #    define mach_type_known  # endif +# if defined(__APPLE__) && defined(__MACH__) && defined(__i386__) +#    define MACOSX +#    define I386 +     --> Not really supported, but at least we recognize it. +# endif  # if defined(NeXT) && defined(mc68000)  #   define M68K  #   define NEXT  #   define mach_type_known  # endif -# if defined(NeXT) && defined(i386) +# if defined(NeXT) && (defined(i386) || defined(__i386__))  #   define I386  #   define NEXT  #   define mach_type_known  # endif -# if defined(__OpenBSD__) && defined(i386) +# if defined(__OpenBSD__) && (defined(i386) || defined(__i386__))  #   define I386  #   define OPENBSD  #   define mach_type_known  # endif -# if defined(__FreeBSD__) && defined(i386) +# if defined(__FreeBSD__) && (defined(i386) || defined(__i386__))  #   define I386  #   define FREEBSD  #   define mach_type_known  # endif -# if defined(__NetBSD__) && defined(i386) +# if defined(__NetBSD__) && (defined(i386) || defined(__i386__))  #   define I386 -#   define NETBSD  #   define mach_type_known  # endif -# if defined(bsdi) && defined(i386) +# if defined(bsdi) && (defined(i386) || defined(__i386__))  #    define I386  #    define BSDI  #    define mach_type_known @@ -225,11 +273,26 @@      /* DGUX defined */  #   define mach_type_known  # endif -# if (defined(_MSDOS) || defined(_MSC_VER)) && (_M_IX86 >= 300) \ -     || defined(_WIN32) && !defined(__CYGWIN32__) && !defined(__CYGWIN__) -#   define I386 -#   define MSWIN32	/* or Win32s */ +# if defined(_WIN32_WCE) +    /* SH3, SH4, MIPS already defined for corresponding architectures */ +#   if defined(SH3) || defined(SH4) +#     define SH +#   endif +#   if defined(x86) +#     define I386 +#   endif +#   if defined(ARM) +#     define ARM32 +#   endif +#   define MSWINCE  #   define mach_type_known +# else +#   if (defined(_MSDOS) || defined(_MSC_VER)) && (_M_IX86 >= 300) \ +        || defined(_WIN32) && !defined(__CYGWIN32__) && !defined(__CYGWIN__) +#     define I386 +#     define MSWIN32	/* or Win32s */ +#     define mach_type_known +#   endif  # endif  # if defined(__DJGPP__)  #   define I386 @@ -243,6 +306,11 @@  #   define CYGWIN32  #   define mach_type_known  # endif +# if defined(__MINGW32__) +#   define I386 +#   define MSWIN32 +#   define mach_type_known +# endif  # if defined(__BORLANDC__)  #   define I386  #   define MSWIN32 @@ -253,6 +321,15 @@  #   define UTS4  #   define mach_type_known  # endif +# if defined(__pj__) +#   define PJ +#   define mach_type_known +# endif +# if defined(__embedded__) && defined(PPC) +#   define POWERPC +#   define NOSYS +#   define mach_type_known +# endif  /* Ivan Demakov */  # if defined(__WATCOMC__) && defined(__386__)  #   define I386 @@ -269,6 +346,18 @@  #   endif  #   define mach_type_known  # endif +# if defined(__s390__) && defined(LINUX) +#    define S370 +#    define mach_type_known +# endif +# if defined(__GNU__) +#   if defined(__i386__) +/* The Debian Hurd running on generic PC */   +#     define  HURD +#     define  I386 +#     define  mach_type_known +#    endif  +# endif  /* Feel free to add more clauses here */ @@ -297,9 +386,9 @@                      /*		   RS6000     ==> IBM RS/6000 AIX3.X	*/                      /*		   RT	      ==> IBM PC/RT		*/                      /*		   HP_PA      ==> HP9000/700 & /800	*/ -                    /*				  HP/UX			*/ -		    /*		   SPARC      ==> SPARC under SunOS	*/ -		    /*			(SUNOS4, SUNOS5,		*/ +                    /*				  HP/UX, LINUX			*/ +		    /*		   SPARC      ==> SPARC	v7/v8/v9	*/ +		    /*			(SUNOS4, SUNOS5, LINUX,		*/  		    /*			 DRSNX variants)		*/  		    /* 		   ALPHA      ==> DEC Alpha 		*/  		    /*			(OSF1 and LINUX variants)	*/ @@ -307,6 +396,12 @@  		    /* 		        (CX_UX and DGUX)		*/  		    /* 		   S370	      ==> 370-like machine	*/  		    /* 			running Amdahl UTS4		*/ +		    /*			or a 390 running LINUX		*/ +		    /* 		   ARM32      ==> Intel StrongARM	*/ +		    /* 		   IA64	      ==> Intel IA64		*/ +		    /*				  (e.g. Itanium)	*/ +		    /*		   SH	      ==> Hitachi SuperH	*/ +		    /* 			(LINUX & MSWINCE)		*/  /* @@ -355,6 +450,13 @@   *		to the nearest plausible page boundary, and use that instead   *		of STACKBOTTOM.   * + * Gustavo Rodriguez-Rivera points out that on most (all?) Unix machines, + * the value of environ is a pointer that can serve as STACKBOTTOM. + * I expect that HEURISTIC2 can be replaced by this approach, which + * interferes far less with debugging.  However it has the disadvantage + * that it's confused by a putenv call before the collector is initialized. + * This could be dealt with by intercepting putenv ... + *   * If no expression for STACKBOTTOM can be found, and neither of the above   * heuristics are usable, the collector can still be used with all of the above   * undefined, provided one of the following is done: @@ -392,8 +494,25 @@   *   * An architecture may define DYNAMIC_LOADING if dynamic_load.c   * defined GC_register_dynamic_libraries() for the architecture. + * + * An architecture may define PREFETCH(x) to preload the cache with *x. + * This defaults to a no-op. + * + * PREFETCH_FOR_WRITE(x) is used if *x is about to be written. + * + * An architecture may also define CLEAR_DOUBLE(x) to be a fast way to + * clear the two words at GC_malloc-aligned address x.  By default, + * word stores of 0 are used instead.   */ +/* If we are using a recent version of gcc, we can use __builtin_unwind_init() + * to push the relevant registers onto the stack.  This generally makes + * USE_GENERIC_PUSH_REGS the preferred approach for marking from registers. + */ +# if defined(__GNUC__) && ((__GNUC__ >= 3) || \ +			   (__GNUC__ == 2 && __GNUC_MINOR__ >= 8)) +#   define HAVE_BUILTIN_UNWIND_INIT +# endif  # define STACK_GRAN 0x1000000  # ifdef M68K @@ -417,8 +536,12 @@  #       define MPROTECT_VDB  #       ifdef __ELF__  #            define DYNAMIC_LOADING -             extern char **__environ; -#            define DATASTART ((ptr_t)(&__environ)) +#	     include <features.h> +#	     if defined(__GLIBC__)&& __GLIBC__>=2 +#              define LINUX_DATA_START +#	     else /* !GLIBC2 */ +               extern char **__environ; +#              define DATASTART ((ptr_t)(&__environ))                               /* hideous kludge: __environ is the first */                               /* word in crt0.o, and delimits the start */                               /* of the data segment, no matter which   */ @@ -427,6 +550,7 @@                               /* would include .rodata, which may       */                               /* contain large read-only data tables    */                               /* that we'd rather not scan.             */ +#	     endif /* !GLIBC2 */               extern int _end;  #            define DATAEND (&_end)  #       else @@ -513,19 +637,43 @@  				/* This was 2, but that didn't sound right. */  #     define OS_TYPE "LINUX"  #     define HEURISTIC1 +#     define DYNAMIC_LOADING  #     undef STACK_GRAN  #     define STACK_GRAN 0x10000000  	/* Stack usually starts at 0x80000000 */ -#     define DATASTART GC_data_start +#     define LINUX_DATA_START        extern int _end;  #     define DATAEND (&_end)  #   endif  #   ifdef MACOSX +      /* There are reasons to suspect this may not be reliable. 	*/  #     define ALIGNMENT 4  #     define OS_TYPE "MACOSX"  #     define DATASTART ((ptr_t) get_etext())  #     define STACKBOTTOM ((ptr_t) 0xc0000000)  #     define DATAEND	/* not needed */ +#     define MPROTECT_VDB +#     include <unistd.h> +#     define GETPAGESIZE() getpagesize() +#   endif +#   ifdef NETBSD +#     define ALIGNMENT 4 +#     define OS_TYPE "NETBSD" +#     define HEURISTIC2 +      extern char etext; +#     define DATASTART GC_data_start +#     define DYNAMIC_LOADING +#   endif +#   ifdef NOSYS +#     define ALIGNMENT 4 +#     define OS_TYPE "NOSYS" +      extern void __end, __dso_handle; +#     define DATASTART (&__dso_handle)  /* OK, that's ugly.  */ +#     define DATAEND (&__end) +	/* Stack starts at 0xE0000000 for the simulator.  */ +#     undef STACK_GRAN +#     define STACK_GRAN 0x10000000 +#     define HEURISTIC1  #   endif  # endif @@ -554,9 +702,12 @@  # ifdef SPARC  #   define MACH_TYPE "SPARC" -#   define ALIGNMENT 4	/* Required by hardware	*/ +#   if defined(__arch64__) || defined(__sparcv9) +#     define ALIGNMENT 8 +#   else +#     define ALIGNMENT 4	/* Required by hardware	*/ +#   endif  #   define ALIGN_DOUBLE -    extern int etext;  #   ifdef SUNOS5  #	define OS_TYPE "SUNOS5"  	extern int _etext; @@ -573,10 +724,18 @@  #	  define HEAP_START DATAEND  #       endif  #	define PROC_VDB -/*	HEURISTIC1 reportedly no longer works under 2.7.  Thus we	*/ -/* 	switched to HEURISTIC2, eventhough it creates some debugging	*/ -/*	issues.								*/ -#	define HEURISTIC2 +/*	HEURISTIC1 reportedly no longer works under 2.7.  		*/ +/*  	HEURISTIC2 probably works, but this appears to be preferable.	*/ +/*	Apparently USRSTACK is defined to be USERLIMIT, but in some	*/ +/* 	installations that's undefined.  We work around this with a	*/ +/*	gross hack:							*/ +#       include <sys/vmparam.h> +#	ifdef USERLIMIT +	  /* This should work everywhere, but doesn't.	*/ +#	  define STACKBOTTOM USRSTACK +#       else +#	  define HEURISTIC2 +#       endif  #	include <unistd.h>  #       define GETPAGESIZE()  sysconf(_SC_PAGESIZE)  		/* getpagesize() appeared to be missing from at least one */ @@ -615,21 +774,40 @@  #   ifdef LINUX  #     define OS_TYPE "LINUX"  #     ifdef __ELF__ -#         define DATASTART GC_data_start -#         define DYNAMIC_LOADING +#       define DYNAMIC_LOADING  #     else -          Linux Sparc non elf ? +          Linux Sparc/a.out not supported  #     endif        extern int _end; +      extern int _etext;  #     define DATAEND (&_end)  #     define SVR4 -#     define STACKBOTTOM ((ptr_t) 0xf0000000) +#     ifdef __arch64__ +#       define STACKBOTTOM ((ptr_t) 0x80000000000ULL) +#	define DATASTART (ptr_t)GC_SysVGetDataStart(0x100000, &_etext) +#	define CPP_WORDSZ 64 +#     else +#       define STACKBOTTOM ((ptr_t) 0xf0000000) +#	define DATASTART (ptr_t)GC_SysVGetDataStart(0x10000, &_etext) +#     endif  #   endif  #   ifdef OPENBSD  #     define OS_TYPE "OPENBSD"  #     define STACKBOTTOM ((ptr_t) 0xf8000000) +      extern int etext;  #     define DATASTART ((ptr_t)(&etext))  #   endif +#   ifdef NETBSD +#     define OS_TYPE "NETBSD" +#     define HEURISTIC2 +#     ifdef __ELF__ +#	define DATASTART GC_data_start +#	define DYNAMIC_LOADING +#     else +	extern char etext; +#	define DATASTART ((ptr_t)(&etext)) +#     endif +#   endif  # endif  # ifdef I386 @@ -642,18 +820,34 @@  #     define ALIGN_DOUBLE /* Not strictly necessary, but may give speed   */  			  /* improvement on Pentiums.			  */  #   endif +#   ifdef HAVE_BUILTIN_UNWIND_INIT +#	define USE_GENERIC_PUSH_REGS +#   endif  #   ifdef SEQUENT  #	define OS_TYPE "SEQUENT"  	extern int etext;  #       define DATASTART ((ptr_t)((((word) (&etext)) + 0xfff) & ~0xfff))  #       define STACKBOTTOM ((ptr_t) 0x3ffff000)   #   endif +#   ifdef BEOS +#     define OS_TYPE "BEOS" +#     include <OS.h> +#     define GETPAGESIZE() B_PAGE_SIZE +      extern int etext; +#     define DATASTART ((ptr_t)((((word) (&etext)) + 0xfff) & ~0xfff)) +#   endif  #   ifdef SUNOS5  #	define OS_TYPE "SUNOS5"    	extern int etext, _start;    	extern char * GC_SysVGetDataStart();  #       define DATASTART GC_SysVGetDataStart(0x1000, &etext) -#	define STACKBOTTOM ((ptr_t)(&_start)) +/*	# define STACKBOTTOM ((ptr_t)(&_start)) worked through 2.7,  	*/ +/*      but reportedly breaks under 2.8.  It appears that the stack	*/ +/* 	base is a property of the executable, so this should not break	*/ +/* 	old executables.						*/ +/*  	HEURISTIC2 probably works, but this appears to be preferable.	*/ +#       include <sys/vmparam.h> +#	define STACKBOTTOM USRSTACK  /** At least in Solaris 2.5, PROC_VDB gives wrong values for dirty bits. */  /*#	define PROC_VDB*/  #	define DYNAMIC_LOADING @@ -684,13 +878,16 @@  #   endif  #   ifdef LINUX  #	define OS_TYPE "LINUX" -#       define HEURISTIC1 -#       undef STACK_GRAN -#       define STACK_GRAN 0x10000000 -	/* STACKBOTTOM is usually 0xc0000000, but this changes with	*/ -	/* different kernel configurations.  In particular, systems	*/ -	/* with 2GB physical memory will usually move the user		*/ -	/* address space limit, and hence initial SP to 0x80000000.	*/ +#       define LINUX_STACKBOTTOM +#	if 0 +#	  define HEURISTIC1 +#         undef STACK_GRAN +#         define STACK_GRAN 0x10000000 +	  /* STACKBOTTOM is usually 0xc0000000, but this changes with	*/ +	  /* different kernel configurations.  In particular, systems	*/ +	  /* with 2GB physical memory will usually move the user	*/ +	  /* address space limit, and hence initial SP to 0x80000000.	*/ +#       endif  #       if !defined(LINUX_THREADS) || !defined(REDIRECT_MALLOC)  #	    define MPROTECT_VDB  #	else @@ -706,8 +903,7 @@  #	     endif  #	     include <features.h>  #	     if defined(__GLIBC__) && __GLIBC__ >= 2 -		 extern int __data_start; -#		 define DATASTART ((ptr_t)(&__data_start)) +#		 define LINUX_DATA_START  #	     else       	         extern char **__environ;  #                define DATASTART ((ptr_t)(&__environ)) @@ -726,6 +922,26 @@  	     extern int etext;  #            define DATASTART ((ptr_t)((((word) (&etext)) + 0xfff) & ~0xfff))  #       endif +#	ifdef USE_I686_PREFETCH +#	  define PREFETCH(x) \ +	    __asm__ __volatile__ ("	prefetchnta	%0": : "m"(*(char *)(x))) +	    /* Empirically prefetcht0 is much more effective at reducing	*/ +	    /* cache miss stalls for the targetted load instructions.  But it	*/ +	    /* seems to interfere enough with other cache traffic that the net	*/ +	    /* result is worse than prefetchnta.				*/ +#         if 0  +	    /* Using prefetches for write seems to have a slight negative	*/ +	    /* impact on performance, at least for a PIII/500.			*/ +#	    define PREFETCH_FOR_WRITE(x) \ +	      __asm__ __volatile__ ("	prefetcht0	%0": : "m"(*(char *)(x))) +#	  endif +#	endif +#	ifdef USE_3DNOW_PREFETCH +#	  define PREFETCH(x) \ +	    __asm__ __volatile__ ("	prefetch	%0": : "m"(*(char *)(x))) +#	  define PREFETCH_FOR_WRITE(x) \ +	    __asm__ __volatile__ ("	prefetchw	%0": : "m"(*(char *)(x))) +#	endif  #   endif  #   ifdef CYGWIN32  #       define OS_TYPE "CYGWIN32" @@ -755,6 +971,7 @@  		/* os_dep.c. OS2 actually has the right			*/  		/* system call!						*/  #	define DATAEND	/* not needed */ +#	define USE_GENERIC_PUSH_REGS  #   endif  #   ifdef MSWIN32  #	define OS_TYPE "MSWIN32" @@ -765,6 +982,10 @@  #	endif  #       define DATAEND  /* not needed */  #   endif +#   ifdef MSWINCE +#	define OS_TYPE "MSWINCE" +#       define DATAEND  /* not needed */ +#   endif  #   ifdef DJGPP  #       define OS_TYPE "DJGPP"  #       include "stubinfo.h" @@ -783,6 +1004,7 @@  #   ifdef FREEBSD  #	define OS_TYPE "FREEBSD"  #	define MPROTECT_VDB +#	define FREEBSD_STACKBOTTOM  #   endif  #   ifdef NETBSD  #	define OS_TYPE "NETBSD" @@ -793,7 +1015,7 @@  #   ifdef BSDI  #	define OS_TYPE "BSDI"  #   endif -#   if defined(OPENBSD) || defined(FREEBSD) || defined(NETBSD) \ +#   if defined(OPENBSD) || defined(NETBSD) || defined(FREEBSD) \          || defined(THREE86BSD) || defined(BSDI)  #	define HEURISTIC2  	extern char etext; @@ -820,6 +1042,17 @@  #     define DATASTART ((ptr_t) &__nullarea)  #     define DATAEND ((ptr_t) &_end)  #   endif +#   ifdef HURD +#     define OS_TYPE "HURD" +#     define STACK_GROWS_DOWN +#     define HEURISTIC2 +      extern int  __data_start; +#     define DATASTART ( (ptr_t) (&__data_start)) +      extern int   _end; +#     define DATAEND ( (ptr_t) (&_end)) +/* #     define MPROTECT_VDB  Not quite working yet? */ +#     define DYNAMIC_LOADING +#   endif  # endif  # ifdef NS32K @@ -836,35 +1069,48 @@  # ifdef MIPS  #   define MACH_TYPE "MIPS" -#   ifndef IRIX5 -#     define DATASTART (ptr_t)0x10000000 +#   ifdef LINUX +      /* This was developed for a linuxce style platform.  Probably	*/ +      /* needs to be tweaked for workstation class machines.		*/ +#     define OS_TYPE "LINUX" +      extern int __data_start; +#     define DATASTART ((ptr_t)(&__data_start)) +#     define ALIGNMENT 4 +#     define USE_GENERIC_PUSH_REGS +#     define STACKBOTTOM ((ptr_t)0x7fff8000) +        /* Older toolchains may need 0x80000000.	*/ +	/* In many cases, this should probably use LINUX_STACKBOTTOM 	*/ +	/* instead. But some kernel versions seem to give the wrong	*/ +	/* value from /proc.						*/ +#   endif /* Linux */ +#   ifdef ULTRIX +#	define HEURISTIC2 +#       define DATASTART (ptr_t)0x10000000  			      /* Could probably be slightly higher since */  			      /* startup code allocates lots of stuff.   */ -#   else -      extern int _fdata; -#     define DATASTART ((ptr_t)(&_fdata)) -#     ifdef USE_MMAP -#         define HEAP_START (ptr_t)0x30000000 -#     else -#	  define HEAP_START DATASTART -#     endif -			      /* Lowest plausible heap address.		*/ -			      /* In the MMAP case, we map there.	*/ -			      /* In either case it is used to identify	*/ -			      /* heap sections so they're not 		*/ -			      /* considered as roots.			*/ -#   endif /* IRIX5 */ -#   define HEURISTIC2 -/* #   define STACKBOTTOM ((ptr_t)0x7fff8000)  sometimes also works.  */ -#   ifdef ULTRIX  #	define OS_TYPE "ULTRIX"  #       define ALIGNMENT 4  #   endif  #   ifdef RISCOS +#	define HEURISTIC2 +#       define DATASTART (ptr_t)0x10000000  #	define OS_TYPE "RISCOS"  #   	define ALIGNMENT 4  /* Required by hardware */  #   endif  #   ifdef IRIX5 +#	define HEURISTIC2 +        extern int _fdata; +#       define DATASTART ((ptr_t)(&_fdata)) +#       ifdef USE_MMAP +#         define HEAP_START (ptr_t)0x30000000 +#       else +#	  define HEAP_START DATASTART +#       endif +			      /* Lowest plausible heap address.		*/ +			      /* In the MMAP case, we map there.	*/ +			      /* In either case it is used to identify	*/ +			      /* heap sections so they're not 		*/ +			      /* considered as roots.			*/  #	define OS_TYPE "IRIX5"  #       define MPROTECT_VDB  #       ifdef _MIPS_SZPTR @@ -879,6 +1125,28 @@  #	endif  #	define DYNAMIC_LOADING  #   endif +#   ifdef MSWINCE +#       define OS_TYPE "MSWINCE" +#       define ALIGNMENT 4 +#       define DATAEND /* not needed */ +#   endif +#   if defined(NETBSD) +      /* This also checked for __MIPSEL__ .  Why?  NETBSD recognition	*/ +      /* should be handled at the top of the file.			*/ +#     define ALIGNMENT 4 +#     define OS_TYPE "NETBSD" +#     define HEURISTIC2 +#     define USE_GENERIC_PUSH_REGS +#     ifdef __ELF__ +        extern int etext; +#       define DATASTART GC_data_start +#       define NEED_FIND_LIMIT +#       define DYNAMIC_LOADING +#     else +#       define DATASTART ((ptr_t) 0x10000000) +#       define STACKBOTTOM ((ptr_t) 0x7ffff000) +#     endif /* _ELF_ */ +#  endif  # endif  # ifdef RS6000 @@ -893,11 +1161,34 @@  # ifdef HP_PA  #   define MACH_TYPE "HP_PA" -#   define ALIGNMENT 4 -#   define ALIGN_DOUBLE -    extern int __data_start; -#   define DATASTART ((ptr_t)(&__data_start)) -#   if 0 +#   define OS_TYPE "HPUX" +#   ifdef __LP64__ +#     define CPP_WORDSZ 64 +#     define ALIGNMENT 8 +#   else +#     define CPP_WORDSZ 32 +#     define ALIGNMENT 4 +#     define ALIGN_DOUBLE +#   endif +#   if !defined(GC_HPUX_THREADS) && !defined(HPUX_THREADS) \ +       && !defined(GC_LINUX_THREADS) && !defined(LINUX_THREADS) +#     ifndef LINUX /* For now. */ +#       define MPROTECT_VDB +#     endif +#   else +#     define GENERIC_COMPARE_AND_SWAP +	/* No compare-and-swap instruction.  Use pthread mutexes 	*/ +	/* when we absolutely have to.					*/ +#     ifdef PARALLEL_MARK +#	define USE_MARK_BYTES +		/* Minimize compare-and-swap usage.		*/ +#     endif +#   endif +#   define STACK_GROWS_UP +#   ifdef HPUX +      extern int __data_start; +#     define DATASTART ((ptr_t)(&__data_start)) +#     if 0  	/* The following appears to work for 7xx systems running HP/UX	*/  	/* 9.xx Furthermore, it might result in much faster		*/  	/* collections than HEURISTIC2, which may involve scanning	*/ @@ -906,28 +1197,66 @@  	/* combinations. (Thanks to Raymond X.T. Nijssen for uncovering	*/  	/* this.)							*/  #       define STACKBOTTOM ((ptr_t) 0x7b033000)  /* from /etc/conf/h/param.h */ -#   else -#       define HEURISTIC2 -#   endif -#   define STACK_GROWS_UP -#   define DYNAMIC_LOADING -#   include <unistd.h> -#   define GETPAGESIZE() sysconf(_SC_PAGE_SIZE) -	/* They misspelled the Posix macro?	*/ -# endif +#     else +	/* Gustavo Rodriguez-Rivera suggested changing HEURISTIC2	*/ +	/* to this.  Note that the GC must be initialized before the	*/ +    	/* first putenv call.						*/ +	extern char ** environ; +#       define STACKBOTTOM ((ptr_t)environ) +#     endif +#     define DYNAMIC_LOADING +#     include <unistd.h> +#     define GETPAGESIZE() sysconf(_SC_PAGE_SIZE) +#     ifndef __GNUC__ +#       define PREFETCH(x)  { \ +                              register long addr = (long)(x); \ +                              (void) _asm ("LDW", 0, 0, addr, 0); \ +                            } +#     endif +#   endif /* HPUX */ +#   ifdef LINUX +#     define OS_TYPE "LINUX" +#     define LINUX_STACKBOTTOM +#     define DYNAMIC_LOADING +#     define LINUX_DATA_START +      extern int _end; +#     define DATAEND (&_end) +#   endif /* LINUX */ +# endif /* HP_PA */  # ifdef ALPHA  #   define MACH_TYPE "ALPHA"  #   define ALIGNMENT 8 +#   define USE_GENERIC_PUSH_REGS +    /* Gcc and probably the DEC/Compaq compiler spill pointers to preserved */ +    /* fp registers in some cases when the target is a 21264.  The assembly */ +    /* code doesn't handle that yet, and version dependencies make that a   */ +    /* bit tricky.  Do the easy thing for now.				    */ +#   ifdef NETBSD +#	define OS_TYPE "NETBSD" +#	define HEURISTIC2 +#	define DATASTART GC_data_start +#	define ELFCLASS32 32 +#	define ELFCLASS64 64 +#	define ELF_CLASS ELFCLASS64 +#   	define CPP_WORDSZ 64 +#       define DYNAMIC_LOADING +#   endif  #   ifdef OSF1  #	define OS_TYPE "OSF1"  #   	define DATASTART ((ptr_t) 0x140000000) -	extern _end; +	extern int _end;  #   	define DATAEND ((ptr_t) &_end) -#   	define HEURISTIC2 + 	extern char ** environ; +	/* round up from the value of environ to the nearest page boundary */ +	/* Probably breaks if putenv is called before collector 	   */ +	/* initialization.						   */ +#	define STACKBOTTOM ((ptr_t)(((word)(environ) | (getpagesize()-1))+1)) +/* #   	define HEURISTIC2 */  	/* Normally HEURISTIC2 is too conervative, since		*/  	/* the text segment immediately follows the stack.		*/  	/* Hence we give an upper pound.				*/ +	/* This is currently unused, since we disabled HEURISTIC2	*/      	extern int __start;  #   	define HEURISTIC2_LIMIT ((ptr_t)((word)(&__start) & ~(getpagesize()-1)))  #   	define CPP_WORDSZ 64 @@ -939,12 +1268,8 @@  #       define CPP_WORDSZ 64  #       define STACKBOTTOM ((ptr_t) 0x120000000)  #       ifdef __ELF__ -#   	  if 0 -	    /* __data_start apparently disappeared in some recent releases. */ -            extern int __data_start; -#           define DATASTART &__data_start -#	  endif -#         define DATASTART GC_data_start +#	  define SEARCH_FOR_DATA_START +#	  define DATASTART GC_data_start  #         define DYNAMIC_LOADING  #       else  #           define DATASTART ((ptr_t) 0x140000000) @@ -957,6 +1282,55 @@  #   endif  # endif +# ifdef IA64 +#   define MACH_TYPE "IA64" +#   define ALIGN_DOUBLE +	/* Requires 16 byte alignment for malloc */ +#   define ALIGNMENT 8 +#   define USE_GENERIC_PUSH_REGS +	/* We need to get preserved registers in addition to register   */ +	/* windows.   That's easiest to do with setjmp.			*/ +#   ifdef PARALLEL_MARK +#	define USE_MARK_BYTES +	    /* Compare-and-exchange is too expensive to use for 	*/ +	    /* setting mark bits.					*/ +#   endif +#   ifdef HPUX +	--> needs work +#   endif +#   ifdef LINUX +#       define OS_TYPE "LINUX" +#       define CPP_WORDSZ 64 +	/* The following works on NUE and older kernels:	*/ +/* #       define STACKBOTTOM ((ptr_t) 0xa000000000000000l)	*/ +	/* This does not work on NUE:				*/ +#       define LINUX_STACKBOTTOM +	/* We also need the base address of the register stack	*/ +	/* backing store.  This is computed in 			*/ +	/* GC_linux_register_stack_base based on the following	*/ +	/* constants:						*/ +#       define BACKING_STORE_ALIGNMENT 0x100000 +#       define BACKING_STORE_DISPLACEMENT 0x80000000 +	extern char * GC_register_stackbottom; +#	define BACKING_STORE_BASE ((ptr_t)GC_register_stackbottom) +#	define SEARCH_FOR_DATA_START +#	define DATASTART GC_data_start +#       define DYNAMIC_LOADING +#	define MPROTECT_VDB +		/* Requires Linux 2.3.47 or later.	*/ +	extern int _end; +#	define DATAEND (&_end) +#       ifdef __GNUC__ +#	  define PREFETCH(x) \ +	    __asm__ ("	lfetch	[%0]": : "r"((void *)(x))) +#	  define PREFETCH_FOR_WRITE(x) \ +	    __asm__ ("	lfetch.excl	[%0]": : "r"((void *)(x))) +#	  define CLEAR_DOUBLE(x) \ +	    __asm__ ("	stf.spill	[%0]=f0": : "r"((void *)(x))) +#       endif +#   endif +# endif +  # ifdef M88K  #   define MACH_TYPE "M88K"  #   define ALIGNMENT 4 @@ -976,17 +1350,127 @@  # ifdef S370  #   define MACH_TYPE "S370" -#   define OS_TYPE "UTS4"  #   define ALIGNMENT 4	/* Required by hardware	*/ -    extern int etext; +#   define USE_GENERIC_PUSH_REGS +#   ifdef UTS4 +#       define OS_TYPE "UTS4" +        extern int etext;  	extern int _etext;  	extern int _end;  	extern char * GC_SysVGetDataStart();  #       define DATASTART (ptr_t)GC_SysVGetDataStart(0x10000, &_etext)  #	define DATAEND (&_end)  #	define HEURISTIC2 +#   endif +#   ifdef LINUX +#       define OS_TYPE "LINUX" +#       define HEURISTIC1 +#       define DYNAMIC_LOADING +        extern int __data_start; +#       define DATASTART ((ptr_t)(&__data_start)) +#   endif +# endif + +# if defined(PJ) +#   define ALIGNMENT 4 +    extern int _etext; +#   define DATASTART ((ptr_t)(&_etext)) +#   define HEURISTIC1 +# endif + +# ifdef ARM32 +#   define CPP_WORDSZ 32 +#   define MACH_TYPE "ARM32" +#   define ALIGNMENT 4 +#   ifdef NETBSD +#       define OS_TYPE "NETBSD" +#       define HEURISTIC2 +        extern char etext; +#       define DATASTART ((ptr_t)(&etext)) +#       define USE_GENERIC_PUSH_REGS +#   endif +#   ifdef LINUX +#       define OS_TYPE "LINUX" +#       define HEURISTIC1 +#       undef STACK_GRAN +#       define STACK_GRAN 0x10000000 +#       define USE_GENERIC_PUSH_REGS +#       ifdef __ELF__ +#            define DYNAMIC_LOADING +#	     include <features.h> +#	     if defined(__GLIBC__) && __GLIBC__ >= 2 +#		 define LINUX_DATA_START +#	     else +     	         extern char **__environ; +#                define DATASTART ((ptr_t)(&__environ)) +			      /* hideous kludge: __environ is the first */ +			      /* word in crt0.o, and delimits the start */ +			      /* of the data segment, no matter which   */ +			      /* ld options were passed through.        */ +			      /* We could use _etext instead, but that  */ +			      /* would include .rodata, which may       */ +			      /* contain large read-only data tables    */ +			      /* that we'd rather not scan.		*/ +#	     endif +	     extern int _end; +#	     define DATAEND (&_end) +#	else +	     extern int etext; +#            define DATASTART ((ptr_t)((((word) (&etext)) + 0xfff) & ~0xfff)) +#       endif +#   endif +#   ifdef MSWINCE +#     define OS_TYPE "MSWINCE" +#     define DATAEND /* not needed */ +#   endif +#endif + +# ifdef SH +#   define MACH_TYPE "SH" +#   define ALIGNMENT 4 +#   ifdef MSWINCE +#     define OS_TYPE "MSWINCE" +#     define DATAEND /* not needed */ +#   endif +#   ifdef LINUX +#     define OS_TYPE "LINUX" +#     define STACKBOTTOM ((ptr_t) 0x7c000000) +#     define USE_GENERIC_PUSH_REGS +#     define DYNAMIC_LOADING +#     define LINUX_DATA_START +      extern int _end; +#     define DATAEND (&_end) +#   endif  # endif +  +# ifdef SH4 +#   define MACH_TYPE "SH4" +#   define OS_TYPE "MSWINCE" +#   define ALIGNMENT 4 +#   define DATAEND /* not needed */ +# endif + +#ifdef LINUX_DATA_START +    /* Some Linux distributions arrange to define __data_start.  Some	*/ +    /* define data_start as a weak symbol.  The latter is technically	*/ +    /* broken, since the user program may define data_start, in which	*/ +    /* case we lose.  Nonetheless, we try both, prefering __data_start.	*/ +    /* We assume gcc.	*/ +#   pragma weak __data_start +    extern int __data_start; +#   pragma weak data_start +    extern int data_start; +#   define DATASTART ((ptr_t)(&__data_start != 0? &__data_start : &data_start)) +#endif +#if defined(LINUX) && defined(REDIRECT_MALLOC) +    /* Rld appears to allocate some memory with its own allocator, and	*/ +    /* some through malloc, which might be redirected.  To make this	*/ +    /* work with collectable memory, we have to scan memory allocated	*/ +    /* by rld's internal malloc.					*/ +#   define USE_PROC_FOR_LIBRARIES +#endif +      # ifndef STACK_GROWS_UP  #   define STACK_GROWS_DOWN  # endif @@ -1029,6 +1513,16 @@  #   define SUNOS5SIGS  # endif +# if defined(HPUX) +#   define SUNOS5SIGS +# endif + +# if defined(SVR4) || defined(LINUX) || defined(IRIX) || defined(HPUX) \ +    || defined(OPENBSD) || defined(NETBSD) || defined(FREEBSD) \ +    || defined(BSD) || defined(AIX) || defined(MACOSX) || defined(OSF1) +#   define UNIX_LIKE   /* Basic Unix-like system calls work.	*/ +# endif +  # if CPP_WORDSZ != 32 && CPP_WORDSZ != 64     -> bad word size  # endif @@ -1059,41 +1553,107 @@  #   undef MPROTECT_VDB  /* Can't deal with address space holes. */  # endif +# ifdef PARALLEL_MARK +#   undef MPROTECT_VDB  /* For now.	*/ +# endif +  # if !defined(PCR_VDB) && !defined(PROC_VDB) && !defined(MPROTECT_VDB)  #   define DEFAULT_VDB  # endif +# ifndef PREFETCH +#   define PREFETCH(x) +#   define NO_PREFETCH +# endif + +# ifndef PREFETCH_FOR_WRITE +#   define PREFETCH_FOR_WRITE(x) +#   define NO_PREFETCH_FOR_WRITE +# endif + +# ifndef CACHE_LINE_SIZE +#   define CACHE_LINE_SIZE 32	/* Wild guess	*/ +# endif + +# ifndef CLEAR_DOUBLE +#   define CLEAR_DOUBLE(x) \ +	((word*)x)[0] = 0; \ +	((word*)x)[1] = 0; +# endif /* CLEAR_DOUBLE */ + +/* Internally to the collector we test only the XXX_THREADS macros	*/ +/* not the GC_XXX_THREADS versions.  Here we make sure the latter	*/ +/* are treated as equivalent.						*/ +#if defined(GC_SOLARIS_THREADS) && !defined(_SOLARIS_THREADS) +#   define _SOLARIS_THREADS +#endif +#if defined(GC_SOLARIS_THREADS) && !defined(_SOLARIS_PTHREADS) +#   define _SOLARIS_PTHREADS +#endif +#if defined(GC_IRIX_THREADS) && !defined(IRIX_THREADS) +#   define IRIX_THREADS +#endif +#if defined(GC_LINUX_THREADS) && !defined(LINUX_THREADS) +#   define LINUX_THREADS +#endif +#if defined(GC_WIN32_THREADS) && !defined(WIN32_THREADS) +#   define WIN32_THREADS +#endif +#if defined(GC_HPUX_THREADS) && !defined(HPUX_THREADS) +#   define HPUX_THREADS +#endif +#if defined(GC_OSF1_THREADS) && !defined(OSF1_THREADS) +#   define OSF1_THREADS +#endif + +/* Internally we use SOLARIS_THREADS to test for either old or pthreads. */  # if defined(_SOLARIS_PTHREADS) && !defined(SOLARIS_THREADS)  #   define SOLARIS_THREADS  # endif  # if defined(IRIX_THREADS) && !defined(IRIX5)  --> inconsistent configuration  # endif -# if defined(IRIX_JDK_THREADS) && !defined(IRIX5) ---> inconsistent configuration -# endif  # if defined(LINUX_THREADS) && !defined(LINUX)  --> inconsistent configuration  # endif  # if defined(SOLARIS_THREADS) && !defined(SUNOS5)  --> inconsistent configuration  # endif +# if defined(HPUX_THREADS) && !defined(HPUX) +--> inconsistent configuration +# endif +# if defined(WIN32_THREADS) && !defined(MSWIN32) +    /* Ideally CYGWIN32 should work, in addition to MSWIN32.  I suspect the necessary code	*/ +    /* is mostly there, but nobody has actually made sure the right combination of pieces is	*/ +    /* compiled in, etc.									*/ +--> inconsistent configuration +# endif  # if defined(PCR) || defined(SRC_M3) || \  	defined(SOLARIS_THREADS) || defined(WIN32_THREADS) || \  	defined(IRIX_THREADS) || defined(LINUX_THREADS) || \ -	defined(IRIX_JDK_THREADS) +	defined(HPUX_THREADS) || defined(OSF1_THREADS)  #   define THREADS  # endif -# if defined(HP_PA) || defined(M88K) || defined(POWERPC) \ -     || (defined(I386) && defined(OS2)) || defined(UTS4) || defined(LINT) -	/* Use setjmp based hack to mark from callee-save registers. */ +# if defined(HP_PA) || defined(M88K) || defined(POWERPC) && !defined(MACOSX) \ +     || defined(LINT) || defined(MSWINCE) \ +     || (defined(I386) && defined(__LCC__)) +	/* Use setjmp based hack to mark from callee-save registers.    */ +	/* The define should move to the individual platform 		*/ +	/* descriptions.						*/  #	define USE_GENERIC_PUSH_REGS  # endif -# if defined(SPARC) && !defined(LINUX) +# if defined(I386) && defined(LINUX) +    /* SAVE_CALL_CHAIN is supported if the code is compiled to save	*/ +    /* frame pointers by default, i.e. no -fomit-frame-pointer flag.	*/ +# ifdef SAVE_CALL_COUNT +#   define SAVE_CALL_CHAIN  +# endif +# endif +# if defined(SPARC)  #   define SAVE_CALL_CHAIN  #   define ASM_CLEAR_CODE	/* Stack clearing is crucial, and we 	*/  				/* include assembly code to do it well.	*/  # endif -# endif +# endif /* GCCONFIG_H */ diff --git a/gc/include/private/solaris_threads.h b/gc/include/private/solaris_threads.h new file mode 100644 index 0000000..b2cdb36 --- /dev/null +++ b/gc/include/private/solaris_threads.h @@ -0,0 +1,34 @@ +#ifdef SOLARIS_THREADS + +/* The set of all known threads.  We intercept thread creation and     */ +/* joins.  We never actually create detached threads.  We allocate all */ +/* new thread stacks ourselves.  These allow us to maintain this       */ +/* data structure.                                                     */ +/* Protected by GC_thr_lock.                                           */ +/* Some of this should be declared volatile, but that's incosnsistent  */ +/* with some library routine declarations.  In particular, the 	       */ +/* definition of cond_t doesn't mention volatile!                      */ +  typedef struct GC_Thread_Rep { +    struct GC_Thread_Rep * next; +    thread_t id; +    word flags; +#      define FINISHED 1       /* Thread has exited.   */ +#      define DETACHED 2       /* Thread is intended to be detached.   */ +#      define CLIENT_OWNS_STACK        4 +                               /* Stack was supplied by client.        */ +#      define SUSPENDED 8      /* Currently suspended. */ +    ptr_t stack; +    size_t stack_size; +    cond_t join_cv; +    void * status; +  } * GC_thread; +  extern GC_thread GC_new_thread(thread_t id); + +  extern GC_bool GC_thr_initialized; +  extern volatile GC_thread GC_threads[]; +  extern size_t GC_min_stack_sz; +  extern size_t GC_page_sz; +  extern void GC_thr_init(void); + +# endif /* SOLARIS_THREADS */ + diff --git a/gc/include/private/specific.h b/gc/include/private/specific.h new file mode 100644 index 0000000..60c152c --- /dev/null +++ b/gc/include/private/specific.h @@ -0,0 +1,83 @@ +/* + * This is a reimplementation of a subset of the pthread_getspecific/setspecific + * interface. This appears to outperform the standard linuxthreads one + * by a significant margin. + * The major restriction is that each thread may only make a single + * pthread_setspecific call on a single key.  (The current data structure + * doesn't really require that.  The restriction should be easily removable.) + * We don't currently support the destruction functions, though that + * could be done. + * We also currently assume that only one pthread_setspecific call + * can be executed at a time, though that assumption would be easy to remove + * by adding a lock. + */ + +#include <errno.h> + +/* Called during key creation or setspecific.		*/ +/* For the GC we already hold lock.			*/ +/* Currently allocated objects leak on thread exit.	*/ +/* That's hard to fix, but OK if we allocate garbage	*/ +/* collected memory.					*/ +#define MALLOC_CLEAR(n) GC_INTERNAL_MALLOC(n, NORMAL) +#define PREFIXED(name) GC_##name + +#define TS_CACHE_SIZE 1024 +#define CACHE_HASH(n) (((((long)n) >> 8) ^ (long)n) & (TS_CACHE_SIZE - 1)) +#define TS_HASH_SIZE 1024 +#define HASH(n) (((((long)n) >> 8) ^ (long)n) & (TS_HASH_SIZE - 1)) + +typedef struct thread_specific_entry { +	unsigned long qtid;	/* quick thread id, only for cache */ +	void * value; +	pthread_t thread; +	struct thread_specific_entry *next; +} tse; + + +/* We represent each thread-specific datum as two tables.  The first is	*/ +/* a cache, index by a "quick thread identifier".  The "quick" thread	*/ +/* identifier is an easy to compute value, which is guaranteed to	*/ +/* determine the thread, though a thread may correspond to more than	*/ +/* one value.  We typically use the address of a page in the stack.	*/ +/* The second is a hash table, indexed by pthread_self().  It is used	*/ +/* only as a backup.							*/ + +/* Return the "quick thread id".  Default version.  Assumes page size,	*/ +/* or at least thread stack separation, is at least 4K.			*/ +static __inline__ long quick_thread_id() { +    int dummy; +    return (long)(&dummy) >> 12; +} + +#define INVALID_QTID ((unsigned long)(-1)) + +typedef struct thread_specific_data { +    tse * volatile cache[TS_CACHE_SIZE]; +			/* A faster index to the hash table */ +    tse * hash[TS_HASH_SIZE]; +    pthread_mutex_t lock; +} tsd; + +typedef tsd * PREFIXED(key_t); + +extern int PREFIXED(key_create) (tsd ** key_ptr, void (* destructor)(void *)); + +extern int PREFIXED(setspecific) (tsd * key, void * value); + +extern void PREFIXED(remove_specific) (tsd * key); + +/* An internal version of getspecific that assumes a cache miss.	*/ +void * PREFIXED(slow_getspecific) (tsd * key, unsigned long qtid, +				   tse * volatile * cache_entry); + +static __inline__ void * PREFIXED(getspecific) (tsd * key) { +    long qtid = quick_thread_id(); +    unsigned hash_val = CACHE_HASH(qtid); +    tse * volatile * entry_ptr = key -> cache + hash_val; +    tse * entry = *entry_ptr;   /* Must be loaded only once.	*/ +    if (entry -> qtid == qtid) return entry -> value; +    return PREFIXED(slow_getspecific) (key, qtid, entry_ptr); +} + + | 
