| version 1.6, 2002/07/24 08:00:10 |
version 1.7, 2003/06/24 05:11:33 |
|
|
| # include <stdio.h> |
# include <stdio.h> |
| # include "private/gc_pmark.h" |
# include "private/gc_pmark.h" |
| |
|
| |
#if defined(MSWIN32) && defined(__GNUC__) |
| |
# include <excpt.h> |
| |
#endif |
| |
|
| /* We put this here to minimize the risk of inlining. */ |
/* We put this here to minimize the risk of inlining. */ |
| /*VARARGS*/ |
/*VARARGS*/ |
| #ifdef __WATCOMC__ |
#ifdef __WATCOMC__ |
| Line 261 static void alloc_mark_stack(); |
|
| Line 265 static void alloc_mark_stack(); |
|
| /* remains valid until all marking is complete. */ |
/* remains valid until all marking is complete. */ |
| /* A zero value indicates that it's OK to miss some */ |
/* A zero value indicates that it's OK to miss some */ |
| /* register values. */ |
/* register values. */ |
| GC_bool GC_mark_some(cold_gc_frame) |
/* We hold the allocation lock. In the case of */ |
| ptr_t cold_gc_frame; |
/* incremental collection, the world may not be stopped.*/ |
| |
#ifdef MSWIN32 |
| |
/* For win32, this is called after we establish a structured */ |
| |
/* exception handler, in case Windows unmaps one of our root */ |
| |
/* segments. See below. In either case, we acquire the */ |
| |
/* allocator lock long before we get here. */ |
| |
GC_bool GC_mark_some_inner(cold_gc_frame) |
| |
ptr_t cold_gc_frame; |
| |
#else |
| |
GC_bool GC_mark_some(cold_gc_frame) |
| |
ptr_t cold_gc_frame; |
| |
#endif |
| { |
{ |
| #if defined(MSWIN32) && !defined(__GNUC__) |
|
| /* 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 /* defined(MSWIN32) && !defined(__GNUC__) */ |
|
| switch(GC_mark_state) { |
switch(GC_mark_state) { |
| case MS_NONE: |
case MS_NONE: |
| return(FALSE); |
return(FALSE); |
| Line 395 ptr_t cold_gc_frame; |
|
| Line 399 ptr_t cold_gc_frame; |
|
| ABORT("GC_mark_some: bad state"); |
ABORT("GC_mark_some: bad state"); |
| return(FALSE); |
return(FALSE); |
| } |
} |
| #if defined(MSWIN32) && !defined(__GNUC__) |
} |
| } __except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? |
|
| EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { |
|
| # ifdef CONDPRINT |
#ifdef MSWIN32 |
| if (GC_print_stats) { |
|
| GC_printf0("Caught ACCESS_VIOLATION in marker. " |
# ifdef __GNUC__ |
| "Memory mapping disappeared.\n"); |
|
| |
typedef struct { |
| |
EXCEPTION_REGISTRATION ex_reg; |
| |
void *alt_path; |
| |
} ext_ex_regn; |
| |
|
| |
|
| |
static EXCEPTION_DISPOSITION mark_ex_handler( |
| |
struct _EXCEPTION_RECORD *ex_rec, |
| |
void *est_frame, |
| |
struct _CONTEXT *context, |
| |
void *disp_ctxt) |
| |
{ |
| |
if (ex_rec->ExceptionCode == STATUS_ACCESS_VIOLATION) { |
| |
ext_ex_regn *xer = (ext_ex_regn *)est_frame; |
| |
|
| |
/* Unwind from the inner function assuming the standard */ |
| |
/* function prologue. */ |
| |
/* Assumes code has not been compiled with */ |
| |
/* -fomit-frame-pointer. */ |
| |
context->Esp = context->Ebp; |
| |
context->Ebp = *((DWORD *)context->Esp); |
| |
context->Esp = context->Esp - 8; |
| |
|
| |
/* Resume execution at the "real" handler within the */ |
| |
/* wrapper function. */ |
| |
context->Eip = (DWORD )(xer->alt_path); |
| |
|
| |
return ExceptionContinueExecution; |
| |
|
| |
} else { |
| |
return ExceptionContinueSearch; |
| |
} |
| |
} |
| |
# endif /* __GNUC__ */ |
| |
|
| |
|
| |
GC_bool GC_mark_some(cold_gc_frame) |
| |
ptr_t cold_gc_frame; |
| |
{ |
| |
GC_bool ret_val; |
| |
|
| |
# ifndef __GNUC__ |
| |
/* 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 { |
| |
|
| |
# else /* __GNUC__ */ |
| |
|
| |
/* Manually install an exception handler since GCC does */ |
| |
/* not yet support Structured Exception Handling (SEH) on */ |
| |
/* Win32. */ |
| |
|
| |
ext_ex_regn er; |
| |
|
| |
er.alt_path = &&handle_ex; |
| |
er.ex_reg.handler = mark_ex_handler; |
| |
asm volatile ("movl %%fs:0, %0" : "=r" (er.ex_reg.prev)); |
| |
asm volatile ("movl %0, %%fs:0" : : "r" (&er)); |
| |
|
| |
# endif /* __GNUC__ */ |
| |
|
| |
ret_val = GC_mark_some_inner(cold_gc_frame); |
| |
|
| |
# ifndef __GNUC__ |
| |
|
| |
} __except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? |
| |
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { |
| |
|
| |
# else /* __GNUC__ */ |
| |
|
| |
/* Prevent GCC from considering the following code unreachable */ |
| |
/* and thus eliminating it. */ |
| |
if (er.alt_path != 0) |
| |
goto rm_handler; |
| |
|
| |
handle_ex: |
| |
/* Execution resumes from here on an access violation. */ |
| |
|
| |
# endif /* __GNUC__ */ |
| |
|
| |
# ifdef CONDPRINT |
| |
if (GC_print_stats) { |
| |
GC_printf0("Caught ACCESS_VIOLATION in marker. " |
| |
"Memory mapping disappeared.\n"); |
| |
} |
| |
# endif /* CONDPRINT */ |
| |
|
| |
/* We have bad roots on the stack. Discard mark stack. */ |
| |
/* Rescan from marked objects. Redetermine roots. */ |
| |
GC_invalidate_mark_state(); |
| |
scan_ptr = 0; |
| |
|
| |
ret_val = FALSE; |
| |
|
| |
# ifndef __GNUC__ |
| |
|
| } |
} |
| # endif /* CONDPRINT */ |
|
| /* We have bad roots on the stack. Discard mark stack. */ |
# else /* __GNUC__ */ |
| /* Rescan from marked objects. Redetermine roots. */ |
|
| GC_invalidate_mark_state(); |
rm_handler: |
| scan_ptr = 0; |
/* Uninstall the exception handler */ |
| return FALSE; |
asm volatile ("mov %0, %%fs:0" : : "r" (er.ex_reg.prev)); |
| |
|
| |
# endif /* __GNUC__ */ |
| |
|
| |
return ret_val; |
| } |
} |
| #endif /* defined(MSWIN32) && !defined(__GNUC__) */ |
#endif /* MSWIN32 */ |
| } |
|
| |
|
| |
|
| GC_bool GC_mark_stack_empty() |
GC_bool GC_mark_stack_empty() |
| Line 539 mse * mark_stack_limit; |
|
| Line 650 mse * mark_stack_limit; |
|
| /* Large length. */ |
/* Large length. */ |
| /* Process part of the range to avoid pushing too much on the */ |
/* Process part of the range to avoid pushing too much on the */ |
| /* stack. */ |
/* stack. */ |
| GC_ASSERT(descr < GC_greatest_plausible_heap_addr |
GC_ASSERT(descr < (word)GC_greatest_plausible_heap_addr |
| - GC_least_plausible_heap_addr); |
- (word)GC_least_plausible_heap_addr); |
| # ifdef PARALLEL_MARK |
# ifdef PARALLEL_MARK |
| # define SHARE_BYTES 2048 |
# define SHARE_BYTES 2048 |
| if (descr > SHARE_BYTES && GC_parallel |
if (descr > SHARE_BYTES && GC_parallel |
| Line 571 mse * mark_stack_limit; |
|
| Line 682 mse * mark_stack_limit; |
|
| while (descr != 0) { |
while (descr != 0) { |
| if ((signed_word)descr < 0) { |
if ((signed_word)descr < 0) { |
| current = *current_p; |
current = *current_p; |
| |
FIXUP_POINTER(current); |
| if ((ptr_t)current >= least_ha && (ptr_t)current < greatest_ha) { |
if ((ptr_t)current >= least_ha && (ptr_t)current < greatest_ha) { |
| PREFETCH(current); |
PREFETCH(current); |
| HC_PUSH_CONTENTS((ptr_t)current, mark_stack_top, |
HC_PUSH_CONTENTS((ptr_t)current, mark_stack_top, |
| Line 645 mse * mark_stack_limit; |
|
| Line 757 mse * mark_stack_limit; |
|
| PREFETCH((ptr_t)limit - PREF_DIST*CACHE_LINE_SIZE); |
PREFETCH((ptr_t)limit - PREF_DIST*CACHE_LINE_SIZE); |
| GC_ASSERT(limit >= current_p); |
GC_ASSERT(limit >= current_p); |
| deferred = *limit; |
deferred = *limit; |
| |
FIXUP_POINTER(deferred); |
| limit = (word *)((char *)limit - ALIGNMENT); |
limit = (word *)((char *)limit - ALIGNMENT); |
| if ((ptr_t)deferred >= least_ha && (ptr_t)deferred < greatest_ha) { |
if ((ptr_t)deferred >= least_ha && (ptr_t)deferred < greatest_ha) { |
| PREFETCH(deferred); |
PREFETCH(deferred); |
| Line 654 mse * mark_stack_limit; |
|
| Line 767 mse * mark_stack_limit; |
|
| /* Unroll once, so we don't do too many of the prefetches */ |
/* Unroll once, so we don't do too many of the prefetches */ |
| /* based on limit. */ |
/* based on limit. */ |
| deferred = *limit; |
deferred = *limit; |
| |
FIXUP_POINTER(deferred); |
| limit = (word *)((char *)limit - ALIGNMENT); |
limit = (word *)((char *)limit - ALIGNMENT); |
| if ((ptr_t)deferred >= least_ha && (ptr_t)deferred < greatest_ha) { |
if ((ptr_t)deferred >= least_ha && (ptr_t)deferred < greatest_ha) { |
| PREFETCH(deferred); |
PREFETCH(deferred); |
| Line 668 mse * mark_stack_limit; |
|
| Line 782 mse * mark_stack_limit; |
|
| /* Since HC_PUSH_CONTENTS expands to a lot of code, */ |
/* Since HC_PUSH_CONTENTS expands to a lot of code, */ |
| /* we don't. */ |
/* we don't. */ |
| current = *current_p; |
current = *current_p; |
| |
FIXUP_POINTER(current); |
| PREFETCH((ptr_t)current_p + PREF_DIST*CACHE_LINE_SIZE); |
PREFETCH((ptr_t)current_p + PREF_DIST*CACHE_LINE_SIZE); |
| if ((ptr_t)current >= least_ha && (ptr_t)current < greatest_ha) { |
if ((ptr_t)current >= least_ha && (ptr_t)current < greatest_ha) { |
| /* Prefetch the contents of the object we just pushed. It's */ |
/* Prefetch the contents of the object we just pushed. It's */ |
| Line 719 mse * GC_steal_mark_stack(mse * low, mse * high, mse * |
|
| Line 834 mse * GC_steal_mark_stack(mse * low, mse * high, mse * |
|
| mse *top = local - 1; |
mse *top = local - 1; |
| unsigned i = 0; |
unsigned i = 0; |
| |
|
| |
/* Make sure that prior writes to the mark stack are visible. */ |
| |
/* On some architectures, the fact that the reads are */ |
| |
/* volatile should suffice. */ |
| |
# if !defined(IA64) && !defined(HP_PA) && !defined(I386) |
| |
GC_memory_barrier(); |
| |
# endif |
| GC_ASSERT(high >= low-1 && high - low + 1 <= GC_mark_stack_size); |
GC_ASSERT(high >= low-1 && high - low + 1 <= GC_mark_stack_size); |
| for (p = low; p <= high && i <= max; ++p) { |
for (p = low; p <= high && i <= max; ++p) { |
| word descr = *(volatile word *) &(p -> mse_descr); |
word descr = *(volatile word *) &(p -> mse_descr); |
| |
/* In the IA64 memory model, the following volatile store is */ |
| |
/* ordered after this read of descr. Thus a thread must read */ |
| |
/* the original nonzero value. HP_PA appears to be similar, */ |
| |
/* and if I'm reading the P4 spec correctly, X86 is probably */ |
| |
/* also OK. In some other cases we need a barrier. */ |
| |
# if !defined(IA64) && !defined(HP_PA) && !defined(I386) |
| |
GC_memory_barrier(); |
| |
# endif |
| if (descr != 0) { |
if (descr != 0) { |
| *(volatile word *) &(p -> mse_descr) = 0; |
*(volatile word *) &(p -> mse_descr) = 0; |
| |
/* More than one thread may get this entry, but that's only */ |
| |
/* a minor performance problem. */ |
| ++top; |
++top; |
| top -> mse_descr = descr; |
top -> mse_descr = descr; |
| top -> mse_start = p -> mse_start; |
top -> mse_start = p -> mse_start; |
| GC_ASSERT( top -> mse_descr & GC_DS_TAGS != GC_DS_LENGTH || |
GC_ASSERT( top -> mse_descr & GC_DS_TAGS != GC_DS_LENGTH || |
| top -> mse_descr < GC_greatest_plausible_heap_addr |
top -> mse_descr < GC_greatest_plausible_heap_addr |
| - GC_least_plausible_heap_addr); |
- GC_least_plausible_heap_addr); |
| /* There is no synchronization here. We assume that at */ |
|
| /* least one thread will see the original descriptor. */ |
|
| /* Otherwise we need a barrier. */ |
|
| /* More than one thread may get this entry, but that's only */ |
|
| /* a minor performance problem. */ |
|
| /* If this is a big object, count it as */ |
/* If this is a big object, count it as */ |
| /* size/256 + 1 objects. */ |
/* size/256 + 1 objects. */ |
| ++i; |
++i; |
| Line 771 void GC_return_mark_stack(mse * low, mse * high) |
|
| Line 897 void GC_return_mark_stack(mse * low, mse * high) |
|
| BCOPY(low, my_start, stack_size * sizeof(mse)); |
BCOPY(low, my_start, stack_size * sizeof(mse)); |
| GC_ASSERT(GC_mark_stack_top = my_top); |
GC_ASSERT(GC_mark_stack_top = my_top); |
| # if !defined(IA64) && !defined(HP_PA) |
# if !defined(IA64) && !defined(HP_PA) |
| GC_memory_write_barrier(); |
GC_memory_barrier(); |
| # endif |
# endif |
| /* On IA64, the volatile write acts as a release barrier. */ |
/* On IA64, the volatile write acts as a release barrier. */ |
| GC_mark_stack_top = my_top + stack_size; |
GC_mark_stack_top = my_top + stack_size; |
|
|
| # define GC_least_plausible_heap_addr least_ha |
# define GC_least_plausible_heap_addr least_ha |
| |
|
| if (top == 0) return; |
if (top == 0) return; |
| /* check all pointers in range and put in push if they appear */ |
/* check all pointers in range and push if they appear */ |
| /* to be valid. */ |
/* to be valid. */ |
| lim = t - 1 /* longword */; |
lim = t - 1 /* longword */; |
| for (p = b; p <= lim; p = (word *)(((char *)p) + ALIGNMENT)) { |
for (p = b; p <= lim; p = (word *)(((char *)p) + ALIGNMENT)) { |
| q = *p; |
q = *p; |
|
|
| ptr_t top; |
ptr_t top; |
| ptr_t cold_gc_frame; |
ptr_t cold_gc_frame; |
| { |
{ |
| if (GC_all_interior_pointers) { |
if (!NEED_FIXUP_POINTER && GC_all_interior_pointers) { |
| # define EAGER_BYTES 1024 |
# define EAGER_BYTES 1024 |
| /* Push the hot end of the stack eagerly, so that register values */ |
/* Push the hot end of the stack eagerly, so that register values */ |
| /* saved inside GC frames are marked before they disappear. */ |
/* saved inside GC frames are marked before they disappear. */ |
| Line 1368 ptr_t cold_gc_frame; |
|
| Line 1494 ptr_t cold_gc_frame; |
|
| GC_push_all_stack(bottom, top); |
GC_push_all_stack(bottom, top); |
| return; |
return; |
| } |
} |
| |
GC_ASSERT(bottom <= cold_gc_frame && cold_gc_frame <= top); |
| # ifdef STACK_GROWS_DOWN |
# ifdef STACK_GROWS_DOWN |
| GC_push_all(cold_gc_frame - sizeof(ptr_t), top); |
GC_push_all(cold_gc_frame - sizeof(ptr_t), top); |
| GC_push_all_eager(bottom, cold_gc_frame); |
GC_push_all_eager(bottom, cold_gc_frame); |
| Line 1388 void GC_push_all_stack(bottom, top) |
|
| Line 1515 void GC_push_all_stack(bottom, top) |
|
| ptr_t bottom; |
ptr_t bottom; |
| ptr_t top; |
ptr_t top; |
| { |
{ |
| if (GC_all_interior_pointers) { |
if (!NEED_FIXUP_POINTER && GC_all_interior_pointers) { |
| GC_push_all(bottom, top); |
GC_push_all(bottom, top); |
| } else { |
} else { |
| GC_push_all_eager(bottom, top); |
GC_push_all_eager(bottom, top); |