diff options
author | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
---|---|---|
committer | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
commit | 554fd8c5195424bdbcabf5de30fdc183aba391bd (patch) | |
tree | 976dc5ab7fddf506dadce60ae936f43f58787092 /boehm-gc/include/private | |
download | cbb-gcc-4.6.4-15d2061ac0796199866debe9ac87130894b0cdd3.tar.bz2 cbb-gcc-4.6.4-15d2061ac0796199866debe9ac87130894b0cdd3.tar.xz |
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig;
imported gcc-4.6.4 source tree from verified upstream tarball.
downloading a git-generated archive based on the 'upstream' tag
should provide you with a source tree that is binary identical
to the one extracted from the above tarball.
if you have obtained the source via the command 'git clone',
however, do note that line-endings of files in your working
directory might differ from line-endings of the respective
files in the upstream repository.
Diffstat (limited to 'boehm-gc/include/private')
-rw-r--r-- | boehm-gc/include/private/cord_pos.h | 118 | ||||
-rw-r--r-- | boehm-gc/include/private/darwin_semaphore.h | 68 | ||||
-rw-r--r-- | boehm-gc/include/private/darwin_stop_world.h | 22 | ||||
-rw-r--r-- | boehm-gc/include/private/dbg_mlc.h | 175 | ||||
-rw-r--r-- | boehm-gc/include/private/gc_hdrs.h | 233 | ||||
-rw-r--r-- | boehm-gc/include/private/gc_locks.h | 684 | ||||
-rw-r--r-- | boehm-gc/include/private/gc_pmark.h | 394 | ||||
-rw-r--r-- | boehm-gc/include/private/gc_priv.h | 2015 | ||||
-rw-r--r-- | boehm-gc/include/private/gcconfig.h | 2430 | ||||
-rw-r--r-- | boehm-gc/include/private/pthread_stop_world.h | 11 | ||||
-rw-r--r-- | boehm-gc/include/private/pthread_support.h | 103 | ||||
-rw-r--r-- | boehm-gc/include/private/specific.h | 95 |
12 files changed, 6348 insertions, 0 deletions
diff --git a/boehm-gc/include/private/cord_pos.h b/boehm-gc/include/private/cord_pos.h new file mode 100644 index 000000000..d2b24bb8a --- /dev/null +++ b/boehm-gc/include/private/cord_pos.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. 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. + */ +/* Boehm, May 19, 1994 2:23 pm PDT */ +# ifndef CORD_POSITION_H + +/* The representation of CORD_position. This is private to the */ +/* implementation, but the size is known to clients. Also */ +/* the implementation of some exported macros relies on it. */ +/* Don't use anything defined here and not in cord.h. */ + +# define MAX_DEPTH 48 + /* The maximum depth of a balanced cord + 1. */ + /* We don't let cords get deeper than MAX_DEPTH. */ + +struct CORD_pe { + CORD pe_cord; + size_t pe_start_pos; +}; + +/* A structure describing an entry on the path from the root */ +/* to current position. */ +typedef struct CORD_Pos { + size_t cur_pos; + int path_len; +# define CORD_POS_INVALID (0x55555555) + /* path_len == INVALID <==> position invalid */ + const char *cur_leaf; /* Current leaf, if it is a string. */ + /* If the current leaf is a function, */ + /* then this may point to function_buf */ + /* containing the next few characters. */ + /* Always points to a valid string */ + /* containing the current character */ + /* unless cur_end is 0. */ + size_t cur_start; /* Start position of cur_leaf */ + size_t cur_end; /* Ending position of cur_leaf */ + /* 0 if cur_leaf is invalid. */ + struct CORD_pe path[MAX_DEPTH + 1]; + /* path[path_len] is the leaf corresponding to cur_pos */ + /* path[0].pe_cord is the cord we point to. */ +# define FUNCTION_BUF_SZ 8 + char function_buf[FUNCTION_BUF_SZ]; /* Space for next few chars */ + /* from function node. */ +} CORD_pos[1]; + +/* Extract the cord from a position: */ +CORD CORD_pos_to_cord(CORD_pos p); + +/* Extract the current index from a position: */ +size_t CORD_pos_to_index(CORD_pos p); + +/* Fetch the character located at the given position: */ +char CORD_pos_fetch(CORD_pos p); + +/* Initialize the position to refer to the give cord and index. */ +/* Note that this is the most expensive function on positions: */ +void CORD_set_pos(CORD_pos p, CORD x, size_t i); + +/* Advance the position to the next character. */ +/* P must be initialized and valid. */ +/* Invalidates p if past end: */ +void CORD_next(CORD_pos p); + +/* Move the position to the preceding character. */ +/* P must be initialized and valid. */ +/* Invalidates p if past beginning: */ +void CORD_prev(CORD_pos p); + +/* Is the position valid, i.e. inside the cord? */ +int CORD_pos_valid(CORD_pos p); + +char CORD__pos_fetch(CORD_pos); +void CORD__next(CORD_pos); +void CORD__prev(CORD_pos); + +#define CORD_pos_fetch(p) \ + (((p)[0].cur_end != 0)? \ + (p)[0].cur_leaf[(p)[0].cur_pos - (p)[0].cur_start] \ + : CORD__pos_fetch(p)) + +#define CORD_next(p) \ + (((p)[0].cur_pos + 1 < (p)[0].cur_end)? \ + (p)[0].cur_pos++ \ + : (CORD__next(p), 0)) + +#define CORD_prev(p) \ + (((p)[0].cur_end != 0 && (p)[0].cur_pos > (p)[0].cur_start)? \ + (p)[0].cur_pos-- \ + : (CORD__prev(p), 0)) + +#define CORD_pos_to_index(p) ((p)[0].cur_pos) + +#define CORD_pos_to_cord(p) ((p)[0].path[0].pe_cord) + +#define CORD_pos_valid(p) ((p)[0].path_len != CORD_POS_INVALID) + +/* Some grubby stuff for performance-critical friends: */ +#define CORD_pos_chars_left(p) ((long)((p)[0].cur_end) - (long)((p)[0].cur_pos)) + /* Number of characters in cache. <= 0 ==> none */ + +#define CORD_pos_advance(p,n) ((p)[0].cur_pos += (n) - 1, CORD_next(p)) + /* Advance position by n characters */ + /* 0 < n < CORD_pos_chars_left(p) */ + +#define CORD_pos_cur_char_addr(p) \ + (p)[0].cur_leaf + ((p)[0].cur_pos - (p)[0].cur_start) + /* address of current character in cache. */ + +#endif diff --git a/boehm-gc/include/private/darwin_semaphore.h b/boehm-gc/include/private/darwin_semaphore.h new file mode 100644 index 000000000..0f43982d5 --- /dev/null +++ b/boehm-gc/include/private/darwin_semaphore.h @@ -0,0 +1,68 @@ +#ifndef GC_DARWIN_SEMAPHORE_H +#define GC_DARWIN_SEMAPHORE_H + +#if !defined(GC_DARWIN_THREADS) +#error darwin_semaphore.h included with GC_DARWIN_THREADS not defined +#endif + +/* + This is a very simple semaphore implementation for darwin. It + is implemented in terms of pthreads calls so it isn't async signal + safe. This isn't a problem because signals aren't used to + suspend threads on darwin. +*/ + +typedef struct { + pthread_mutex_t mutex; + pthread_cond_t cond; + int value; +} sem_t; + +static int sem_init(sem_t *sem, int pshared, int value) { + int ret; + if(pshared) + GC_abort("sem_init with pshared set"); + sem->value = value; + + ret = pthread_mutex_init(&sem->mutex,NULL); + if(ret < 0) return -1; + ret = pthread_cond_init(&sem->cond,NULL); + if(ret < 0) return -1; + return 0; +} + +static int sem_post(sem_t *sem) { + if(pthread_mutex_lock(&sem->mutex) < 0) + return -1; + sem->value++; + if(pthread_cond_signal(&sem->cond) < 0) { + pthread_mutex_unlock(&sem->mutex); + return -1; + } + if(pthread_mutex_unlock(&sem->mutex) < 0) + return -1; + return 0; +} + +static int sem_wait(sem_t *sem) { + if(pthread_mutex_lock(&sem->mutex) < 0) + return -1; + while(sem->value == 0) { + pthread_cond_wait(&sem->cond,&sem->mutex); + } + sem->value--; + if(pthread_mutex_unlock(&sem->mutex) < 0) + return -1; + return 0; +} + +static int sem_destroy(sem_t *sem) { + int ret; + ret = pthread_cond_destroy(&sem->cond); + if(ret < 0) return -1; + ret = pthread_mutex_destroy(&sem->mutex); + if(ret < 0) return -1; + return 0; +} + +#endif diff --git a/boehm-gc/include/private/darwin_stop_world.h b/boehm-gc/include/private/darwin_stop_world.h new file mode 100644 index 000000000..f6f5314ee --- /dev/null +++ b/boehm-gc/include/private/darwin_stop_world.h @@ -0,0 +1,22 @@ +#ifndef GC_DARWIN_STOP_WORLD_H +#define GC_DARWIN_STOP_WORLD_H + +#if !defined(GC_DARWIN_THREADS) +#error darwin_stop_world.h included without GC_DARWIN_THREADS defined +#endif + +#include <mach/mach.h> +#include <mach/thread_act.h> + +struct thread_stop_info { + mach_port_t mach_thread; +}; + +struct GC_mach_thread { + thread_act_t thread; + int already_suspended; +}; + +void GC_darwin_register_mach_handler_thread(mach_port_t thread); + +#endif diff --git a/boehm-gc/include/private/dbg_mlc.h b/boehm-gc/include/private/dbg_mlc.h new file mode 100644 index 000000000..e0a994de5 --- /dev/null +++ b/boehm-gc/include/private/dbg_mlc.h @@ -0,0 +1,175 @@ +/* + * 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) \ + || defined(MAKE_BACK_GRAPH) + /* 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 { +# if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) + /* We potentially keep two different kinds of back */ + /* pointers. KEEP_BACK_PTRS stores a single back */ + /* pointer in each reachable object to allow reporting */ + /* of why an object was retained. MAKE_BACK_GRAPH */ + /* builds a graph containing the inverse of all */ + /* "points-to" edges including those involving */ + /* objects that have just become unreachable. This */ + /* allows detection of growing chains of unreachable */ + /* objects. It may be possible to eventually combine */ + /* both, but for now we keep them separate. Both */ + /* kinds of back pointers are hidden using the */ + /* following macros. In both cases, the plain version */ + /* is constrained to have an least significant bit of 1,*/ + /* to allow it to be distinguished from a free list */ + /* link. This means the plain version must have an */ + /* lsb of 0. */ + /* Note that blocks dropped by black-listing will */ + /* also have the lsb clear once debugging has */ + /* started. */ + /* We're careful never to overwrite a value with lsb 0. */ +# 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 KEEP_BACK_PTRS + GC_hidden_pointer oh_back_ptr; +# endif +# ifdef MAKE_BACK_GRAPH + GC_hidden_pointer oh_bg_ptr; +# endif +# if defined(ALIGN_DOUBLE) && \ + (defined(KEEP_BACK_PTRS) != defined(MAKE_BACK_GRAPH)) + 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)) +# define UNCOLLECTABLE_DEBUG_BYTES DEBUG_BYTES +#else + /* Add space for END_FLAG, but use any extra space that was already */ + /* added to catch off-the-end pointers. */ + /* For uncollectable objects, the extra byte is not added. */ +# define UNCOLLECTABLE_DEBUG_BYTES (sizeof (oh) + sizeof (word)) +# define DEBUG_BYTES (UNCOLLECTABLE_DEBUG_BYTES - EXTRA_BYTES) +#endif + +/* 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) + +/* ADD_CALL_CHAIN stores a (partial) call chain into an object */ +/* header. It may be called with or without the allocation */ +/* lock. */ +/* PRINT_CALL_CHAIN prints the call chain stored in an object */ +/* to stderr. It requires that we do not hold the lock. */ +#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 + +#if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) +# define GC_HAS_DEBUG_INFO(p) \ + ((*((word *)p) & 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/boehm-gc/include/private/gc_hdrs.h b/boehm-gc/include/private/gc_hdrs.h new file mode 100644 index 000000000..70dfefe8f --- /dev/null +++ b/boehm-gc/include/private/gc_hdrs.h @@ -0,0 +1,233 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. 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. + */ +/* Boehm, July 11, 1995 11:54 am PDT */ +# ifndef GC_HEADERS_H +# define GC_HEADERS_H +typedef struct hblkhdr hdr; + +# if CPP_WORDSZ != 32 && CPP_WORDSZ < 36 + --> Get a real machine. +# endif + +/* + * 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 +# define HASH_TL +# endif + +/* Define appropriate out-degrees for each of the two tree levels */ +# ifdef SMALL_CONFIG +# define LOG_BOTTOM_SZ 11 + /* Keep top index size reasonable with smaller blocks. */ +# else +# define LOG_BOTTOM_SZ 10 +# endif +# ifndef HASH_TL +# define LOG_TOP_SZ (WORDSZ - LOG_BOTTOM_SZ - LOG_HBLKSIZE) +# else +# define LOG_TOP_SZ 11 +# endif +# 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); \ + 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_invalid_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]; + /* + * The bottom level index contains one of three kinds of values: + * 0 means we're not responsible for this block, + * or this is a block other than the first one in a free block. + * 1 < (long)X <= MAX_JUMP means the block starts at least + * X * HBLKSIZE bytes before the current address. + * A valid pointer points to a hdr structure. (The above can't be + * valid pointers due to the GET_MEM return convention.) + */ + struct bi * asc_link; /* All indices are linked in */ + /* ascending order... */ + struct bi * desc_link; /* ... and in descending order. */ + word key; /* high order address bits. */ +# ifdef HASH_TL + struct bi * hash_link; /* Hash chain link. */ +# endif +} bottom_index; + +/* extern bottom_index GC_all_nils; - really part of GC_arrays */ + +/* extern bottom_index * GC_top_index []; - really part of GC_arrays */ + /* Each entry points to a bottom_index. */ + /* On a 32 bit machine, it points to */ + /* the index for a set of high order */ + /* bits equal to the index. For longer */ + /* addresses, we hash the high order */ + /* bits to compute the index in */ + /* GC_top_index, and each entry points */ + /* to a hash chain. */ + /* The last entry in each chain is */ + /* GC_all_nils. */ + + +# define MAX_JUMP (HBLKSIZE - 1) + +# define HDR_FROM_BI(bi, p) \ + ((bi)->index[((word)(p) >> LOG_HBLKSIZE) & (BOTTOM_SZ - 1)]) +# ifndef HASH_TL +# define BI(p) (GC_top_index \ + [(word)(p) >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE)]) +# define HDR_INNER(p) HDR_FROM_BI(BI(p),p) +# ifdef SMALL_CONFIG +# define HDR(p) GC_find_header((ptr_t)(p)) +# else +# define HDR(p) HDR_INNER(p) +# endif +# define GET_BI(p, bottom_indx) (bottom_indx) = BI(p) +# define GET_HDR(p, hhdr) (hhdr) = HDR(p) +# define SET_HDR(p, hhdr) HDR_INNER(p) = (hhdr) +# define GET_HDR_ADDR(p, ha) (ha) = &(HDR_INNER(p)) +# else /* hash */ +/* Hash function for tree top level */ +# define TL_HASH(hi) ((hi) & (TOP_SZ - 1)) +/* Set bottom_indx to point to the bottom index for address p */ +# define GET_BI(p, bottom_indx) \ + { \ + register word hi = \ + (word)(p) >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE); \ + register bottom_index * _bi = GC_top_index[TL_HASH(hi)]; \ + \ + while (_bi -> key != hi && _bi != GC_all_nils) \ + _bi = _bi -> hash_link; \ + (bottom_indx) = _bi; \ + } +# define GET_HDR_ADDR(p, ha) \ + { \ + register bottom_index * bi; \ + \ + GET_BI(p, bi); \ + (ha) = &(HDR_FROM_BI(bi, p)); \ + } +# define GET_HDR(p, hhdr) { register hdr ** _ha; GET_HDR_ADDR(p, _ha); \ + (hhdr) = *_ha; } +# define SET_HDR(p, hhdr) { register hdr ** _ha; GET_HDR_ADDR(p, _ha); \ + *_ha = (hhdr); } +# define HDR(p) GC_find_header((ptr_t)(p)) +# endif + +/* Is the result a forwarding address to someplace closer to the */ +/* beginning of the block or NIL? */ +# define IS_FORWARDING_ADDR_OR_NIL(hhdr) ((unsigned long) (hhdr) <= MAX_JUMP) + +/* Get an HBLKSIZE aligned address closer to the beginning of the block */ +/* h. Assumes hhdr == HDR(h) and IS_FORWARDING_ADDR(hhdr). */ +# define FORWARDED_ADDR(h, hhdr) ((struct hblk *)(h) - (unsigned long)(hhdr)) +# endif /* GC_HEADERS_H */ diff --git a/boehm-gc/include/private/gc_locks.h b/boehm-gc/include/private/gc_locks.h new file mode 100644 index 000000000..54cfdb92d --- /dev/null +++ b/boehm-gc/include/private/gc_locks.h @@ -0,0 +1,684 @@ +/* + * 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 GC_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) +# include <ia64intrin.h> + inline static int GC_test_and_set(volatile unsigned int *addr) { + return __sync_lock_test_and_set(addr, 1); + } +# define GC_TEST_AND_SET_DEFINED + inline static void GC_clear(volatile unsigned int *addr) { + *addr = 0; + } +# 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) +# define GC_TEST_AND_SET_DEFINED +# define GC_CLEAR_DEFINED +# if (__GNUC__>4)||((__GNUC__==4)&&(__GNUC_MINOR__>=4)) +# define GC_test_and_set(addr) __sync_lock_test_and_set (addr, 1) +# define GC_clear(addr) __sync_lock_release (addr) +# else + inline static int GC_test_and_set(volatile unsigned int *addr) { + int oldval; + int temp = 1; /* locked value */ + + __asm__ __volatile__( + "\n1:\n" + "\tlwarx %0,%y3\n" /* load and reserve, 32-bits */ + "\tcmpwi %0, 0\n" /* if load is */ + "\tbne 2f\n" /* non-zero, return already set */ + "\tstwcx. %2,%y3\n" /* else store conditional */ + "\tbne- 1b\n" /* retry if lost reservation */ + "\tsync\n" /* import barrier */ + "2:\t\n" /* oldval is zero if we set */ + : "=&r"(oldval), "=m"(addr) + : "r"(temp), "Z"(addr) + : "cr0","memory"); + return oldval; + } + inline static void GC_clear(volatile unsigned int *addr) { + __asm__ __volatile__("lwsync" : : : "memory"); + *(addr) = 0; + } +# endif +# 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" +# ifdef __ELF__ + " beq %0,3f\n" +# else + " beq %0,1b\n" +# endif + " mb\n" + "2:\n" +# ifdef __ELF__ + ".section .text2,\"ax\"\n" + "3: br 1b\n" + ".previous" +# endif + :"=&r" (temp), "=m" (*addr), "=&r" (oldvalue) + :"Ir" (1), "m" (*addr) + :"memory"); + + return oldvalue; + } +# define GC_TEST_AND_SET_DEFINED + inline static void GC_clear(volatile unsigned int *addr) { + __asm__ __volatile__("mb" : : : "memory"); + *(addr) = 0; + } +# define GC_CLEAR_DEFINED +# endif /* ALPHA */ +# ifdef ARM32 +# define GC_TEST_AND_SET_DEFINED +# if (__GNUC__>4)||((__GNUC__==4)&&(__GNUC_MINOR__>=5)) && defined(__ARM_EABI__) +# define GC_CLEAR_DEFINED +# define GC_test_and_set(addr) __sync_lock_test_and_set (addr, 1) +# define GC_clear(addr) __sync_lock_release (addr) +# else + 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) + : "0"(1), "r"(addr) + : "memory"); + return oldval; + } +# endif +# endif /* ARM32 */ +# ifdef CRIS + inline static int GC_test_and_set(volatile unsigned int *addr) { + /* Ripped from linuxthreads/sysdeps/cris/pt-machine.h. */ + /* Included with Hans-Peter Nilsson's permission. */ + register unsigned long int ret; + + /* Note the use of a dummy output of *addr to expose the write. + * The memory barrier is to stop *other* writes being moved past + * this code. + */ + __asm__ __volatile__("clearf\n" + "0:\n\t" + "movu.b [%2],%0\n\t" + "ax\n\t" + "move.b %3,[%2]\n\t" + "bwf 0b\n\t" + "clearf" + : "=&r" (ret), "=m" (*addr) + : "r" (addr), "r" ((int) 1), "m" (*addr) + : "memory"); + return ret; + } +# define GC_TEST_AND_SET_DEFINED +# endif /* CRIS */ +# ifdef S390 + inline static int GC_test_and_set(volatile unsigned int *addr) { + int ret; + __asm__ __volatile__ ( + " l %0,0(%2)\n" + "0: cs %0,%1,0(%2)\n" + " jl 0b" + : "=&d" (ret) + : "d" (1), "a" (addr) + : "cc", "memory"); + return ret; + } +# endif +# endif /* __GNUC__ */ +# if (defined(ALPHA) && !defined(__GNUC__)) +# ifndef OSF1 + --> We currently assume that if gcc is not used, we are + --> running under Tru64. +# endif +# include <machine/builtins.h> +# include <c_asm.h> +# define GC_test_and_set(addr) __ATOMIC_EXCH_LONG(addr, 1) +# define GC_TEST_AND_SET_DEFINED +# define GC_clear(addr) { asm("mb"); *(volatile unsigned *)addr = 0; } +# define GC_CLEAR_DEFINED +# endif +# if defined(MSWIN32) +# define GC_test_and_set(addr) InterlockedExchange((LPLONG)addr,1) +# define GC_TEST_AND_SET_DEFINED +# endif +# ifdef MIPS +# ifdef LINUX +# include <sys/tas.h> +# define GC_test_and_set(addr) _test_and_set((int *) addr,1) +# define GC_TEST_AND_SET_DEFINED +# elif __mips < 3 || !(defined (_ABIN32) || defined(_ABI64)) \ + || !defined(_COMPILER_VERSION) || _COMPILER_VERSION < 700 +# ifdef __GNUC__ +# define GC_test_and_set(addr) _test_and_set((void *)addr,1) +# else +# define GC_test_and_set(addr) test_and_set((void *)addr,1) +# endif +# else +# include <sgidefs.h> +# include <mutex.h> +# define GC_test_and_set(addr) __test_and_set32((void *)addr,1) +# define GC_clear(addr) __lock_release(addr); +# define GC_CLEAR_DEFINED +# endif +# define GC_TEST_AND_SET_DEFINED +# endif /* MIPS */ +# if defined(_AIX) +# include <sys/atomic_op.h> +# if (defined(_POWER) || defined(_POWERPC)) +# if defined(__GNUC__) + inline static void GC_memsync() { + __asm__ __volatile__ ("sync" : : : "memory"); + } +# else +# ifndef inline +# define inline __inline +# endif +# pragma mc_func GC_memsync { \ + "7c0004ac" /* sync (same opcode used for dcs)*/ \ + } +# endif +# else +# error dont know how to memsync +# endif + inline static int GC_test_and_set(volatile unsigned int * addr) { + int oldvalue = 0; + if (compare_and_swap((void *)addr, &oldvalue, 1)) { + GC_memsync(); + return 0; + } else return 1; + } +# define GC_TEST_AND_SET_DEFINED + inline static void GC_clear(volatile unsigned int *addr) { + GC_memsync(); + *(addr) = 0; + } +# define GC_CLEAR_DEFINED + +# endif +# 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(GC_PTHREADS) && !defined(GC_SOLARIS_THREADS) \ + && !defined(GC_WIN32_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), "a"(old) : "memory"); + return (GC_bool) result; + } +# endif /* !GENERIC_COMPARE_AND_SWAP */ + inline static void GC_memory_barrier() + { + /* We believe the processor ensures at least processor */ + /* consistent ordering. Thus a compiler barrier */ + /* should suffice. */ + __asm__ __volatile__("" : : : "memory"); + } +# endif /* I386 */ + +# if defined(POWERPC) +# if !defined(GENERIC_COMPARE_AND_SWAP) +# if CPP_WORDSZ == 64 + /* 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) + { + unsigned long result, dummy; + __asm__ __volatile__( + "1:\tldarx %0,0,%5\n" + "\tcmpd %0,%4\n" + "\tbne 2f\n" + "\tstdcx. %3,0,%2\n" + "\tbne- 1b\n" + "\tsync\n" + "\tli %1, 1\n" + "\tb 3f\n" + "2:\tli %1, 0\n" + "3:\t\n" + : "=&r" (dummy), "=r" (result), "=p" (addr) + : "r" (new_val), "r" (old), "2"(addr) + : "cr0","memory"); + return (GC_bool) result; + } +# else + /* 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) + { + int result, dummy; + __asm__ __volatile__( + "1:\tlwarx %0,0,%5\n" + "\tcmpw %0,%4\n" + "\tbne 2f\n" + "\tstwcx. %3,0,%2\n" + "\tbne- 1b\n" + "\tsync\n" + "\tli %1, 1\n" + "\tb 3f\n" + "2:\tli %1, 0\n" + "3:\t\n" + : "=&r" (dummy), "=r" (result), "=p" (addr) + : "r" (new_val), "r" (old), "2"(addr) + : "cr0","memory"); + return (GC_bool) result; + } +# endif +# endif /* !GENERIC_COMPARE_AND_SWAP */ + inline static void GC_memory_barrier() + { + __asm__ __volatile__("sync" : : : "memory"); + } +# endif /* POWERPC */ + +# 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) + { + return __sync_bool_compare_and_swap (addr, old, new_val); + } +# endif /* !GENERIC_COMPARE_AND_SWAP */ +# if 0 + /* Shouldn't be needed; we use volatile stores instead. */ + inline static void GC_memory_barrier() + { + __sync_synchronize (); + } +# endif /* 0 */ +# endif /* IA64 */ +# if defined(ALPHA) +# if !defined(GENERIC_COMPARE_AND_SWAP) +# if defined(__GNUC__) + inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr, + GC_word old, GC_word new_val) + { + unsigned long was_equal; + unsigned long temp; + + __asm__ __volatile__( + "1: ldq_l %0,%1\n" + " cmpeq %0,%4,%2\n" + " mov %3,%0\n" + " beq %2,2f\n" + " stq_c %0,%1\n" + " beq %0,1b\n" + "2:\n" + " mb\n" + :"=&r" (temp), "=m" (*addr), "=&r" (was_equal) + : "r" (new_val), "Ir" (old) + :"memory"); + return was_equal; + } +# else /* !__GNUC__ */ + inline static GC_bool GC_compare_and_exchange(volatile GC_word *addr, + GC_word old, GC_word new_val) + { + return __CMP_STORE_QUAD(addr, old, new_val, addr); + } +# endif /* !__GNUC__ */ +# endif /* !GENERIC_COMPARE_AND_SWAP */ +# ifdef __GNUC__ + inline static void GC_memory_barrier() + { + __asm__ __volatile__("mb" : : : "memory"); + } +# else +# define GC_memory_barrier() asm("mb") +# endif /* !__GNUC__ */ +# endif /* ALPHA */ +# if defined(S390) +# if !defined(GENERIC_COMPARE_AND_SWAP) + inline static GC_bool GC_compare_and_exchange(volatile C_word *addr, + GC_word old, GC_word new_val) + { + int retval; + __asm__ __volatile__ ( +# ifndef __s390x__ + " cs %1,%2,0(%3)\n" +# else + " csg %1,%2,0(%3)\n" +# endif + " ipm %0\n" + " srl %0,28\n" + : "=&d" (retval), "+d" (old) + : "d" (new_val), "a" (addr) + : "cc", "memory"); + return retval == 0; + } +# endif +# endif +# 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 */ +# if defined(NO_PTHREAD_TRYLOCK) +# define LOCK() GC_lock(); +# else /* !defined(NO_PTHREAD_TRYLOCK) */ +# define LOCK() \ + { if (0 != pthread_mutex_trylock(&GC_allocate_ml)) GC_lock(); } +# endif +# 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 /* GC_PTHREADS with linux_threads.c implementation */ +# if defined(GC_WIN32_THREADS) +# if defined(GC_PTHREADS) +# include <pthread.h> + extern pthread_mutex_t GC_allocate_ml; +# define LOCK() pthread_mutex_lock(&GC_allocate_ml) +# define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml) +# else +# include <windows.h> + GC_API CRITICAL_SECTION GC_allocate_ml; +# define LOCK() EnterCriticalSection(&GC_allocate_ml); +# define UNLOCK() LeaveCriticalSection(&GC_allocate_ml); +# endif +# 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/boehm-gc/include/private/gc_pmark.h b/boehm-gc/include/private/gc_pmark.h new file mode 100644 index 000000000..6b15d8ad2 --- /dev/null +++ b/boehm-gc/include/private/gc_pmark.h @@ -0,0 +1,394 @@ +/* + * 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 + +# include "gc.h" /* For configuration */ + +# 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__ + ptr_t GC_find_start(ptr_t current, hdr *hhdr, hdr **new_hdr_p); +#else + ptr_t GC_find_start(); +#endif + +mse * GC_signal_mark_stack_overflow GC_PROTO((mse *msp)); + +# 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; \ + } \ +} + +/* 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); \ + 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); \ + \ + 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. + * Invoke FIXUP_POINTER(p) before any further processing. + * 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. + */ + +# if NEED_FIXUP_POINTER + /* Try both the raw version and the fixed up one. */ +# 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); \ + } \ + FIXUP_POINTER(p); \ + 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); \ + } +# else /* !NEED_FIXUP_POINTER */ +# 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); \ + } +# endif + + +/* + * As above, but interior pointer recognition as for + * normal for heap pointers. + */ +# define GC_PUSH_ONE_HEAP(p,source) \ + FIXUP_POINTER(p); \ + 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/boehm-gc/include/private/gc_priv.h b/boehm-gc/include/private/gc_priv.h new file mode 100644 index 000000000..4dbfa7d1f --- /dev/null +++ b/boehm-gc/include/private/gc_priv.h @@ -0,0 +1,2015 @@ +/* + * 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. + * + * 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_PRIVATE_H +# define GC_PRIVATE_H + +/* Autoconf definitions. */ +/* FIXME: This should really be included directly from each .c file. */ +#include <gc_config.h> + +#if defined(mips) && defined(SYSTYPE_BSD) && defined(sony_news) + /* sony RISC NEWS, NEWSOS 4 */ +# define BSD_TIME +/* typedef long ptrdiff_t; -- necessary on some really old systems */ +#endif + +#if defined(mips) && defined(SYSTYPE_BSD43) + /* MIPS RISCOS 4 */ +# define BSD_TIME +#endif + +#ifdef DGUX +# include <sys/types.h> +# include <sys/time.h> +# include <sys/resource.h> +#endif /* DGUX */ + +#ifdef BSD_TIME +# include <sys/types.h> +# include <sys/time.h> +# include <sys/resource.h> +#endif /* BSD_TIME */ + +# ifndef _GC_H +# include "../gc.h" +# endif + +# ifndef GC_MARK_H +# include "../gc_mark.h" +# endif + +typedef GC_word word; +typedef GC_signed_word signed_word; + +typedef int GC_bool; +# define TRUE 1 +# define FALSE 0 + +typedef char * ptr_t; /* A generic pointer to which we can add */ + /* byte displacements. */ + /* Preferably identical to caddr_t, if it */ + /* exists. */ + +# ifndef GCCONFIG_H +# include "gcconfig.h" +# endif + +# 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 +#else +# ifdef MSWIN32 +# include <stdlib.h> +# endif +# define VOLATILE +#endif + +#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 */ +/* collector */ +/* */ +/*********************************/ + +/*********************************/ +/* */ +/* Easily changeable parameters */ +/* */ +/*********************************/ + +/* #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 */ + /* object to be considered valid. Also causes the */ + /* sizes of all objects to be inflated by at least */ + /* one byte. This should suffice to guarantee */ + /* that in the presence of a compiler that does */ + /* not perform garbage-collector-unsafe */ + /* optimizations, all portable, strictly ANSI */ + /* conforming C programs should be safely usable */ + /* with malloc replaced by GC_malloc and free */ + /* calls removed. There are several disadvantages: */ + /* 1. There are probably no interesting, portable, */ + /* strictly ANSI conforming C programs. */ + /* 2. This option makes it hard for the collector */ + /* to allocate space that is not ``pointed to'' */ + /* by integers, etc. Under SunOS 4.X with a */ + /* statically linked libc, we empiricaly */ + /* observed that it would be difficult to */ + /* allocate individual objects larger than 100K. */ + /* Even if only smaller objects are allocated, */ + /* more swap space is likely to be needed. */ + /* Fortunately, much of this will never be */ + /* 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 */ + +#define PRINTTIMES /* Print the amount of time consumed by each garbage */ + /* collection. */ + +#define PRINTBLOCKS /* Print object sizes associated with heap blocks, */ + /* whether the objects are atomic or composite, and */ + /* whether or not the block was found to be empty */ + /* during the reclaim phase. Typically generates */ + /* about one screenful per garbage collection. */ +#undef PRINTBLOCKS + +#ifdef SILENT +# ifdef PRINTSTATS +# undef PRINTSTATS +# endif +# ifdef PRINTTIMES +# undef PRINTTIMES +# endif +# ifdef PRINTNBLOCKS +# undef PRINTNBLOCKS +# endif +#endif + +#if defined(PRINTSTATS) && !defined(GATHERSTATS) +# define GATHERSTATS +#endif + +#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 */ + /* user generated code that calls GC_allocobj and */ + /* GC_allocaobj directly. */ + /* Slows down average programs slightly. May however */ + /* substantially reduce fragmentation if allocation */ + /* request sizes are widely scattered. */ + /* May save significant amounts of space for obj_map */ + /* entries. */ + +#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 + +#if defined(GC_GCJ_SUPPORT) && ALIGNMENT < 8 && !defined(ALIGN_DOUBLE) + /* GCJ's Hashtable synchronization code requires 64-bit alignment. */ +# define ALIGN_DOUBLE +#endif + +/* ALIGN_DOUBLE requires MERGE_SIZES at present. */ +# if defined(ALIGN_DOUBLE) && !defined(MERGE_SIZES) +# define MERGE_SIZES +# endif + +#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 2048 /* Maximum heap increment, in blocks */ +# else +# define MINHINCR 64 +# define MAXHINCR 4096 +# endif + +# define TIME_LIMIT 50 /* We try to keep pause times from exceeding */ + /* this by much. In milliseconds. */ + +# define BL_LIMIT GC_black_list_spacing + /* If we need a block of N bytes, and we have */ + /* a block of N + BL_LIMIT bytes available, */ + /* and N > BL_LIMIT, */ + /* but all possible positions in it are */ + /* blacklisted, we just use it anyway (and */ + /* print a warning, if warnings are enabled). */ + /* This risks subsequently leaking the block */ + /* due to a false reference. But not using */ + /* the block risks unreasonable immediate */ + /* heap growth. */ + +/*********************************/ +/* */ +/* Stack saving for debugging */ +/* */ +/*********************************/ + +#ifdef NEED_CALLINFO + struct callinfo { + word ci_pc; /* Caller, not callee, pc */ +# if NARGS > 0 + word ci_arg[NARGS]; /* bit-wise complement to avoid retention */ +# endif +# if defined(ALIGN_DOUBLE) && (NFRAMES * (NARGS + 1)) % 2 == 1 + /* Likely alignment problem. */ + word ci_dummy; +# endif + }; +#endif + +#ifdef SAVE_CALL_CHAIN + +/* 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 GC_PROTO((struct callinfo info[NFRAMES])); + +void GC_print_callers GC_PROTO((struct callinfo info[NFRAMES])); + +#endif + + +/*********************************/ +/* */ +/* OS interface routines */ +/* */ +/*********************************/ + +#ifdef BSD_TIME +# undef CLOCK_TYPE +# undef GET_TIME +# undef MS_TIME_DIFF +# define CLOCK_TYPE struct timeval +# define GET_TIME(x) { struct rusage rusage; \ + getrusage (RUSAGE_SELF, &rusage); \ + x = rusage.ru_utime; } +# 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 */ +# endif +# if defined(FREEBSD) && !defined(CLOCKS_PER_SEC) +# include <machine/limits.h> +# define CLOCKS_PER_SEC CLK_TCK +# endif +# if !defined(CLOCKS_PER_SEC) +# define CLOCKS_PER_SEC 1000000 +/* + * This is technically a bug in the implementation. ANSI requires that + * CLOCKS_PER_SEC be defined. But at least under SunOS4.1.1, it isn't. + * Also note that the combination of ANSI C and POSIX is incredibly gross + * here. The type clock_t is used by both clock() and times(). But on + * some machines these use different notions of a clock tick, CLOCKS_PER_SEC + * seems to apply only to clock. Hence we use it here. On many machines, + * including SunOS, clock actually uses units of microseconds (which are + * not really clock ticks). + */ +# endif +# define CLOCK_TYPE clock_t +# 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. */ +# if defined(SPARC) && defined(SUNOS4) +# define BCOPY_EXISTS +# endif +# if defined(M68K) && defined(AMIGA) +# define BCOPY_EXISTS +# endif +# if defined(M68K) && defined(NEXT) +# define BCOPY_EXISTS +# endif +# if defined(VAX) +# define BCOPY_EXISTS +# endif +# if defined(AMIGA) +# include <string.h> +# define BCOPY_EXISTS +# endif +# if defined(DARWIN) +# include <string.h> +# define BCOPY_EXISTS +# endif + +# ifndef BCOPY_EXISTS +# include <string.h> +# define BCOPY(x,y,n) memcpy(y, x, (size_t)(n)) +# define BZERO(x,n) memset(x, 0, (size_t)(n)) +# else +# define BCOPY(x,y,n) bcopy((char *)(x),(char *)(y),(int)(n)) +# define BZERO(x,n) bzero((char *)(x),(int)(n)) +# 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 */ +/* clear on that point). Standard malloc implementations are usually */ +/* neither interruptable nor thread-safe, and thus correspond to */ +/* empty definitions. */ +/* It probably doesn't make any sense to declare these to be nonempty */ +/* if the code is being optimized, since signal safety relies on some */ +/* ordering constraints that are typically not obeyed by optimizing */ +/* compilers. */ +# ifdef PCR +# define DISABLE_SIGNALS() \ + PCR_Th_SetSigMask(PCR_allSigsBlocked,&GC_old_sig_mask) +# define ENABLE_SIGNALS() \ + PCR_Th_SetSigMask(&GC_old_sig_mask, NIL) +# else +# if defined(THREADS) || defined(AMIGA) \ + || defined(MSWIN32) || defined(MSWINCE) || defined(MACOS) \ + || defined(DJGPP) || defined(NO_SIGNALS) + /* Also useful for debugging. */ + /* Should probably use thr_sigsetmask for GC_SOLARIS_THREADS. */ +# define DISABLE_SIGNALS() +# define ENABLE_SIGNALS() +# else +# define DISABLE_SIGNALS() GC_disable_signals() + void GC_disable_signals(); +# define ENABLE_SIGNALS() GC_enable_signals() + void GC_enable_signals(); +# endif +# endif + +/* + * Stop and restart mutator threads. + */ +# ifdef PCR +# include "th/PCR_ThCtl.h" +# define STOP_WORLD() \ + PCR_ThCtl_SetExclusiveMode(PCR_ThCtl_ExclusiveMode_stopNormal, \ + PCR_allSigsBlocked, \ + PCR_waitForever) +# define START_WORLD() \ + PCR_ThCtl_SetExclusiveMode(PCR_ThCtl_ExclusiveMode_null, \ + PCR_allSigsBlocked, \ + PCR_waitForever); +# else +# if defined(GC_SOLARIS_THREADS) || defined(GC_WIN32_THREADS) \ + || defined(GC_PTHREADS) + void GC_stop_world(); + void GC_start_world(); +# define STOP_WORLD() GC_stop_world() +# define START_WORLD() GC_start_world() +# else +# define STOP_WORLD() +# define START_WORLD() +# endif +# endif + +/* Abandon ship */ +# ifdef PCR +# define ABORT(s) PCR_Base_Panic(s) +# else +# ifdef SMALL_CONFIG +# define ABORT(msg) abort(); +# else + GC_API void GC_abort GC_PROTO((GC_CONST char * msg)); +# define ABORT(msg) GC_abort(msg); +# endif +# endif + +/* Exit abnormally, but without making a mess (e.g. out of memory) */ +# ifdef PCR +# define EXIT() PCR_Base_Exit(1,PCR_waitForever) +# else +# define EXIT() (void)exit(1) +# endif + +/* Print warning message, e.g. almost out of memory. */ +# define WARN(msg,arg) (*GC_current_warn_proc)("GC Warning: " msg, (GC_word)(arg)) +extern GC_warn_proc GC_current_warn_proc; + +/* Get environment entry */ +#if !defined(NO_GETENV) +# if defined(EMPTY_GETENV_RESULTS) + /* Workaround for a reputed Wine bug. */ + static inline char * fixed_getenv(const char *name) + { + char * tmp = getenv(name); + if (tmp == 0 || strlen(tmp) == 0) + return 0; + return tmp; + } +# define GETENV(name) fixed_getenv(name) +# else +# define GETENV(name) getenv(name) +# endif +#else +# define GETENV(name) 0 +#endif + +#if defined(DARWIN) +# if defined(POWERPC) +# if CPP_WORDSZ == 32 +# define GC_THREAD_STATE_T ppc_thread_state_t +# define GC_MACH_THREAD_STATE PPC_THREAD_STATE +# define GC_MACH_THREAD_STATE_COUNT PPC_THREAD_STATE_COUNT +# define GC_MACH_HEADER mach_header +# define GC_MACH_SECTION section +# define GC_GETSECTBYNAME getsectbynamefromheader +# else +# define GC_THREAD_STATE_T ppc_thread_state64_t +# define GC_MACH_THREAD_STATE PPC_THREAD_STATE64 +# define GC_MACH_THREAD_STATE_COUNT PPC_THREAD_STATE64_COUNT +# define GC_MACH_HEADER mach_header_64 +# define GC_MACH_SECTION section_64 +# define GC_GETSECTBYNAME getsectbynamefromheader_64 +# endif +# elif defined(I386) || defined(X86_64) +# if CPP_WORDSZ == 32 +# define GC_THREAD_STATE_T x86_thread_state32_t +# define GC_MACH_THREAD_STATE x86_THREAD_STATE32 +# define GC_MACH_THREAD_STATE_COUNT x86_THREAD_STATE32_COUNT +# define GC_MACH_HEADER mach_header +# define GC_MACH_SECTION section +# define GC_GETSECTBYNAME getsectbynamefromheader +# else +# define GC_THREAD_STATE_T x86_thread_state64_t +# define GC_MACH_THREAD_STATE x86_THREAD_STATE64 +# define GC_MACH_THREAD_STATE_COUNT x86_THREAD_STATE64_COUNT +# define GC_MACH_HEADER mach_header_64 +# define GC_MACH_SECTION section_64 +# define GC_GETSECTBYNAME getsectbynamefromheader_64 +# endif +# else +# error define GC_THREAD_STATE_T +# define GC_MACH_THREAD_STATE MACHINE_THREAD_STATE +# define GC_MACH_THREAD_STATE_COUNT MACHINE_THREAD_STATE_COUNT +# endif +/* Try to work out the right way to access thread state structure members. + The structure has changed its definition in different Darwin versions. + This now defaults to the (older) names without __, thus hopefully, + not breaking any existing Makefile.direct builds. */ +# if defined (HAS_PPC_THREAD_STATE___R0) \ + || defined (HAS_PPC_THREAD_STATE64___R0) \ + || defined (HAS_X86_THREAD_STATE32___EAX) \ + || defined (HAS_X86_THREAD_STATE64___RAX) +# define THREAD_FLD(x) __ ## x +# else +# define THREAD_FLD(x) x +# endif +#endif +/*********************************/ +/* */ +/* Word-size-dependent defines */ +/* */ +/*********************************/ + +#if CPP_WORDSZ == 32 +# define WORDS_TO_BYTES(x) ((x)<<2) +# define BYTES_TO_WORDS(x) ((x)>>2) +# define LOGWL ((word)5) /* log[2] of CPP_WORDSZ */ +# define modWORDSZ(n) ((n) & 0x1f) /* n mod size of word */ +# if ALIGNMENT != 4 +# define UNALIGNED +# endif +#endif + +#if CPP_WORDSZ == 64 +# define WORDS_TO_BYTES(x) ((x)<<3) +# define BYTES_TO_WORDS(x) ((x)>>3) +# define LOGWL ((word)6) /* log[2] of CPP_WORDSZ */ +# define modWORDSZ(n) ((n) & 0x3f) /* n mod size of word */ +# if ALIGNMENT != 8 +# define UNALIGNED +# endif +#endif + +#define WORDSZ ((word)CPP_WORDSZ) +#define SIGNB ((word)1 << (WORDSZ-1)) +#define BYTES_PER_WORD ((word)(sizeof (word))) +#define ONES ((word)(signed_word)(-1)) +#define divWORDSZ(n) ((n) >> LOGWL) /* divide n by size of word */ + +/*********************/ +/* */ +/* Size Parameters */ +/* */ +/*********************/ + +/* heap block size, bytes. Should be power of 2 */ + +#ifndef HBLKSIZE +# ifdef SMALL_CONFIG +# define CPP_LOG_HBLKSIZE 10 +# else +# if (CPP_WORDSZ == 32) || (defined(HPUX) && defined(HP_PA)) + /* HPUX/PA seems to use 4K pages with the 64 bit ABI */ +# define CPP_LOG_HBLKSIZE 12 +# else +# define CPP_LOG_HBLKSIZE 13 +# endif +# endif +#else +# if HBLKSIZE == 512 +# define CPP_LOG_HBLKSIZE 9 +# endif +# if HBLKSIZE == 1024 +# define CPP_LOG_HBLKSIZE 10 +# endif +# if HBLKSIZE == 2048 +# define CPP_LOG_HBLKSIZE 11 +# endif +# if HBLKSIZE == 4096 +# define CPP_LOG_HBLKSIZE 12 +# endif +# if HBLKSIZE == 8192 +# define CPP_LOG_HBLKSIZE 13 +# endif +# if HBLKSIZE == 16384 +# define CPP_LOG_HBLKSIZE 14 +# endif +# ifndef CPP_LOG_HBLKSIZE + --> fix HBLKSIZE +# endif +# undef HBLKSIZE +#endif +# define CPP_HBLKSIZE (1 << CPP_LOG_HBLKSIZE) +# define LOG_HBLKSIZE ((word)CPP_LOG_HBLKSIZE) +# define HBLKSIZE ((word)CPP_HBLKSIZE) + + +/* 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_MAXOBJBYTES) +#define MAXOBJSZ ((word)CPP_MAXOBJSZ) + +# define divHBLKSZ(n) ((n) >> LOG_HBLKSIZE) + +# define HBLK_PTR_DIFF(p,q) divHBLKSZ((ptr_t)p - (ptr_t)q) + /* Equivalent to subtracting 2 hblk pointers. */ + /* We do it this way because a compiler should */ + /* find it hard to use an integer division */ + /* instead of a shift. The bundled SunOS 4.1 */ + /* o.w. sometimes pessimizes the subtraction to */ + /* involve a call to .div. */ + +# define modHBLKSZ(n) ((n) & (HBLKSIZE-1)) + +# define HBLKPTR(objptr) ((struct hblk *)(((word) (objptr)) & ~(HBLKSIZE-1))) + +# define HBLKDISPL(objptr) (((word) (objptr)) & (HBLKSIZE-1)) + +/* Round up byte allocation requests to integral number of words, etc. */ +# 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 + 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 +# endif + + +/* + * Hash table representation of sets of pages. This assumes it is + * OK to add spurious entries to sets. + * Used by black-listing code, and perhaps by dirty bit maintenance code. + */ + +# ifdef LARGE_CONFIG +# define LOG_PHT_ENTRIES 20 /* Collisions likely at 1M blocks, */ + /* which is >= 4GB. Each table takes */ + /* 128KB, some of which may never be */ + /* touched. */ +# else +# 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 64K hblks >= 256MB. */ + /* Each hash table occupies 8K bytes. */ + /* Even for somewhat smaller heaps, */ + /* say half that, collisions may be an */ + /* issue because we blacklist */ + /* addresses outside the heap. */ +# endif +# endif +# define PHT_ENTRIES ((word)1 << LOG_PHT_ENTRIES) +# define PHT_SIZE (PHT_ENTRIES >> LOGWL) +typedef word page_hash_table[PHT_SIZE]; + +# define PHT_HASH(addr) ((((word)(addr)) >> LOG_HBLKSIZE) & (PHT_ENTRIES - 1)) + +# define get_pht_entry_from_index(bl, index) \ + (((bl)[divWORDSZ(index)] >> modWORDSZ(index)) & 1) +# define set_pht_entry_from_index(bl, index) \ + (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 + + + +/********************************************/ +/* */ +/* H e a p B l o c k s */ +/* */ +/********************************************/ + +/* heap block header */ +#define HBLKMASK (HBLKSIZE-1) + +#define BITS_PER_HBLK (CPP_HBLKSIZE * 8) + +#define MARK_BITS_PER_HBLK (BITS_PER_HBLK/CPP_WORDSZ) + /* upper bound */ + /* We allocate 1 bit/word, unless USE_MARK_BYTES */ + /* is defined. Only the first word */ + /* in each object is actually marked. */ + +# 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) +# endif + +/* 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. */ + /* if free, the size in bytes of the whole block */ + struct hblk * hb_next; /* Link field for hblk free list */ + /* and for lists of chunks waiting to be */ + /* reclaimed. */ + struct hblk * hb_prev; /* Backwards link for free list. */ + word hb_descr; /* object descriptor for marking. See */ + /* mark.h. */ + 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. */ + unsigned char hb_obj_kind; + /* Kind of objects in the block. Each kind */ + /* identifies a mark procedure and a set of */ + /* list headers. Sometimes called regions. */ + unsigned char hb_flags; +# define IGNORE_OFF_PAGE 1 /* Ignore pointers that do not */ + /* point to the first page of */ + /* this object. */ +# define WAS_UNMAPPED 2 /* This is a free block, which has */ + /* been unmapped from the address */ + /* space. */ + /* GC_remap must be invoked on it */ + /* before it can be reallocated. */ + /* Only set with USE_MUNMAP. */ + unsigned short hb_last_reclaimed; + /* Value of GC_gc_no when block was */ + /* last allocated or swept. May wrap. */ + /* For a free block, this is maintained */ + /* only for USE_MUNMAP, and indicates */ + /* when the header was allocated, or */ + /* when the size of the block last */ + /* changed. */ +# 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 BODY_SZ (HBLKSIZE/sizeof(word)) + +struct hblk { + word hb_body[BODY_SZ]; +}; + +# define HBLK_IS_FREE(hdr) ((hdr) -> hb_map == GC_invalid_map) + +# define OBJ_SZ_TO_BLOCKS(sz) \ + 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)) + +# define LOG_MAX_MARK_PROCS 6 +# define MAX_MARK_PROCS (1 << LOG_MAX_MARK_PROCS) + +/* Root sets. Logically private to mark_rts.c. But we don't want the */ +/* tables scanned, so we put them here. */ +/* MAX_ROOT_SETS is the maximum number of ranges that can be */ +/* registered as static roots. */ +# ifdef LARGE_CONFIG +# define MAX_ROOT_SETS 4096 +# else + /* GCJ LOCAL: MAX_ROOT_SETS increased to permit more shared */ + /* libraries to be loaded. */ +# define MAX_ROOT_SETS 1024 +# endif + +# define MAX_EXCLUSIONS (MAX_ROOT_SETS/4) +/* Maximum number of segments that can be excluded from root sets. */ + +/* + * Data structure for excluded static roots. + */ +struct exclusion { + ptr_t e_start; + ptr_t e_end; +}; + +/* Data structure for list of root sets. */ +/* We keep a hash table, so that we can filter out duplicate additions. */ +/* Under Win32, we need to do a better job of filtering overlaps, so */ +/* we resort to sequential search, and pay the price. */ +struct roots { + ptr_t r_start; + ptr_t r_end; +# if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) + struct roots * r_next; +# endif + GC_bool r_tmp; + /* Delete before registering new dynamic libraries */ +}; + +#if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) + /* 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 */ +#endif + +/* Lists of all heap blocks and free lists */ +/* as well as other random data structures */ +/* that should not be scanned by the */ +/* collector. */ +/* These are grouped together in a struct */ +/* so that they can be easily skipped by the */ +/* GC_mark routine. */ +/* The ordering is weird to make GC_malloc */ +/* faster by keeping the important fields */ +/* sufficiently close together that a */ +/* single load of a base register will do. */ +/* Scalars that could easily appear to */ +/* be pointers are also put here. */ +/* The main fields should precede any */ +/* conditionally included fields, so that */ +/* gc_inl.h will work even if a different set */ +/* of macros is defined when the client is */ +/* compiled. */ + +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. */ +# 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 */ + /* blocks, since last gc. Approximate. */ + word _words_finalized; + /* Approximate number of words in objects (and headers) */ + /* That became ready for finalization in the last */ + /* collection. */ + word _non_gc_bytes_at_gc; + /* Number of explicitly managed bytes of storage */ + /* at last collection. */ + word _mem_freed; + /* Number of explicitly deallocated words of memory */ + /* since last collection. */ + word _finalizer_mem_freed; + /* Words of memory explicitly deallocated while */ + /* finalizers were running. Used to approximate mem. */ + /* explicitly deallocated by finalizers. */ + 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. */ + +# ifndef SEPARATE_GLOBALS + ptr_t _objfreelist[MAXOBJSZ+1]; + /* free list for objects */ + ptr_t _aobjfreelist[MAXOBJSZ+1]; + /* free list for atomic objs */ +# endif + + ptr_t _uobjfreelist[MAXOBJSZ+1]; + /* uncollectable but traced objs */ + /* objects on this and auobjfreelist */ + /* are always marked, except during */ + /* garbage collections. */ +# ifdef ATOMIC_UNCOLLECTABLE + ptr_t _auobjfreelist[MAXOBJSZ+1]; +# endif + /* uncollectable but traced objs */ + +# ifdef GATHERSTATS + word _composite_in_use; + /* Number of words in accessible composite */ + /* objects. */ + word _atomic_in_use; + /* Number of words in accessible atomic */ + /* objects. */ +# endif +# ifdef USE_MUNMAP + word _unmapped_bytes; +# endif +# ifdef MERGE_SIZES + unsigned _size_map[WORDS_TO_BYTES(MAXOBJSZ+1)]; + /* Number of words to allocate for a given allocation request in */ + /* bytes. */ +# endif + +# ifdef STUBBORN_ALLOC + ptr_t _sobjfreelist[MAXOBJSZ+1]; +# endif + /* free list for immutable objects */ + 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 + */ + /* 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 \ + : WORDS_TO_BYTES(CPP_MAXOBJSZ)+1) + char _valid_offsets[VALID_OFFSET_SZ]; + /* GC_valid_offsets[i] == TRUE ==> i */ + /* is registered as a displacement. */ + char _modws_valid_offsets[sizeof(word)]; + /* GC_valid_offsets[i] ==> */ + /* GC_modws_valid_offsets[i%sizeof(word)] */ +# define OFFSET_VALID(displ) \ + (GC_all_interior_pointers || GC_valid_offsets[displ]) +# ifdef STUBBORN_ALLOC + page_hash_table _changed_pages; + /* Stubborn object pages that were changes since last call to */ + /* GC_read_changed. */ + page_hash_table _prev_changed_pages; + /* Stubborn object pages that were changes before last call to */ + /* GC_read_changed. */ +# endif +# if defined(PROC_VDB) || defined(MPROTECT_VDB) + page_hash_table _grungy_pages; /* Pages that were dirty at last */ + /* GC_read_dirty. */ +# endif +# ifdef MPROTECT_VDB + VOLATILE page_hash_table _dirty_pages; + /* Pages dirtied since last GC_read_dirty. */ +# endif +# ifdef PROC_VDB + page_hash_table _written_pages; /* Pages ever dirtied */ +# endif +# ifdef LARGE_CONFIG +# if CPP_WORDSZ > 32 +# define MAX_HEAP_SECTS 4096 /* overflows at roughly 64 GB */ +# else +# define MAX_HEAP_SECTS 768 /* Separately added heap sections. */ +# endif +# else +# ifdef SMALL_CONFIG +# define MAX_HEAP_SECTS 128 /* Roughly 256MB (128*2048*1K) */ +# else +# define MAX_HEAP_SECTS 384 /* Roughly 3GB */ +# endif +# endif + struct HeapSect { + ptr_t hs_start; word hs_bytes; + } _heap_sects[MAX_HEAP_SECTS]; +# if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) + 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]; +# if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) + struct roots * _root_index[RT_SIZE]; +# endif + struct exclusion _excl_table[MAX_EXCLUSIONS]; + /* Block header index; see gc_headers.h */ + bottom_index * _all_nils; + bottom_index * _top_index [TOP_SZ]; +#ifdef SAVE_CALL_CHAIN + struct callinfo _last_stack[NFRAMES]; /* Stack at last garbage collection.*/ + /* Useful for debugging mysterious */ + /* object disappearances. */ + /* In the multithreaded case, we */ + /* currently only save the calling */ + /* stack. */ +#endif +}; + +GC_API GC_FAR struct _GC_arrays GC_arrays; + +# 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 +# endif +# define GC_sobjfreelist GC_arrays._sobjfreelist +# define GC_valid_offsets GC_arrays._valid_offsets +# define GC_modws_valid_offsets GC_arrays._modws_valid_offsets +# ifdef STUBBORN_ALLOC +# define GC_changed_pages GC_arrays._changed_pages +# define GC_prev_changed_pages GC_arrays._prev_changed_pages +# endif +# 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_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_finalizer_mem_freed GC_arrays._finalizer_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 +# if defined(MSWIN32) || defined(MSWINCE) || defined (CYGWIN32) +# 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 +# define GC_all_nils GC_arrays._all_nils +# define GC_top_index GC_arrays._top_index +# if defined(PROC_VDB) || defined(MPROTECT_VDB) +# define GC_grungy_pages GC_arrays._grungy_pages +# endif +# ifdef MPROTECT_VDB +# define GC_dirty_pages GC_arrays._dirty_pages +# endif +# ifdef PROC_VDB +# define GC_written_pages GC_arrays._written_pages +# endif +# ifdef GATHERSTATS +# define GC_composite_in_use GC_arrays._composite_in_use +# define GC_atomic_in_use GC_arrays._atomic_in_use +# endif +# ifdef MERGE_SIZES +# define GC_size_map GC_arrays._size_map +# endif + +# 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 + +extern struct obj_kind { + ptr_t *ok_freelist; /* Array of free listheaders for this kind of object */ + /* Point either to GC_arrays or to storage allocated */ + /* with GC_scratch_alloc. */ + struct hblk **ok_reclaim_list; + /* List headers for lists of blocks waiting to be */ + /* swept. */ + word ok_descriptor; /* Descriptor template for objects in this */ + /* block. */ + GC_bool ok_relocate_descr; + /* Add object size in bytes to descriptor */ + /* template to obtain descriptor. Otherwise */ + /* template is used as is. */ + GC_bool ok_init; /* Clear objects before putting them on the free list. */ +} GC_obj_kinds[MAXOBJKINDS]; + +# define beginGC_obj_kinds ((ptr_t)(&GC_obj_kinds)) +# define endGC_obj_kinds (beginGC_obj_kinds + (sizeof GC_obj_kinds)) + +/* 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 +# define NORMAL 1 +# define UNCOLLECTABLE 2 +# ifdef ATOMIC_UNCOLLECTABLE +# define AUNCOLLECTABLE 3 +# define STUBBORN 4 +# define IS_UNCOLLECTABLE(k) (((k) & ~1) == UNCOLLECTABLE) +# else +# define STUBBORN 3 +# define IS_UNCOLLECTABLE(k) ((k) == UNCOLLECTABLE) +# endif + +extern int GC_n_kinds; + +GC_API word GC_fo_entries; + +extern word GC_n_heap_sects; /* Number of separately added heap */ + /* sections. */ + +extern word GC_page_size; + +# if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) + 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; + /* Number of bytes on stack blacklist. */ + +extern word GC_black_list_spacing; + /* Average number of bytes between blacklisted */ + /* blocks. Approximate. */ + /* Counts only blocks that are */ + /* "stack-blacklisted", i.e. that are */ + /* problematic in the interior of an object. */ + +extern map_entry_type * GC_invalid_map; + /* Pointer to the nowhere valid hblk map */ + /* Blocks pointing to this map are free. */ + +extern struct hblk * GC_hblkfreelist[]; + /* List of completely empty heap blocks */ + /* Linked through hb_next field of */ + /* header structure associated with */ + /* block. */ + +extern GC_bool GC_objects_are_marked; /* There are marked objects in */ + /* the heap. */ + +#ifndef SMALL_CONFIG + extern GC_bool GC_incremental; + /* Using incremental/generational collection. */ +# define TRUE_INCREMENTAL \ + (GC_incremental && GC_time_limit != GC_TIME_UNLIMITED) + /* True incremental, not just generational, mode */ +#else +# define GC_incremental FALSE + /* Hopefully allow optimizer to remove some code. */ +# define TRUE_INCREMENTAL FALSE +#endif + +extern GC_bool GC_dirty_maintained; + /* Dirty bits are being maintained, */ + /* either for incremental collection, */ + /* or to limit the root set. */ + +extern word GC_root_size; /* Total size of registered root sections */ + +extern GC_bool GC_debugging_started; /* GC_debug_malloc has been called. */ + +extern long GC_large_alloc_warn_interval; + /* Interval between unsuppressed warnings. */ + +extern long GC_large_alloc_warn_suppressed; + /* Number of warnings suppressed so far. */ + +#ifdef THREADS + extern GC_bool GC_world_stopped; +#endif + +/* Operations */ +# ifndef abs +# define abs(x) ((x) < 0? (-(x)) : (x)) +# endif + + +/* Marks are in a reserved area in */ +/* each heap block. Each word has one mark bit associated */ +/* 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 */ + +/* + * Retrieve, set, clear the mark bit corresponding + * to the nth word in a given heap block. + * + * (Recall that bit n corresponds to object beginning at word n + * 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) \ + 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_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 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 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 GC_PROTO((ptr_t b, ptr_t t)); + /* As above, but consider */ + /* interior pointers as valid */ +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 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 ensure that callee- */ + /* save registers are not missed. */ +#else + /* In the threads case, we push part of the current thread stack */ + /* with GC_push_all_eager when we push the registers. This gets the */ + /* callee-save registers that may disappear. The remainder of the */ + /* stacks are scheduled for scanning in *GC_push_other_roots, which */ + /* is thread-package-specific. */ +#endif +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 +# if defined(SPARC) || defined(IA64) + /* Cause all stacked registers to be saved in memory. Return a */ + /* pointer to the top of the corresponding memory stack. */ + word GC_save_regs_in_stack 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 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 +# 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 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 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)); +void GC_remove_roots_inner GC_PROTO((char * b, char * e)); +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) || defined(CYGWIN32) +GC_bool GC_is_tmp_root GC_PROTO((ptr_t p)); + /* Is the address p in one of the temporary static */ + /* root sections? */ +# endif +void GC_register_dynamic_libraries GC_PROTO((void)); + /* Add dynamic library data sections to the root set. */ + +GC_bool GC_register_main_static_data GC_PROTO((void)); + /* We need to register the main data segment. Returns */ + /* TRUE unless this is done implicitly as part of */ + /* dynamic library registration. */ + +/* Machine dependent startup routines */ +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 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 */ +# define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \ + 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 + void GC_add_to_black_list_normal GC_PROTO((word p)); +# define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \ + if (GC_all_interior_pointers) { \ + GC_add_to_black_list_stack(bits); \ + } else { \ + GC_add_to_black_list_normal(bits); \ + } +# endif + +# 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 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 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. */ + +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_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 */ + /* TRUE. Returns TRUE if it completes */ + /* successfully. */ +# define GC_gcollect_inner() \ + (void) GC_try_to_collect_inner(GC_never_stop_func) +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. */ + +extern GC_bool GC_is_initialized; /* GC_init() has been run. */ + +#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. */ + /* The actual decl is in gc_mark.h. */ +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_inner GC_PROTO((word lw, int k)); + /* Analogous to the above, but assumes */ + /* a small object size, and bypasses */ + /* MERGE_SIZES mechanism. */ +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_free_inner(GC_PTR p); + +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. */ + +GC_API GC_PTR GC_make_closure GC_PROTO((GC_finalization_proc fn, GC_PTR data)); +GC_API void GC_debug_invoke_finalizer GC_PROTO((GC_PTR obj, GC_PTR data)); + /* Auxiliary fns to make finalization work */ + /* correctly with displaced pointers introduced */ + /* by the debugging allocators. */ + +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. */ + /* Add any that are not to GC_smashed list. */ +extern void (*GC_print_all_smashed) GC_PROTO((void)); + /* Print GC_smashed if it's not empty. */ + /* Clear GC_smashed list. */ +extern void GC_print_all_errors GC_PROTO((void)); + /* Print smashed and leaked objects, if any. */ + /* Clear the lists of such objects. */ +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. */ +#if defined(LINUX) && defined(__ELF__) && !defined(SMALL_CONFIG) + void GC_print_address_map GC_PROTO((void)); + /* Print an address map of the process. */ +#endif + +extern GC_bool GC_have_errors; /* We saw a smashed or leaked object. */ + /* Call error printing routine */ + /* occasionally. */ +extern GC_bool GC_print_stats; /* Produce at least some logging output */ + /* Set from environment variable. */ + +#ifndef NO_DEBUGGING + extern GC_bool GC_dump_regularly; /* Generate regular debugging dumps. */ +# define COND_DUMP if (GC_dump_regularly) GC_dump(); +#else +# define COND_DUMP +#endif + +#ifdef KEEP_BACK_PTRS + extern long GC_backtraces; + void GC_generate_random_backtrace_no_gc(void); +#endif + +extern GC_bool GC_print_back_height; + +#ifdef MAKE_BACK_GRAPH + void GC_print_back_graph_stats(void); +#endif + +/* 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); + void GC_merge_unmapped(void); + void GC_unmap(ptr_t start, word bytes); + void GC_remap(ptr_t start, word bytes); + void GC_unmap_gap(ptr_t start1, word bytes1, ptr_t start2, word bytes2); +#endif + +/* Virtual dirty bit implementation: */ +/* Each implementation exports the following: */ +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_remove_protection GC_PROTO((struct hblk *h, word nblocks, + GC_bool pointerfree)); + /* h is about to be writteni or allocated. Ensure */ + /* that it's not write protected by the virtual */ + /* dirty bit implementation. */ + +void GC_dirty_init GC_PROTO((void)); + +/* Slow/general mark bit manipulation: */ +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 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 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_print_finalization_stats GC_PROTO((void)); +void GC_dump GC_PROTO((void)); + +#ifdef KEEP_BACK_PTRS + void GC_store_back_pointer(ptr_t source, ptr_t dest); + void GC_marked_for_finalization(ptr_t dest); +# define GC_STORE_BACK_PTR(source, dest) GC_store_back_pointer(source, dest) +# define GC_MARKED_FOR_FINALIZATION(dest) GC_marked_for_finalization(dest) +#else +# define GC_STORE_BACK_PTR(source, dest) +# define GC_MARKED_FOR_FINALIZATION(dest) +#endif + +/* Make arguments appear live to compiler */ +# ifdef __WATCOMC__ + void GC_noop(void*, ...); +# else +# ifdef __DMC__ + GC_API void GC_noop(...); +# else + GC_API void GC_noop(); +# endif +# endif + +void GC_noop1 GC_PROTO((word)); + +/* Logging and diagnostic output: */ +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 */ + /* portability. Restricted to 6 args and */ + /* 1K total output length. */ + /* (We use sprintf. Hopefully that doesn't */ + /* allocate for long arguments.) */ +# define GC_printf0(f) GC_printf(f, 0l, 0l, 0l, 0l, 0l, 0l) +# define GC_printf1(f,a) GC_printf(f, (long)a, 0l, 0l, 0l, 0l, 0l) +# define GC_printf2(f,a,b) GC_printf(f, (long)a, (long)b, 0l, 0l, 0l, 0l) +# define GC_printf3(f,a,b,c) GC_printf(f, (long)a, (long)b, (long)c, 0l, 0l, 0l) +# define GC_printf4(f,a,b,c,d) GC_printf(f, (long)a, (long)b, (long)c, \ + (long)d, 0l, 0l) +# define GC_printf5(f,a,b,c,d,e) GC_printf(f, (long)a, (long)b, (long)c, \ + (long)d, (long)e, 0l) +# 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) + +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) +# define GC_err_printf3(f,a,b,c) GC_err_printf(f, (long)a, (long)b, (long)c, \ + 0l, 0l, 0l) +# define GC_err_printf4(f,a,b,c,d) GC_err_printf(f, (long)a, (long)b, \ + (long)c, (long)d, 0l, 0l) +# define GC_err_printf5(f,a,b,c,d,e) GC_err_printf(f, (long)a, (long)b, \ + (long)c, (long)d, \ + (long)e, 0l) +# define GC_err_printf6(f,a,b,c,d,e,g) GC_err_printf(f, (long)a, (long)b, \ + (long)c, (long)d, \ + (long)e, (long)g) + /* Ditto, writes to stderr. */ + +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 +# define GC_ASSERT(expr) if(!(expr)) {\ + GC_err_printf2("Assertion failure: %s:%ld\n", \ + __FILE__, (unsigned long)__LINE__); \ + ABORT("assertion failure"); } +# else +# define GC_ASSERT(expr) +# endif + +/* Check a compile time assertion at compile time. The error */ +/* message for failure is a bit baroque, but ... */ +#if defined(mips) && !defined(__GNUC__) +/* DOB: MIPSPro C gets an internal error taking the sizeof an array type. + This code works correctly (ugliness is to avoid "unused var" warnings) */ +# define GC_STATIC_ASSERT(expr) do { if (0) { char j[(expr)? 1 : -1]; j[0]='\0'; j[0]=j[0]; } } while(0) +#else +# define GC_STATIC_ASSERT(expr) sizeof(char[(expr)? 1 : -1]) +#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(GC_PTHREADS) && !defined(GC_SOLARIS_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(GC_LINUX_THREADS) || defined(GC_DGUX386_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 itself uses SIGUSR1 and SIGUSR2. */ +# define SIG_SUSPEND SIGPWR +# endif +# else /* !GC_LINUX_THREADS */ +# if defined(_SIGRTMIN) +# define SIG_SUSPEND _SIGRTMIN + 6 +# else +# define SIG_SUSPEND SIGRTMIN + 6 +# endif +# endif +# endif /* !SIG_SUSPEND */ + +# endif + +# endif /* GC_PRIVATE_H */ diff --git a/boehm-gc/include/private/gcconfig.h b/boehm-gc/include/private/gcconfig.h new file mode 100644 index 000000000..f2b568135 --- /dev/null +++ b/boehm-gc/include/private/gcconfig.h @@ -0,0 +1,2430 @@ +/* + * 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-2004 Hewlett-Packard Development Company, L.P. + * + * 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 header is private to the gc. It is almost always included from + * gc_priv.h. However it is possible to include it by itself if just the + * configuration macros are needed. In that + * case, a few declarations relying on types declared in gc_priv.h will be + * omitted. + */ + +#ifndef GCCONFIG_H + +# define GCCONFIG_H + +# ifndef GC_PRIVATE_H + /* Fake ptr_t declaration, just to avoid compilation errors. */ + /* This avoids many instances if "ifndef GC_PRIVATE_H" below. */ + typedef struct GC_undefined_struct * ptr_t; +# endif + +/* Machine dependent parameters. Some tuning parameters can be found */ +/* near the top of gc_private.h. */ + +/* Machine specific parts contributed by various people. See README file. */ + +/* First a unified test for Linux: */ +# if defined(linux) || defined(__linux__) +# ifndef LINUX +# define LINUX +# endif +# endif + +/* And one for NetBSD: */ +# if defined(__NetBSD__) +# define NETBSD +# endif + +/* And one for OpenBSD: */ +# if defined(__OpenBSD__) +# define OPENBSD +# endif + +/* And one for FreeBSD: */ +# if ( defined(__FreeBSD__) || defined(__FreeBSD_kernel__) ) && !defined(FREEBSD) +# define FREEBSD +# endif + +/* Determine the machine type: */ +# if defined(__arm__) || defined(__thumb__) +# define ARM32 +# if !defined(LINUX) && !defined(NETBSD) +# define NOSYS +# define mach_type_known +# endif +# endif +# if defined(sun) && defined(mc68000) +# define M68K +# define SUNOS4 +# define mach_type_known +# endif +# if defined(hp9000s300) +# define M68K +# define HP +# define mach_type_known +# endif +# if defined(OPENBSD) && defined(m68k) +# define M68K +# define mach_type_known +# endif +# if defined(OPENBSD) && defined(__sparc__) +# define SPARC +# define mach_type_known +# endif +# if defined(NETBSD) && (defined(m68k) || defined(__m68k__)) +# define M68K +# define mach_type_known +# endif +# if defined(NETBSD) && defined(__powerpc__) +# define POWERPC +# define mach_type_known +# endif +# if defined(NETBSD) && (defined(__arm32__) || defined(__arm__)) +# define ARM32 +# define mach_type_known +# endif +# if defined(NETBSD) && defined(__sh__) +# define SH +# define mach_type_known +# endif +# if defined(vax) +# define VAX +# ifdef ultrix +# define ULTRIX +# else +# define BSD +# endif +# define mach_type_known +# endif +# if defined(__NetBSD__) && defined(__vax__) +# define VAX +# define mach_type_known +# endif +# if defined(mips) || defined(__mips) || defined(_mips) +# define MIPS +# if defined(nec_ews) || defined(_nec_ews) +# define EWS4800 +# endif +# if !defined(LINUX) && !defined(EWS4800) && !defined(NETBSD) +# if defined(ultrix) || defined(__ultrix) +# 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(DGUX) && (defined(i386) || defined(__i386__)) +# define I386 +# ifndef _USING_DGUX +# define _USING_DGUX +# endif +# define mach_type_known +# endif +# if defined(sequent) && (defined(i386) || defined(__i386__)) +# define I386 +# define SEQUENT +# define mach_type_known +# endif +# if defined(sun) && (defined(i386) || defined(__i386__)) +# define I386 +# define SUNOS5 +# define mach_type_known +# endif +# if defined(sun) && defined(__amd64) +# define X86_64 +# define SUNOS5 +# define mach_type_known +# endif +# if (defined(__OS2__) || defined(__EMX__)) && defined(__32BIT__) +# define I386 +# define OS2 +# define mach_type_known +# endif +# if defined(ibm032) +# define RT +# define mach_type_known +# endif +# if defined(sun) && (defined(sparc) || defined(__sparc)) +# define SPARC + /* Test for SunOS 5.x */ +# include <errno.h> +# ifdef ECHRNG +# define SUNOS5 +# else +# define SUNOS4 +# endif +# define mach_type_known +# endif +# if defined(sparc) && defined(unix) && !defined(sun) && !defined(linux) \ + && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__FreeBSD__) +# define SPARC +# define DRSNX +# define mach_type_known +# endif +# if defined(_IBMR2) +# 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 +# if defined(_SCO_ELF) +# define SCO_ELF +# else +# define SCO +# endif +# define mach_type_known +# endif +# if defined(_AUX_SOURCE) +# define M68K +# define SYSV +# define mach_type_known +# endif +# if defined(_PA_RISC1_0) || defined(_PA_RISC1_1) || defined(_PA_RISC2_0) \ + || defined(hppa) || defined(__hppa__) +# define HP_PA +# if !defined(LINUX) && !defined(HPUX) +# define HPUX +# endif +# define mach_type_known +# endif +# if defined(__ia64) && defined(_HPUX_SOURCE) +# define IA64 +# ifndef HPUX +# 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(__x86_64__) +# define X86_64 +# define mach_type_known +# endif +# if defined(LINUX) && (defined(__ia64__) || defined(__ia64)) +# define IA64 +# define mach_type_known +# endif +# if defined(LINUX) && defined(__arm__) +# define ARM32 +# define mach_type_known +# endif +# if defined(LINUX) && defined(__cris__) +# ifndef CRIS +# define CRIS +# endif +# define mach_type_known +# endif +# if defined(LINUX) && (defined(powerpc) || defined(__powerpc__) || \ + defined(powerpc64) || defined(__powerpc64__)) +# define POWERPC +# define mach_type_known +# endif +# if defined(FREEBSD) && (defined(powerpc) || defined(__powerpc__)) +# define POWERPC +# define mach_type_known +# endif +# if defined(LINUX) && defined(__mc68000__) +# define M68K +# define mach_type_known +# endif +# 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(LINUX) && defined(__m32r__) +# define M32R +# define mach_type_known +# endif +# if defined(__alpha) || defined(__alpha__) +# define ALPHA +# if !defined(LINUX) && !defined(NETBSD) && !defined(OPENBSD) && !defined(FREEBSD) +# define OSF1 /* a.k.a Digital Unix */ +# endif +# define mach_type_known +# endif +# if defined(_AMIGA) && !defined(AMIGA) +# define AMIGA +# endif +# ifdef AMIGA +# define M68K +# define mach_type_known +# endif +# if defined(THINK_C) || defined(__MWERKS__) && !defined(__powerc) +# define M68K +# define MACOS +# define mach_type_known +# endif +# if defined(__MWERKS__) && defined(__powerc) && !defined(__MACH__) +# define POWERPC +# define MACOS +# define mach_type_known +# endif +# if defined(macosx) || (defined(__APPLE__) && defined(__MACH__)) +# define DARWIN +# if defined(__ppc__) || defined(__ppc64__) +# define POWERPC +# define mach_type_known +# elif defined(__x86_64__) +# define X86_64 +# define mach_type_known +# elif defined(__i386__) +# define I386 +# define mach_type_known +# endif +# endif +# if defined(NeXT) && defined(mc68000) +# define M68K +# define NEXT +# define mach_type_known +# endif +# if defined(NeXT) && (defined(i386) || defined(__i386__)) +# define I386 +# define NEXT +# define mach_type_known +# endif +# if defined(__OpenBSD__) && (defined(i386) || defined(__i386__)) +# define I386 +# define OPENBSD +# define mach_type_known +# endif +# if defined(FREEBSD) && (defined(i386) || defined(__i386__)) +# define I386 +# define mach_type_known +# endif +# if defined(FREEBSD) && defined(__x86_64__) +# define X86_64 +# define mach_type_known +# endif +# if defined(__NetBSD__) && (defined(i386) || defined(__i386__)) +# define I386 +# define mach_type_known +# endif +# if defined(__NetBSD__) && defined(__x86_64__) +# define X86_64 +# define mach_type_known +# endif +# if defined(FREEBSD) && defined(__sparc__) +# define SPARC +# define mach_type_known +#endif +# if defined(bsdi) && (defined(i386) || defined(__i386__)) +# define I386 +# define BSDI +# define mach_type_known +# endif +# if !defined(mach_type_known) && defined(__386BSD__) +# define I386 +# define THREE86BSD +# define mach_type_known +# endif +# if defined(_CX_UX) && defined(_M88K) +# define M88K +# define CX_UX +# define mach_type_known +# endif +# if defined(DGUX) && defined(m88k) +# define M88K + /* DGUX defined */ +# define mach_type_known +# endif +# 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 +# if defined(_MSC_VER) && defined(_M_IA64) +# define IA64 +# define MSWIN32 /* Really win64, but we don't treat 64-bit */ + /* variants as a differnt platform. */ +# endif +# endif +# if defined(__DJGPP__) +# define I386 +# ifndef DJGPP +# define DJGPP /* MSDOS running the DJGPP port of GCC */ +# endif +# define mach_type_known +# endif +# if defined(__CYGWIN32__) || defined(__CYGWIN__) +# define I386 +# 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 +# define mach_type_known +# endif +# if defined(_UTS) && !defined(mach_type_known) +# define S370 +# 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 +# if !defined(OS2) && !defined(MSWIN32) && !defined(DOS4GW) +# if defined(__OS2__) +# define OS2 +# else +# if defined(__WINDOWS_386__) || defined(__NT__) +# define MSWIN32 +# else +# define DOS4GW +# endif +# endif +# endif +# define mach_type_known +# endif +# if defined(__s390__) && defined(LINUX) +# define S390 +# 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 */ + +/* Or manually define the machine type here. A machine type is */ +/* characterized by the architecture. Some */ +/* machine types are further subdivided by OS. */ +/* the macros ULTRIX, RISCOS, and BSD to distinguish. */ +/* Note that SGI IRIX is treated identically to RISCOS. */ +/* SYSV on an M68K actually means A/UX. */ +/* The distinction in these cases is usually the stack starting address */ +# ifndef mach_type_known + --> unknown machine type +# endif + /* Mapping is: M68K ==> Motorola 680X0 */ + /* (SUNOS4,HP,NEXT, and SYSV (A/UX), */ + /* MACOS and AMIGA variants) */ + /* I386 ==> Intel 386 */ + /* (SEQUENT, OS2, SCO, LINUX, NETBSD, */ + /* FREEBSD, THREE86BSD, MSWIN32, */ + /* BSDI,SUNOS5, NEXT, other variants) */ + /* NS32K ==> Encore Multimax */ + /* MIPS ==> R2000 or R3000 */ + /* (RISCOS, ULTRIX variants) */ + /* VAX ==> DEC VAX */ + /* (BSD, ULTRIX variants) */ + /* RS6000 ==> IBM RS/6000 AIX3.X */ + /* RT ==> IBM PC/RT */ + /* HP_PA ==> HP9000/700 & /800 */ + /* HP/UX, LINUX */ + /* SPARC ==> SPARC v7/v8/v9 */ + /* (SUNOS4, SUNOS5, LINUX, */ + /* DRSNX variants) */ + /* ALPHA ==> DEC Alpha */ + /* (OSF1 and LINUX variants) */ + /* M88K ==> Motorola 88XX0 */ + /* (CX_UX and DGUX) */ + /* S370 ==> 370-like machine */ + /* running Amdahl UTS4 */ + /* S390 ==> 390-like machine */ + /* running LINUX */ + /* ARM32 ==> Intel StrongARM */ + /* IA64 ==> Intel IPF */ + /* (e.g. Itanium) */ + /* (LINUX and HPUX) */ + /* SH ==> Hitachi SuperH */ + /* (LINUX & MSWINCE) */ + /* X86_64 ==> AMD x86-64 */ + /* POWERPC ==> IBM/Apple PowerPC */ + /* (MACOS(<=9),DARWIN(incl.MACOSX),*/ + /* LINUX, NETBSD, NOSYS variants) */ + /* Handles 32 and 64-bit variants. */ + /* AIX should be handled here, but */ + /* that's called an RS6000. */ + /* CRIS ==> Axis Etrax */ + /* M32R ==> Renesas M32R */ + + +/* + * For each architecture and OS, the following need to be defined: + * + * CPP_WORDSZ is a simple integer constant representing the word size. + * in bits. We assume byte addressibility, where a byte has 8 bits. + * We also assume CPP_WORDSZ is either 32 or 64. + * (We care about the length of pointers, not hardware + * bus widths. Thus a 64 bit processor with a C compiler that uses + * 32 bit pointers should use CPP_WORDSZ of 32, not 64. Default is 32.) + * + * MACH_TYPE is a string representation of the machine type. + * OS_TYPE is analogous for the OS. + * + * ALIGNMENT is the largest N, such that + * all pointer are guaranteed to be aligned on N byte boundaries. + * defining it to be 1 will always work, but perform poorly. + * + * DATASTART is the beginning of the data segment. + * On some platforms SEARCH_FOR_DATA_START is defined. + * SEARCH_FOR_DATASTART will cause GC_data_start to + * be set to an address determined by accessing data backwards from _end + * until an unmapped page is found. DATASTART will be defined to be + * GC_data_start. + * On UNIX-like systems, the collector will scan the area between DATASTART + * and DATAEND for root pointers. + * + * DATAEND, if not `end' where `end' is defined as ``extern int end[];''. + * RTH suggests gaining access to linker script synth'd values with + * this idiom instead of `&end' where `end' is defined as ``extern int end;'' . + * Otherwise, ``GCC will assume these are in .sdata/.sbss'' and it will, e.g., + * cause failures on alpha*-*-* with ``-msmall-data or -fpic'' or mips-*-* + * without any special options. + * + * ALIGN_DOUBLE of GC_malloc should return blocks aligned to twice + * the pointer size. + * + * STACKBOTTOM is the cool end of the stack, which is usually the + * highest address in the stack. + * Under PCR or OS/2, we have other ways of finding thread stacks. + * For each machine, the following should: + * 1) define STACK_GROWS_UP if the stack grows toward higher addresses, and + * 2) define exactly one of + * STACKBOTTOM (should be defined to be an expression) + * LINUX_STACKBOTTOM + * HEURISTIC1 + * HEURISTIC2 + * If STACKBOTTOM is defined, then it's value will be used directly as the + * stack base. If LINUX_STACKBOTTOM is defined, then it will be determined + * with a method appropriate for most Linux systems. Currently we look + * first for __libc_stack_end, and if that fails read it from /proc. + * If either of the last two macros are defined, then STACKBOTTOM is computed + * during collector startup using one of the following two heuristics: + * HEURISTIC1: Take an address inside GC_init's frame, and round it up to + * the next multiple of STACK_GRAN. + * HEURISTIC2: Take an address inside GC_init's frame, increment it repeatedly + * in small steps (decrement if STACK_GROWS_UP), and read the value + * at each location. Remember the value when the first + * Segmentation violation or Bus error is signalled. Round that + * 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: + * 1) GC_mark_roots can be changed to somehow mark from the correct stack(s) + * without reference to STACKBOTTOM. This is appropriate for use in + * conjunction with thread packages, since there will be multiple stacks. + * (Allocating thread stacks in the heap, and treating them as ordinary + * heap data objects is also possible as a last resort. However, this is + * likely to introduce significant amounts of excess storage retention + * unless the dead parts of the thread stacks are periodically cleared.) + * 2) Client code may set GC_stackbottom before calling any GC_ routines. + * If the author of the client code controls the main program, this is + * easily accomplished by introducing a new main program, setting + * GC_stackbottom to the address of a local variable, and then calling + * the original main program. The new main program would read something + * like: + * + * # include "gc_private.h" + * + * main(argc, argv, envp) + * int argc; + * char **argv, **envp; + * { + * int dummy; + * + * GC_stackbottom = (ptr_t)(&dummy); + * return(real_main(argc, argv, envp)); + * } + * + * + * Each architecture may also define the style of virtual dirty bit + * implementation to be used: + * MPROTECT_VDB: Write protect the heap and catch faults. + * PROC_VDB: Use the SVR4 /proc primitives to read dirty bits. + * + * 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. + * + * HEAP_START may be defined as the initial address hint for mmap-based + * allocation. + */ + +/* 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)) \ + && !defined(__INTEL_COMPILER) \ + && !defined(__PATHCC__) +# define HAVE_BUILTIN_UNWIND_INIT +# endif + +# define STACK_GRAN 0x1000000 +# ifdef M68K +# define MACH_TYPE "M68K" +# define ALIGNMENT 2 +# ifdef OPENBSD +# define OS_TYPE "OPENBSD" +# define HEURISTIC2 +# ifdef __ELF__ +# define DATASTART GC_data_start +# define DYNAMIC_LOADING +# else + extern char etext[]; +# define DATASTART ((ptr_t)(etext)) +# endif +# define USE_GENERIC_PUSH_REGS +# 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 +# define USE_GENERIC_PUSH_REGS +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# define LINUX_STACKBOTTOM +# define USE_GENERIC_PUSH_REGS + /* We never got around to the assembly version. */ +# define MPROTECT_VDB +# ifdef __ELF__ +# define DYNAMIC_LOADING +# include <features.h> +# if defined(__GLIBC__)&& __GLIBC__>=2 +# define SEARCH_FOR_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 */ + /* 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 /* !GLIBC2 */ + extern int _end[]; +# define DATAEND (_end) +# else + extern int etext[]; +# define DATASTART ((ptr_t)((((word) (etext)) + 0xfff) & ~0xfff)) +# endif +# endif +# ifdef SUNOS4 +# define OS_TYPE "SUNOS4" + extern char etext[]; +# define DATASTART ((ptr_t)((((word) (etext)) + 0x1ffff) & ~0x1ffff)) +# define HEURISTIC1 /* differs */ +# define DYNAMIC_LOADING +# endif +# ifdef HP +# define OS_TYPE "HP" + extern char etext[]; +# define DATASTART ((ptr_t)((((word) (etext)) + 0xfff) & ~0xfff)) +# define STACKBOTTOM ((ptr_t) 0xffeffffc) + /* empirically determined. seems to work. */ +# include <unistd.h> +# define GETPAGESIZE() sysconf(_SC_PAGE_SIZE) +# endif +# ifdef SYSV +# define OS_TYPE "SYSV" + extern etext[]; +# define DATASTART ((ptr_t)((((word) (etext)) + 0x3fffff) \ + & ~0x3fffff) \ + +((word)etext & 0x1fff)) + /* This only works for shared-text binaries with magic number 0413. + The other sorts of SysV binaries put the data at the end of the text, + in which case the default of etext would work. Unfortunately, + handling both would require having the magic-number available. + -- Parag + */ +# define STACKBOTTOM ((ptr_t)0xFFFFFFFE) + /* The stack starts at the top of memory, but */ + /* 0x0 cannot be used as setjump_test complains */ + /* that the stack direction is incorrect. Two */ + /* bytes down from 0x0 should be safe enough. */ + /* --Parag */ +# include <sys/mmu.h> +# define GETPAGESIZE() PAGESIZE /* Is this still right? */ +# endif +# ifdef AMIGA +# define OS_TYPE "AMIGA" + /* STACKBOTTOM and DATASTART handled specially */ + /* in os_dep.c */ +# define DATAEND /* not needed */ +# define GETPAGESIZE() 4096 +# endif +# ifdef MACOS +# ifndef __LOWMEM__ +# include <LowMem.h> +# endif +# define OS_TYPE "MACOS" + /* see os_dep.c for details of global data segments. */ +# define STACKBOTTOM ((ptr_t) LMGetCurStackBase()) +# define DATAEND /* not needed */ +# define GETPAGESIZE() 4096 +# endif +# ifdef NEXT +# define OS_TYPE "NEXT" +# define DATASTART ((ptr_t) get_etext()) +# define STACKBOTTOM ((ptr_t) 0x4000000) +# define DATAEND /* not needed */ +# endif +# endif + +# if defined(POWERPC) +# define MACH_TYPE "POWERPC" +# ifdef MACOS +# define ALIGNMENT 2 /* Still necessary? Could it be 4? */ +# ifndef __LOWMEM__ +# include <LowMem.h> +# endif +# define OS_TYPE "MACOS" + /* see os_dep.c for details of global data segments. */ +# define STACKBOTTOM ((ptr_t) LMGetCurStackBase()) +# define DATAEND /* not needed */ +# endif +# ifdef LINUX +# if defined(__powerpc64__) +# define ALIGNMENT 8 +# define CPP_WORDSZ 64 +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# else +# define ALIGNMENT 4 +# endif +# define OS_TYPE "LINUX" + /* HEURISTIC1 has been reliably reported to fail for a 32-bit */ + /* executable on a 64 bit kernel. */ +# define LINUX_STACKBOTTOM +# define DYNAMIC_LOADING +# define SEARCH_FOR_DATA_START + extern int _end[]; +# define DATAEND (_end) +# endif +# ifdef DARWIN +# define OS_TYPE "DARWIN" +# define DYNAMIC_LOADING +# if defined(__ppc64__) +# define ALIGNMENT 8 +# define CPP_WORDSZ 64 +# define STACKBOTTOM ((ptr_t) 0x7fff5fc00000) +# define CACHE_LINE_SIZE 64 +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# else +# define ALIGNMENT 4 +# define STACKBOTTOM ((ptr_t) 0xc0000000) +# endif + /* XXX: see get_end(3), get_etext() and get_end() should not be used. + These aren't used when dyld support is enabled (it is by default) */ +# define DATASTART ((ptr_t) get_etext()) +# define DATAEND ((ptr_t) get_end()) +# define USE_MMAP +# define USE_MMAP_ANON +# define USE_ASM_PUSH_REGS +# ifdef GC_DARWIN_THREADS +# define MPROTECT_VDB +# endif +# include <unistd.h> +# define GETPAGESIZE() getpagesize() +# if defined(USE_PPC_PREFETCH) && defined(__GNUC__) + /* The performance impact of prefetches is untested */ +# define PREFETCH(x) \ + __asm__ __volatile__ ("dcbt 0,%0" : : "r" ((const void *) (x))) +# define PREFETCH_FOR_WRITE(x) \ + __asm__ __volatile__ ("dcbtst 0,%0" : : "r" ((const void *) (x))) +# endif + /* There seems to be some issues with trylock hanging on darwin. This + should be looked into some more */ +# define NO_PTHREAD_TRYLOCK +# endif +# ifdef FREEBSD +# define ALIGNMENT 4 +# define OS_TYPE "FREEBSD" +# ifndef GC_FREEBSD_THREADS +# define MPROTECT_VDB +# endif +# define SIG_SUSPEND SIGUSR1 +# define SIG_THR_RESTART SIGUSR2 +# define FREEBSD_STACKBOTTOM +# ifdef __ELF__ +# define DYNAMIC_LOADING +# endif + extern char etext[]; + extern char * GC_FreeBSDGetDataStart(); +# define DATASTART GC_FreeBSDGetDataStart(0x1000, &etext) +# 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 + +# ifdef VAX +# define MACH_TYPE "VAX" +# define ALIGNMENT 4 /* Pointers are longword aligned by 4.2 C compiler */ + extern char etext[]; +# define DATASTART ((ptr_t)(etext)) +# ifdef BSD +# define OS_TYPE "BSD" +# define HEURISTIC1 + /* HEURISTIC2 may be OK, but it's hard to test. */ +# endif +# ifdef ULTRIX +# define OS_TYPE "ULTRIX" +# define STACKBOTTOM ((ptr_t) 0x7fffc800) +# endif +# endif + +# ifdef RT +# define MACH_TYPE "RT" +# define ALIGNMENT 4 +# define DATASTART ((ptr_t) 0x10000000) +# define STACKBOTTOM ((ptr_t) 0x1fffd800) +# endif + +# ifdef SPARC +# define MACH_TYPE "SPARC" +# if defined(__arch64__) || defined(__sparcv9) +# define ALIGNMENT 8 +# define CPP_WORDSZ 64 +# define ELF_CLASS ELFCLASS64 +# else +# define ALIGNMENT 4 /* Required by hardware */ +# define CPP_WORDSZ 32 +# endif +# define ALIGN_DOUBLE +# ifdef SUNOS5 +# define OS_TYPE "SUNOS5" + extern int _etext[]; + extern int _end[]; + extern ptr_t GC_SysVGetDataStart(); +# define DATASTART GC_SysVGetDataStart(0x10000, _etext) +# define DATAEND (_end) +# if !defined(USE_MMAP) && defined(REDIRECT_MALLOC) +# define USE_MMAP + /* Otherwise we now use calloc. Mmap may result in the */ + /* heap interleaved with thread stacks, which can result in */ + /* excessive blacklisting. Sbrk is unusable since it */ + /* doesn't interact correctly with the system malloc. */ +# endif +# ifdef USE_MMAP +# define HEAP_START (ptr_t)0x40000000 +# else +# define HEAP_START DATAEND +# endif +# define PROC_VDB +/* 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 */ + /* Solaris 5.4 installation. Weird. */ +# define DYNAMIC_LOADING +# endif +# ifdef SUNOS4 +# define OS_TYPE "SUNOS4" + /* [If you have a weak stomach, don't read this.] */ + /* We would like to use: */ +/* # define DATASTART ((ptr_t)((((word) (etext)) + 0x1fff) & ~0x1fff)) */ + /* This fails occasionally, due to an ancient, but very */ + /* persistent ld bug. etext is set 32 bytes too high. */ + /* We instead read the text segment size from the a.out */ + /* header, which happens to be mapped into our address space */ + /* at the start of the text segment. The detective work here */ + /* was done by Robert Ehrlich, Manuel Serrano, and Bernard */ + /* Serpette of INRIA. */ + /* This assumes ZMAGIC, i.e. demand-loadable executables. */ +# define TEXTSTART 0x2000 +# define DATASTART ((ptr_t)(*(int *)(TEXTSTART+0x4)+TEXTSTART)) +# define MPROTECT_VDB +# define HEURISTIC1 +# define DYNAMIC_LOADING +# endif +# ifdef DRSNX +# define OS_TYPE "DRSNX" + extern ptr_t GC_SysVGetDataStart(); + extern int etext[]; +# define DATASTART GC_SysVGetDataStart(0x10000, etext) +# define MPROTECT_VDB +# define STACKBOTTOM ((ptr_t) 0xdfff0000) +# define DYNAMIC_LOADING +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# ifdef __ELF__ +# define DYNAMIC_LOADING +# else + Linux Sparc/a.out not supported +# endif + extern int _end[]; + extern int _etext[]; +# define DATAEND (_end) +# define SVR4 + extern ptr_t GC_SysVGetDataStart(); +# ifdef __arch64__ +# define DATASTART GC_SysVGetDataStart(0x100000, _etext) +# else +# define DATASTART GC_SysVGetDataStart(0x10000, _etext) +# endif +# define LINUX_STACKBOTTOM +# 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 +# ifdef FREEBSD +# define OS_TYPE "FREEBSD" +# define SIG_SUSPEND SIGUSR1 +# define SIG_THR_RESTART SIGUSR2 +# define FREEBSD_STACKBOTTOM +# ifdef __ELF__ +# define DYNAMIC_LOADING +# endif + extern char etext[]; + extern char edata[]; + extern char end[]; +# define NEED_FIND_LIMIT +# define DATASTART ((ptr_t)(&etext)) +# define DATAEND (GC_find_limit (DATASTART, TRUE)) +# define DATASTART2 ((ptr_t)(&edata)) +# define DATAEND2 ((ptr_t)(&end)) +# endif +# endif + +# ifdef I386 +# define MACH_TYPE "I386" +# if defined(__LP64__) || defined(_WIN64) +# define CPP_WORDSZ 64 +# define ALIGNMENT 8 +# else +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 + /* Appears to hold for all "32 bit" compilers */ + /* except Borland. The -a4 option fixes */ + /* Borland. */ + /* Ivan Demakov: For Watcom the option is -zp4. */ +# endif +# ifndef SMALL_CONFIG +# 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[], _end[]; + extern ptr_t GC_SysVGetDataStart(); +# define DATASTART GC_SysVGetDataStart(0x1000, _etext) +# define DATAEND (_end) +/* # 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/vm.h> +# define STACKBOTTOM USRSTACK +/* At least in Solaris 2.5, PROC_VDB gives wrong values for dirty bits. */ +/* It appears to be fixed in 2.8 and 2.9. */ +# ifdef SOLARIS25_PROC_VDB_BUG_FIXED +# define PROC_VDB +# endif +# define DYNAMIC_LOADING +# if !defined(USE_MMAP) && defined(REDIRECT_MALLOC) +# define USE_MMAP + /* Otherwise we now use calloc. Mmap may result in the */ + /* heap interleaved with thread stacks, which can result in */ + /* excessive blacklisting. Sbrk is unusable since it */ + /* doesn't interact correctly with the system malloc. */ +# endif +# ifdef USE_MMAP +# define HEAP_START (ptr_t)0x40000000 +# else +# define HEAP_START DATAEND +# endif +# endif +# ifdef SCO +# define OS_TYPE "SCO" + extern int etext[]; +# define DATASTART ((ptr_t)((((word) (etext)) + 0x3fffff) \ + & ~0x3fffff) \ + +((word)etext & 0xfff)) +# define STACKBOTTOM ((ptr_t) 0x7ffffffc) +# endif +# ifdef SCO_ELF +# define OS_TYPE "SCO_ELF" + extern int etext[]; +# define DATASTART ((ptr_t)(etext)) +# define STACKBOTTOM ((ptr_t) 0x08048000) +# define DYNAMIC_LOADING +# define ELF_CLASS ELFCLASS32 +# endif +# ifdef DGUX +# define OS_TYPE "DGUX" + extern int _etext, _end; + extern ptr_t GC_SysVGetDataStart(); +# define DATASTART GC_SysVGetDataStart(0x1000, &_etext) +# define DATAEND (&_end) +# define STACK_GROWS_DOWN +# define HEURISTIC2 +# include <unistd.h> +# define GETPAGESIZE() sysconf(_SC_PAGESIZE) +# define DYNAMIC_LOADING +# ifndef USE_MMAP +# define USE_MMAP +# endif /* USE_MMAP */ +# define MAP_FAILED (void *) -1 +# ifdef USE_MMAP +# define HEAP_START (ptr_t)0x40000000 +# else /* USE_MMAP */ +# define HEAP_START DATAEND +# endif /* USE_MMAP */ +# endif /* DGUX */ + +# ifdef LINUX +# ifndef __GNUC__ + /* The Intel compiler doesn't like inline assembly */ +# define USE_GENERIC_PUSH_REGS +# endif +# define OS_TYPE "LINUX" +# 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(GC_LINUX_THREADS) || !defined(REDIRECT_MALLOC) +# define MPROTECT_VDB +# else + /* We seem to get random errors in incremental mode, */ + /* possibly because Linux threads is itself a malloc client */ + /* and can't deal with the signals. */ +# endif +# define HEAP_START (ptr_t)0x1000 + /* This encourages mmap to give us low addresses, */ + /* thus allowing the heap to grow to ~3GB */ +# ifdef __ELF__ +# define DYNAMIC_LOADING +# ifdef UNDEFINED /* includes ro data */ + extern int _etext[]; +# define DATASTART ((ptr_t)((((word) (_etext)) + 0xfff) & ~0xfff)) +# endif +# include <features.h> +# if defined(__GLIBC__) && __GLIBC__ >= 2 +# define SEARCH_FOR_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 +# ifdef USE_I686_PREFETCH + /* FIXME: Thus should use __builtin_prefetch, but we'll leave that */ + /* for the next rtelease. */ +# 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" +# define DATASTART ((ptr_t)GC_DATASTART) /* From gc.h */ +# define DATAEND ((ptr_t)GC_DATAEND) +# undef STACK_GRAN +# define STACK_GRAN 0x10000 +# define HEURISTIC1 +# endif +# ifdef OS2 +# define OS_TYPE "OS2" + /* STACKBOTTOM and DATASTART are handled specially in */ + /* 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" + /* STACKBOTTOM and DATASTART are handled specially in */ + /* os_dep.c. */ +# ifndef __WATCOMC__ +# define MPROTECT_VDB +# 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" + extern int etext[]; + extern int _stklen; + extern int __djgpp_stack_limit; +# define DATASTART ((ptr_t)((((word) (etext)) + 0x1ff) & ~0x1ff)) +/* # define STACKBOTTOM ((ptr_t)((word) _stubinfo + _stubinfo->size \ + + _stklen)) */ +# define STACKBOTTOM ((ptr_t)((word) __djgpp_stack_limit + _stklen)) + /* This may not be right. */ +# endif +# ifdef OPENBSD +# define OS_TYPE "OPENBSD" +# endif +# ifdef FREEBSD +# define OS_TYPE "FREEBSD" +# ifndef GC_FREEBSD_THREADS +# define MPROTECT_VDB +# endif +# ifdef __GLIBC__ +# define SIG_SUSPEND (32+6) +# define SIG_THR_RESTART (32+5) + extern int _end[]; +# define DATAEND (_end) +# else +# define SIG_SUSPEND SIGUSR1 +# define SIG_THR_RESTART SIGUSR2 +# endif +# define FREEBSD_STACKBOTTOM +# ifdef __ELF__ +# define DYNAMIC_LOADING +# endif + extern char etext[]; + extern char * GC_FreeBSDGetDataStart(); +# define DATASTART GC_FreeBSDGetDataStart(0x1000, &etext) +# endif +# ifdef NETBSD +# define OS_TYPE "NETBSD" +# ifdef __ELF__ +# define DYNAMIC_LOADING +# endif +# endif +# ifdef THREE86BSD +# define OS_TYPE "THREE86BSD" +# endif +# ifdef BSDI +# define OS_TYPE "BSDI" +# endif +# if defined(OPENBSD) || defined(NETBSD) \ + || defined(THREE86BSD) || defined(BSDI) +# define HEURISTIC2 + extern char etext[]; +# define DATASTART ((ptr_t)(etext)) +# endif +# ifdef NEXT +# define OS_TYPE "NEXT" +# define DATASTART ((ptr_t) get_etext()) +# define STACKBOTTOM ((ptr_t)0xc0000000) +# define DATAEND /* not needed */ +# endif +# ifdef DOS4GW +# define OS_TYPE "DOS4GW" + extern long __nullarea; + extern char _end; + extern char *_STACKTOP; + /* Depending on calling conventions Watcom C either precedes + or does not precedes with undescore names of C-variables. + Make sure startup code variables always have the same names. */ + #pragma aux __nullarea "*"; + #pragma aux _end "*"; +# define STACKBOTTOM ((ptr_t) _STACKTOP) + /* confused? me too. */ +# 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 +# ifdef DARWIN +# define OS_TYPE "DARWIN" +# define DARWIN_DONT_PARSE_STACK +# define DYNAMIC_LOADING + /* XXX: see get_end(3), get_etext() and get_end() should not be used. + These aren't used when dyld support is enabled (it is by default) */ +# define DATASTART ((ptr_t) get_etext()) +# define DATAEND ((ptr_t) get_end()) +# ifdef HAVE_PTHREAD_GET_STACKADDR_NP +# define STACKBOTTOM (ptr_t)pthread_get_stackaddr_np(pthread_self()) +# else +# define STACKBOTTOM ((ptr_t) 0xc0000000) +# endif +# define USE_MMAP +# define USE_MMAP_ANON +# define USE_ASM_PUSH_REGS +# ifdef GC_DARWIN_THREADS +# define MPROTECT_VDB +# endif +# include <unistd.h> +# define GETPAGESIZE() getpagesize() + /* There seems to be some issues with trylock hanging on darwin. This + should be looked into some more */ +# define NO_PTHREAD_TRYLOCK +# endif /* DARWIN */ +# endif + +# ifdef NS32K +# define MACH_TYPE "NS32K" +# define ALIGNMENT 4 + 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. */ +# define STACKBOTTOM ((ptr_t) 0xfffff000) /* for Encore */ +# endif + +# ifdef MIPS +# define MACH_TYPE "MIPS" +# ifdef LINUX + /* This was developed for a linuxce style platform. Probably */ + /* needs to be tweaked for workstation class machines. */ +# define OS_TYPE "LINUX" +# define DYNAMIC_LOADING + extern int _end[]; +# define DATAEND (_end) + extern int __data_start[]; +# define DATASTART ((ptr_t)(__data_start)) +# ifdef _MIPS_SZPTR +# define CPP_WORDSZ _MIPS_SZPTR +# define ALIGNMENT (_MIPS_SZPTR/8) +# else +# define ALIGNMENT 4 +# endif +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# define USE_GENERIC_PUSH_REGS +# if __GLIBC__ == 2 && __GLIBC_MINOR__ >= 2 || __GLIBC__ > 2 +# define LINUX_STACKBOTTOM +# else +# define STACKBOTTOM 0x80000000 +# endif +# endif /* Linux */ +# ifdef EWS4800 +# define HEURISTIC2 +# if defined(_MIPS_SZPTR) && (_MIPS_SZPTR == 64) + extern int _fdata[], _end[]; +# define DATASTART ((ptr_t)_fdata) +# define DATAEND ((ptr_t)_end) +# define CPP_WORDSZ _MIPS_SZPTR +# define ALIGNMENT (_MIPS_SZPTR/8) +# else + extern int etext[], edata[], end[]; + extern int _DYNAMIC_LINKING[], _gp[]; +# define DATASTART ((ptr_t)((((word)etext + 0x3ffff) & ~0x3ffff) \ + + ((word)etext & 0xffff))) +# define DATAEND (edata) +# define DATASTART2 (_DYNAMIC_LINKING \ + ? (ptr_t)(((word)_gp + 0x8000 + 0x3ffff) & ~0x3ffff) \ + : (ptr_t)edata) +# define DATAEND2 (end) +# define ALIGNMENT 4 +# endif +# define OS_TYPE "EWS4800" +# define USE_GENERIC_PUSH_REGS 1 +# endif +# ifdef ULTRIX +# define HEURISTIC2 +# define DATASTART (ptr_t)0x10000000 + /* Could probably be slightly higher since */ + /* startup code allocates lots of stuff. */ +# 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 DOB: this should work, but there is evidence */ +/* of recent breakage. */ +# ifdef _MIPS_SZPTR +# define CPP_WORDSZ _MIPS_SZPTR +# define ALIGNMENT (_MIPS_SZPTR/8) +# if CPP_WORDSZ != 64 +# define ALIGN_DOUBLE +# endif +# else +# define ALIGNMENT 4 +# define ALIGN_DOUBLE +# endif +# define DYNAMIC_LOADING +# endif +# ifdef MSWINCE +# define OS_TYPE "MSWINCE" +# define ALIGNMENT 4 +# define DATAEND /* not needed */ +# endif +# if defined(NETBSD) +# 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 +# define MACH_TYPE "RS6000" +# ifdef ALIGNMENT +# undef ALIGNMENT +# endif +# ifdef IA64 +# undef IA64 /* DOB: some AIX installs stupidly define IA64 in /usr/include/sys/systemcfg.h */ +# endif +# ifdef __64BIT__ +# define ALIGNMENT 8 +# define CPP_WORDSZ 64 +# define STACKBOTTOM ((ptr_t)0x1000000000000000) +# else +# define ALIGNMENT 4 +# define CPP_WORDSZ 32 +# define STACKBOTTOM ((ptr_t)((ulong)&errno)) +# endif +# define USE_MMAP +# define USE_MMAP_ANON + /* From AIX linker man page: + _text Specifies the first location of the program. + _etext Specifies the first location after the program. + _data Specifies the first location of the data. + _edata Specifies the first location after the initialized data + _end or end Specifies the first location after all data. + */ + extern int _data[], _end[]; +# define DATASTART ((ptr_t)((ulong)_data)) +# define DATAEND ((ptr_t)((ulong)_end)) + extern int errno; +# define USE_GENERIC_PUSH_REGS +# define DYNAMIC_LOADING + /* For really old versions of AIX, this may have to be removed. */ +# endif + +# ifdef HP_PA +# define MACH_TYPE "HP_PA" +# 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(GC_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 +# define OS_TYPE "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 */ + /* segments that directly precede the stack. It is not the */ + /* default, since it may not work on older machine/OS */ + /* combinations. (Thanks to Raymond X.T. Nijssen for uncovering */ + /* this.) */ +# define STACKBOTTOM ((ptr_t) 0x7b033000) /* from /etc/conf/h/param.h */ +# 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 SEARCH_FOR_DATA_START + extern int _end[]; +# define DATAEND (&_end) +# endif /* LINUX */ +# endif /* HP_PA */ + +# ifdef ALPHA +# define MACH_TYPE "ALPHA" +# define ALIGNMENT 8 +# define CPP_WORDSZ 64 +# ifndef LINUX +# 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. */ +# endif +# ifdef NETBSD +# define OS_TYPE "NETBSD" +# define HEURISTIC2 +# define DATASTART GC_data_start +# define ELFCLASS32 32 +# define ELFCLASS64 64 +# define ELF_CLASS ELFCLASS64 +# define DYNAMIC_LOADING +# endif +# ifdef OPENBSD +# define OS_TYPE "OPENBSD" +# define HEURISTIC2 +# ifdef __ELF__ /* since OpenBSD/Alpha 2.9 */ +# define DATASTART GC_data_start +# define ELFCLASS32 32 +# define ELFCLASS64 64 +# define ELF_CLASS ELFCLASS64 +# else /* ECOFF, until OpenBSD/Alpha 2.7 */ +# define DATASTART ((ptr_t) 0x140000000) +# endif +# endif +# ifdef FREEBSD +# define OS_TYPE "FREEBSD" +/* MPROTECT_VDB is not yet supported at all on FreeBSD/alpha. */ +# define SIG_SUSPEND SIGUSR1 +# define SIG_THR_RESTART SIGUSR2 +# define FREEBSD_STACKBOTTOM +# ifdef __ELF__ +# define DYNAMIC_LOADING +# endif +/* Handle unmapped hole alpha*-*-freebsd[45]* puts between etext and edata. */ + extern char etext[]; + extern char edata[]; + extern char end[]; +# define NEED_FIND_LIMIT +# define DATASTART ((ptr_t)(&etext)) +# define DATAEND (GC_find_limit (DATASTART, TRUE)) +# define DATASTART2 ((ptr_t)(&edata)) +# define DATAEND2 ((ptr_t)(&end)) +# endif +# ifdef OSF1 +# define OS_TYPE "OSF1" +# define DATASTART ((ptr_t) 0x140000000) + extern int _end[]; +# define DATAEND ((ptr_t) &_end) + 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))) +# ifndef GC_OSF1_THREADS + /* Unresolved signal issues with threads. */ +# define MPROTECT_VDB +# endif +# define DYNAMIC_LOADING +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# define LINUX_STACKBOTTOM +# ifdef __ELF__ +# define SEARCH_FOR_DATA_START +# define DYNAMIC_LOADING +# else +# define DATASTART ((ptr_t) 0x140000000) +# endif + extern int _end[]; +# define DATAEND (_end) +# define MPROTECT_VDB + /* Has only been superficially tested. May not */ + /* work on all versions. */ +# endif +# endif + +# ifdef IA64 +# define MACH_TYPE "IA64" +# 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 +# ifdef _ILP32 +# define CPP_WORDSZ 32 +# define ALIGN_DOUBLE + /* Requires 8 byte alignment for malloc */ +# define ALIGNMENT 4 +# else +# ifndef _LP64 + ---> unknown ABI +# endif +# define CPP_WORDSZ 64 +# define ALIGN_DOUBLE + /* Requires 16 byte alignment for malloc */ +# define ALIGNMENT 8 +# endif +# define OS_TYPE "HPUX" + extern int __data_start[]; +# define DATASTART ((ptr_t)(__data_start)) + /* 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) +# define HPUX_STACKBOTTOM +# define DYNAMIC_LOADING +# include <unistd.h> +# define GETPAGESIZE() sysconf(_SC_PAGE_SIZE) + /* The following was empirically determined, and is probably */ + /* not very robust. */ + /* Note that the backing store base seems to be at a nice */ + /* address minus one page. */ +# define BACKING_STORE_DISPLACEMENT 0x1000000 +# define BACKING_STORE_ALIGNMENT 0x1000 + extern ptr_t GC_register_stackbottom; +# define BACKING_STORE_BASE GC_register_stackbottom + /* Known to be wrong for recent HP/UX versions!!! */ +# endif +# ifdef LINUX +# define CPP_WORDSZ 64 +# define ALIGN_DOUBLE + /* Requires 16 byte alignment for malloc */ +# define ALIGNMENT 8 +# define OS_TYPE "LINUX" + /* 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 ptr_t GC_register_stackbottom; +# define BACKING_STORE_BASE GC_register_stackbottom +# define SEARCH_FOR_DATA_START +# ifdef __GNUC__ +# define DYNAMIC_LOADING +# else + /* In the Intel compiler environment, we seem to end up with */ + /* statically linked executables and an undefined reference */ + /* to _DYNAMIC */ +# endif +# define MPROTECT_VDB + /* Requires Linux 2.3.47 or later. */ + extern int _end[]; +# define DATAEND (_end) +# ifdef __GNUC__ +# ifndef __INTEL_COMPILER +# define PREFETCH(x) \ + __asm__ (" lfetch [%0]": : "r"(x)) +# define PREFETCH_FOR_WRITE(x) \ + __asm__ (" lfetch.excl [%0]": : "r"(x)) +# define CLEAR_DOUBLE(x) \ + __asm__ (" stf.spill [%0]=f0": : "r"((void *)(x))) +# else +# include <ia64intrin.h> +# define PREFETCH(x) \ + __lfetch(__lfhint_none, (x)) +# define PREFETCH_FOR_WRITE(x) \ + __lfetch(__lfhint_nta, (x)) +# define CLEAR_DOUBLE(x) \ + __stf_spill((void *)(x), 0) +# endif // __INTEL_COMPILER +# endif +# endif +# ifdef MSWIN32 + /* FIXME: This is a very partial guess. There is no port, yet. */ +# define OS_TYPE "MSWIN32" + /* STACKBOTTOM and DATASTART are handled specially in */ + /* os_dep.c. */ +# define DATAEND /* not needed */ +# if defined(_WIN64) +# define CPP_WORDSZ 64 +# else +# define CPP_WORDSZ 32 /* Is this possible? */ +# endif +# define ALIGNMENT 8 +# endif +# endif + +# ifdef M88K +# define MACH_TYPE "M88K" +# define ALIGNMENT 4 +# define ALIGN_DOUBLE + extern int etext[]; +# ifdef CX_UX +# define OS_TYPE "CX_UX" +# define DATASTART ((((word)etext + 0x3fffff) & ~0x3fffff) + 0x10000) +# endif +# ifdef DGUX +# define OS_TYPE "DGUX" + extern ptr_t GC_SysVGetDataStart(); +# define DATASTART GC_SysVGetDataStart(0x10000, etext) +# endif +# define STACKBOTTOM ((char*)0xf0000000) /* determined empirically */ +# endif + +# ifdef S370 + /* If this still works, and if anyone cares, this should probably */ + /* be moved to the S390 category. */ +# define MACH_TYPE "S370" +# define ALIGNMENT 4 /* Required by hardware */ +# define USE_GENERIC_PUSH_REGS +# ifdef UTS4 +# define OS_TYPE "UTS4" + extern int etext[]; + extern int _etext[]; + extern int _end[]; + extern ptr_t GC_SysVGetDataStart(); +# define DATASTART GC_SysVGetDataStart(0x10000, _etext) +# define DATAEND (_end) +# define HEURISTIC2 +# endif +# endif + +# ifdef S390 +# define MACH_TYPE "S390" +# define USE_GENERIC_PUSH_REGS +# ifndef __s390x__ +# define ALIGNMENT 4 +# define CPP_WORDSZ 32 +# else +# define ALIGNMENT 8 +# define CPP_WORDSZ 64 +# endif +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# define LINUX_STACKBOTTOM +# define DYNAMIC_LOADING + extern int __data_start[]; +# define DATASTART ((ptr_t)(__data_start)) + extern int _end[]; +# define DATAEND (_end) +# define CACHE_LINE_SIZE 256 +# define GETPAGESIZE() 4096 +# 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 +# ifdef __ELF__ +# define DATASTART GC_data_start +# define DYNAMIC_LOADING +# else + extern char etext[]; +# define DATASTART ((ptr_t)(etext)) +# endif +# define USE_GENERIC_PUSH_REGS +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# define LINUX_STACKBOTTOM +# 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 SEARCH_FOR_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 +# ifdef NOSYS + /* __data_start is usually defined in the target linker script. */ + extern int __data_start[]; +# define DATASTART (ptr_t)(__data_start) +# define USE_GENERIC_PUSH_REGS + /* __stack_base__ is set in newlib/libc/sys/arm/crt0.S */ + extern void *__stack_base__; +# define STACKBOTTOM ((ptr_t) (__stack_base__)) +# endif +#endif + +# ifdef CRIS +# define MACH_TYPE "CRIS" +# define CPP_WORDSZ 32 +# define ALIGNMENT 1 +# define OS_TYPE "LINUX" +# define DYNAMIC_LOADING +# define LINUX_STACKBOTTOM +# define USE_GENERIC_PUSH_REGS +# define SEARCH_FOR_DATA_START + extern int _end[]; +# define DATAEND (_end) +# 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 LINUX_STACKBOTTOM +# define USE_GENERIC_PUSH_REGS +# define DYNAMIC_LOADING +# define SEARCH_FOR_DATA_START + extern int _end[]; +# define DATAEND (_end) +# endif +# ifdef NETBSD +# define OS_TYPE "NETBSD" +# define HEURISTIC2 +# define DATASTART GC_data_start +# define USE_GENERIC_PUSH_REGS +# define DYNAMIC_LOADING +# endif +# endif + +# ifdef SH4 +# define MACH_TYPE "SH4" +# define OS_TYPE "MSWINCE" +# define ALIGNMENT 4 +# define DATAEND /* not needed */ +# endif + +# ifdef M32R +# define CPP_WORDSZ 32 +# define MACH_TYPE "M32R" +# define ALIGNMENT 4 +# ifdef LINUX +# define OS_TYPE "LINUX" +# define LINUX_STACKBOTTOM +# undef STACK_GRAN +# define STACK_GRAN 0x10000000 +# define USE_GENERIC_PUSH_REGS +# define DYNAMIC_LOADING +# define SEARCH_FOR_DATA_START + extern int _end[]; +# define DATAEND (_end) +# endif +# endif + +# ifdef X86_64 +# define MACH_TYPE "X86_64" +# define ALIGNMENT 8 +# define CPP_WORDSZ 64 +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# define CACHE_LINE_SIZE 64 +# define USE_GENERIC_PUSH_REGS +# ifdef LINUX +# define OS_TYPE "LINUX" +# define LINUX_STACKBOTTOM +# if !defined(GC_LINUX_THREADS) || !defined(REDIRECT_MALLOC) +# define MPROTECT_VDB +# else + /* We seem to get random errors in incremental mode, */ + /* possibly because Linux threads is itself a malloc client */ + /* and can't deal with the signals. */ +# endif +# ifdef __ELF__ +# define DYNAMIC_LOADING +# ifdef UNDEFINED /* includes ro data */ + extern int _etext[]; +# define DATASTART ((ptr_t)((((word) (_etext)) + 0xfff) & ~0xfff)) +# endif +# include <features.h> +# define SEARCH_FOR_DATA_START + extern int _end[]; +# define DATAEND (_end) +# else + extern int etext[]; +# define DATASTART ((ptr_t)((((word) (etext)) + 0xfff) & ~0xfff)) +# endif +# if defined(__GNUC__) && __GNUC >= 3 +# define PREFETCH(x) __builtin_prefetch((x), 0, 0) +# define PREFETCH_FOR_WRITE(x) __builtin_prefetch((x), 1) +# endif +# endif +# ifdef DARWIN +# define OS_TYPE "DARWIN" +# define DARWIN_DONT_PARSE_STACK +# define DYNAMIC_LOADING + /* XXX: see get_end(3), get_etext() and get_end() should not be used. + These aren't used when dyld support is enabled (it is by default) */ +# define DATASTART ((ptr_t) get_etext()) +# define DATAEND ((ptr_t) get_end()) +# ifdef HAVE_PTHREAD_GET_STACKADDR_NP +# define STACKBOTTOM (ptr_t)pthread_get_stackaddr_np(pthread_self()) +# else +# define STACKBOTTOM ((ptr_t) 0x7fff5fc00000) +# endif +# define USE_MMAP +# define USE_MMAP_ANON +# ifdef GC_DARWIN_THREADS +# define MPROTECT_VDB +# endif +# include <unistd.h> +# define GETPAGESIZE() getpagesize() + /* There seems to be some issues with trylock hanging on darwin. This + should be looked into some more */ +# define NO_PTHREAD_TRYLOCK +# endif +# ifdef FREEBSD +# define OS_TYPE "FREEBSD" +# ifndef GC_FREEBSD_THREADS +# define MPROTECT_VDB +# endif +# ifdef __GLIBC__ +# define SIG_SUSPEND (32+6) +# define SIG_THR_RESTART (32+5) + extern int _end[]; +# define DATAEND (_end) +# else +# define SIG_SUSPEND SIGUSR1 +# define SIG_THR_RESTART SIGUSR2 +# endif +# define FREEBSD_STACKBOTTOM +# ifdef __ELF__ +# define DYNAMIC_LOADING +# endif + extern char etext[]; + extern char * GC_FreeBSDGetDataStart(); +# define DATASTART GC_FreeBSDGetDataStart(0x1000, &etext) +# endif +# ifdef NETBSD +# define OS_TYPE "NETBSD" +# ifdef __ELF__ +# define DYNAMIC_LOADING +# endif +# define HEURISTIC2 + extern char etext[]; +# define SEARCH_FOR_DATA_START +# endif +# ifdef SUNOS5 +# define ELF_CLASS ELFCLASS64 +# define OS_TYPE "SUNOS5" + extern int _etext[], _end[]; + extern ptr_t GC_SysVGetDataStart(); +# define DATASTART GC_SysVGetDataStart(0x1000, _etext) +# define DATAEND (_end) +/* # 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/vm.h> */ +/* # define STACKBOTTOM USRSTACK */ +# define HEURISTIC2 +# define PROC_VDB +# define DYNAMIC_LOADING +# if !defined(USE_MMAP) && defined(REDIRECT_MALLOC) +# define USE_MMAP + /* Otherwise we now use calloc. Mmap may result in the */ + /* heap interleaved with thread stacks, which can result in */ + /* excessive blacklisting. Sbrk is unusable since it */ + /* doesn't interact correctly with the system malloc. */ +# endif +# ifdef USE_MMAP +# define HEAP_START (ptr_t)0x40000000 +# else +# define HEAP_START DATAEND +# endif +# endif +# endif + +#if defined(LINUX) && defined(USE_MMAP) + /* The kernel may do a somewhat better job merging mappings etc. */ + /* with anonymous mappings. */ +# define USE_MMAP_ANON +#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 + +# ifndef CPP_WORDSZ +# define CPP_WORDSZ 32 +# endif + +# ifndef OS_TYPE +# define OS_TYPE "" +# endif + +# ifndef DATAEND + extern int end[]; +# define DATAEND (end) +# endif + +# if defined(SVR4) && !defined(GETPAGESIZE) +# include <unistd.h> +# define GETPAGESIZE() sysconf(_SC_PAGESIZE) +# endif + +# ifndef GETPAGESIZE +# if defined(SUNOS5) || defined(IRIX5) +# include <unistd.h> +# endif +# define GETPAGESIZE() getpagesize() +# endif + +# if defined(SUNOS5) || defined(DRSNX) || defined(UTS4) + /* OS has SVR4 generic features. Probably others also qualify. */ +# define SVR4 +# endif + +# if defined(SUNOS5) || defined(DRSNX) + /* OS has SUNOS5 style semi-undocumented interface to dynamic */ + /* loader. */ +# define SUNOS5DL + /* OS has SUNOS5 style signal handlers. */ +# define SUNOS5SIGS +# endif + +# if defined(HPUX) +# define SUNOS5SIGS +# endif + +# if defined(FREEBSD) && ((__FreeBSD__ >= 4) || (__FreeBSD_kernel__ >= 4)) +# define SUNOS5SIGS +# endif + +# if defined(SVR4) || defined(LINUX) || defined(IRIX5) || defined(HPUX) \ + || defined(OPENBSD) || defined(NETBSD) || defined(FREEBSD) \ + || defined(DGUX) || defined(BSD) || defined(SUNOS4) \ + || defined(_AIX) || defined(DARWIN) || defined(OSF1) +# define UNIX_LIKE /* Basic Unix-like system calls work. */ +# endif + +# if CPP_WORDSZ != 32 && CPP_WORDSZ != 64 + -> bad word size +# endif + +# ifdef PCR +# undef DYNAMIC_LOADING +# undef STACKBOTTOM +# undef HEURISTIC1 +# undef HEURISTIC2 +# undef PROC_VDB +# undef MPROTECT_VDB +# define PCR_VDB +# endif + +# ifdef SRC_M3 + /* Postponed for now. */ +# undef PROC_VDB +# undef MPROTECT_VDB +# endif + +# ifdef SMALL_CONFIG + /* Presumably not worth the space it takes. */ +# undef PROC_VDB +# undef MPROTECT_VDB +# endif + +# ifdef USE_MUNMAP +# 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 + +# if defined(LINUX) || defined(__GLIBC__) +# define REGISTER_LIBRARIES_EARLY + /* We sometimes use dl_iterate_phdr, which may acquire an internal */ + /* lock. This isn't safe after the world has stopped. So we must */ + /* call GC_register_dynamic_libraries before stopping the world. */ + /* For performance reasons, this may be beneficial on other */ + /* platforms as well, though it should be avoided in win32. */ +# endif /* LINUX */ + +# if defined(SEARCH_FOR_DATA_START) + extern ptr_t GC_data_start; +# define DATASTART GC_data_start +# endif + +# ifndef CLEAR_DOUBLE +# define CLEAR_DOUBLE(x) \ + ((word*)x)[0] = 0; \ + ((word*)x)[1] = 0; +# endif /* CLEAR_DOUBLE */ + +# if defined(GC_IRIX_THREADS) && !defined(IRIX5) + --> inconsistent configuration +# endif +# if defined(GC_LINUX_THREADS) && !defined(LINUX) + --> inconsistent configuration +# endif +# if defined(GC_SOLARIS_THREADS) && !defined(SUNOS5) + --> inconsistent configuration +# endif +# if defined(GC_HPUX_THREADS) && !defined(HPUX) + --> inconsistent configuration +# endif +# if defined(GC_AIX_THREADS) && !defined(_AIX) + --> inconsistent configuration +# endif +# if defined(GC_WIN32_THREADS) && !defined(MSWIN32) && !defined(CYGWIN32) + --> inconsistent configuration +# endif + +# if defined(PCR) || defined(SRC_M3) || \ + defined(GC_SOLARIS_THREADS) || defined(GC_WIN32_THREADS) || \ + defined(GC_PTHREADS) +# define THREADS +# endif + +# if defined(HP_PA) || defined(M88K) \ + || defined(POWERPC) && !defined(DARWIN) \ + || defined(LINT) || defined(MSWINCE) || defined(ARM32) || defined(CRIS) \ + || (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(MSWINCE) +# define NO_GETENV +# endif + +# if defined(SPARC) +# define ASM_CLEAR_CODE /* Stack clearing is crucial, and we */ + /* include assembly code to do it well. */ +# endif + + /* Can we save call chain in objects for debugging? */ + /* SET NFRAMES (# of saved frames) and NARGS (#of args for each */ + /* frame) to reasonable values for the platform. */ + /* Set SAVE_CALL_CHAIN if we can. SAVE_CALL_COUNT can be specified */ + /* at build time, though we feel free to adjust it slightly. */ + /* Define NEED_CALLINFO if we either save the call stack or */ + /* GC_ADD_CALLER is defined. */ + /* GC_CAN_SAVE_CALL_STACKS is set in gc.h. */ + +#if defined(SPARC) +# define CAN_SAVE_CALL_ARGS +#endif +#if (defined(I386) || defined(X86_64)) && (defined(LINUX) || defined(__GLIBC__)) + /* SAVE_CALL_CHAIN is supported if the code is compiled to save */ + /* frame pointers by default, i.e. no -fomit-frame-pointer flag. */ +# define CAN_SAVE_CALL_ARGS +#endif + +# if defined(SAVE_CALL_COUNT) && !defined(GC_ADD_CALLER) \ + && defined(GC_CAN_SAVE_CALL_STACKS) +# define SAVE_CALL_CHAIN +# endif +# ifdef SAVE_CALL_CHAIN +# if defined(SAVE_CALL_NARGS) && defined(CAN_SAVE_CALL_ARGS) +# define NARGS SAVE_CALL_NARGS +# else +# define NARGS 0 /* Number of arguments to save for each call. */ +# endif +# endif +# ifdef SAVE_CALL_CHAIN +# 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 NEED_CALLINFO +# endif /* SAVE_CALL_CHAIN */ +# ifdef GC_ADD_CALLER +# define NFRAMES 1 +# define NARGS 0 +# define NEED_CALLINFO +# endif + +# if defined(MAKE_BACK_GRAPH) && !defined(DBG_HDRS_ALL) +# define DBG_HDRS_ALL +# endif + +# if defined(POINTER_MASK) && !defined(POINTER_SHIFT) +# define POINTER_SHIFT 0 +# endif + +# if defined(POINTER_SHIFT) && !defined(POINTER_MASK) +# define POINTER_MASK ((GC_word)(-1)) +# endif + +# if !defined(FIXUP_POINTER) && defined(POINTER_MASK) +# define FIXUP_POINTER(p) (p) = ((p) & (POINTER_MASK) << POINTER_SHIFT) +# endif + +# if defined(FIXUP_POINTER) +# define NEED_FIXUP_POINTER 1 +# else +# define NEED_FIXUP_POINTER 0 +# define FIXUP_POINTER(p) +# endif + +#ifdef GC_PRIVATE_H + /* This relies on some type definitions from gc_priv.h, from */ + /* where it's normally included. */ + /* */ + /* How to get heap memory from the OS: */ + /* Note that sbrk()-like allocation is preferred, since it */ + /* usually makes it possible to merge consecutively allocated */ + /* chunks. It also avoids unintented recursion with */ + /* -DREDIRECT_MALLOC. */ + /* GET_MEM() returns a HLKSIZE aligned chunk. */ + /* 0 is taken to mean failure. */ + /* In the case os USE_MMAP, the argument must also be a */ + /* physical page size. */ + /* 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 gc_priv.h. */ +# ifdef PCR + char * real_malloc(); +# define GET_MEM(bytes) HBLKPTR(real_malloc((size_t)bytes + GC_page_size) \ + + GC_page_size-1) +# else +# ifdef OS2 + void * os2_alloc(size_t bytes); +# define GET_MEM(bytes) HBLKPTR((ptr_t)os2_alloc((size_t)bytes \ + + GC_page_size) \ + + GC_page_size-1) +# else +# if defined(NEXT) || defined(DOS4GW) || \ + (defined(AMIGA) && !defined(GC_AMIGA_FASTALLOC)) || \ + (defined(SUNOS5) && !defined(USE_MMAP)) +# define GET_MEM(bytes) HBLKPTR((size_t) \ + calloc(1, (size_t)bytes + GC_page_size) \ + + GC_page_size-1) +# else +# if defined(MSWIN32) || defined(CYGWIN32) + extern ptr_t GC_win32_get_mem(); +# define GET_MEM(bytes) (struct hblk *)GC_win32_get_mem(bytes) +# else +# ifdef MACOS +# if defined(USE_TEMPORARY_MEMORY) + extern Ptr GC_MacTemporaryNewPtr(size_t size, + Boolean clearMemory); +# define GET_MEM(bytes) HBLKPTR( \ + GC_MacTemporaryNewPtr(bytes + GC_page_size, true) \ + + GC_page_size-1) +# else +# define GET_MEM(bytes) HBLKPTR( \ + NewPtrClear(bytes + GC_page_size) + GC_page_size-1) +# endif +# else +# 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 + +#endif /* GC_PRIVATE_H */ + +#if defined(_AIX) && !defined(__GNUC__) && !defined(__STDC__) + /* IBMs xlc compiler doesn't appear to follow the convention of */ + /* defining __STDC__ to be zero in extended mode. */ +# define __STDC__ 0 +#endif + +# endif /* GCCONFIG_H */ diff --git a/boehm-gc/include/private/pthread_stop_world.h b/boehm-gc/include/private/pthread_stop_world.h new file mode 100644 index 000000000..6f9197a1f --- /dev/null +++ b/boehm-gc/include/private/pthread_stop_world.h @@ -0,0 +1,11 @@ +#ifndef GC_PTHREAD_STOP_WORLD_H +#define GC_PTHREAD_STOP_WORLD_H + +struct thread_stop_info { + word last_stop_count; /* GC_last_stop_count value when thread */ + /* last successfully handled a suspend */ + /* signal. */ + ptr_t stack_ptr; /* Valid only when stopped. */ +}; + +#endif diff --git a/boehm-gc/include/private/pthread_support.h b/boehm-gc/include/private/pthread_support.h new file mode 100644 index 000000000..2186c0790 --- /dev/null +++ b/boehm-gc/include/private/pthread_support.h @@ -0,0 +1,103 @@ +#ifndef GC_PTHREAD_SUPPORT_H +#define GC_PTHREAD_SUPPORT_H + +# include "private/gc_priv.h" + +# if defined(GC_PTHREADS) && !defined(GC_SOLARIS_THREADS) \ + && !defined(GC_WIN32_THREADS) + +#if defined(GC_DARWIN_THREADS) +# include "private/darwin_stop_world.h" +#else +# include "private/pthread_stop_world.h" +#endif + +/* We use the allocation lock to protect thread-related data structures. */ + +/* The set of all known threads. We intercept thread creation and */ +/* joins. */ +/* Protected by allocation/GC lock. */ +/* Some of this should be declared volatile, but that's inconsistent */ +/* with some library routine declarations. */ +typedef struct GC_Thread_Rep { + struct GC_Thread_Rep * next; /* More recently allocated threads */ + /* with a given pthread id come */ + /* first. (All but the first are */ + /* guaranteed to be dead, but we may */ + /* not yet have registered the join.) */ + pthread_t id; + /* Extra bookkeeping information the stopping code uses */ + struct thread_stop_info stop_info; + + short flags; +# define FINISHED 1 /* Thread has exited. */ +# define DETACHED 2 /* Thread is intended to be detached. */ +# define MAIN_THREAD 4 /* True for the original thread only. */ +# define SUSPENDED 8 /* True if thread was suspended externally */ + short thread_blocked; /* Protected by GC lock. */ + /* Treated as a boolean value. If set, */ + /* thread will acquire GC lock before */ + /* doing any pointer manipulations, and */ + /* has set its sp value. Thus it does */ + /* not need to be sent a signal to stop */ + /* it. */ + ptr_t stack_end; /* Cold end of the stack. */ +# ifdef IA64 + ptr_t backing_store_end; + ptr_t backing_store_ptr; +# endif + void * status; /* The value returned from the thread. */ + /* Used only to avoid premature */ + /* reclamation of any data it might */ + /* reference. */ +# ifdef THREAD_LOCAL_ALLOC +# if CPP_WORDSZ == 64 && defined(ALIGN_DOUBLE) +# define GRANULARITY 16 +# define NFREELISTS 49 +# else +# define GRANULARITY 8 +# define NFREELISTS 65 +# endif + /* The ith free list corresponds to size i*GRANULARITY */ +# define INDEX_FROM_BYTES(n) ((ADD_SLOP(n) + GRANULARITY - 1)/GRANULARITY) +# define BYTES_FROM_INDEX(i) ((i) * GRANULARITY - EXTRA_BYTES) +# define SMALL_ENOUGH(bytes) (ADD_SLOP(bytes) <= \ + (NFREELISTS-1)*GRANULARITY) + ptr_t ptrfree_freelists[NFREELISTS]; + ptr_t normal_freelists[NFREELISTS]; +# ifdef GC_GCJ_SUPPORT + ptr_t gcj_freelists[NFREELISTS]; +# endif + /* Free lists contain either a pointer or a small count */ + /* reflecting the number of granules allocated at that */ + /* size. */ + /* 0 ==> thread-local allocation in use, free list */ + /* empty. */ + /* > 0, <= DIRECT_GRANULES ==> Using global allocation, */ + /* too few objects of this size have been */ + /* allocated by this thread. */ + /* >= HBLKSIZE => pointer to nonempty free list. */ + /* > DIRECT_GRANULES, < HBLKSIZE ==> transition to */ + /* local alloc, equivalent to 0. */ +# define DIRECT_GRANULES (HBLKSIZE/GRANULARITY) + /* Don't use local free lists for up to this much */ + /* allocation. */ +# endif +} * GC_thread; + +# define THREAD_TABLE_SZ 128 /* Must be power of 2 */ +extern volatile GC_thread GC_threads[THREAD_TABLE_SZ]; + +extern GC_bool GC_thr_initialized; + +GC_thread GC_lookup_thread(pthread_t id); + +void GC_stop_init(); + +extern GC_bool GC_in_thread_creation; + /* We may currently be in thread creation or destruction. */ + /* Only set to TRUE while allocation lock is held. */ + /* When set, it is OK to run GC from unknown thread. */ + +#endif /* GC_PTHREADS && !GC_SOLARIS_THREADS.... etc */ +#endif /* GC_PTHREAD_SUPPORT_H */ diff --git a/boehm-gc/include/private/specific.h b/boehm-gc/include/private/specific.h new file mode 100644 index 000000000..d04e19f5a --- /dev/null +++ b/boehm-gc/include/private/specific.h @@ -0,0 +1,95 @@ +/* + * 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)) + +/* An entry describing a thread-specific value for a given thread. */ +/* All such accessible structures preserve the invariant that if either */ +/* thread is a valid pthread id or qtid is a valid "quick tread id" */ +/* for a thread, then value holds the corresponding thread specific */ +/* value. This invariant must be preserved at ALL times, since */ +/* asynchronous reads are allowed. */ +typedef struct thread_specific_entry { + unsigned long qtid; /* quick thread id, only for cache */ + void * value; + struct thread_specific_entry *next; + pthread_t thread; +} tse; + + +/* We represent each thread-specific datum as two tables. The first is */ +/* a cache, indexed 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. */ +/* Must be defined so that it never returns 0. (Page 0 can't really */ +/* be part of any stack, since that would make 0 a valid stack pointer.)*/ +static __inline__ unsigned long quick_thread_id() { + int dummy; + return (unsigned long)(&dummy) >> 12; +} + +#define INVALID_QTID ((unsigned long)0) +#define INVALID_THREADID ((pthread_t)0) + +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 (EXPECT(entry -> qtid == qtid, 1)) { + GC_ASSERT(entry -> thread == pthread_self()); + return entry -> value; + } + return PREFIXED(slow_getspecific) (key, qtid, entry_ptr); +} + + |