Back to index

plt-scheme  4.2.1
gcc_support.c
Go to the documentation of this file.
00001 /***************************************************************************
00002 
00003 Interface between g++ and Boehm GC
00004 
00005     Copyright (c) 1991-1995 by Xerox Corporation.  All rights reserved.
00006 
00007     THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
00008     OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
00009 
00010     Permission is hereby granted to copy this code for any purpose,
00011     provided the above notices are retained on all copies.
00012 
00013     Last modified on Sun Jul 16 23:21:14 PDT 1995 by ellis
00014 
00015 This module provides runtime support for implementing the
00016 Ellis/Detlefs GC proposal, "Safe, Efficient Garbage Collection for
00017 C++", within g++, using its -fgc-keyword extension.  It defines
00018 versions of __builtin_new, __builtin_new_gc, __builtin_vec_new,
00019 __builtin_vec_new_gc, __builtin_delete, and __builtin_vec_delete that
00020 invoke the Bohem GC.  It also implements the WeakPointer.h interface.
00021 
00022 This module assumes the following configuration options of the Boehm GC:
00023 
00024     -DALL_INTERIOR_POINTERS
00025     -DDONT_ADD_BYTE_AT_END   
00026 
00027 This module adds its own required padding to the end of objects to
00028 support C/C++ "one-past-the-object" pointer semantics.
00029 
00030 ****************************************************************************/
00031 
00032 #include <stddef.h>
00033 #include "gc.h"
00034 
00035 #if defined(__STDC__) 
00036 #   define PROTO( args ) args
00037 #else
00038 #    define PROTO( args ) ()
00039 #    endif
00040 
00041 #define BITSPERBYTE 8     
00042     /* What's the portable way to do this? */
00043 
00044 
00045 typedef void (*vfp) PROTO(( void ));
00046 extern vfp __new_handler;
00047 extern void __default_new_handler PROTO(( void ));
00048 
00049 
00050 /* A destructor_proc is the compiler generated procedure representing a 
00051 C++ destructor.  The "flag" argument is a hidden argument following some
00052 compiler convention. */
00053 
00054 typedef (*destructor_proc) PROTO(( void* this, int flag ));
00055 
00056 
00057 /***************************************************************************
00058 
00059 A BI_header is the header the compiler adds to the front of
00060 new-allocated arrays of objects with destructors.  The header is
00061 padded out to a double, because that's what the compiler does to
00062 ensure proper alignment of array elements on some architectures.  
00063 
00064 int NUM_ARRAY_ELEMENTS (void* o)
00065     returns the number of array elements for array object o.
00066 
00067 char* FIRST_ELEMENT_P (void* o)
00068     returns the address of the first element of array object o.
00069 
00070 ***************************************************************************/
00071 
00072 typedef struct BI_header {
00073     int nelts;
00074     char padding [sizeof( double ) - sizeof( int )]; 
00075         /* Better way to do this? */
00076 } BI_header;
00077 
00078 #define NUM_ARRAY_ELEMENTS( o ) \
00079   (((BI_header*) o)->nelts)
00080 
00081 #define FIRST_ELEMENT_P( o ) \
00082   ((char*) o + sizeof( BI_header ))
00083 
00084 
00085 /***************************************************************************
00086 
00087 The __builtin_new routines add a descriptor word to the end of each
00088 object.   The descriptor serves two purposes.  
00089 
00090 First, the descriptor acts as padding, implementing C/C++ pointer
00091 semantics.  C and C++ allow a valid array pointer to be incremented
00092 one past the end of an object.  The extra padding ensures that the
00093 collector will recognize that such a pointer points to the object and
00094 not the next object in memory.
00095 
00096 Second, the descriptor stores three extra pieces of information,
00097 whether an object has a registered finalizer (destructor), whether it
00098 may have any weak pointers referencing it, and for collectible arrays,
00099 the element size of the array.  The element size is required for the
00100 array's finalizer to iterate through the elements of the array.  (An
00101 alternative design would have the compiler generate a finalizer
00102 procedure for each different array type.  But given the overhead of
00103 finalization, there isn't any efficiency to be gained by that.)
00104 
00105 The descriptor must be added to non-collectible as well as collectible
00106 objects, since the Ellis/Detlefs proposal allows "pointer to gc T" to
00107 be assigned to a "pointer to T", which could then be deleted.  Thus,
00108 __builtin_delete must determine at runtime whether an object is
00109 collectible, whether it has weak pointers referencing it, and whether
00110 it may have a finalizer that needs unregistering.  Though
00111 GC_REGISTER_FINALIZER doesn't care if you ask it to unregister a
00112 finalizer for an object that doesn't have one, it is a non-trivial
00113 procedure that does a hash look-up, etc.  The descriptor trades a
00114 little extra space for a significant increase in time on the fast path
00115 through delete.  (A similar argument applies to
00116 GC_UNREGISTER_DISAPPEARING_LINK).
00117 
00118 For non-array types, the space for the descriptor could be shrunk to a
00119 single byte for storing the "has finalizer" flag.  But this would save
00120 space only on arrays of char (whose size is not a multiple of the word
00121 size) and structs whose largest member is less than a word in size
00122 (very infrequent).  And it would require that programmers actually
00123 remember to call "delete[]" instead of "delete" (which they should,
00124 but there are probably lots of buggy programs out there).  For the
00125 moment, the space savings seems not worthwhile, especially considering
00126 that the Boehm GC is already quite space competitive with other
00127 malloc's.
00128 
00129 
00130 Given a pointer o to the base of an object:
00131 
00132 Descriptor* DESCRIPTOR (void* o) 
00133      returns a pointer to the descriptor for o.
00134 
00135 The implementation of descriptors relies on the fact that the GC
00136 implementation allocates objects in units of the machine's natural
00137 word size (e.g. 32 bits on a SPARC, 64 bits on an Alpha).
00138 
00139 **************************************************************************/
00140 
00141 typedef struct Descriptor {
00142     unsigned has_weak_pointers: 1;
00143     unsigned has_finalizer: 1;
00144     unsigned element_size: BITSPERBYTE * sizeof( unsigned ) - 2; 
00145 } Descriptor;
00146 
00147 #define DESCRIPTOR( o ) \
00148   ((Descriptor*) ((char*)(o) + GC_size( o ) - sizeof( Descriptor )))
00149 
00150 
00151 /**************************************************************************
00152 
00153 Implementations of global operator new() and operator delete()
00154 
00155 ***************************************************************************/
00156 
00157 
00158 void* __builtin_new( size ) 
00159     size_t size;
00160     /* 
00161     For non-gc non-array types, the compiler generates calls to
00162     __builtin_new, which allocates non-collected storage via
00163     GC_MALLOC_UNCOLLECTABLE.  This ensures that the non-collected
00164     storage will be part of the collector's root set, required by the
00165     Ellis/Detlefs semantics. */
00166 {
00167     vfp handler = __new_handler ? __new_handler : __default_new_handler;
00168 
00169     while (1) {
00170         void* o = GC_MALLOC_UNCOLLECTABLE( size + sizeof( Descriptor ) );
00171         if (o != 0) return o;
00172         (*handler) ();}}
00173 
00174 
00175 void* __builtin_vec_new( size ) 
00176     size_t size;
00177     /* 
00178     For non-gc array types, the compiler generates calls to
00179     __builtin_vec_new. */
00180 {
00181     return __builtin_new( size );}
00182 
00183 
00184 void* __builtin_new_gc( size )
00185     size_t size;
00186     /* 
00187     For gc non-array types, the compiler generates calls to
00188     __builtin_new_gc, which allocates collected storage via
00189     GC_MALLOC. */
00190 {
00191     vfp handler = __new_handler ? __new_handler : __default_new_handler;
00192 
00193     while (1) {
00194         void* o = GC_MALLOC( size + sizeof( Descriptor ) );
00195         if (o != 0) return o;
00196         (*handler) ();}}
00197 
00198 
00199 void* __builtin_new_gc_a( size )
00200     size_t size;
00201     /* 
00202     For non-pointer-containing gc non-array types, the compiler
00203     generates calls to __builtin_new_gc_a, which allocates collected
00204     storage via GC_MALLOC_ATOMIC. */
00205 {
00206     vfp handler = __new_handler ? __new_handler : __default_new_handler;
00207 
00208     while (1) {
00209         void* o = GC_MALLOC_ATOMIC( size + sizeof( Descriptor ) );
00210         if (o != 0) return o;
00211         (*handler) ();}}
00212 
00213 
00214 void* __builtin_vec_new_gc( size )
00215     size_t size;
00216     /*
00217     For gc array types, the compiler generates calls to
00218     __builtin_vec_new_gc. */
00219 {
00220     return __builtin_new_gc( size );}
00221 
00222 
00223 void* __builtin_vec_new_gc_a( size )
00224     size_t size;
00225     /*
00226     For non-pointer-containing gc array types, the compiler generates
00227     calls to __builtin_vec_new_gc_a. */
00228 {
00229     return __builtin_new_gc_a( size );}
00230 
00231 
00232 static void call_destructor( o, data )
00233     void* o;
00234     void* data;
00235     /* 
00236     call_destructor is the GC finalizer proc registered for non-array
00237     gc objects with destructors.  Its client data is the destructor
00238     proc, which it calls with the magic integer 2, a special flag
00239     obeying the compiler convention for destructors. */
00240 {
00241     ((destructor_proc) data)( o, 2 );}
00242 
00243 
00244 void* __builtin_new_gc_dtor( o, d )
00245     void* o;
00246     destructor_proc d;
00247     /* 
00248     The compiler generates a call to __builtin_new_gc_dtor to register
00249     the destructor "d" of a non-array gc object "o" as a GC finalizer.
00250     The destructor is registered via
00251     GC_REGISTER_FINALIZER_IGNORE_SELF, which causes the collector to
00252     ignore pointers from the object to itself when determining when
00253     the object can be finalized.  This is necessary due to the self
00254     pointers used in the internal representation of multiply-inherited
00255     objects. */
00256 {
00257     Descriptor* desc = DESCRIPTOR( o );
00258 
00259     GC_REGISTER_FINALIZER_IGNORE_SELF( o, call_destructor, d, 0, 0 );
00260     desc->has_finalizer = 1;}
00261 
00262 
00263 static void call_array_destructor( o, data )
00264     void* o;
00265     void* data;
00266     /*
00267     call_array_destructor is the GC finalizer proc registered for gc
00268     array objects whose elements have destructors. Its client data is
00269     the destructor proc.  It iterates through the elements of the
00270     array in reverse order, calling the destructor on each. */
00271 {
00272     int num = NUM_ARRAY_ELEMENTS( o );
00273     Descriptor* desc = DESCRIPTOR( o );
00274     size_t size = desc->element_size;
00275     char* first_p = FIRST_ELEMENT_P( o );
00276     char* p = first_p + (num - 1) * size;
00277 
00278     if (num > 0) {
00279         while (1) {
00280             ((destructor_proc) data)( p, 2 );
00281             if (p == first_p) break;
00282             p -= size;}}}
00283 
00284 
00285 void* __builtin_vec_new_gc_dtor( first_elem, d, element_size )
00286     void* first_elem;
00287     destructor_proc d;
00288     size_t element_size;
00289     /* 
00290     The compiler generates a call to __builtin_vec_new_gc_dtor to
00291     register the destructor "d" of a gc array object as a GC
00292     finalizer.  "first_elem" points to the first element of the array,
00293     *not* the beginning of the object (this makes the generated call
00294     to this function smaller).  The elements of the array are of size
00295     "element_size".  The destructor is registered as in
00296     _builtin_new_gc_dtor. */
00297 {
00298     void* o = (char*) first_elem - sizeof( BI_header );
00299     Descriptor* desc = DESCRIPTOR( o );
00300 
00301     GC_REGISTER_FINALIZER_IGNORE_SELF( o, call_array_destructor, d, 0, 0 );
00302     desc->element_size = element_size;
00303     desc->has_finalizer = 1;}
00304 
00305 
00306 void __builtin_delete( o )
00307     void* o;
00308     /* 
00309     The compiler generates calls to __builtin_delete for operator
00310     delete().  The GC currently requires that any registered
00311     finalizers be unregistered before explicitly freeing an object.
00312     If the object has any weak pointers referencing it, we can't
00313     actually free it now. */
00314 {
00315   if (o != 0) { 
00316       Descriptor* desc = DESCRIPTOR( o );
00317       if (desc->has_finalizer) GC_REGISTER_FINALIZER( o, 0, 0, 0, 0 );
00318       if (! desc->has_weak_pointers) GC_FREE( o );}}
00319 
00320 
00321 void __builtin_vec_delete( o )
00322     void* o;
00323     /* 
00324     The compiler generates calls to __builitn_vec_delete for operator
00325     delete[](). */
00326 {
00327   __builtin_delete( o );}
00328 
00329 
00330 /**************************************************************************
00331 
00332 Implementations of the template class WeakPointer from WeakPointer.h
00333 
00334 ***************************************************************************/
00335 
00336 typedef struct WeakPointer {
00337     void* pointer; 
00338 } WeakPointer;
00339 
00340 
00341 void* _WeakPointer_New( t )
00342     void* t;
00343 {
00344     if (t == 0) {
00345         return 0;}
00346     else {
00347         void* base = GC_base( t );
00348         WeakPointer* wp = 
00349             (WeakPointer*) GC_MALLOC_ATOMIC( sizeof( WeakPointer ) );
00350         Descriptor* desc = DESCRIPTOR( base );
00351 
00352         wp->pointer = t;
00353         desc->has_weak_pointers = 1;
00354         GC_general_register_disappearing_link( &wp->pointer, base );
00355         return wp;}}
00356 
00357 
00358 static void* PointerWithLock( wp ) 
00359     WeakPointer* wp;
00360 {
00361     if (wp == 0 || wp->pointer == 0) {
00362       return 0;}
00363     else {
00364         return (void*) wp->pointer;}}
00365 
00366 
00367 void* _WeakPointer_Pointer( wp )
00368     WeakPointer* wp;
00369 {
00370     return (void*) GC_call_with_alloc_lock( PointerWithLock, wp );}
00371 
00372 
00373 typedef struct EqualClosure {
00374     WeakPointer* wp1;
00375     WeakPointer* wp2;
00376 } EqualClosure;
00377 
00378 
00379 static void* EqualWithLock( ec )
00380     EqualClosure* ec;
00381 {
00382     if (ec->wp1 == 0 || ec->wp2 == 0) {
00383         return (void*) (ec->wp1 == ec->wp2);}
00384     else {
00385       return (void*) (ec->wp1->pointer == ec->wp2->pointer);}}
00386 
00387 
00388 int _WeakPointer_Equal( wp1,  wp2 )
00389     WeakPointer* wp1;
00390     WeakPointer* wp2;
00391 {
00392     EqualClosure ec;
00393 
00394     ec.wp1 = wp1;
00395     ec.wp2 = wp2;
00396     return (int) GC_call_with_alloc_lock( EqualWithLock, &ec );}
00397 
00398 
00399 int _WeakPointer_Hash( wp )
00400     WeakPointer* wp;
00401 {
00402     return (int) _WeakPointer_Pointer( wp );}
00403 
00404 
00405 /**************************************************************************
00406 
00407 Implementations of the template class CleanUp from WeakPointer.h
00408 
00409 ***************************************************************************/
00410 
00411 typedef struct Closure {
00412     void (*c) PROTO(( void* d, void* t ));
00413     ptrdiff_t t_offset; 
00414     void* d;
00415 } Closure;
00416 
00417 
00418 static void _CleanUp_CallClosure( obj, data ) 
00419     void* obj;
00420     void* data;
00421 {
00422     Closure* closure = (Closure*) data;
00423     closure->c( closure->d, (char*) obj + closure->t_offset );}
00424 
00425 
00426 void _CleanUp_Set( t, c, d ) 
00427     void* t;
00428     void (*c) PROTO(( void* d, void* t ));
00429     void* d;
00430 {
00431     void* base = GC_base( t );
00432     Descriptor* desc = DESCRIPTOR( t );
00433 
00434     if (c == 0) {
00435         GC_REGISTER_FINALIZER_IGNORE_SELF( base, 0, 0, 0, 0 );
00436         desc->has_finalizer = 0;}
00437     else {
00438         Closure* closure = (Closure*) GC_MALLOC( sizeof( Closure ) );
00439         closure->c = c;
00440         closure->t_offset = (char*) t - (char*) base;
00441         closure->d = d;
00442         GC_REGISTER_FINALIZER_IGNORE_SELF( base, _CleanUp_CallClosure, 
00443                                            closure, 0, 0 );
00444         desc->has_finalizer = 1;}}
00445 
00446 
00447 void _CleanUp_Call( t ) 
00448     void* t;
00449 {
00450       /* ? Aren't we supposed to deactivate weak pointers to t too? 
00451          Why? */
00452     void* base = GC_base( t );
00453     void* d;
00454     GC_finalization_proc f;
00455 
00456     GC_REGISTER_FINALIZER( base, 0, 0, &f, &d );
00457     f( base, d );}
00458 
00459 
00460 typedef struct QueueElem {
00461     void* o;
00462     GC_finalization_proc f;
00463     void* d;
00464     struct QueueElem* next; 
00465 } QueueElem;
00466 
00467 
00468 void* _CleanUp_Queue_NewHead()
00469 {
00470     return GC_MALLOC( sizeof( QueueElem ) );}
00471     
00472      
00473 static void _CleanUp_Queue_Enqueue( obj, data )
00474     void* obj; 
00475     void* data;
00476 {
00477     QueueElem* q = (QueueElem*) data;
00478     QueueElem* head = q->next;
00479 
00480     q->o = obj;
00481     q->next = head->next;
00482     head->next = q;}
00483     
00484     
00485 void _CleanUp_Queue_Set( h, t ) 
00486     void* h;
00487     void* t;
00488 {
00489     QueueElem* head = (QueueElem*) h;
00490     void* base = GC_base( t );
00491     void* d;
00492     GC_finalization_proc f;
00493     QueueElem* q = (QueueElem*) GC_MALLOC( sizeof( QueueElem ) );
00494      
00495     GC_REGISTER_FINALIZER( base, _CleanUp_Queue_Enqueue, q, &f, &d );
00496     q->f = f;
00497     q->d = d;
00498     q->next = head;}
00499     
00500 
00501 int _CleanUp_Queue_Call( h ) 
00502     void* h;
00503 {
00504     QueueElem* head = (QueueElem*) h;
00505     QueueElem* q = head->next;
00506 
00507     if (q == 0) {
00508         return 0;}
00509     else {
00510         head->next = q->next;
00511         q->next = 0;
00512         if (q->f != 0) q->f( q->o, q->d );
00513         return 1;}}
00514 
00515 
00516