X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=ghc%2Frts%2FStorage.h;h=f746d1f0ca5f6bf4235c9ef01798839c4913cc81;hb=9428b42b8e4b21493055b54f176cffa0a5b879b6;hp=b4a22b98f3b933203e4365e08503b90a94633fd2;hpb=bf739c105b606764ae81a437f3cd9893c977db2f;p=ghc-hetmet.git diff --git a/ghc/rts/Storage.h b/ghc/rts/Storage.h index b4a22b9..f746d1f 100644 --- a/ghc/rts/Storage.h +++ b/ghc/rts/Storage.h @@ -1,5 +1,7 @@ /* ----------------------------------------------------------------------------- - * $Id: Storage.h,v 1.6 1999/02/02 14:21:34 simonm Exp $ + * $Id: Storage.h,v 1.37 2001/11/22 14:25:12 simonmar Exp $ + * + * (c) The GHC Team, 1998-1999 * * External Storage Manger Interface * @@ -11,6 +13,9 @@ #include "Block.h" #include "BlockAlloc.h" #include "StoragePriv.h" +#ifdef PROFILING +#include "LdvProfile.h" +#endif /* ----------------------------------------------------------------------------- Initialisation / De-initialisation @@ -22,12 +27,24 @@ extern void exitStorage(void); /* ----------------------------------------------------------------------------- Generic allocation - StgPtr allocate(int n) Allocates a chunk of contiguous store + StgPtr allocate(nat n) Allocates a chunk of contiguous store n words long, returning a pointer to the first word. Always succeeds. + StgPtr allocatePinned(nat n) Allocates a chunk of contiguous store + n words long, which is at a fixed + address (won't be moved by GC). + Returns a pointer to the first word. + Always succeeds. + + NOTE: the GC can't in general handle + pinned objects, so allocatePinned() + can only be used for ByteArrays at the + moment. + Don't forget to TICK_ALLOC_XXX(...) - after calling allocate, for the + after calling allocate or + allocatePinned, for the benefit of the ticky-ticky profiler. rtsBool doYouWantToGC(void) Returns True if the storage manager is @@ -36,14 +53,20 @@ extern void exitStorage(void); lnat allocated_bytes(void) Returns the number of bytes allocated via allocate() since the last GC. Used in the reoprting of statistics. + + SMP: allocate and doYouWantToGC can be used from STG code, they are + surrounded by a mutex. -------------------------------------------------------------------------- */ -extern StgPtr allocate(nat n); -static inline rtsBool doYouWantToGC(void) +extern StgPtr allocate ( nat n ); +extern StgPtr allocatePinned ( nat n ); +extern lnat allocated_bytes ( void ); + +static inline rtsBool +doYouWantToGC( void ) { return (alloc_blocks >= alloc_blocks_lim); } -extern lnat allocated_bytes(void); /* ----------------------------------------------------------------------------- ExtendNursery(hp,hplim) When hplim is reached, try to grab @@ -54,9 +77,9 @@ extern lnat allocated_bytes(void); -------------------------------------------------------------------------- */ #define ExtendNursery(hp,hplim) \ - (current_nursery->free = (P_)(hp)+1, \ - current_nursery->link == NULL ? rtsFalse : \ - (current_nursery = current_nursery->link, \ + (CurrentNursery->free = (P_)(hp)+1, \ + CurrentNursery->link == NULL ? rtsFalse : \ + (CurrentNursery = CurrentNursery->link, \ OpenNursery(hp,hplim), \ rtsTrue)) @@ -73,35 +96,50 @@ extern void PleaseStopAllocating(void); MarkRoot(StgClosure *p) Returns the new location of the root. -------------------------------------------------------------------------- */ -extern void GarbageCollect(void (*get_roots)(void)); -extern StgClosure *MarkRoot(StgClosure *p); +extern void GarbageCollect(void (*get_roots)(evac_fn),rtsBool force_major_gc); /* ----------------------------------------------------------------------------- Generational garbage collection support - RecordMutable(StgPtr p) Informs the garbage collector that a + recordMutable(StgPtr p) Informs the garbage collector that a previously immutable object has become (permanently) mutable. Used by thawArray and similar. - UpdateWithIndirection(p1,p2) Updates the object at p1 with an + updateWithIndirection(p1,p2) Updates the object at p1 with an indirection pointing to p2. This is normally called for objects in an old generation (>0) when they are updated. + updateWithPermIndirection(p1,p2) As above but uses a permanent indir. + -------------------------------------------------------------------------- */ +/* + * Storage manager mutex + */ +#ifdef SMP +extern pthread_mutex_t sm_mutex; +#endif + +/* ToDo: shouldn't recordMutable and recordOldToNewPtrs acquire some + * kind of lock in the SMP case? + */ static inline void recordMutable(StgMutClosure *p) { bdescr *bd; +#ifdef SMP + ASSERT(p->header.info == &stg_WHITEHOLE_info || closure_MUTABLE(p)); +#else ASSERT(closure_MUTABLE(p)); +#endif bd = Bdescr((P_)p); - if (bd->gen->no > 0) { - p->mut_link = bd->gen->mut_list; - bd->gen->mut_list = p; + if (bd->gen_no > 0) { + p->mut_link = generations[bd->gen_no].mut_list; + generations[bd->gen_no].mut_list = p; } } @@ -111,37 +149,455 @@ recordOldToNewPtrs(StgMutClosure *p) bdescr *bd; bd = Bdescr((P_)p); - if (bd->gen->no > 0) { - p->mut_link = bd->gen->mut_once_list; - bd->gen->mut_once_list = p; + if (bd->gen_no > 0) { + p->mut_link = generations[bd->gen_no].mut_once_list; + generations[bd->gen_no].mut_once_list = p; } } +// @LDV profiling +// We zero out the slop when PROFILING is on. +// #ifndef DEBUG +#if !defined(DEBUG) && !defined(PROFILING) +#define updateWithIndirection(info, p1, p2) \ + { \ + bdescr *bd; \ + \ + bd = Bdescr((P_)p1); \ + if (bd->gen_no == 0) { \ + ((StgInd *)p1)->indirectee = p2; \ + SET_INFO(p1,&stg_IND_info); \ + TICK_UPD_NEW_IND(); \ + } else { \ + ((StgIndOldGen *)p1)->indirectee = p2; \ + if (info != &stg_BLACKHOLE_BQ_info) { \ + ACQUIRE_LOCK(&sm_mutex); \ + ((StgIndOldGen *)p1)->mut_link = generations[bd->gen_no].mut_once_list; \ + generations[bd->gen_no].mut_once_list = (StgMutClosure *)p1; \ + RELEASE_LOCK(&sm_mutex); \ + } \ + SET_INFO(p1,&stg_IND_OLDGEN_info); \ + TICK_UPD_OLD_IND(); \ + } \ + } +#elif defined(PROFILING) +// @LDV profiling +// We call LDV_recordDead_FILL_SLOP_DYNAMIC(p1) regardless of the generation in +// which p1 resides. +// +// Note: +// After all, we do *NOT* need to call LDV_recordCreate() for both IND and +// IND_OLDGEN closures because they are inherently used. But, it corrupts +// the invariants that every closure keeps its creation time in the profiling +// field. So, we call LDV_recordCreate(). + +#define updateWithIndirection(info, p1, p2) \ + { \ + bdescr *bd; \ + \ + LDV_recordDead_FILL_SLOP_DYNAMIC((p1)); \ + bd = Bdescr((P_)p1); \ + if (bd->gen_no == 0) { \ + ((StgInd *)p1)->indirectee = p2; \ + SET_INFO(p1,&stg_IND_info); \ + LDV_recordCreate((p1)); \ + TICK_UPD_NEW_IND(); \ + } else { \ + ((StgIndOldGen *)p1)->indirectee = p2; \ + if (info != &stg_BLACKHOLE_BQ_info) { \ + ACQUIRE_LOCK(&sm_mutex); \ + ((StgIndOldGen *)p1)->mut_link = generations[bd->gen_no].mut_once_list; \ + generations[bd->gen_no].mut_once_list = (StgMutClosure *)p1; \ + RELEASE_LOCK(&sm_mutex); \ + } \ + SET_INFO(p1,&stg_IND_OLDGEN_info); \ + LDV_recordCreate((p1)); \ + } \ + } + +#else + +/* In the DEBUG case, we also zero out the slop of the old closure, + * so that the sanity checker can tell where the next closure is. + * + * Two important invariants: we should never try to update a closure + * to point to itself, and the closure being updated should not + * already have been updated (the mutable list will get messed up + * otherwise). + */ +#define updateWithIndirection(info, p1, p2) \ + { \ + bdescr *bd; \ + \ + ASSERT( p1 != p2 && !closure_IND(p1) ); \ + bd = Bdescr((P_)p1); \ + if (bd->gen_no == 0) { \ + ((StgInd *)p1)->indirectee = p2; \ + SET_INFO(p1,&stg_IND_info); \ + TICK_UPD_NEW_IND(); \ + } else { \ + if (info != &stg_BLACKHOLE_BQ_info) { \ + { \ + StgInfoTable *inf = get_itbl(p1); \ + nat np = inf->layout.payload.ptrs, \ + nw = inf->layout.payload.nptrs, i; \ + if (inf->type != THUNK_SELECTOR) { \ + for (i = np; i < np + nw; i++) { \ + ((StgClosure *)p1)->payload[i] = 0; \ + } \ + } \ + } \ + ACQUIRE_LOCK(&sm_mutex); \ + ((StgIndOldGen *)p1)->mut_link = generations[bd->gen_no].mut_once_list; \ + generations[bd->gen_no].mut_once_list = (StgMutClosure *)p1; \ + RELEASE_LOCK(&sm_mutex); \ + } \ + ((StgIndOldGen *)p1)->indirectee = p2; \ + SET_INFO(p1,&stg_IND_OLDGEN_info); \ + TICK_UPD_OLD_IND(); \ + } \ + } +#endif + +/* Static objects all live in the oldest generation + */ +#define updateWithStaticIndirection(info, p1, p2) \ + { \ + ASSERT( p1 != p2 && !closure_IND(p1) ); \ + ASSERT( ((StgMutClosure*)p1)->mut_link == NULL ); \ + \ + ACQUIRE_LOCK(&sm_mutex); \ + ((StgMutClosure *)p1)->mut_link = oldest_gen->mut_once_list; \ + oldest_gen->mut_once_list = (StgMutClosure *)p1; \ + RELEASE_LOCK(&sm_mutex); \ + \ + ((StgInd *)p1)->indirectee = p2; \ + SET_INFO((StgInd *)p1, &stg_IND_STATIC_info); \ + TICK_UPD_STATIC_IND(); \ + } + +#if defined(TICKY_TICKY) || defined(PROFILING) static inline void -updateWithIndirection(StgClosure *p1, StgClosure *p2) +updateWithPermIndirection(const StgInfoTable *info, StgClosure *p1, StgClosure *p2) { bdescr *bd; + ASSERT( p1 != p2 && !closure_IND(p1) ); + + // @LDV profiling + // Destroy the old closure. + LDV_recordDead_FILL_SLOP_DYNAMIC(p1); bd = Bdescr((P_)p1); - if (bd->gen->no == 0) { - SET_INFO(p1,&IND_info); + if (bd->gen_no == 0) { ((StgInd *)p1)->indirectee = p2; - TICK_UPD_NEW_IND(); + SET_INFO(p1,&stg_IND_PERM_info); + // @LDV profiling + // We have just created a new closure. + LDV_recordCreate(p1); + TICK_UPD_NEW_PERM_IND(p1); } else { - SET_INFO(p1,&IND_OLDGEN_info); ((StgIndOldGen *)p1)->indirectee = p2; - ((StgIndOldGen *)p1)->mut_link = bd->gen->mut_once_list; - bd->gen->mut_once_list = (StgMutClosure *)p1; - TICK_UPD_OLD_IND(); + if (info != &stg_BLACKHOLE_BQ_info) { + ACQUIRE_LOCK(&sm_mutex); + ((StgIndOldGen *)p1)->mut_link = generations[bd->gen_no].mut_once_list; + generations[bd->gen_no].mut_once_list = (StgMutClosure *)p1; + RELEASE_LOCK(&sm_mutex); + } + SET_INFO(p1,&stg_IND_OLDGEN_PERM_info); + // @LDV profiling + // We have just created a new closure. + LDV_recordCreate(p1); + TICK_UPD_OLD_PERM_IND(); } } +#endif + +/* ----------------------------------------------------------------------------- + The CAF table - used to let us revert CAFs + -------------------------------------------------------------------------- */ + +void revertCAFs( void ); + +#if defined(DEBUG) +void printMutOnceList(generation *gen); +void printMutableList(generation *gen); +#endif /* DEBUG */ + +/* -------------------------------------------------------------------------- + Address space layout macros + -------------------------------------------------------------------------- + + Here are the assumptions GHC makes about address space layout. + Broadly, it thinks there are three sections: + + CODE Read-only. Contains code and read-only data (such as + info tables) + Also called "text" + + DATA Read-write data. Contains static closures (and on some + architectures, info tables too) + + HEAP Dynamically-allocated closures + + USER None of the above. The only way USER things arise right + now is when GHCi allocates a constructor info table, which + it does by mallocing them. + + Three macros identify these three areas: + IS_CODE(p), IS_DATA(p), HEAP_ALLOCED(p) + + HEAP_ALLOCED is called FOR EVERY SINGLE CLOSURE during GC. + It needs to be FAST. + + Implementation of HEAP_ALLOCED + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Concerning HEAP, most of the time (certainly under [Static] and [GHCi], + we ensure that the heap is allocated above some fixed address HEAP_BASE + (defined in MBlock.h). In this case we set TEXT_BEFORE_HEAP, and we + get a nice fast test. + + Sometimes we can't be quite sure. For example in Windows, we can't + fix where our heap address space comes from. In this case we un-set + TEXT_BEFORE_HEAP. That makes it more expensive to test whether a pointer + comes from the HEAP section, because we need to look at the allocator's + address maps (see HEAP_ALLOCED macro) + + Implementation of CODE and DATA + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Concerning CODE and DATA, there are three main regimes: + + [Static] Totally The segments are contiguous, and laid out + statically linked exactly as above + + [GHCi] Static, GHCi may load new modules, but it knows the + except for GHCi address map, so for any given address it can + still tell which section it belongs to + + [DLL] OS-supported Chunks of CODE and DATA may be mixed in + dynamic loading the address space, and we can't tell how + + + For the [Static] case, we assume memory is laid out like this + (in order of increasing addresses) + + Start of memory + CODE section + TEXT_SECTION_END_MARKER (usually _etext) + DATA section + DATA_SECTION_END_MARKER (usually _end) + USER section + HEAP_BASE + HEAP section + + For the [GHCi] case, we have to consult GHCi's dynamic linker's + address maps, which is done by macros + is_dynamically_loaded_code_or_rodata_ptr + is_dynamically_loaded_code_or_rwdata_ptr + + For the [DLL] case, IS_CODE and IS_DATA are really not usable at all. + */ + + +#undef TEXT_BEFORE_HEAP +#ifndef mingw32_TARGET_OS +#define TEXT_BEFORE_HEAP 1 +#endif + +extern void* TEXT_SECTION_END_MARKER_DECL; +extern void* DATA_SECTION_END_MARKER_DECL; + +/* Take into account code sections in dynamically loaded object files. */ +#define IS_CODE_PTR(p) ( ((P_)(p) < (P_)&TEXT_SECTION_END_MARKER) \ + || is_dynamically_loaded_code_or_rodata_ptr((char *)p) ) +#define IS_DATA_PTR(p) ( ((P_)(p) >= (P_)&TEXT_SECTION_END_MARKER && \ + (P_)(p) < (P_)&DATA_SECTION_END_MARKER) \ + || is_dynamically_loaded_rwdata_ptr((char *)p) ) +#define IS_USER_PTR(p) ( ((P_)(p) >= (P_)&DATA_SECTION_END_MARKER) \ + && is_not_dynamically_loaded_ptr((char *)p) ) + +/* The HEAP_ALLOCED test below is called FOR EVERY SINGLE CLOSURE + * during GC. It needs to be FAST. + * + * BEWARE: when we're dynamically loading code (for GHCi), make sure + * that we don't load any code above HEAP_BASE, or this test won't work. + */ +#ifdef TEXT_BEFORE_HEAP +# define HEAP_ALLOCED(x) ((StgPtr)(x) >= (StgPtr)(HEAP_BASE)) +#else +extern int is_heap_alloced(const void* x); +# define HEAP_ALLOCED(x) (is_heap_alloced(x)) +#endif + + +/* -------------------------------------------------------------------------- + Macros for distinguishing data pointers from code pointers + -------------------------------------------------------------------------- + + Specification + ~~~~~~~~~~~~~ + The garbage collector needs to make some critical distinctions between pointers. + In particular we need + + LOOKS_LIKE_GHC_INFO(p) p points to an info table + + For both of these macros, p is + *either* a pointer to a closure (static or heap allocated) + *or* a return address on the (Haskell) stack + + (Return addresses are in fact info-pointers, so that the Haskell stack + looks very like a chunk of heap.) + + The garbage collector uses LOOKS_LIKE_GHC_INFO when walking the stack, as it + walks over the "pending arguments" on its way to the next return address. + It is called moderately often, but not as often as HEAP_ALLOCED + + ToDo: LOOKS_LIKE_GHC_INFO(p) does not return True when p points to a + constructor info table allocated by GHCi. We should really rename + LOOKS_LIKE_GHC_INFO to LOOKS_LIKE_GHC_RETURN_INFO. + + Implementation + ~~~~~~~~~~~~~~ + LOOKS_LIKE_GHC_INFO is more complicated because of the need to distinguish + between static closures and info tables. It's a known portability problem. + We have three approaches: + + Plan A: Address-space partitioning. + Keep info tables in the (single, contiguous) text segment: IS_CODE_PTR(p) + and static closures in the (single, contiguous) data segment: IS_DATA_PTR(p) + + Plan A can fail for two reasons: + * In many environments (eg. dynamic loading), + text and data aren't in a single contiguous range. + * When we compile through vanilla C (no mangling) we sometimes + can't guaranteee to put info tables in the text section. This + happens eg. on MacOS where the C compiler refuses to put const + data in the text section if it has any code pointers in it + (which info tables do *only* when we're compiling without + TABLES_NEXT_TO_CODE). + + Hence, Plan B: (compile-via-C-with-mangling, or native code generation) + Put a zero word before each static closure. + When compiling to native code, or via C-with-mangling, info tables + are laid out "backwards" from the address specified in the info pointer + (the entry code goes forward from the info pointer). Hence, the word + before the one referenced the info pointer is part of the info table, + and is guaranteed non-zero. + + For reasons nobody seems to fully understand, the statically-allocated tables + of INTLIKE and CHARLIKE closures can't have this zero word, so we + have to test separately for them. + + Plan B fails altogether for the compile-through-vanilla-C route, because + info tables aren't laid out backwards. + + + Hence, Plan C: (unregisterised, compile-through-vanilla-C route only) + If we didn't manage to get info tables into the text section, then + we can distinguish between a static closure pointer and an info + pointer as follows: the first word of an info table is a code pointer, + and therefore in text space, whereas the first word of a closure pointer + is an info pointer, and therefore not. Shazam! +*/ + + +/* When working with Win32 DLLs, static closures are identified by + being prefixed with a zero word. This is needed so that we can + distinguish between pointers to static closures and (reversed!) + info tables. + + This 'scheme' breaks down for closure tables such as CHARLIKE, + so we catch these separately. + + LOOKS_LIKE_STATIC_CLOSURE() + - discriminates between static closures and info tbls + (needed by LOOKS_LIKE_GHC_INFO() below - [Win32 DLLs only.]) + LOOKS_LIKE_STATIC() + - distinguishes between static and heap allocated data. + */ +#if defined(ENABLE_WIN32_DLL_SUPPORT) + /* definitely do not enable for mingw DietHEP */ +#define LOOKS_LIKE_STATIC(r) (!(HEAP_ALLOCED(r))) + +/* Tiresome predicates needed to check for pointers into the closure tables */ +#define IS_CHARLIKE_CLOSURE(p) \ + ( (P_)(p) >= (P_)stg_CHARLIKE_closure && \ + (char*)(p) <= ((char*)stg_CHARLIKE_closure + \ + (MAX_CHARLIKE-MIN_CHARLIKE) * sizeof(StgIntCharlikeClosure)) ) +#define IS_INTLIKE_CLOSURE(p) \ + ( (P_)(p) >= (P_)stg_INTLIKE_closure && \ + (char*)(p) <= ((char*)stg_INTLIKE_closure + \ + (MAX_INTLIKE-MIN_INTLIKE) * sizeof(StgIntCharlikeClosure)) ) + +#define LOOKS_LIKE_STATIC_CLOSURE(r) (((*(((unsigned long *)(r))-1)) == 0) || IS_CHARLIKE_CLOSURE(r) || IS_INTLIKE_CLOSURE(r)) +#else +#define LOOKS_LIKE_STATIC(r) IS_DATA_PTR(r) +#define LOOKS_LIKE_STATIC_CLOSURE(r) IS_DATA_PTR(r) +#endif + /* ----------------------------------------------------------------------------- - The CAF list - used to let us revert CAFs + Macros for distinguishing infotables from closures. + + You'd think it'd be easy to tell an info pointer from a closure pointer: + closures live on the heap and infotables are in read only memory. Right? + Wrong! Static closures live in read only memory and Hugs allocates + infotables for constructors on the (writable) C heap. + -------------------------------------------------------------------------- */ + +/* not accurate by any means, but stops the assertions failing... */ +/* TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO */ +#define IS_HUGS_CONSTR_INFO(info) IS_USER_PTR(info) + +/* LOOKS_LIKE_GHC_INFO is called moderately often during GC, but + * Certainly not as often as HEAP_ALLOCED. + */ +#ifdef TEXT_BEFORE_HEAP /* needed for mingw DietHEP */ +# define LOOKS_LIKE_GHC_INFO(info) IS_CODE_PTR(info) +#else +# define LOOKS_LIKE_GHC_INFO(info) (!HEAP_ALLOCED(info) \ + && !LOOKS_LIKE_STATIC_CLOSURE(info)) +#endif + +/* ----------------------------------------------------------------------------- + Macros for calculating how big a closure will be (used during allocation) -------------------------------------------------------------------------- */ -extern StgCAF* enteredCAFs; +static __inline__ StgOffset AP_sizeW ( nat n_args ) +{ return sizeofW(StgAP_UPD) + n_args; } + +static __inline__ StgOffset PAP_sizeW ( nat n_args ) +{ return sizeofW(StgPAP) + n_args; } + +static __inline__ StgOffset CONSTR_sizeW( nat p, nat np ) +{ return sizeofW(StgHeader) + p + np; } + +static __inline__ StgOffset THUNK_SELECTOR_sizeW ( void ) +{ return sizeofW(StgHeader) + MIN_UPD_SIZE; } + +static __inline__ StgOffset BLACKHOLE_sizeW ( void ) +{ return sizeofW(StgHeader) + MIN_UPD_SIZE; } + +/* -------------------------------------------------------------------------- + * Sizes of closures + * ------------------------------------------------------------------------*/ + +static __inline__ StgOffset sizeW_fromITBL( const StgInfoTable* itbl ) +{ return sizeofW(StgClosure) + + sizeofW(StgPtr) * itbl->layout.payload.ptrs + + sizeofW(StgWord) * itbl->layout.payload.nptrs; } + +static __inline__ StgOffset pap_sizeW( StgPAP* x ) +{ return PAP_sizeW(x->n_args); } + +static __inline__ StgOffset arr_words_sizeW( StgArrWords* x ) +{ return sizeofW(StgArrWords) + x->words; } + +static __inline__ StgOffset mut_arr_ptrs_sizeW( StgMutArrPtrs* x ) +{ return sizeofW(StgMutArrPtrs) + x->ptrs; } + +static __inline__ StgWord tso_sizeW ( StgTSO *tso ) +{ return TSO_STRUCT_SIZEW + tso->stack_size; } -#endif STORAGE_H +#endif /* STORAGE_H */