From: Trevor Saunders <tsaund...@mozilla.com> Hi,
This implements finalizers by keeping a list of registered finalizers and after every mark but before sweeping check to see if any of them are for unmarked blocks. bootstrapped + regtested on x86_64-unknown-linux-gnu, ok? Trev gcc/ChangeLog: * ggc-common.c (ggc_internal_cleared_alloc): Adjust. * ggc-none.c (ggc_internal_alloc): Assert if a finalizer is passed. (ggc_internal_cleared_alloc): Likewise. * ggc-page.c (finalizer): New class. (globals::finalizer_lists): New member. (ggc_internal_alloc): Record the finalizer if any for the block being allocated. (ggc_handle_finalizers): New function. (ggc_collect): Call ggc_handle_finalizers. * ggc.h (ggc_internal_alloc):Add arguments to allow installing a finalizer. (ggc_internal_cleared_alloc): Likewise. (finalize): New function. (need_finalization_p): Likewise. (ggc_alloc): Install the type's destructor as the finalizer if it might do something. (ggc_cleared_alloc): Likewise. (ggc_vec_alloc): Likewise. (ggc_cleared_vec_alloc): Likewise. --- gcc/ggc-common.c | 5 +++-- gcc/ggc-none.c | 8 ++++++-- gcc/ggc-page.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++++- gcc/ggc.h | 52 ++++++++++++++++++++++++++++++++++++++++++++++------ 4 files changed, 104 insertions(+), 11 deletions(-) diff --git a/gcc/ggc-common.c b/gcc/ggc-common.c index e89cc64..b11a10c 100644 --- a/gcc/ggc-common.c +++ b/gcc/ggc-common.c @@ -174,9 +174,10 @@ ggc_mark_roots (void) /* Allocate a block of memory, then clear it. */ void * -ggc_internal_cleared_alloc (size_t size MEM_STAT_DECL) +ggc_internal_cleared_alloc (size_t size, void (*f)(void *), size_t s, size_t n + MEM_STAT_DECL) { - void *buf = ggc_internal_alloc (size PASS_MEM_STAT); + void *buf = ggc_internal_alloc (size, f, s, n PASS_MEM_STAT); memset (buf, 0, size); return buf; } diff --git a/gcc/ggc-none.c b/gcc/ggc-none.c index aad89bf..97d3566 100644 --- a/gcc/ggc-none.c +++ b/gcc/ggc-none.c @@ -41,14 +41,18 @@ ggc_round_alloc_size (size_t requested_size) } void * -ggc_internal_alloc (size_t size MEM_STAT_DECL) +ggc_internal_alloc (size_t size, void (*f)(void *), size_t, size_t + MEM_STAT_DECL) { + gcc_assert (!f); // ggc-none doesn't support finalizers return xmalloc (size); } void * -ggc_internal_cleared_alloc (size_t size MEM_STAT_DECL) +ggc_internal_cleared_alloc (size_t size, void (*f)(void *), size_t, size_t + MEM_STAT_DECL) { + gcc_assert (!f); // ggc-none doesn't support finalizers return xcalloc (size, 1); } diff --git a/gcc/ggc-page.c b/gcc/ggc-page.c index ae5e88a..208b526 100644 --- a/gcc/ggc-page.c +++ b/gcc/ggc-page.c @@ -332,6 +332,27 @@ typedef struct page_table_chain #endif +class finalizer +{ +public: + finalizer (uintptr_t addr, void (*f)(void *), size_t s, size_t n) : + m_addr (addr), m_function (f), m_object_size (s), m_n_objects (n) {} + + void call () const + { + for (size_t i = 0; i < m_n_objects; i++) + m_function (reinterpret_cast<void *> (m_addr + (i * m_object_size))); + } + + uintptr_t addr () const { return m_addr; } + +private: + uintptr_t m_addr; + void (*m_function)(void *); + size_t m_object_size; + size_t m_n_objects; +}; + #ifdef ENABLE_GC_ALWAYS_COLLECT /* List of free objects to be verified as actually free on the next collection. */ @@ -425,6 +446,9 @@ static struct globals better runtime data access pattern. */ unsigned long **save_in_use; + /* finalizer_lists[i] is the list of finalizers for blocks of order i. */ + vec<finalizer> finalizer_lists[NUM_ORDERS]; + #ifdef ENABLE_GC_ALWAYS_COLLECT /* List of free objects to be verified as actually free on the next collection. */ @@ -1202,7 +1226,8 @@ ggc_round_alloc_size (size_t requested_size) /* Allocate a chunk of memory of SIZE bytes. Its contents are undefined. */ void * -ggc_internal_alloc (size_t size MEM_STAT_DECL) +ggc_internal_alloc (size_t size, void (*f)(void *), size_t s, size_t n + MEM_STAT_DECL) { size_t order, word, bit, object_offset, object_size; struct page_entry *entry; @@ -1345,6 +1370,10 @@ ggc_internal_alloc (size_t size MEM_STAT_DECL) /* For timevar statistics. */ timevar_ggc_mem_total += object_size; + if (f) + G.finalizer_lists[order] + .safe_push (finalizer (reinterpret_cast<uintptr_t> (result), f, s, n)); + if (GATHER_STATISTICS) { size_t overhead = object_size - size; @@ -1811,6 +1840,24 @@ clear_marks (void) } } +static void +ggc_handle_finalizers () +{ + for (unsigned int order = 2; order < NUM_ORDERS; order++) + { + unsigned int length = G.finalizer_lists[order].length (); + for (unsigned int i = length - 1; i < length; i--) + { + finalizer &f = G.finalizer_lists[order][i]; + if (!ggc_marked_p (reinterpret_cast<void *> (f.addr ()))) + { + f.call (); + G.finalizer_lists[order].ordered_remove (i); + } + } + } +} + /* Free all empty pages. Partially empty pages need no attention because the `mark' bit doubles as an `unused' bit. */ @@ -2075,6 +2122,7 @@ ggc_collect (void) clear_marks (); ggc_mark_roots (); + ggc_handle_finalizers (); if (GATHER_STATISTICS) ggc_prune_overhead_list (); diff --git a/gcc/ggc.h b/gcc/ggc.h index 50fb199..4c59597 100644 --- a/gcc/ggc.h +++ b/gcc/ggc.h @@ -136,13 +136,16 @@ extern void gt_pch_save (FILE *f); /* Allocation. */ /* The internal primitive. */ -extern void *ggc_internal_alloc (size_t CXX_MEM_STAT_INFO) ATTRIBUTE_MALLOC; +extern void *ggc_internal_alloc (size_t, void (*)(void *) = NULL, size_t = 0, + size_t = 1 CXX_MEM_STAT_INFO) + ATTRIBUTE_MALLOC; extern size_t ggc_round_alloc_size (size_t requested_size); /* Allocates cleared memory. */ -extern void *ggc_internal_cleared_alloc (size_t CXX_MEM_STAT_INFO) - ATTRIBUTE_MALLOC; +extern void *ggc_internal_cleared_alloc (size_t, void (*)(void *) = NULL, + size_t = 0, size_t = 1 + CXX_MEM_STAT_INFO) ATTRIBUTE_MALLOC; /* Resize a block. */ extern void *ggc_realloc (void *, size_t CXX_MEM_STAT_INFO); @@ -157,24 +160,55 @@ extern void dump_ggc_loc_statistics (bool); ((T *) ggc_realloc ((P), (N) * sizeof (T) MEM_STAT_INFO)) template<typename T> +void +finalize (void *p) +{ + static_cast<T *> (p)->~T (); +} + +template<typename T> +static inline bool +need_finalization_p () +{ +#if GCC_VERSION >= 4003 + return !__has_trivial_destructor (T); +#else + return true; +#endif +} + +template<typename T> static inline T * ggc_alloc (ALONE_CXX_MEM_STAT_INFO) { - return static_cast<T *> (ggc_internal_alloc (sizeof (T) PASS_MEM_STAT)); + if (need_finalization_p<T> ()) + return static_cast<T *> (ggc_internal_alloc (sizeof (T), finalize<T> + PASS_MEM_STAT)); + else + return static_cast<T *> (ggc_internal_alloc (sizeof (T) PASS_MEM_STAT)); } template<typename T> static inline T * ggc_cleared_alloc (ALONE_CXX_MEM_STAT_INFO) { - return static_cast<T *> (ggc_internal_cleared_alloc (sizeof (T) - PASS_MEM_STAT)); + if (need_finalization_p<T> ()) + return static_cast<T *> (ggc_internal_cleared_alloc (sizeof (T), + finalize<T> + PASS_MEM_STAT)); + else + return static_cast<T *> (ggc_internal_cleared_alloc (sizeof (T) + PASS_MEM_STAT)); } template<typename T> static inline T * ggc_vec_alloc (size_t c CXX_MEM_STAT_INFO) { + if (need_finalization_p<T> ()) + return static_cast<T *> (ggc_internal_alloc (c * sizeof (T), finalize<T>, + sizeof (T), c PASS_MEM_STAT)); + else return static_cast<T *> (ggc_internal_alloc (c * sizeof (T) PASS_MEM_STAT)); } @@ -183,6 +217,12 @@ template<typename T> static inline T * ggc_cleared_vec_alloc (size_t c CXX_MEM_STAT_INFO) { + if (need_finalization_p<T> ()) + return static_cast<T *> (ggc_internal_cleared_alloc (c * sizeof (T), + finalize<T>, + sizeof (T), c + PASS_MEM_STAT)); + else return static_cast<T *> (ggc_internal_cleared_alloc (c * sizeof (T) PASS_MEM_STAT)); } -- 2.0.0.rc0