diff options
-rw-r--r-- | gcc/ChangeLog | 10 | ||||
-rw-r--r-- | gcc/cp/ChangeLog | 4 | ||||
-rw-r--r-- | gcc/cp/exception.cc | 27 | ||||
-rw-r--r-- | gcc/eh-common.h | 18 | ||||
-rw-r--r-- | gcc/libgcc2.c | 69 |
5 files changed, 105 insertions, 23 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 7a6fcc9cc71..3f24d77affd 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,13 @@ +2000-06-06 Nathan Sidwell <nathan@codesourcery.com> + + * eh-common.h (EH_ALLOC_SIZE, EH_ALLOC_ALIGN): New #defines. + (eh_context): Add alloc_mask and alloc_buffer emergency fallback + space. + * libgcc2.c (__eh_alloc): Moved from cp/exception.cc. Fallback on + emergency eh_context buffer, if malloc fails. + (__eh_free): Moved from cp/exception.cc. Release to emergency + eh_context buffer, if appropriate. + 2000-06-06 Jason Merrill <jason@casey.soma.redhat.com> * expr.c (store_expr): Fix typo. diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 949f4c8cf0d..198ff67c02a 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,7 @@ +2000-06-06 Nathan Sidwell <nathan@codesourcery.com> + + * exception.cc: (__eh_alloc, __eh_free): Moved to libgcc2.c + 2000-06-05 Jason Merrill <jason@casey.soma.redhat.com> * search.c (maybe_suppress_debug_info): Don't check diff --git a/gcc/cp/exception.cc b/gcc/cp/exception.cc index 9263bfeaa01..1ffd7624a09 100644 --- a/gcc/cp/exception.cc +++ b/gcc/cp/exception.cc @@ -118,6 +118,10 @@ struct cp_eh_info extern "C" cp_eh_info **__get_eh_info (); // actually void ** +/* Exception allocate and free, defined in libgcc2. */ +extern "C" void *__eh_alloc(size_t); +extern "C" void __eh_free(); + /* Is P the type_info node for a pointer of some kind? */ extern bool __is_pointer (void *); @@ -159,29 +163,6 @@ __start_cp_handler (void) return p; } -/* Allocate a buffer for a cp_eh_info and an exception object of size SIZE, - and return a pointer to the beginning of the object's space. */ - -extern "C" void * malloc (size_t); -extern "C" void * -__eh_alloc (size_t size) -{ - void *p = malloc (size); - if (p == 0) - terminate (); - return p; -} - -/* Free the memory for an cp_eh_info and associated exception, given - a pointer to the cp_eh_info. */ - -extern "C" void free (void *); -extern "C" void -__eh_free (void *p) -{ - free (p); -} - extern "C" int __throw_type_match_rtti_2 (const void *, const void *, void *, void **); diff --git a/gcc/eh-common.h b/gcc/eh-common.h index 20adfd613f0..5303d6db474 100644 --- a/gcc/eh-common.h +++ b/gcc/eh-common.h @@ -39,6 +39,20 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ The routine get_dynamic_handler_chain() also has a dependancy on the location of 'dynamic_handler_chain'. If its location is changed, that routine must be modified as well. */ +#ifndef EH_ALLOC_SIZE +/* 192 bytes means the entire eh_context plus malloc overhead fits in 256 + bytes (assuming 8 byte pointers). 192 bytes gives an eh_info and object + size limit of 96 bytes. This should be sufficient for throwing bad_alloc. */ +#define EH_ALLOC_SIZE 192 +#endif +#ifndef EH_ALLOC_ALIGN +/* We can't use BIGGEST_ALIGNMENT, because on some systems, that expands to + a check on a compile time switch like + 'target_flags & MASK_ALIGN_DOUBLE ? 64 : 32'. There's no macro for + 'largest alignment for any code this compiler can build for', which is + really what is needed. */ +#define EH_ALLOC_ALIGN 16 +#endif struct eh_context { @@ -48,6 +62,10 @@ struct eh_context void *info; /* This is used to remember where we threw for re-throws */ void *table_index; /* address of exception table entry to rethrow from */ + /* emergency fallback space, if malloc fails during handling */ + char alloc_buffer[EH_ALLOC_SIZE] + __attribute__((__aligned__(EH_ALLOC_ALIGN))); + unsigned alloc_mask; }; #ifndef EH_TABLE_LOOKUP diff --git a/gcc/libgcc2.c b/gcc/libgcc2.c index 822d84e0851..305d7f085d5 100644 --- a/gcc/libgcc2.c +++ b/gcc/libgcc2.c @@ -3190,6 +3190,75 @@ eh_context_specific (void) } #endif /* __GTHREADS */ +/* Support routines for alloc/free during exception handling */ + +/* __eh_alloc and __eh_free attempt allocation using malloc, but fall back to + the small arena in the eh_context. This is needed because throwing an + out-of-memory exception would fail otherwise. The emergency space is + allocated in blocks of size EH_ALLOC_ALIGN, the + minimum allocation being two blocks. A bitmask indicates which blocks + have been allocated. To indicate the size of an allocation, the bit for + the final block is not set. Hence each allocation is a run of 1s followed + by a zero. */ +void * +__eh_alloc (size_t size) +{ + void *p; + + if (!size) + abort(); + p = malloc (size); + if (p == 0) + { + struct eh_context *eh = __get_eh_context (); + unsigned blocks = (size + EH_ALLOC_ALIGN - 1) / EH_ALLOC_ALIGN; + unsigned real_mask = eh->alloc_mask | (eh->alloc_mask << 1); + unsigned our_mask; + unsigned ix; + + if (blocks > EH_ALLOC_SIZE / EH_ALLOC_ALIGN) + __terminate (); + blocks += blocks == 1; + our_mask = (1 << blocks) - 1; + + for (ix = EH_ALLOC_SIZE / EH_ALLOC_ALIGN - blocks; ix; ix--) + if (! ((real_mask >> ix) & our_mask)) + { + /* found some space */ + p = &eh->alloc_buffer[ix * EH_ALLOC_ALIGN]; + eh->alloc_mask |= (our_mask >> 1) << ix; + return p; + } + __terminate (); + } + return p; +} + +/* Free the memory for an cp_eh_info and associated exception, given + a pointer to the cp_eh_info. */ +void +__eh_free (void *p) +{ + struct eh_context *eh = __get_eh_context (); + + ptrdiff_t diff = (char *)p - &eh->alloc_buffer[0]; + if (diff >= 0 && diff < EH_ALLOC_SIZE) + { + unsigned mask = eh->alloc_mask; + unsigned bit = 1 << (diff / EH_ALLOC_ALIGN); + + do + { + mask ^= bit; + bit <<= 1; + } + while (mask & bit); + eh->alloc_mask = mask; + } + else + free (p); +} + /* Support routines for setjmp/longjmp exception handling. */ /* Calls to __sjthrow are generated by the compiler when an exception |