version 1.1.1.2, 2000/04/14 11:07:58 |
version 1.1.1.3, 2000/12/01 14:48:24 |
|
|
|
|
/* mark_proc GC_mark_procs[MAX_MARK_PROCS] = {0} -- declared in gc_priv.h */ |
/* mark_proc GC_mark_procs[MAX_MARK_PROCS] = {0} -- declared in gc_priv.h */ |
|
|
word GC_n_mark_procs = 0; |
word GC_n_mark_procs = GC_RESERVED_MARK_PROCS; |
|
|
/* Initialize GC_obj_kinds properly and standard free lists properly. */ |
/* Initialize GC_obj_kinds properly and standard free lists properly. */ |
/* This must be done statically since they may be accessed before */ |
/* This must be done statically since they may be accessed before */ |
Line 252 static void alloc_mark_stack(); |
|
Line 252 static void alloc_mark_stack(); |
|
GC_bool GC_mark_some(cold_gc_frame) |
GC_bool GC_mark_some(cold_gc_frame) |
ptr_t cold_gc_frame; |
ptr_t cold_gc_frame; |
{ |
{ |
|
#ifdef MSWIN32 |
|
/* Windows 98 appears to asynchronously create and remove writable */ |
|
/* memory mappings, for reasons we haven't yet understood. Since */ |
|
/* we look for writable regions to determine the root set, we may */ |
|
/* try to mark from an address range that disappeared since we */ |
|
/* started the collection. Thus we have to recover from faults here. */ |
|
/* This code does not appear to be necessary for Windows 95/NT/2000. */ |
|
/* Note that this code should never generate an incremental GC write */ |
|
/* fault. */ |
|
__try { |
|
#endif |
switch(GC_mark_state) { |
switch(GC_mark_state) { |
case MS_NONE: |
case MS_NONE: |
return(FALSE); |
return(FALSE); |
Line 342 ptr_t cold_gc_frame; |
|
Line 353 ptr_t cold_gc_frame; |
|
ABORT("GC_mark_some: bad state"); |
ABORT("GC_mark_some: bad state"); |
return(FALSE); |
return(FALSE); |
} |
} |
|
#ifdef MSWIN32 |
|
} __except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? |
|
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { |
|
# ifdef PRINTSTATS |
|
GC_printf0("Caught ACCESS_VIOLATION in marker. " |
|
"Memory mapping disappeared.\n"); |
|
# endif /* PRINTSTATS */ |
|
/* We have bad roots on the stack. Discard mark stack. */ |
|
/* Rescan from marked objects. Redetermine roots. */ |
|
GC_invalidate_mark_state(); |
|
scan_ptr = 0; |
|
return FALSE; |
|
} |
|
#endif /* MSWIN32 */ |
} |
} |
|
|
|
|
Line 365 GC_bool GC_mark_stack_empty() |
|
Line 390 GC_bool GC_mark_stack_empty() |
|
/* with IGNORE_OFF_PAGE set. */ |
/* with IGNORE_OFF_PAGE set. */ |
/*ARGSUSED*/ |
/*ARGSUSED*/ |
# ifdef PRINT_BLACK_LIST |
# ifdef PRINT_BLACK_LIST |
word GC_find_start(current, hhdr, source) |
ptr_t GC_find_start(current, hhdr, source) |
word source; |
word source; |
# else |
# else |
word GC_find_start(current, hhdr) |
ptr_t GC_find_start(current, hhdr) |
# define source 0 |
# define source 0 |
# endif |
# endif |
register word current; |
register ptr_t current; |
register hdr * hhdr; |
register hdr * hhdr; |
{ |
{ |
# ifdef ALL_INTERIOR_POINTERS |
# ifdef ALL_INTERIOR_POINTERS |
if (hhdr != 0) { |
if (hhdr != 0) { |
register word orig = current; |
register ptr_t orig = current; |
|
|
current = (word)HBLKPTR(current) + HDR_BYTES; |
current = (ptr_t)HBLKPTR(current) + HDR_BYTES; |
do { |
do { |
current = current - HBLKSIZE*(word)hhdr; |
current = current - HBLKSIZE*(word)hhdr; |
hhdr = HDR(current); |
hhdr = HDR(current); |
|
|
* is never 0. A mark stack entry never has size 0. |
* is never 0. A mark stack entry never has size 0. |
* We try to traverse on the order of a hblk of memory before we return. |
* We try to traverse on the order of a hblk of memory before we return. |
* Caller is responsible for calling this until the mark stack is empty. |
* Caller is responsible for calling this until the mark stack is empty. |
|
* Note that this is the most performance critical routine in the |
|
* collector. Hence it contains all sorts of ugly hacks to speed |
|
* things up. In particular, we avoid procedure calls on the common |
|
* path, we take advantage of peculiarities of the mark descriptor |
|
* encoding, we optionally maintain a cache for the block address to |
|
* header mapping, we prefetch when an object is "grayed", etc. |
*/ |
*/ |
void GC_mark_from_mark_stack() |
void GC_mark_from_mark_stack() |
{ |
{ |
Line 443 void GC_mark_from_mark_stack() |
|
Line 474 void GC_mark_from_mark_stack() |
|
register word descr; |
register word descr; |
register ptr_t greatest_ha = GC_greatest_plausible_heap_addr; |
register ptr_t greatest_ha = GC_greatest_plausible_heap_addr; |
register ptr_t least_ha = GC_least_plausible_heap_addr; |
register ptr_t least_ha = GC_least_plausible_heap_addr; |
|
DECLARE_HDR_CACHE; |
|
|
# define SPLIT_RANGE_WORDS 128 /* Must be power of 2. */ |
# define SPLIT_RANGE_WORDS 128 /* Must be power of 2. */ |
|
|
GC_objects_are_marked = TRUE; |
GC_objects_are_marked = TRUE; |
|
INIT_HDR_CACHE; |
# ifdef OS2 /* Use untweaked version to circumvent compiler problem */ |
# ifdef OS2 /* Use untweaked version to circumvent compiler problem */ |
while (GC_mark_stack_top_reg >= GC_mark_stack_reg && credit >= 0) { |
while (GC_mark_stack_top_reg >= GC_mark_stack_reg && credit >= 0) { |
# else |
# else |
Line 453 void GC_mark_from_mark_stack() |
|
Line 487 void GC_mark_from_mark_stack() |
|
>= 0) { |
>= 0) { |
# endif |
# endif |
current_p = GC_mark_stack_top_reg -> mse_start; |
current_p = GC_mark_stack_top_reg -> mse_start; |
retry: |
|
descr = GC_mark_stack_top_reg -> mse_descr; |
descr = GC_mark_stack_top_reg -> mse_descr; |
|
retry: |
|
/* current_p and descr describe the current object. */ |
|
/* *GC_mark_stack_top_reg is vacant. */ |
|
/* The following is 0 only for small objects described by a simple */ |
|
/* length descriptor. For many applications this is the common */ |
|
/* case, so we try to detect it quickly. */ |
if (descr & ((~(WORDS_TO_BYTES(SPLIT_RANGE_WORDS) - 1)) | DS_TAGS)) { |
if (descr & ((~(WORDS_TO_BYTES(SPLIT_RANGE_WORDS) - 1)) | DS_TAGS)) { |
word tag = descr & DS_TAGS; |
word tag = descr & DS_TAGS; |
|
|
Line 465 void GC_mark_from_mark_stack() |
|
Line 504 void GC_mark_from_mark_stack() |
|
/* stack. */ |
/* stack. */ |
GC_mark_stack_top_reg -> mse_start = |
GC_mark_stack_top_reg -> mse_start = |
limit = current_p + SPLIT_RANGE_WORDS-1; |
limit = current_p + SPLIT_RANGE_WORDS-1; |
GC_mark_stack_top_reg -> mse_descr -= |
GC_mark_stack_top_reg -> mse_descr = |
WORDS_TO_BYTES(SPLIT_RANGE_WORDS-1); |
descr - WORDS_TO_BYTES(SPLIT_RANGE_WORDS-1); |
/* Make sure that pointers overlapping the two ranges are */ |
/* Make sure that pointers overlapping the two ranges are */ |
/* considered. */ |
/* considered. */ |
limit = (word *)((char *)limit + sizeof(word) - ALIGNMENT); |
limit = (word *)((char *)limit + sizeof(word) - ALIGNMENT); |
Line 479 void GC_mark_from_mark_stack() |
|
Line 518 void GC_mark_from_mark_stack() |
|
if ((signed_word)descr < 0) { |
if ((signed_word)descr < 0) { |
current = *current_p; |
current = *current_p; |
if ((ptr_t)current >= least_ha && (ptr_t)current < greatest_ha) { |
if ((ptr_t)current >= least_ha && (ptr_t)current < greatest_ha) { |
PUSH_CONTENTS(current, GC_mark_stack_top_reg, mark_stack_limit, |
PREFETCH(current); |
current_p, exit1); |
HC_PUSH_CONTENTS((ptr_t)current, GC_mark_stack_top_reg, |
|
mark_stack_limit, current_p, exit1); |
} |
} |
} |
} |
descr <<= 1; |
descr <<= 1; |
Line 496 void GC_mark_from_mark_stack() |
|
Line 536 void GC_mark_from_mark_stack() |
|
mark_stack_limit, ENV(descr)); |
mark_stack_limit, ENV(descr)); |
continue; |
continue; |
case DS_PER_OBJECT: |
case DS_PER_OBJECT: |
GC_mark_stack_top_reg -> mse_descr = |
if ((signed_word)descr >= 0) { |
*(word *)((ptr_t)current_p + descr - tag); |
/* Descriptor is in the object. */ |
|
descr = *(word *)((ptr_t)current_p + descr - DS_PER_OBJECT); |
|
} else { |
|
/* Descriptor is in type descriptor pointed to by first */ |
|
/* word in object. */ |
|
ptr_t type_descr = *(ptr_t *)current_p; |
|
/* type_descr is either a valid pointer to the descriptor */ |
|
/* structure, or this object was on a free list. If it */ |
|
/* it was anything but the last object on the free list, */ |
|
/* we will misinterpret the next object on the free list as */ |
|
/* the type descriptor, and get a 0 GC descriptor, which */ |
|
/* is ideal. Unfortunately, we need to check for the last */ |
|
/* object case explicitly. */ |
|
if (0 == type_descr) { |
|
/* Rarely executed. */ |
|
GC_mark_stack_top_reg--; |
|
continue; |
|
} |
|
descr = *(word *)(type_descr |
|
- (descr - (DS_PER_OBJECT - INDIR_PER_OBJ_BIAS))); |
|
} |
|
if (0 == descr) { |
|
GC_mark_stack_top_reg--; |
|
continue; |
|
} |
goto retry; |
goto retry; |
} |
} |
} else { |
} else /* Small object with length descriptor */ { |
GC_mark_stack_top_reg--; |
GC_mark_stack_top_reg--; |
limit = (word *)(((ptr_t)current_p) + (word)descr); |
limit = (word *)(((ptr_t)current_p) + (word)descr); |
} |
} |
/* The simple case in which we're scanning a range. */ |
/* The simple case in which we're scanning a range. */ |
credit -= (ptr_t)limit - (ptr_t)current_p; |
credit -= (ptr_t)limit - (ptr_t)current_p; |
limit -= 1; |
limit -= 1; |
while (current_p <= limit) { |
{ |
current = *current_p; |
# define PREF_DIST 4 |
if ((ptr_t)current >= least_ha && (ptr_t)current < greatest_ha) { |
|
PUSH_CONTENTS(current, GC_mark_stack_top_reg, |
# ifndef SMALL_CONFIG |
mark_stack_limit, current_p, exit2); |
word deferred; |
|
|
|
/* Try to prefetch the next pointer to be examined asap. */ |
|
/* Empirically, this also seems to help slightly without */ |
|
/* prefetches, at least on linux/X86. Presumably this loop */ |
|
/* ends up with less register pressure, and gcc thus ends up */ |
|
/* generating slightly better code. Overall gcc code quality */ |
|
/* for this loop is still not great. */ |
|
for(;;) { |
|
PREFETCH((ptr_t)limit - PREF_DIST*CACHE_LINE_SIZE); |
|
deferred = *limit; |
|
limit = (word *)((char *)limit - ALIGNMENT); |
|
if ((ptr_t)deferred >= least_ha && (ptr_t)deferred < greatest_ha) { |
|
PREFETCH(deferred); |
|
break; |
|
} |
|
if (current_p > limit) goto next_object; |
|
/* Unroll once, so we don't do too many of the prefetches */ |
|
/* based on limit. */ |
|
deferred = *limit; |
|
limit = (word *)((char *)limit - ALIGNMENT); |
|
if ((ptr_t)deferred >= least_ha && (ptr_t)deferred < greatest_ha) { |
|
PREFETCH(deferred); |
|
break; |
|
} |
|
if (current_p > limit) goto next_object; |
|
} |
|
# endif |
|
|
|
while (current_p <= limit) { |
|
/* Empirically, unrolling this loop doesn't help a lot. */ |
|
/* Since HC_PUSH_CONTENTS expands to a lot of code, */ |
|
/* we don't. */ |
|
current = *current_p; |
|
PREFETCH((ptr_t)current_p + PREF_DIST*CACHE_LINE_SIZE); |
|
if ((ptr_t)current >= least_ha && (ptr_t)current < greatest_ha) { |
|
/* Prefetch the contents of the object we just pushed. It's */ |
|
/* likely we will need them soon. */ |
|
PREFETCH(current); |
|
HC_PUSH_CONTENTS((ptr_t)current, GC_mark_stack_top_reg, |
|
mark_stack_limit, current_p, exit2); |
|
} |
|
current_p = (word *)((char *)current_p + ALIGNMENT); |
} |
} |
current_p = (word *)((char *)current_p + ALIGNMENT); |
|
|
# ifndef SMALL_CONFIG |
|
/* We still need to mark the entry we previously prefetched. */ |
|
/* We alrady know that it passes the preliminary pointer */ |
|
/* validity test. */ |
|
HC_PUSH_CONTENTS((ptr_t)deferred, GC_mark_stack_top_reg, |
|
mark_stack_limit, current_p, exit4); |
|
next_object:; |
|
# endif |
} |
} |
} |
} |
GC_mark_stack_top = GC_mark_stack_top_reg; |
GC_mark_stack_top = GC_mark_stack_top_reg; |
|
|
return; |
return; |
} |
} |
# endif |
# endif |
GC_PUSH_ONE_STACK(p, 0); |
GC_PUSH_ONE_STACK(p, MARKED_FROM_REGISTER); |
} |
} |
|
|
# ifdef __STDC__ |
# ifdef __STDC__ |
Line 1029 struct hblk *h; |
|
Line 1143 struct hblk *h; |
|
register hdr * hhdr; |
register hdr * hhdr; |
{ |
{ |
register int sz = hhdr -> hb_sz; |
register int sz = hhdr -> hb_sz; |
|
register int descr = hhdr -> hb_descr; |
register word * p; |
register word * p; |
register int word_no; |
register int word_no; |
register word * lim; |
register word * lim; |
Line 1036 register hdr * hhdr; |
|
Line 1151 register hdr * hhdr; |
|
register mse * mark_stack_limit = &(GC_mark_stack[GC_mark_stack_size]); |
register mse * mark_stack_limit = &(GC_mark_stack[GC_mark_stack_size]); |
|
|
/* Some quick shortcuts: */ |
/* Some quick shortcuts: */ |
{ |
if ((0 | DS_LENGTH) == descr) return; |
struct obj_kind *ok = &(GC_obj_kinds[hhdr -> hb_obj_kind]); |
|
if ((0 | DS_LENGTH) == ok -> ok_descriptor |
|
&& FALSE == ok -> ok_relocate_descr) |
|
return; |
|
} |
|
if (GC_block_empty(hhdr)/* nothing marked */) return; |
if (GC_block_empty(hhdr)/* nothing marked */) return; |
# ifdef GATHERSTATS |
# ifdef GATHERSTATS |
GC_n_rescuing_pages++; |
GC_n_rescuing_pages++; |
# endif |
# endif |
GC_objects_are_marked = TRUE; |
GC_objects_are_marked = TRUE; |
if (sz > MAXOBJSZ) { |
if (sz > MAXOBJSZ) { |
lim = (word *)(h + 1); |
lim = (word *)h + HDR_WORDS; |
} else { |
} else { |
lim = (word *)(h + 1) - sz; |
lim = (word *)(h + 1) - sz; |
} |
} |
Line 1071 register hdr * hhdr; |
|
Line 1181 register hdr * hhdr; |
|
GC_mark_stack_top_reg = GC_mark_stack_top; |
GC_mark_stack_top_reg = GC_mark_stack_top; |
for (p = (word *)h + HDR_WORDS, word_no = HDR_WORDS; p <= lim; |
for (p = (word *)h + HDR_WORDS, word_no = HDR_WORDS; p <= lim; |
p += sz, word_no += sz) { |
p += sz, word_no += sz) { |
/* This ignores user specified mark procs. This currently */ |
|
/* doesn't matter, since marking from the whole object */ |
|
/* is always sufficient, and we will eventually use the user */ |
|
/* mark proc to avoid any bogus pointers. */ |
|
if (mark_bit_from_hdr(hhdr, word_no)) { |
if (mark_bit_from_hdr(hhdr, word_no)) { |
/* Mark from fields inside the object */ |
/* Mark from fields inside the object */ |
PUSH_OBJ((word *)p, hhdr, GC_mark_stack_top_reg, mark_stack_limit); |
PUSH_OBJ((word *)p, hhdr, GC_mark_stack_top_reg, mark_stack_limit); |