/* Simple garbage collection for the GNU compiler.
- Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
- Free Software Foundation, Inc.
+ Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
+ 2009, 2010 Free Software Foundation, Inc.
This file is part of GCC.
#include "coretypes.h"
#include "hashtab.h"
#include "ggc.h"
-#include "toplev.h"
+#include "ggc-internal.h"
+#include "diagnostic-core.h"
#include "params.h"
#include "hosthooks.h"
#include "hosthooks-def.h"
-
-#ifdef HAVE_SYS_RESOURCE_H
-# include <sys/resource.h>
-#endif
-
-#ifdef HAVE_MMAP_FILE
-# include <sys/mman.h>
-# ifdef HAVE_MINCORE
-/* This is on Solaris. */
-# include <sys/types.h>
-# endif
-#endif
-
-#ifndef MAP_FAILED
-# define MAP_FAILED ((void *)-1)
-#endif
-
-#ifdef ENABLE_VALGRIND_CHECKING
-# ifdef HAVE_VALGRIND_MEMCHECK_H
-# include <valgrind/memcheck.h>
-# elif defined HAVE_MEMCHECK_H
-# include <memcheck.h>
-# else
-# include <valgrind.h>
-# endif
-#else
-/* Avoid #ifdef:s when we can help it. */
-#define VALGRIND_DISCARD(x)
-#endif
+#include "plugin.h"
+#include "vec.h"
+#include "timevar.h"
/* When set, ggc_collect will do collection. */
bool ggc_force_collect;
+/* When true, protect the contents of the identifier hash table. */
+bool ggc_protect_identifiers = true;
+
/* Statistics about the allocation. */
static ggc_statistics *ggc_stats;
static void relocate_ptrs (void *, void *);
static void write_pch_globals (const struct ggc_root_tab * const *tab,
struct traversal_state *state);
-static double ggc_rlimit_bound (double);
/* Maintain global roots that are preserved during GC. */
return 1;
}
+
+/* This extra vector of dynamically registered root_tab-s is used by
+ ggc_mark_roots and gives the ability to dynamically add new GGC root
+ tables, for instance from some plugins; this vector is on the heap
+ since it is used by GGC internally. */
+typedef const struct ggc_root_tab *const_ggc_root_tab_t;
+DEF_VEC_P(const_ggc_root_tab_t);
+DEF_VEC_ALLOC_P(const_ggc_root_tab_t, heap);
+static VEC(const_ggc_root_tab_t, heap) *extra_root_vec;
+
+/* Dynamically register a new GGC root table RT. This is useful for
+ plugins. */
+
+void
+ggc_register_root_tab (const struct ggc_root_tab* rt)
+{
+ if (rt)
+ VEC_safe_push (const_ggc_root_tab_t, heap, extra_root_vec, rt);
+}
+
+/* This extra vector of dynamically registered cache_tab-s is used by
+ ggc_mark_roots and gives the ability to dynamically add new GGC cache
+ tables, for instance from some plugins; this vector is on the heap
+ since it is used by GGC internally. */
+typedef const struct ggc_cache_tab *const_ggc_cache_tab_t;
+DEF_VEC_P(const_ggc_cache_tab_t);
+DEF_VEC_ALLOC_P(const_ggc_cache_tab_t, heap);
+static VEC(const_ggc_cache_tab_t, heap) *extra_cache_vec;
+
+/* Dynamically register a new GGC cache table CT. This is useful for
+ plugins. */
+
+void
+ggc_register_cache_tab (const struct ggc_cache_tab* ct)
+{
+ if (ct)
+ VEC_safe_push (const_ggc_cache_tab_t, heap, extra_cache_vec, ct);
+}
+
+/* Scan a hash table that has objects which are to be deleted if they are not
+ already marked. */
+
+static void
+ggc_scan_cache_tab (const_ggc_cache_tab_t ctp)
+{
+ const struct ggc_cache_tab *cti;
+
+ for (cti = ctp; cti->base != NULL; cti++)
+ if (*cti->base)
+ {
+ ggc_set_mark (*cti->base);
+ htab_traverse_noresize (*cti->base, ggc_htab_delete,
+ CONST_CAST (void *, (const void *)cti));
+ ggc_set_mark ((*cti->base)->entries);
+ }
+}
+
+/* Mark all the roots in the table RT. */
+
+static void
+ggc_mark_root_tab (const_ggc_root_tab_t rt)
+{
+ size_t i;
+
+ for ( ; rt->base != NULL; rt++)
+ for (i = 0; i < rt->nelt; i++)
+ (*rt->cb) (*(void **) ((char *)rt->base + rt->stride * i));
+}
+
/* Iterate through all registered roots and mark each element. */
void
ggc_mark_roots (void)
{
const struct ggc_root_tab *const *rt;
- const struct ggc_root_tab *rti;
+ const_ggc_root_tab_t rtp, rti;
const struct ggc_cache_tab *const *ct;
- const struct ggc_cache_tab *cti;
+ const_ggc_cache_tab_t ctp;
size_t i;
for (rt = gt_ggc_deletable_rtab; *rt; rt++)
memset (rti->base, 0, rti->stride);
for (rt = gt_ggc_rtab; *rt; rt++)
- for (rti = *rt; rti->base != NULL; rti++)
- for (i = 0; i < rti->nelt; i++)
- (*rti->cb)(*(void **)((char *)rti->base + rti->stride * i));
+ ggc_mark_root_tab (*rt);
+
+ FOR_EACH_VEC_ELT (const_ggc_root_tab_t, extra_root_vec, i, rtp)
+ ggc_mark_root_tab (rtp);
- ggc_mark_stringpool ();
+ if (ggc_protect_identifiers)
+ ggc_mark_stringpool ();
/* Now scan all hash tables that have objects which are to be deleted if
they are not already marked. */
for (ct = gt_ggc_cache_rtab; *ct; ct++)
- for (cti = *ct; cti->base != NULL; cti++)
- if (*cti->base)
- {
- ggc_set_mark (*cti->base);
- htab_traverse_noresize (*cti->base, ggc_htab_delete, (void *) cti);
- ggc_set_mark ((*cti->base)->entries);
- }
+ ggc_scan_cache_tab (*ct);
+
+ FOR_EACH_VEC_ELT (const_ggc_cache_tab_t, extra_cache_vec, i, ctp)
+ ggc_scan_cache_tab (ctp);
+
+ if (! ggc_protect_identifiers)
+ ggc_purge_stringpool ();
+
+ /* Some plugins may call ggc_set_mark from here. */
+ invoke_plugin_callbacks (PLUGIN_GGC_MARKING, NULL);
}
/* Allocate a block of memory, then clear it. */
void *
-ggc_alloc_cleared_stat (size_t size MEM_STAT_DECL)
+ggc_internal_cleared_alloc_stat (size_t size MEM_STAT_DECL)
{
- void *buf = ggc_alloc_stat (size PASS_MEM_STAT);
+ void *buf = ggc_internal_alloc_stat (size PASS_MEM_STAT);
memset (buf, 0, size);
return buf;
}
size_t old_size;
if (x == NULL)
- return ggc_alloc_stat (size PASS_MEM_STAT);
+ return ggc_internal_alloc_stat (size PASS_MEM_STAT);
old_size = ggc_get_size (x);
old_size as reachable, but that would lose tracking of writes
after the end of the object (by small offsets). Discard the
handle to avoid handle leak. */
- VALGRIND_DISCARD (VALGRIND_MAKE_NOACCESS ((char *) x + size,
- old_size - size));
- VALGRIND_DISCARD (VALGRIND_MAKE_READABLE (x, size));
+ VALGRIND_DISCARD (VALGRIND_MAKE_MEM_NOACCESS ((char *) x + size,
+ old_size - size));
+ VALGRIND_DISCARD (VALGRIND_MAKE_MEM_DEFINED (x, size));
return x;
}
- r = ggc_alloc_stat (size PASS_MEM_STAT);
+ r = ggc_internal_alloc_stat (size PASS_MEM_STAT);
/* Since ggc_get_size returns the size of the pool, not the size of the
individually allocated object, we'd access parts of the old object
that were marked invalid with the memcpy below. We lose a bit of the
initialization-tracking since some of it may be uninitialized. */
- VALGRIND_DISCARD (VALGRIND_MAKE_READABLE (x, old_size));
+ VALGRIND_DISCARD (VALGRIND_MAKE_MEM_DEFINED (x, old_size));
memcpy (r, x, old_size);
return r;
}
-/* Like ggc_alloc_cleared, but performs a multiplication. */
void *
-ggc_calloc (size_t s1, size_t s2)
+ggc_cleared_alloc_htab_ignore_args (size_t c ATTRIBUTE_UNUSED,
+ size_t n ATTRIBUTE_UNUSED)
+{
+ gcc_assert (c * n == sizeof (struct htab));
+ return ggc_alloc_cleared_htab ();
+}
+
+/* TODO: once we actually use type information in GGC, create a new tag
+ gt_gcc_ptr_array and use it for pointer arrays. */
+void *
+ggc_cleared_alloc_ptr_array_two_args (size_t c, size_t n)
{
- return ggc_alloc_cleared (s1 * s2);
+ gcc_assert (sizeof (PTR *) == n);
+ return ggc_internal_cleared_vec_alloc (sizeof (PTR *), c);
}
/* These are for splay_tree_new_ggc. */
void *
-ggc_splay_alloc (int sz, void *nl)
+ggc_splay_alloc (enum gt_types_enum obj_type ATTRIBUTE_UNUSED, int sz,
+ void *nl)
{
gcc_assert (!nl);
- return ggc_alloc (sz);
+ return ggc_internal_alloc (sz);
}
void
return 0;
}
- *slot = xcalloc (sizeof (struct ptr_data), 1);
+ *slot = XCNEW (struct ptr_data);
(*slot)->obj = obj;
(*slot)->note_ptr_fn = note_ptr_fn;
(*slot)->note_ptr_cookie = note_ptr_cookie;
if (note_ptr_fn == gt_pch_p_S)
- (*slot)->size = strlen (obj) + 1;
+ (*slot)->size = strlen ((const char *)obj) + 1;
else
(*slot)->size = ggc_get_size (obj);
(*slot)->type = type;
if (obj == NULL || obj == (void *) 1)
return;
- data = htab_find_with_hash (saving_htab, obj, POINTER_HASH (obj));
+ data = (struct ptr_data *)
+ htab_find_with_hash (saving_htab, obj, POINTER_HASH (obj));
gcc_assert (data && data->note_ptr_cookie == note_ptr_cookie);
data->reorder_fn = reorder_fn;
static int
compare_ptr_data (const void *p1_p, const void *p2_p)
{
- struct ptr_data *p1 = *(struct ptr_data *const *)p1_p;
- struct ptr_data *p2 = *(struct ptr_data *const *)p2_p;
+ const struct ptr_data *const p1 = *(const struct ptr_data *const *)p1_p;
+ const struct ptr_data *const p2 = *(const struct ptr_data *const *)p2_p;
return (((size_t)p1->new_addr > (size_t)p2->new_addr)
- ((size_t)p1->new_addr < (size_t)p2->new_addr));
}
if (*ptr == NULL || *ptr == (void *)1)
return;
- result = htab_find_with_hash (saving_htab, *ptr, POINTER_HASH (*ptr));
+ result = (struct ptr_data *)
+ htab_find_with_hash (saving_htab, *ptr, POINTER_HASH (*ptr));
gcc_assert (result);
*ptr = result->new_addr;
}
{
if (fwrite (&ptr, sizeof (void *), 1, state->f)
!= 1)
- fatal_error ("can't write PCH file: %m");
+ fatal_error ("can%'t write PCH file: %m");
}
else
{
- new_ptr = htab_find_with_hash (saving_htab, ptr,
- POINTER_HASH (ptr));
+ new_ptr = (struct ptr_data *)
+ htab_find_with_hash (saving_htab, ptr, POINTER_HASH (ptr));
if (fwrite (&new_ptr->new_addr, sizeof (void *), 1, state->f)
!= 1)
- fatal_error ("can't write PCH file: %m");
+ fatal_error ("can%'t write PCH file: %m");
}
}
}
gt_pch_save_stringpool ();
+ timevar_push (TV_PCH_PTR_REALLOC);
saving_htab = htab_create (50000, saving_htab_hash, saving_htab_eq, free);
for (rt = gt_ggc_rtab; *rt; rt++)
/* Prepare the objects for writing, determine addresses and such. */
state.f = f;
- state.d = init_ggc_pch();
+ state.d = init_ggc_pch ();
state.count = 0;
htab_traverse (saving_htab, call_count, &state);
/* Try to arrange things so that no relocation is necessary, but
don't try very hard. On most platforms, this will always work,
- and on the rest it's a lot of work to do better.
+ and on the rest it's a lot of work to do better.
(The extra work goes in HOST_HOOKS_GT_PCH_GET_ADDRESS and
HOST_HOOKS_GT_PCH_USE_ADDRESS.) */
mmi.preferred_base = host_hooks.gt_pch_get_address (mmi.size, fileno (f));
-
+
ggc_pch_this_base (state.d, mmi.preferred_base);
state.ptrs = XNEWVEC (struct ptr_data *, state.count);
state.ptrs_i = 0;
+
htab_traverse (saving_htab, call_alloc, &state);
+ timevar_pop (TV_PCH_PTR_REALLOC);
+
+ timevar_push (TV_PCH_PTR_SORT);
qsort (state.ptrs, state.count, sizeof (*state.ptrs), compare_ptr_data);
+ timevar_pop (TV_PCH_PTR_SORT);
/* Write out all the scalar variables. */
for (rt = gt_pch_scalar_rtab; *rt; rt++)
for (rti = *rt; rti->base != NULL; rti++)
if (fwrite (rti->base, rti->stride, 1, f) != 1)
- fatal_error ("can't write PCH file: %m");
+ fatal_error ("can%'t write PCH file: %m");
/* Write out all the global pointers, after translation. */
write_pch_globals (gt_ggc_rtab, &state);
long o;
o = ftell (state.f) + sizeof (mmi);
if (o == -1)
- fatal_error ("can't get position in PCH file: %m");
+ fatal_error ("can%'t get position in PCH file: %m");
mmi.offset = mmap_offset_alignment - o % mmap_offset_alignment;
if (mmi.offset == mmap_offset_alignment)
mmi.offset = 0;
mmi.offset += o;
}
if (fwrite (&mmi, sizeof (mmi), 1, state.f) != 1)
- fatal_error ("can't write PCH file: %m");
+ fatal_error ("can%'t write PCH file: %m");
if (mmi.offset != 0
&& fseek (state.f, mmi.offset, SEEK_SET) != 0)
- fatal_error ("can't write padding to PCH file: %m");
+ fatal_error ("can%'t write padding to PCH file: %m");
ggc_pch_prepare_write (state.d, state.f);
if (this_object_size < state.ptrs[i]->size)
{
this_object_size = state.ptrs[i]->size;
- this_object = xrealloc (this_object, this_object_size);
+ this_object = XRESIZEVAR (char, this_object, this_object_size);
}
memcpy (this_object, state.ptrs[i]->obj, state.ptrs[i]->size);
if (state.ptrs[i]->reorder_fn != NULL)
for (rt = gt_pch_scalar_rtab; *rt; rt++)
for (rti = *rt; rti->base != NULL; rti++)
if (fread (rti->base, rti->stride, 1, f) != 1)
- fatal_error ("can't read PCH file: %m");
+ fatal_error ("can%'t read PCH file: %m");
/* Read in all the global pointers, in 6 easy loops. */
for (rt = gt_ggc_rtab; *rt; rt++)
for (i = 0; i < rti->nelt; i++)
if (fread ((char *)rti->base + rti->stride * i,
sizeof (void *), 1, f) != 1)
- fatal_error ("can't read PCH file: %m");
+ fatal_error ("can%'t read PCH file: %m");
for (rt = gt_pch_cache_rtab; *rt; rt++)
for (rti = *rt; rti->base != NULL; rti++)
for (i = 0; i < rti->nelt; i++)
if (fread ((char *)rti->base + rti->stride * i,
sizeof (void *), 1, f) != 1)
- fatal_error ("can't read PCH file: %m");
+ fatal_error ("can%'t read PCH file: %m");
if (fread (&mmi, sizeof (mmi), 1, f) != 1)
- fatal_error ("can't read PCH file: %m");
+ fatal_error ("can%'t read PCH file: %m");
result = host_hooks.gt_pch_use_address (mmi.preferred_base, mmi.size,
fileno (f), mmi.offset);
{
if (fseek (f, mmi.offset, SEEK_SET) != 0
|| fread (mmi.preferred_base, mmi.size, 1, f) != 1)
- fatal_error ("can't read PCH file: %m");
+ fatal_error ("can%'t read PCH file: %m");
}
else if (fseek (f, mmi.offset + mmi.size, SEEK_SET) != 0)
- fatal_error ("can't read PCH file: %m");
+ fatal_error ("can%'t read PCH file: %m");
ggc_pch_read (f, mmi.preferred_base);
if (ret == (void *) MAP_FAILED)
ret = NULL;
else
- munmap (ret, size);
+ munmap ((caddr_t) ret, size);
return ret;
}
/* Default version of HOST_HOOKS_GT_PCH_USE_ADDRESS when mmap is present.
- Map SIZE bytes of FD+OFFSET at BASE. Return 1 if we succeeded at
+ Map SIZE bytes of FD+OFFSET at BASE. Return 1 if we succeeded at
mapping the data at BASE, -1 if we couldn't.
This version assumes that the kernel honors the START operand of mmap
if (size == 0)
return -1;
- addr = mmap (base, size, PROT_READ | PROT_WRITE, MAP_PRIVATE,
+ addr = mmap ((caddr_t) base, size, PROT_READ | PROT_WRITE, MAP_PRIVATE,
fd, offset);
return addr == base ? 1 : -1;
}
#endif /* HAVE_MMAP_FILE */
+#if !defined ENABLE_GC_CHECKING && !defined ENABLE_GC_ALWAYS_COLLECT
+
/* Modify the bound based on rlimits. */
static double
ggc_rlimit_bound (double limit)
}
/* Heuristic to set a default for GGC_MIN_EXPAND. */
-int
+static int
ggc_min_expand_heuristic (void)
{
double min_expand = physmem_total();
}
/* Heuristic to set a default for GGC_MIN_HEAPSIZE. */
-int
+static int
ggc_min_heapsize_heuristic (void)
{
double phys_kbytes = physmem_total();
phys_kbytes /= 8;
#if defined(HAVE_GETRLIMIT) && defined (RLIMIT_RSS)
- /* Try not to overrun the RSS limit while doing garbage collection.
+ /* Try not to overrun the RSS limit while doing garbage collection.
The RSS limit is only advisory, so no margin is subtracted. */
{
struct rlimit rlim;
the limit, whichever is larger. If GCC does hit the data limit,
compilation will fail, so this tries to be conservative. */
limit_kbytes = MAX (0, limit_kbytes - MAX (limit_kbytes / 4, 20 * 1024));
- limit_kbytes = (limit_kbytes * 100) / (110 + ggc_min_expand_heuristic());
+ limit_kbytes = (limit_kbytes * 100) / (110 + ggc_min_expand_heuristic ());
phys_kbytes = MIN (phys_kbytes, limit_kbytes);
phys_kbytes = MAX (phys_kbytes, 4 * 1024);
return phys_kbytes;
}
+#endif
void
init_ggc_heuristics (void)
{
#if !defined ENABLE_GC_CHECKING && !defined ENABLE_GC_ALWAYS_COLLECT
- set_param_value ("ggc-min-expand", ggc_min_expand_heuristic());
- set_param_value ("ggc-min-heapsize", ggc_min_heapsize_heuristic());
+ set_default_param_value (GGC_MIN_EXPAND, ggc_min_expand_heuristic ());
+ set_default_param_value (GGC_MIN_HEAPSIZE, ggc_min_heapsize_heuristic ());
#endif
}
static hashval_t
hash_descriptor (const void *p)
{
- const struct loc_descriptor *d = p;
+ const struct loc_descriptor *const d = (const struct loc_descriptor *) p;
return htab_hash_pointer (d->function) | d->line;
}
static int
eq_descriptor (const void *p1, const void *p2)
{
- const struct loc_descriptor *d = p1;
- const struct loc_descriptor *d2 = p2;
+ const struct loc_descriptor *const d = (const struct loc_descriptor *) p1;
+ const struct loc_descriptor *const d2 = (const struct loc_descriptor *) p2;
return (d->file == d2->file && d->line == d2->line
&& d->function == d2->function);
static hashval_t
hash_ptr (const void *p)
{
- const struct ptr_hash_entry *d = p;
+ const struct ptr_hash_entry *const d = (const struct ptr_hash_entry *) p;
return htab_hash_pointer (d->ptr);
}
static int
eq_ptr (const void *p1, const void *p2)
{
- const struct ptr_hash_entry *p = p1;
+ const struct ptr_hash_entry *const p = (const struct ptr_hash_entry *) p1;
return (p->ptr == p2);
}
if (!loc_hash)
loc_hash = htab_create (10, hash_descriptor, eq_descriptor, NULL);
- slot = (struct loc_descriptor **) htab_find_slot (loc_hash, &loc, 1);
+ slot = (struct loc_descriptor **) htab_find_slot (loc_hash, &loc, INSERT);
if (*slot)
return *slot;
- *slot = xcalloc (sizeof (**slot), 1);
+ *slot = XCNEW (struct loc_descriptor);
(*slot)->file = name;
(*slot)->line = line;
(*slot)->function = function;
static int
ggc_prune_ptr (void **slot, void *b ATTRIBUTE_UNUSED)
{
- struct ptr_hash_entry *p = *slot;
+ struct ptr_hash_entry *p = (struct ptr_hash_entry *) *slot;
if (!ggc_marked_p (p->ptr))
{
p->loc->collected += p->size;
{
PTR *slot = htab_find_slot_with_hash (ptr_hash, ptr, htab_hash_pointer (ptr),
NO_INSERT);
- struct ptr_hash_entry *p = *slot;
+ struct ptr_hash_entry *p;
+ /* The pointer might be not found if a PCH read happened between allocation
+ and ggc_free () call. FIXME: account memory properly in the presence of
+ PCH. */
+ if (!slot)
+ return;
+ p = (struct ptr_hash_entry *) *slot;
p->loc->freed += p->size;
htab_clear_slot (ptr_hash, slot);
free (p);
static int
final_cmp_statistic (const void *loc1, const void *loc2)
{
- struct loc_descriptor *l1 = *(struct loc_descriptor **) loc1;
- struct loc_descriptor *l2 = *(struct loc_descriptor **) loc2;
+ const struct loc_descriptor *const l1 =
+ *(const struct loc_descriptor *const *) loc1;
+ const struct loc_descriptor *const l2 =
+ *(const struct loc_descriptor *const *) loc2;
long diff;
diff = ((long)(l1->allocated + l1->overhead - l1->freed) -
(l2->allocated + l2->overhead - l2->freed));
static int
cmp_statistic (const void *loc1, const void *loc2)
{
- struct loc_descriptor *l1 = *(struct loc_descriptor **) loc1;
- struct loc_descriptor *l2 = *(struct loc_descriptor **) loc2;
+ const struct loc_descriptor *const l1 =
+ *(const struct loc_descriptor *const *) loc1;
+ const struct loc_descriptor *const l2 =
+ *(const struct loc_descriptor *const *) loc2;
long diff;
diff = ((long)(l1->allocated + l1->overhead - l1->freed - l1->collected) -
}
/* Collect array of the descriptors from hashtable. */
-struct loc_descriptor **loc_array;
+static struct loc_descriptor **loc_array;
static int
add_statistics (void **slot, void *b)
{
ggc_force_collect = true;
ggc_collect ();
- loc_array = xcalloc (sizeof (*loc_array), loc_hash->n_elements);
+ loc_array = XCNEWVEC (struct loc_descriptor *, loc_hash->n_elements);
fprintf (stderr, "-------------------------------------------------------\n");
fprintf (stderr, "\n%-48s %10s %10s %10s %10s %10s\n",
"source location", "Garbage", "Freed", "Leak", "Overhead", "Times");
fprintf (stderr, "%-48s %10s %10s %10s %10s %10s\n",
"source location", "Garbage", "Freed", "Leak", "Overhead", "Times");
fprintf (stderr, "-------------------------------------------------------\n");
+ ggc_force_collect = false;
#endif
}