diff options
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); +} + + |