| /*************************************************************************** |
| |
| Interface between g++ and Boehm GC |
| |
| Copyright (c) 1991-1995 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 copy this code for any purpose, |
| provided the above notices are retained on all copies. |
| |
| Last modified on Sun Jul 16 23:21:14 PDT 1995 by ellis |
| |
| This module provides runtime support for implementing the |
| Ellis/Detlefs GC proposal, "Safe, Efficient Garbage Collection for |
| C++", within g++, using its -fgc-keyword extension. It defines |
| versions of __builtin_new, __builtin_new_gc, __builtin_vec_new, |
| __builtin_vec_new_gc, __builtin_delete, and __builtin_vec_delete that |
| invoke the Bohem GC. It also implements the WeakPointer.h interface. |
| |
| This module assumes the following configuration options of the Boehm GC: |
| |
| -DALL_INTERIOR_POINTERS |
| -DDONT_ADD_BYTE_AT_END |
| |
| This module adds its own required padding to the end of objects to |
| support C/C++ "one-past-the-object" pointer semantics. |
| |
| ****************************************************************************/ |
| |
| #include <stddef.h> |
| #include "gc.h" |
| |
| #if defined(__STDC__) |
| # define PROTO( args ) args |
| #else |
| # define PROTO( args ) () |
| # endif |
| |
| #define BITSPERBYTE 8 |
| /* What's the portable way to do this? */ |
| |
| |
| typedef void (*vfp) PROTO(( void )); |
| extern vfp __new_handler; |
| extern void __default_new_handler PROTO(( void )); |
| |
| |
| /* A destructor_proc is the compiler generated procedure representing a |
| C++ destructor. The "flag" argument is a hidden argument following some |
| compiler convention. */ |
| |
| typedef (*destructor_proc) PROTO(( void* this, int flag )); |
| |
| |
| /*************************************************************************** |
| |
| A BI_header is the header the compiler adds to the front of |
| new-allocated arrays of objects with destructors. The header is |
| padded out to a double, because that's what the compiler does to |
| ensure proper alignment of array elements on some architectures. |
| |
| int NUM_ARRAY_ELEMENTS (void* o) |
| returns the number of array elements for array object o. |
| |
| char* FIRST_ELEMENT_P (void* o) |
| returns the address of the first element of array object o. |
| |
| ***************************************************************************/ |
| |
| typedef struct BI_header { |
| int nelts; |
| char padding [sizeof( double ) - sizeof( int )]; |
| /* Better way to do this? */ |
| } BI_header; |
| |
| #define NUM_ARRAY_ELEMENTS( o ) \ |
| (((BI_header*) o)->nelts) |
| |
| #define FIRST_ELEMENT_P( o ) \ |
| ((char*) o + sizeof( BI_header )) |
| |
| |
| /*************************************************************************** |
| |
| The __builtin_new routines add a descriptor word to the end of each |
| object. The descriptor serves two purposes. |
| |
| First, the descriptor acts as padding, implementing C/C++ pointer |
| semantics. C and C++ allow a valid array pointer to be incremented |
| one past the end of an object. The extra padding ensures that the |
| collector will recognize that such a pointer points to the object and |
| not the next object in memory. |
| |
| Second, the descriptor stores three extra pieces of information, |
| whether an object has a registered finalizer (destructor), whether it |
| may have any weak pointers referencing it, and for collectible arrays, |
| the element size of the array. The element size is required for the |
| array's finalizer to iterate through the elements of the array. (An |
| alternative design would have the compiler generate a finalizer |
| procedure for each different array type. But given the overhead of |
| finalization, there isn't any efficiency to be gained by that.) |
| |
| The descriptor must be added to non-collectible as well as collectible |
| objects, since the Ellis/Detlefs proposal allows "pointer to gc T" to |
| be assigned to a "pointer to T", which could then be deleted. Thus, |
| __builtin_delete must determine at runtime whether an object is |
| collectible, whether it has weak pointers referencing it, and whether |
| it may have a finalizer that needs unregistering. Though |
| GC_REGISTER_FINALIZER doesn't care if you ask it to unregister a |
| finalizer for an object that doesn't have one, it is a non-trivial |
| procedure that does a hash look-up, etc. The descriptor trades a |
| little extra space for a significant increase in time on the fast path |
| through delete. (A similar argument applies to |
| GC_UNREGISTER_DISAPPEARING_LINK). |
| |
| For non-array types, the space for the descriptor could be shrunk to a |
| single byte for storing the "has finalizer" flag. But this would save |
| space only on arrays of char (whose size is not a multiple of the word |
| size) and structs whose largest member is less than a word in size |
| (very infrequent). And it would require that programmers actually |
| remember to call "delete[]" instead of "delete" (which they should, |
| but there are probably lots of buggy programs out there). For the |
| moment, the space savings seems not worthwhile, especially considering |
| that the Boehm GC is already quite space competitive with other |
| malloc's. |
| |
| |
| Given a pointer o to the base of an object: |
| |
| Descriptor* DESCRIPTOR (void* o) |
| returns a pointer to the descriptor for o. |
| |
| The implementation of descriptors relies on the fact that the GC |
| implementation allocates objects in units of the machine's natural |
| word size (e.g. 32 bits on a SPARC, 64 bits on an Alpha). |
| |
| **************************************************************************/ |
| |
| typedef struct Descriptor { |
| unsigned has_weak_pointers: 1; |
| unsigned has_finalizer: 1; |
| unsigned element_size: BITSPERBYTE * sizeof( unsigned ) - 2; |
| } Descriptor; |
| |
| #define DESCRIPTOR( o ) \ |
| ((Descriptor*) ((char*)(o) + GC_size( o ) - sizeof( Descriptor ))) |
| |
| |
| /************************************************************************** |
| |
| Implementations of global operator new() and operator delete() |
| |
| ***************************************************************************/ |
| |
| |
| void* __builtin_new( size ) |
| size_t size; |
| /* |
| For non-gc non-array types, the compiler generates calls to |
| __builtin_new, which allocates non-collected storage via |
| GC_MALLOC_UNCOLLECTABLE. This ensures that the non-collected |
| storage will be part of the collector's root set, required by the |
| Ellis/Detlefs semantics. */ |
| { |
| vfp handler = __new_handler ? __new_handler : __default_new_handler; |
| |
| while (1) { |
| void* o = GC_MALLOC_UNCOLLECTABLE( size + sizeof( Descriptor ) ); |
| if (o != 0) return o; |
| (*handler) ();}} |
| |
| |
| void* __builtin_vec_new( size ) |
| size_t size; |
| /* |
| For non-gc array types, the compiler generates calls to |
| __builtin_vec_new. */ |
| { |
| return __builtin_new( size );} |
| |
| |
| void* __builtin_new_gc( size ) |
| size_t size; |
| /* |
| For gc non-array types, the compiler generates calls to |
| __builtin_new_gc, which allocates collected storage via |
| GC_MALLOC. */ |
| { |
| vfp handler = __new_handler ? __new_handler : __default_new_handler; |
| |
| while (1) { |
| void* o = GC_MALLOC( size + sizeof( Descriptor ) ); |
| if (o != 0) return o; |
| (*handler) ();}} |
| |
| |
| void* __builtin_new_gc_a( size ) |
| size_t size; |
| /* |
| For non-pointer-containing gc non-array types, the compiler |
| generates calls to __builtin_new_gc_a, which allocates collected |
| storage via GC_MALLOC_ATOMIC. */ |
| { |
| vfp handler = __new_handler ? __new_handler : __default_new_handler; |
| |
| while (1) { |
| void* o = GC_MALLOC_ATOMIC( size + sizeof( Descriptor ) ); |
| if (o != 0) return o; |
| (*handler) ();}} |
| |
| |
| void* __builtin_vec_new_gc( size ) |
| size_t size; |
| /* |
| For gc array types, the compiler generates calls to |
| __builtin_vec_new_gc. */ |
| { |
| return __builtin_new_gc( size );} |
| |
| |
| void* __builtin_vec_new_gc_a( size ) |
| size_t size; |
| /* |
| For non-pointer-containing gc array types, the compiler generates |
| calls to __builtin_vec_new_gc_a. */ |
| { |
| return __builtin_new_gc_a( size );} |
| |
| |
| static void call_destructor( o, data ) |
| void* o; |
| void* data; |
| /* |
| call_destructor is the GC finalizer proc registered for non-array |
| gc objects with destructors. Its client data is the destructor |
| proc, which it calls with the magic integer 2, a special flag |
| obeying the compiler convention for destructors. */ |
| { |
| ((destructor_proc) data)( o, 2 );} |
| |
| |
| void* __builtin_new_gc_dtor( o, d ) |
| void* o; |
| destructor_proc d; |
| /* |
| The compiler generates a call to __builtin_new_gc_dtor to register |
| the destructor "d" of a non-array gc object "o" as a GC finalizer. |
| The destructor is registered via |
| GC_REGISTER_FINALIZER_IGNORE_SELF, which causes the collector to |
| ignore pointers from the object to itself when determining when |
| the object can be finalized. This is necessary due to the self |
| pointers used in the internal representation of multiply-inherited |
| objects. */ |
| { |
| Descriptor* desc = DESCRIPTOR( o ); |
| |
| GC_REGISTER_FINALIZER_IGNORE_SELF( o, call_destructor, d, 0, 0 ); |
| desc->has_finalizer = 1;} |
| |
| |
| static void call_array_destructor( o, data ) |
| void* o; |
| void* data; |
| /* |
| call_array_destructor is the GC finalizer proc registered for gc |
| array objects whose elements have destructors. Its client data is |
| the destructor proc. It iterates through the elements of the |
| array in reverse order, calling the destructor on each. */ |
| { |
| int num = NUM_ARRAY_ELEMENTS( o ); |
| Descriptor* desc = DESCRIPTOR( o ); |
| size_t size = desc->element_size; |
| char* first_p = FIRST_ELEMENT_P( o ); |
| char* p = first_p + (num - 1) * size; |
| |
| if (num > 0) { |
| while (1) { |
| ((destructor_proc) data)( p, 2 ); |
| if (p == first_p) break; |
| p -= size;}}} |
| |
| |
| void* __builtin_vec_new_gc_dtor( first_elem, d, element_size ) |
| void* first_elem; |
| destructor_proc d; |
| size_t element_size; |
| /* |
| The compiler generates a call to __builtin_vec_new_gc_dtor to |
| register the destructor "d" of a gc array object as a GC |
| finalizer. "first_elem" points to the first element of the array, |
| *not* the beginning of the object (this makes the generated call |
| to this function smaller). The elements of the array are of size |
| "element_size". The destructor is registered as in |
| _builtin_new_gc_dtor. */ |
| { |
| void* o = (char*) first_elem - sizeof( BI_header ); |
| Descriptor* desc = DESCRIPTOR( o ); |
| |
| GC_REGISTER_FINALIZER_IGNORE_SELF( o, call_array_destructor, d, 0, 0 ); |
| desc->element_size = element_size; |
| desc->has_finalizer = 1;} |
| |
| |
| void __builtin_delete( o ) |
| void* o; |
| /* |
| The compiler generates calls to __builtin_delete for operator |
| delete(). The GC currently requires that any registered |
| finalizers be unregistered before explicitly freeing an object. |
| If the object has any weak pointers referencing it, we can't |
| actually free it now. */ |
| { |
| if (o != 0) { |
| Descriptor* desc = DESCRIPTOR( o ); |
| if (desc->has_finalizer) GC_REGISTER_FINALIZER( o, 0, 0, 0, 0 ); |
| if (! desc->has_weak_pointers) GC_FREE( o );}} |
| |
| |
| void __builtin_vec_delete( o ) |
| void* o; |
| /* |
| The compiler generates calls to __builitn_vec_delete for operator |
| delete[](). */ |
| { |
| __builtin_delete( o );} |
| |
| |
| /************************************************************************** |
| |
| Implementations of the template class WeakPointer from WeakPointer.h |
| |
| ***************************************************************************/ |
| |
| typedef struct WeakPointer { |
| void* pointer; |
| } WeakPointer; |
| |
| |
| void* _WeakPointer_New( t ) |
| void* t; |
| { |
| if (t == 0) { |
| return 0;} |
| else { |
| void* base = GC_base( t ); |
| WeakPointer* wp = |
| (WeakPointer*) GC_MALLOC_ATOMIC( sizeof( WeakPointer ) ); |
| Descriptor* desc = DESCRIPTOR( base ); |
| |
| wp->pointer = t; |
| desc->has_weak_pointers = 1; |
| GC_general_register_disappearing_link( &wp->pointer, base ); |
| return wp;}} |
| |
| |
| static void* PointerWithLock( wp ) |
| WeakPointer* wp; |
| { |
| if (wp == 0 || wp->pointer == 0) { |
| return 0;} |
| else { |
| return (void*) wp->pointer;}} |
| |
| |
| void* _WeakPointer_Pointer( wp ) |
| WeakPointer* wp; |
| { |
| return (void*) GC_call_with_alloc_lock( PointerWithLock, wp );} |
| |
| |
| typedef struct EqualClosure { |
| WeakPointer* wp1; |
| WeakPointer* wp2; |
| } EqualClosure; |
| |
| |
| static void* EqualWithLock( ec ) |
| EqualClosure* ec; |
| { |
| if (ec->wp1 == 0 || ec->wp2 == 0) { |
| return (void*) (ec->wp1 == ec->wp2);} |
| else { |
| return (void*) (ec->wp1->pointer == ec->wp2->pointer);}} |
| |
| |
| int _WeakPointer_Equal( wp1, wp2 ) |
| WeakPointer* wp1; |
| WeakPointer* wp2; |
| { |
| EqualClosure ec; |
| |
| ec.wp1 = wp1; |
| ec.wp2 = wp2; |
| return (int) GC_call_with_alloc_lock( EqualWithLock, &ec );} |
| |
| |
| int _WeakPointer_Hash( wp ) |
| WeakPointer* wp; |
| { |
| return (int) _WeakPointer_Pointer( wp );} |
| |
| |
| /************************************************************************** |
| |
| Implementations of the template class CleanUp from WeakPointer.h |
| |
| ***************************************************************************/ |
| |
| typedef struct Closure { |
| void (*c) PROTO(( void* d, void* t )); |
| ptrdiff_t t_offset; |
| void* d; |
| } Closure; |
| |
| |
| static void _CleanUp_CallClosure( obj, data ) |
| void* obj; |
| void* data; |
| { |
| Closure* closure = (Closure*) data; |
| closure->c( closure->d, (char*) obj + closure->t_offset );} |
| |
| |
| void _CleanUp_Set( t, c, d ) |
| void* t; |
| void (*c) PROTO(( void* d, void* t )); |
| void* d; |
| { |
| void* base = GC_base( t ); |
| Descriptor* desc = DESCRIPTOR( t ); |
| |
| if (c == 0) { |
| GC_REGISTER_FINALIZER_IGNORE_SELF( base, 0, 0, 0, 0 ); |
| desc->has_finalizer = 0;} |
| else { |
| Closure* closure = (Closure*) GC_MALLOC( sizeof( Closure ) ); |
| closure->c = c; |
| closure->t_offset = (char*) t - (char*) base; |
| closure->d = d; |
| GC_REGISTER_FINALIZER_IGNORE_SELF( base, _CleanUp_CallClosure, |
| closure, 0, 0 ); |
| desc->has_finalizer = 1;}} |
| |
| |
| void _CleanUp_Call( t ) |
| void* t; |
| { |
| /* ? Aren't we supposed to deactivate weak pointers to t too? |
| Why? */ |
| void* base = GC_base( t ); |
| void* d; |
| GC_finalization_proc f; |
| |
| GC_REGISTER_FINALIZER( base, 0, 0, &f, &d ); |
| f( base, d );} |
| |
| |
| typedef struct QueueElem { |
| void* o; |
| GC_finalization_proc f; |
| void* d; |
| struct QueueElem* next; |
| } QueueElem; |
| |
| |
| void* _CleanUp_Queue_NewHead() |
| { |
| return GC_MALLOC( sizeof( QueueElem ) );} |
| |
| |
| static void _CleanUp_Queue_Enqueue( obj, data ) |
| void* obj; |
| void* data; |
| { |
| QueueElem* q = (QueueElem*) data; |
| QueueElem* head = q->next; |
| |
| q->o = obj; |
| q->next = head->next; |
| head->next = q;} |
| |
| |
| void _CleanUp_Queue_Set( h, t ) |
| void* h; |
| void* t; |
| { |
| QueueElem* head = (QueueElem*) h; |
| void* base = GC_base( t ); |
| void* d; |
| GC_finalization_proc f; |
| QueueElem* q = (QueueElem*) GC_MALLOC( sizeof( QueueElem ) ); |
| |
| GC_REGISTER_FINALIZER( base, _CleanUp_Queue_Enqueue, q, &f, &d ); |
| q->f = f; |
| q->d = d; |
| q->next = head;} |
| |
| |
| int _CleanUp_Queue_Call( h ) |
| void* h; |
| { |
| QueueElem* head = (QueueElem*) h; |
| QueueElem* q = head->next; |
| |
| if (q == 0) { |
| return 0;} |
| else { |
| head->next = q->next; |
| q->next = 0; |
| if (q->f != 0) q->f( q->o, q->d ); |
| return 1;}} |
| |
| |
| |