X-Git-Url: http://git.sourceforge.jp/view?p=pf3gnuchains%2Fgcc-fork.git;a=blobdiff_plain;f=gcc%2Fggc-page.c;h=94ffd503dc404590d63f7c536c840ceda9303c39;hp=2a7e1014fd2b5a2cebb589d6ed441722baf872c5;hb=ecc3a7ab76907cdb1a31fdb6502f4022b0cefe79;hpb=791ceafe7e7c0ca0640b64ede4b9cccda4b900e9 diff --git a/gcc/ggc-page.c b/gcc/ggc-page.c index 2a7e1014fd2..94ffd503dc4 100644 --- a/gcc/ggc-page.c +++ b/gcc/ggc-page.c @@ -1,47 +1,80 @@ /* "Bag-of-pages" garbage collector for the GNU compiler. - Copyright (C) 1999, 2000 Free Software Foundation, Inc. + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007 + Free Software Foundation, Inc. -This file is part of GNU CC. +This file is part of GCC. -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ +along with GCC; see the file COPYING3. If not see +. */ #include "config.h" #include "system.h" +#include "coretypes.h" +#include "tm.h" #include "tree.h" #include "rtl.h" #include "tm_p.h" #include "toplev.h" -#include "varray.h" #include "flags.h" #include "ggc.h" #include "timevar.h" +#include "params.h" +#include "tree-flow.h" +#ifdef ENABLE_VALGRIND_CHECKING +# ifdef HAVE_VALGRIND_MEMCHECK_H +# include +# elif defined HAVE_MEMCHECK_H +# include +# else +# include +# endif +#else +/* Avoid #ifdef:s when we can help it. */ +#define VALGRIND_DISCARD(x) +#endif + +/* Prefer MAP_ANON(YMOUS) to /dev/zero, since we don't need to keep a + file open. Prefer either to valloc. */ +#ifdef HAVE_MMAP_ANON +# undef HAVE_MMAP_DEV_ZERO + +# include +# ifndef MAP_FAILED +# define MAP_FAILED -1 +# endif +# if !defined (MAP_ANONYMOUS) && defined (MAP_ANON) +# define MAP_ANONYMOUS MAP_ANON +# endif +# define USING_MMAP -#ifdef HAVE_MMAP_ANYWHERE -#include #endif -#ifndef MAP_FAILED -#define MAP_FAILED -1 +#ifdef HAVE_MMAP_DEV_ZERO + +# include +# ifndef MAP_FAILED +# define MAP_FAILED -1 +# endif +# define USING_MMAP + #endif -#if !defined (MAP_ANONYMOUS) && defined (MAP_ANON) -#define MAP_ANONYMOUS MAP_ANON +#ifndef USING_MMAP +#define USING_MALLOC_PAGE_GROUPS #endif -/* Stategy: +/* Strategy: This garbage-collecting allocator allocates objects on one of a set of pages. Each page can allocate objects of a single size only; @@ -56,7 +89,7 @@ Boston, MA 02111-1307, USA. */ Each page-entry also has a context depth, which is used to track pushing and popping of allocation contexts. Only objects allocated - in the current (highest-numbered) context may be collected. + in the current (highest-numbered) context may be collected. Page entries are arranged in an array of singly-linked lists. The array is indexed by the allocation size, in bits, of the pages on @@ -70,37 +103,18 @@ Boston, MA 02111-1307, USA. */ deallocated at the start of the next collection if they haven't been recycled by then. */ - -/* Define GGC_POISON to poison memory marked unused by the collector. */ -#undef GGC_POISON - -/* Define GGC_ALWAYS_COLLECT to perform collection every time - ggc_collect is invoked. Otherwise, collection is performed only - when a significant amount of memory has been allocated since the - last collection. */ -#undef GGC_ALWAYS_COLLECT - -#ifdef ENABLE_GC_CHECKING -#define GGC_POISON -#endif -#ifdef ENABLE_GC_ALWAYS_COLLECT -#define GGC_ALWAYS_COLLECT -#endif - /* Define GGC_DEBUG_LEVEL to print debugging information. 0: No debugging output. 1: GC statistics only. 2: Page-entry allocations/deallocations as well. 3: Object allocations as well. - 4: Object marks as well. */ + 4: Object marks as well. */ #define GGC_DEBUG_LEVEL (0) #ifndef HOST_BITS_PER_PTR #define HOST_BITS_PER_PTR HOST_BITS_PER_LONG #endif -/* The "" allocated string. */ -char *empty_string; /* A two-level tree is used to look up the page-entry for a given pointer. Two chunks of the pointer's bits are extracted to index @@ -117,7 +131,7 @@ char *empty_string; The bottommost HOST_PAGE_SIZE_BITS are ignored, since page-entry pages are aligned on system page boundaries. The next most significant PAGE_L2_BITS and PAGE_L1_BITS are the second and first - index values in the lookup table, respectively. + index values in the lookup table, respectively. For 32-bit architectures and the settings below, there are no leftover bits. For architectures with wider pointers, the lookup @@ -135,15 +149,122 @@ char *empty_string; #define LOOKUP_L2(p) \ (((size_t) (p) >> G.lg_pagesize) & ((1 << PAGE_L2_BITS) - 1)) +/* The number of objects per allocation page, for objects on a page of + the indicated ORDER. */ +#define OBJECTS_PER_PAGE(ORDER) objects_per_page_table[ORDER] + +/* The number of objects in P. */ +#define OBJECTS_IN_PAGE(P) ((P)->bytes / OBJECT_SIZE ((P)->order)) + +/* The size of an object on a page of the indicated ORDER. */ +#define OBJECT_SIZE(ORDER) object_size_table[ORDER] + +/* For speed, we avoid doing a general integer divide to locate the + offset in the allocation bitmap, by precalculating numbers M, S + such that (O * M) >> S == O / Z (modulo 2^32), for any offset O + within the page which is evenly divisible by the object size Z. */ +#define DIV_MULT(ORDER) inverse_table[ORDER].mult +#define DIV_SHIFT(ORDER) inverse_table[ORDER].shift +#define OFFSET_TO_BIT(OFFSET, ORDER) \ + (((OFFSET) * DIV_MULT (ORDER)) >> DIV_SHIFT (ORDER)) + +/* The number of extra orders, not corresponding to power-of-two sized + objects. */ + +#define NUM_EXTRA_ORDERS ARRAY_SIZE (extra_order_size_table) + +#define RTL_SIZE(NSLOTS) \ + (RTX_HDR_SIZE + (NSLOTS) * sizeof (rtunion)) + +#define TREE_EXP_SIZE(OPS) \ + (sizeof (struct tree_exp) + ((OPS) - 1) * sizeof (tree)) + +/* The Ith entry is the maximum size of an object to be stored in the + Ith extra order. Adding a new entry to this array is the *only* + thing you need to do to add a new special allocation size. */ + +static const size_t extra_order_size_table[] = { + sizeof (struct stmt_ann_d), + sizeof (struct var_ann_d), + sizeof (struct tree_decl_non_common), + sizeof (struct tree_field_decl), + sizeof (struct tree_parm_decl), + sizeof (struct tree_var_decl), + sizeof (struct tree_list), + sizeof (struct tree_ssa_name), + sizeof (struct function), + sizeof (struct basic_block_def), + sizeof (bitmap_element), + sizeof (bitmap_head), + /* PHI nodes with one to three arguments are already covered by the + above sizes. */ + sizeof (struct tree_phi_node) + sizeof (struct phi_arg_d) * 3, + TREE_EXP_SIZE (2), + RTL_SIZE (2), /* MEM, PLUS, etc. */ + RTL_SIZE (9), /* INSN */ +}; + +/* The total number of orders. */ + +#define NUM_ORDERS (HOST_BITS_PER_PTR + NUM_EXTRA_ORDERS) + +/* We use this structure to determine the alignment required for + allocations. For power-of-two sized allocations, that's not a + problem, but it does matter for odd-sized allocations. */ + +struct max_alignment { + char c; + union { + HOST_WIDEST_INT i; + long double d; + } u; +}; + +/* The biggest alignment required. */ + +#define MAX_ALIGNMENT (offsetof (struct max_alignment, u)) + +/* Compute the smallest nonnegative number which when added to X gives + a multiple of F. */ + +#define ROUND_UP_VALUE(x, f) ((f) - 1 - ((f) - 1 + (x)) % (f)) + +/* Compute the smallest multiple of F that is >= X. */ + +#define ROUND_UP(x, f) (CEIL (x, f) * (f)) + +/* The Ith entry is the number of objects on a page or order I. */ + +static unsigned objects_per_page_table[NUM_ORDERS]; + +/* The Ith entry is the size of an object on a page of order I. */ + +static size_t object_size_table[NUM_ORDERS]; + +/* The Ith entry is a pair of numbers (mult, shift) such that + ((k * mult) >> shift) mod 2^32 == (k / OBJECT_SIZE(I)) mod 2^32, + for all k evenly divisible by OBJECT_SIZE(I). */ + +static struct +{ + size_t mult; + unsigned int shift; +} +inverse_table[NUM_ORDERS]; /* A page_entry records the status of an allocation page. This structure is dynamically sized to fit the bitmap in_use_p. */ -typedef struct page_entry +typedef struct page_entry { /* The next page-entry with objects of the same size, or NULL if this is the last page-entry. */ struct page_entry *next; + /* The previous page-entry with objects of the same size, or NULL if + this is the first page-entry. The PREV pointer exists solely to + keep the cost of ggc_free manageable. */ + struct page_entry *prev; + /* The number of bytes allocated. (This will always be a multiple of the host system page size.) */ size_t bytes; @@ -151,9 +272,14 @@ typedef struct page_entry /* The address at which the memory is allocated. */ char *page; - /* Saved in-use bit vector for pages that aren't in the topmost - context during collection. */ - unsigned long *save_in_use_p; +#ifdef USING_MALLOC_PAGE_GROUPS + /* Back pointer to the page group this page came from. */ + struct page_group *group; +#endif + + /* This is the index in the by_depth varray where this page table + can be found. */ + unsigned long index_by_depth; /* Context depth of this page. */ unsigned short context_depth; @@ -174,6 +300,24 @@ typedef struct page_entry unsigned long in_use_p[1]; } page_entry; +#ifdef USING_MALLOC_PAGE_GROUPS +/* A page_group describes a large allocation from malloc, from which + we parcel out aligned pages. */ +typedef struct page_group +{ + /* A linked list of all extant page groups. */ + struct page_group *next; + + /* The address we received from malloc. */ + char *allocation; + + /* The size of the block. */ + size_t alloc_size; + + /* A bitmask of pages in use. */ + unsigned int in_use; +} page_group; +#endif #if HOST_BITS_PER_PTR <= 32 @@ -201,12 +345,12 @@ static struct globals If there are any pages with free objects, they will be at the head of the list. NULL if there are no page-entries for this object size. */ - page_entry *pages[HOST_BITS_PER_PTR]; + page_entry *pages[NUM_ORDERS]; /* The Nth element in this array is the last page with objects of size 2^N. NULL if there are no page-entries for this object size. */ - page_entry *page_tails[HOST_BITS_PER_PTR]; + page_entry *page_tails[NUM_ORDERS]; /* Lookup table for associating allocation pages with object addresses. */ page_table lookup; @@ -224,69 +368,190 @@ static struct globals /* Total amount of memory mapped. */ size_t bytes_mapped; + /* Bit N set if any allocations have been done at context depth N. */ + unsigned long context_depth_allocations; + + /* Bit N set if any collections have been done at context depth N. */ + unsigned long context_depth_collections; + /* The current depth in the context stack. */ unsigned short context_depth; /* A file descriptor open to /dev/zero for reading. */ -#if defined (HAVE_MMAP_ANYWHERE) && !defined(MAP_ANONYMOUS) +#if defined (HAVE_MMAP_DEV_ZERO) int dev_zero_fd; #endif /* A cache of free system pages. */ page_entry *free_pages; +#ifdef USING_MALLOC_PAGE_GROUPS + page_group *page_groups; +#endif + /* The file descriptor for debugging output. */ FILE *debug_file; -} G; + /* Current number of elements in use in depth below. */ + unsigned int depth_in_use; + + /* Maximum number of elements that can be used before resizing. */ + unsigned int depth_max; + + /* Each element of this arry is an index in by_depth where the given + depth starts. This structure is indexed by that given depth we + are interested in. */ + unsigned int *depth; + + /* Current number of elements in use in by_depth below. */ + unsigned int by_depth_in_use; + + /* Maximum number of elements that can be used before resizing. */ + unsigned int by_depth_max; + + /* Each element of this array is a pointer to a page_entry, all + page_entries can be found in here by increasing depth. + index_by_depth in the page_entry is the index into this data + structure where that page_entry can be found. This is used to + speed up finding all page_entries at a particular depth. */ + page_entry **by_depth; + + /* Each element is a pointer to the saved in_use_p bits, if any, + zero otherwise. We allocate them all together, to enable a + better runtime data access pattern. */ + unsigned long **save_in_use; -/* Compute DIVIDEND / DIVISOR, rounded up. */ -#define DIV_ROUND_UP(Dividend, Divisor) \ - (((Dividend) + (Divisor) - 1) / (Divisor)) +#ifdef ENABLE_GC_ALWAYS_COLLECT + /* List of free objects to be verified as actually free on the + next collection. */ + struct free_object + { + void *object; + struct free_object *next; + } *free_object_list; +#endif + +#ifdef GATHER_STATISTICS + struct + { + /* Total memory allocated with ggc_alloc. */ + unsigned long long total_allocated; + /* Total overhead for memory to be allocated with ggc_alloc. */ + unsigned long long total_overhead; + + /* Total allocations and overhead for sizes less than 32, 64 and 128. + These sizes are interesting because they are typical cache line + sizes. */ + + unsigned long long total_allocated_under32; + unsigned long long total_overhead_under32; + + unsigned long long total_allocated_under64; + unsigned long long total_overhead_under64; + + unsigned long long total_allocated_under128; + unsigned long long total_overhead_under128; + + /* The allocations for each of the allocation orders. */ + unsigned long long total_allocated_per_order[NUM_ORDERS]; -/* The number of objects per allocation page, for objects of size - 2^ORDER. */ -#define OBJECTS_PER_PAGE(Order) \ - ((Order) >= G.lg_pagesize ? 1 : G.pagesize / ((size_t)1 << (Order))) + /* The overhead for each of the allocation orders. */ + unsigned long long total_overhead_per_order[NUM_ORDERS]; + } stats; +#endif +} G; /* The size in bytes required to maintain a bitmap for the objects on a page-entry. */ #define BITMAP_SIZE(Num_objects) \ - (DIV_ROUND_UP ((Num_objects), HOST_BITS_PER_LONG) * sizeof(long)) + (CEIL ((Num_objects), HOST_BITS_PER_LONG) * sizeof(long)) + +/* Allocate pages in chunks of this size, to throttle calls to memory + allocation routines. The first page is used, the rest go onto the + free list. This cannot be larger than HOST_BITS_PER_INT for the + in_use bitmask for page_group. Hosts that need a different value + can override this by defining GGC_QUIRE_SIZE explicitly. */ +#ifndef GGC_QUIRE_SIZE +# ifdef USING_MMAP +# define GGC_QUIRE_SIZE 256 +# else +# define GGC_QUIRE_SIZE 16 +# endif +#endif -/* Skip garbage collection if the current allocation is not at least - this factor times the allocation at the end of the last collection. - In other words, total allocation must expand by (this factor minus - one) before collection is performed. */ -#define GGC_MIN_EXPAND_FOR_GC (1.3) +/* Initial guess as to how many page table entries we might need. */ +#define INITIAL_PTE_COUNT 128 + +static int ggc_allocated_p (const void *); +static page_entry *lookup_page_table_entry (const void *); +static void set_page_table_entry (void *, page_entry *); +#ifdef USING_MMAP +static char *alloc_anon (char *, size_t); +#endif +#ifdef USING_MALLOC_PAGE_GROUPS +static size_t page_group_index (char *, char *); +static void set_page_group_in_use (page_group *, char *); +static void clear_page_group_in_use (page_group *, char *); +#endif +static struct page_entry * alloc_page (unsigned); +static void free_page (struct page_entry *); +static void release_pages (void); +static void clear_marks (void); +static void sweep_pages (void); +static void ggc_recalculate_in_use_p (page_entry *); +static void compute_inverse (unsigned); +static inline void adjust_depth (void); +static void move_ptes_to_front (int, int); + +void debug_print_page_list (int); +static void push_depth (unsigned int); +static void push_by_depth (page_entry *, unsigned long *); + +/* Push an entry onto G.depth. */ + +inline static void +push_depth (unsigned int i) +{ + if (G.depth_in_use >= G.depth_max) + { + G.depth_max *= 2; + G.depth = xrealloc (G.depth, G.depth_max * sizeof (unsigned int)); + } + G.depth[G.depth_in_use++] = i; +} -/* Bound `allocated_last_gc' to 4MB, to prevent the memory expansion - test from triggering too often when the heap is small. */ -#define GGC_MIN_LAST_ALLOCATED (4 * 1024 * 1024) +/* Push an entry onto G.by_depth and G.save_in_use. */ - -static int ggc_allocated_p PARAMS ((const void *)); -static page_entry *lookup_page_table_entry PARAMS ((const void *)); -static void set_page_table_entry PARAMS ((void *, page_entry *)); -static char *alloc_anon PARAMS ((char *, size_t)); -static struct page_entry * alloc_page PARAMS ((unsigned)); -static void free_page PARAMS ((struct page_entry *)); -static void release_pages PARAMS ((void)); -static void clear_marks PARAMS ((void)); -static void sweep_pages PARAMS ((void)); -static void ggc_recalculate_in_use_p PARAMS ((page_entry *)); - -#ifdef GGC_POISON -static void poison_pages PARAMS ((void)); +inline static void +push_by_depth (page_entry *p, unsigned long *s) +{ + if (G.by_depth_in_use >= G.by_depth_max) + { + G.by_depth_max *= 2; + G.by_depth = xrealloc (G.by_depth, + G.by_depth_max * sizeof (page_entry *)); + G.save_in_use = xrealloc (G.save_in_use, + G.by_depth_max * sizeof (unsigned long *)); + } + G.by_depth[G.by_depth_in_use] = p; + G.save_in_use[G.by_depth_in_use++] = s; +} + +#if (GCC_VERSION < 3001) +#define prefetch(X) ((void) X) +#else +#define prefetch(X) __builtin_prefetch (X) #endif -void debug_print_page_list PARAMS ((int)); - -/* Returns non-zero if P was allocated in GC'able memory. */ +#define save_in_use_p_i(__i) \ + (G.save_in_use[__i]) +#define save_in_use_p(__p) \ + (save_in_use_p_i (__p->index_by_depth)) + +/* Returns nonzero if P was allocated in GC'able memory. */ static inline int -ggc_allocated_p (p) - const void *p; +ggc_allocated_p (const void *p) { page_entry ***base; size_t L1, L2; @@ -307,19 +572,18 @@ ggc_allocated_p (p) base = &table->table[0]; #endif - /* Extract the level 1 and 2 indicies. */ + /* Extract the level 1 and 2 indices. */ L1 = LOOKUP_L1 (p); L2 = LOOKUP_L2 (p); return base[L1] && base[L1][L2]; } -/* Traverse the page table and find the entry for a page. +/* Traverse the page table and find the entry for a page. Die (probably) if the object wasn't allocated via GC. */ static inline page_entry * -lookup_page_table_entry(p) - const void *p; +lookup_page_table_entry (const void *p) { page_entry ***base; size_t L1, L2; @@ -334,7 +598,7 @@ lookup_page_table_entry(p) base = &table->table[0]; #endif - /* Extract the level 1 and 2 indicies. */ + /* Extract the level 1 and 2 indices. */ L1 = LOOKUP_L1 (p); L2 = LOOKUP_L2 (p); @@ -344,9 +608,7 @@ lookup_page_table_entry(p) /* Set the page table entry for a page. */ static void -set_page_table_entry(p, entry) - void *p; - page_entry *entry; +set_page_table_entry (void *p, page_entry *entry) { page_entry ***base; size_t L1, L2; @@ -361,7 +623,7 @@ set_page_table_entry(p, entry) goto found; /* Not found -- allocate a new table. */ - table = (page_table) xcalloc (1, sizeof(*table)); + table = xcalloc (1, sizeof(*table)); table->next = G.lookup; table->high_bits = high_bits; G.lookup = table; @@ -369,12 +631,12 @@ found: base = &table->table[0]; #endif - /* Extract the level 1 and 2 indicies. */ + /* Extract the level 1 and 2 indices. */ L1 = LOOKUP_L1 (p); L2 = LOOKUP_L2 (p); if (base[L1] == NULL) - base[L1] = (page_entry **) xcalloc (PAGE_L2_SIZE, sizeof (page_entry *)); + base[L1] = XCNEWVEC (page_entry *, PAGE_L2_SIZE); base[L1][L2] = entry; } @@ -382,68 +644,86 @@ found: /* Prints the page-entry for object size ORDER, for debugging. */ void -debug_print_page_list (order) - int order; +debug_print_page_list (int order) { page_entry *p; - printf ("Head=%p, Tail=%p:\n", G.pages[order], G.page_tails[order]); + printf ("Head=%p, Tail=%p:\n", (void *) G.pages[order], + (void *) G.page_tails[order]); p = G.pages[order]; while (p != NULL) { - printf ("%p(%1d|%3d) -> ", p, p->context_depth, p->num_free_objects); + printf ("%p(%1d|%3d) -> ", (void *) p, p->context_depth, + p->num_free_objects); p = p->next; } printf ("NULL\n"); fflush (stdout); } +#ifdef USING_MMAP /* Allocate SIZE bytes of anonymous memory, preferably near PREF, - (if non-null). */ + (if non-null). The ifdef structure here is intended to cause a + compile error unless exactly one of the HAVE_* is defined. */ static inline char * -alloc_anon (pref, size) - char *pref ATTRIBUTE_UNUSED; - size_t size; +alloc_anon (char *pref ATTRIBUTE_UNUSED, size_t size) { - char *page; - -#ifdef HAVE_MMAP_ANYWHERE -#ifdef MAP_ANONYMOUS - page = (char *) mmap (pref, size, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); -#else - page = (char *) mmap (pref, size, PROT_READ | PROT_WRITE, - MAP_PRIVATE, G.dev_zero_fd, 0); +#ifdef HAVE_MMAP_ANON + char *page = mmap (pref, size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); #endif +#ifdef HAVE_MMAP_DEV_ZERO + char *page = mmap (pref, size, PROT_READ | PROT_WRITE, + MAP_PRIVATE, G.dev_zero_fd, 0); +#endif + if (page == (char *) MAP_FAILED) { - fputs ("Virtual memory exhausted!\n", stderr); - exit(1); + perror ("virtual memory exhausted"); + exit (FATAL_EXIT_CODE); } -#else -#ifdef HAVE_VALLOC - page = (char *) valloc (size); - if (!page) - { - fputs ("Virtual memory exhausted!\n", stderr); - exit(1); - } -#endif /* HAVE_VALLOC */ -#endif /* HAVE_MMAP_ANYWHERE */ /* Remember that we allocated this memory. */ G.bytes_mapped += size; + /* Pretend we don't have access to the allocated pages. We'll enable + access to smaller pieces of the area in ggc_alloc. Discard the + handle to avoid handle leak. */ + VALGRIND_DISCARD (VALGRIND_MAKE_NOACCESS (page, size)); + return page; } +#endif +#ifdef USING_MALLOC_PAGE_GROUPS +/* Compute the index for this page into the page group. */ + +static inline size_t +page_group_index (char *allocation, char *page) +{ + return (size_t) (page - allocation) >> G.lg_pagesize; +} + +/* Set and clear the in_use bit for this page in the page group. */ + +static inline void +set_page_group_in_use (page_group *group, char *page) +{ + group->in_use |= 1 << page_group_index (group->allocation, page); +} + +static inline void +clear_page_group_in_use (page_group *group, char *page) +{ + group->in_use &= ~(1 << page_group_index (group->allocation, page)); +} +#endif /* Allocate a new page for allocating objects of size 2^ORDER, and return an entry for it. The entry is not added to the appropriate page_table list. */ static inline struct page_entry * -alloc_page (order) - unsigned order; +alloc_page (unsigned order) { struct page_entry *entry, *p, **pp; char *page; @@ -451,25 +731,35 @@ alloc_page (order) size_t bitmap_size; size_t page_entry_size; size_t entry_size; +#ifdef USING_MALLOC_PAGE_GROUPS + page_group *group; +#endif num_objects = OBJECTS_PER_PAGE (order); bitmap_size = BITMAP_SIZE (num_objects + 1); page_entry_size = sizeof (page_entry) - sizeof (long) + bitmap_size; - entry_size = num_objects * (1 << order); + entry_size = num_objects * OBJECT_SIZE (order); + if (entry_size < G.pagesize) + entry_size = G.pagesize; entry = NULL; page = NULL; /* Check the list of free pages for one we can use. */ - for (pp = &G.free_pages, p = *pp; p ; pp = &p->next, p = *pp) + for (pp = &G.free_pages, p = *pp; p; pp = &p->next, p = *pp) if (p->bytes == entry_size) break; if (p != NULL) { - /* Recycle the allocated memory from this page ... */ + /* Recycle the allocated memory from this page ... */ *pp = p->next; page = p->page; + +#ifdef USING_MALLOC_PAGE_GROUPS + group = p->group; +#endif + /* ... and, if possible, the page entry itself. */ if (p->order == order) { @@ -479,14 +769,107 @@ alloc_page (order) else free (p); } +#ifdef USING_MMAP + else if (entry_size == G.pagesize) + { + /* We want just one page. Allocate a bunch of them and put the + extras on the freelist. (Can only do this optimization with + mmap for backing store.) */ + struct page_entry *e, *f = G.free_pages; + int i; + + page = alloc_anon (NULL, G.pagesize * GGC_QUIRE_SIZE); + + /* This loop counts down so that the chain will be in ascending + memory order. */ + for (i = GGC_QUIRE_SIZE - 1; i >= 1; i--) + { + e = xcalloc (1, page_entry_size); + e->order = order; + e->bytes = G.pagesize; + e->page = page + (i << G.lg_pagesize); + e->next = f; + f = e; + } + + G.free_pages = f; + } + else + page = alloc_anon (NULL, entry_size); +#endif +#ifdef USING_MALLOC_PAGE_GROUPS else { - /* Actually allocate the memory. */ - page = alloc_anon (NULL, entry_size); + /* Allocate a large block of memory and serve out the aligned + pages therein. This results in much less memory wastage + than the traditional implementation of valloc. */ + + char *allocation, *a, *enda; + size_t alloc_size, head_slop, tail_slop; + int multiple_pages = (entry_size == G.pagesize); + + if (multiple_pages) + alloc_size = GGC_QUIRE_SIZE * G.pagesize; + else + alloc_size = entry_size + G.pagesize - 1; + allocation = xmalloc (alloc_size); + + page = (char *) (((size_t) allocation + G.pagesize - 1) & -G.pagesize); + head_slop = page - allocation; + if (multiple_pages) + tail_slop = ((size_t) allocation + alloc_size) & (G.pagesize - 1); + else + tail_slop = alloc_size - entry_size - head_slop; + enda = allocation + alloc_size - tail_slop; + + /* We allocated N pages, which are likely not aligned, leaving + us with N-1 usable pages. We plan to place the page_group + structure somewhere in the slop. */ + if (head_slop >= sizeof (page_group)) + group = (page_group *)page - 1; + else + { + /* We magically got an aligned allocation. Too bad, we have + to waste a page anyway. */ + if (tail_slop == 0) + { + enda -= G.pagesize; + tail_slop += G.pagesize; + } + gcc_assert (tail_slop >= sizeof (page_group)); + group = (page_group *)enda; + tail_slop -= sizeof (page_group); + } + + /* Remember that we allocated this memory. */ + group->next = G.page_groups; + group->allocation = allocation; + group->alloc_size = alloc_size; + group->in_use = 0; + G.page_groups = group; + G.bytes_mapped += alloc_size; + + /* If we allocated multiple pages, put the rest on the free list. */ + if (multiple_pages) + { + struct page_entry *e, *f = G.free_pages; + for (a = enda - G.pagesize; a != page; a -= G.pagesize) + { + e = xcalloc (1, page_entry_size); + e->order = order; + e->bytes = G.pagesize; + e->page = a; + e->group = group; + e->next = f; + f = e; + } + G.free_pages = f; + } } +#endif if (entry == NULL) - entry = (struct page_entry *) xcalloc (1, page_entry_size); + entry = xcalloc (1, page_entry_size); entry->bytes = entry_size; entry->page = page; @@ -495,6 +878,13 @@ alloc_page (order) entry->num_free_objects = num_objects; entry->next_bit_hint = 1; + G.context_depth_allocations |= (unsigned long)1 << G.context_depth; + +#ifdef USING_MALLOC_PAGE_GROUPS + entry->group = group; + set_page_group_in_use (group, page); +#endif + /* Set the one-past-the-end in-use bit. This acts as a sentry as we increment the hint. */ entry->in_use_p[num_objects / HOST_BITS_PER_LONG] @@ -503,26 +893,72 @@ alloc_page (order) set_page_table_entry (page, entry); if (GGC_DEBUG_LEVEL >= 2) - fprintf (G.debug_file, - "Allocating page at %p, object size=%d, data %p-%p\n", entry, - 1 << order, page, page + entry_size - 1); + fprintf (G.debug_file, + "Allocating page at %p, object size=%lu, data %p-%p\n", + (void *) entry, (unsigned long) OBJECT_SIZE (order), page, + page + entry_size - 1); return entry; } -/* For a page that is no longer needed, put it on the free page list. */ +/* Adjust the size of G.depth so that no index greater than the one + used by the top of the G.by_depth is used. */ static inline void -free_page (entry) - page_entry *entry; +adjust_depth (void) +{ + page_entry *top; + + if (G.by_depth_in_use) + { + top = G.by_depth[G.by_depth_in_use-1]; + + /* Peel back indices in depth that index into by_depth, so that + as new elements are added to by_depth, we note the indices + of those elements, if they are for new context depths. */ + while (G.depth_in_use > (size_t)top->context_depth+1) + --G.depth_in_use; + } +} + +/* For a page that is no longer needed, put it on the free page list. */ + +static void +free_page (page_entry *entry) { if (GGC_DEBUG_LEVEL >= 2) - fprintf (G.debug_file, - "Deallocating page at %p, data %p-%p\n", entry, + fprintf (G.debug_file, + "Deallocating page at %p, data %p-%p\n", (void *) entry, entry->page, entry->page + entry->bytes - 1); + /* Mark the page as inaccessible. Discard the handle to avoid handle + leak. */ + VALGRIND_DISCARD (VALGRIND_MAKE_NOACCESS (entry->page, entry->bytes)); + set_page_table_entry (entry->page, NULL); +#ifdef USING_MALLOC_PAGE_GROUPS + clear_page_group_in_use (entry->group, entry->page); +#endif + + if (G.by_depth_in_use > 1) + { + page_entry *top = G.by_depth[G.by_depth_in_use-1]; + int i = entry->index_by_depth; + + /* We cannot free a page from a context deeper than the current + one. */ + gcc_assert (entry->context_depth == top->context_depth); + + /* Put top element into freed slot. */ + G.by_depth[i] = top; + G.save_in_use[i] = G.save_in_use[G.by_depth_in_use-1]; + top->index_by_depth = i; + } + --G.by_depth_in_use; + + adjust_depth (); + entry->next = G.free_pages; G.free_pages = entry; } @@ -530,72 +966,80 @@ free_page (entry) /* Release the free page cache to the system. */ static void -release_pages () +release_pages (void) { -#ifdef HAVE_MMAP_ANYWHERE +#ifdef USING_MMAP page_entry *p, *next; char *start; size_t len; + /* Gather up adjacent pages so they are unmapped together. */ p = G.free_pages; - if (p == NULL) - return; - - next = p->next; - start = p->page; - len = p->bytes; - free (p); - p = next; while (p) { + start = p->page; next = p->next; - /* Gather up adjacent pages so they are unmapped together. */ - if (p->page == start + len) - len += p->bytes; - else - { - munmap (start, len); - G.bytes_mapped -= len; - start = p->page; - len = p->bytes; - } + len = p->bytes; free (p); p = next; - } - munmap (start, len); - G.bytes_mapped -= len; -#else -#ifdef HAVE_VALLOC - page_entry *p, *next; + while (p && p->page == start + len) + { + next = p->next; + len += p->bytes; + free (p); + p = next; + } - for (p = G.free_pages; p ; p = next) - { - next = p->next; - free (p->page); - G.bytes_mapped -= p->bytes; - free (p); + munmap (start, len); + G.bytes_mapped -= len; } -#endif /* HAVE_VALLOC */ -#endif /* HAVE_MMAP_ANYWHERE */ G.free_pages = NULL; +#endif +#ifdef USING_MALLOC_PAGE_GROUPS + page_entry **pp, *p; + page_group **gp, *g; + + /* Remove all pages from free page groups from the list. */ + pp = &G.free_pages; + while ((p = *pp) != NULL) + if (p->group->in_use == 0) + { + *pp = p->next; + free (p); + } + else + pp = &p->next; + + /* Remove all free page groups, and release the storage. */ + gp = &G.page_groups; + while ((g = *gp) != NULL) + if (g->in_use == 0) + { + *gp = g->next; + G.bytes_mapped -= g->alloc_size; + free (g->allocation); + } + else + gp = &g->next; +#endif } /* This table provides a fast way to determine ceil(log_2(size)) for - allocation requests. The minimum allocation size is four bytes. */ - -static unsigned char const size_lookup[257] = -{ - 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, - 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + allocation requests. The minimum allocation size is eight bytes. */ +#define NUM_SIZE_LOOKUP 512 +static unsigned char size_lookup[NUM_SIZE_LOOKUP] = +{ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, + 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, @@ -604,26 +1048,51 @@ static unsigned char const size_lookup[257] = 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8 + 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }; -/* Allocate a chunk of memory of SIZE bytes. If ZERO is non-zero, the - memory is zeroed; otherwise, its contents are undefined. */ +/* Typed allocation function. Does nothing special in this collector. */ + +void * +ggc_alloc_typed_stat (enum gt_types_enum type ATTRIBUTE_UNUSED, size_t size + MEM_STAT_DECL) +{ + return ggc_alloc_stat (size PASS_MEM_STAT); +} + +/* Allocate a chunk of memory of SIZE bytes. Its contents are undefined. */ void * -ggc_alloc (size) - size_t size; +ggc_alloc_stat (size_t size MEM_STAT_DECL) { - unsigned order, word, bit, object_offset; + size_t order, word, bit, object_offset, object_size; struct page_entry *entry; void *result; - if (size <= 256) - order = size_lookup[size]; + if (size < NUM_SIZE_LOOKUP) + { + order = size_lookup[size]; + object_size = OBJECT_SIZE (order); + } else { - order = 9; - while (size > ((size_t) 1 << order)) + order = 10; + while (size > (object_size = OBJECT_SIZE (order))) order++; } @@ -637,13 +1106,27 @@ ggc_alloc (size) { struct page_entry *new_entry; new_entry = alloc_page (order); - - /* If this is the only entry, it's also the tail. */ + + new_entry->index_by_depth = G.by_depth_in_use; + push_by_depth (new_entry, 0); + + /* We can skip context depths, if we do, make sure we go all the + way to the new depth. */ + while (new_entry->context_depth >= G.depth_in_use) + push_depth (G.by_depth_in_use-1); + + /* If this is the only entry, it's also the tail. If it is not + the only entry, then we must update the PREV pointer of the + ENTRY (G.pages[order]) to point to our new page entry. */ if (entry == NULL) G.page_tails[order] = new_entry; - - /* Put new pages at the head of the page list. */ + else + entry->prev = new_entry; + + /* Put new pages at the head of the page list. By definition the + entry at the head of the list always has a NULL pointer. */ new_entry->next = entry; + new_entry->prev = NULL; entry = new_entry; G.pages[order] = new_entry; @@ -663,22 +1146,28 @@ ggc_alloc (size) unsigned hint = entry->next_bit_hint; word = hint / HOST_BITS_PER_LONG; bit = hint % HOST_BITS_PER_LONG; - + /* If the hint didn't work, scan the bitmap from the beginning. */ if ((entry->in_use_p[word] >> bit) & 1) { word = bit = 0; while (~entry->in_use_p[word] == 0) ++word; + +#if GCC_VERSION >= 3004 + bit = __builtin_ctzl (~entry->in_use_p[word]); +#else while ((entry->in_use_p[word] >> bit) & 1) ++bit; +#endif + hint = word * HOST_BITS_PER_LONG + bit; } /* Next time, try the next bit. */ entry->next_bit_hint = hint + 1; - object_offset = hint << order; + object_offset = hint * object_size; } /* Set the in-use bit. */ @@ -692,29 +1181,89 @@ ggc_alloc (size) && entry->next != NULL && entry->next->num_free_objects > 0) { + /* We have a new head for the list. */ G.pages[order] = entry->next; + + /* We are moving ENTRY to the end of the page table list. + The new page at the head of the list will have NULL in + its PREV field and ENTRY will have NULL in its NEXT field. */ + entry->next->prev = NULL; entry->next = NULL; + + /* Append ENTRY to the tail of the list. */ + entry->prev = G.page_tails[order]; G.page_tails[order]->next = entry; G.page_tails[order] = entry; } /* Calculate the object's address. */ result = entry->page + object_offset; +#ifdef GATHER_STATISTICS + ggc_record_overhead (OBJECT_SIZE (order), OBJECT_SIZE (order) - size, + result PASS_MEM_STAT); +#endif + +#ifdef ENABLE_GC_CHECKING + /* Keep poisoning-by-writing-0xaf the object, in an attempt to keep the + exact same semantics in presence of memory bugs, regardless of + ENABLE_VALGRIND_CHECKING. We override this request below. Drop the + handle to avoid handle leak. */ + VALGRIND_DISCARD (VALGRIND_MAKE_WRITABLE (result, object_size)); -#ifdef GGC_POISON /* `Poison' the entire allocated object, including any padding at the end. */ - memset (result, 0xaf, 1 << order); + memset (result, 0xaf, object_size); + + /* Make the bytes after the end of the object unaccessible. Discard the + handle to avoid handle leak. */ + VALGRIND_DISCARD (VALGRIND_MAKE_NOACCESS ((char *) result + size, + object_size - size)); #endif + /* Tell Valgrind that the memory is there, but its content isn't + defined. The bytes at the end of the object are still marked + unaccessible. */ + VALGRIND_DISCARD (VALGRIND_MAKE_WRITABLE (result, size)); + /* Keep track of how many bytes are being allocated. This information is used in deciding when to collect. */ - G.allocated += (size_t) 1 << order; + G.allocated += object_size; + + /* For timevar statistics. */ + timevar_ggc_mem_total += object_size; + +#ifdef GATHER_STATISTICS + { + size_t overhead = object_size - size; + + G.stats.total_overhead += overhead; + G.stats.total_allocated += object_size; + G.stats.total_overhead_per_order[order] += overhead; + G.stats.total_allocated_per_order[order] += object_size; + + if (size <= 32) + { + G.stats.total_overhead_under32 += overhead; + G.stats.total_allocated_under32 += object_size; + } + if (size <= 64) + { + G.stats.total_overhead_under64 += overhead; + G.stats.total_allocated_under64 += object_size; + } + if (size <= 128) + { + G.stats.total_overhead_under128 += overhead; + G.stats.total_allocated_under128 += object_size; + } + } +#endif if (GGC_DEBUG_LEVEL >= 3) - fprintf (G.debug_file, - "Allocating object, requested size=%d, actual=%d at %p on %p\n", - (int) size, 1 << order, result, entry); + fprintf (G.debug_file, + "Allocating object, requested size=%lu, actual=%lu at %p on %p\n", + (unsigned long) size, (unsigned long) object_size, result, + (void *) entry); return result; } @@ -724,8 +1273,7 @@ ggc_alloc (size) static objects, stack variables, or memory allocated with malloc. */ int -ggc_set_mark (p) - const void *p; +ggc_set_mark (const void *p) { page_entry *entry; unsigned bit, word; @@ -734,18 +1282,15 @@ ggc_set_mark (p) /* Look up the page on which the object is alloced. If the object wasn't allocated by the collector, we'll probably die. */ entry = lookup_page_table_entry (p); -#ifdef ENABLE_CHECKING - if (entry == NULL) - abort (); -#endif + gcc_assert (entry); /* Calculate the index of the object on the page; this is its bit position in the in_use_p bitmap. */ - bit = (((const char *) p) - entry->page) >> entry->order; + bit = OFFSET_TO_BIT (((const char *) p) - entry->page, entry->order); word = bit / HOST_BITS_PER_LONG; mask = (unsigned long) 1 << (bit % HOST_BITS_PER_LONG); - - /* If the bit was previously set, skip it. */ + + /* If the bit was previously set, skip it. */ if (entry->in_use_p[word] & mask) return 1; @@ -753,46 +1298,177 @@ ggc_set_mark (p) entry->in_use_p[word] |= mask; entry->num_free_objects -= 1; - G.allocated += (size_t) 1 << entry->order; - if (GGC_DEBUG_LEVEL >= 4) fprintf (G.debug_file, "Marking %p\n", p); return 0; } -/* Mark P, but check first that it was allocated by the collector. */ +/* Return 1 if P has been marked, zero otherwise. + P must have been allocated by the GC allocator; it mustn't point to + static objects, stack variables, or memory allocated with malloc. */ -void -ggc_mark_if_gcable (p) - const void *p; +int +ggc_marked_p (const void *p) { - if (p && ggc_allocated_p (p)) - ggc_set_mark (p); + page_entry *entry; + unsigned bit, word; + unsigned long mask; + + /* Look up the page on which the object is alloced. If the object + wasn't allocated by the collector, we'll probably die. */ + entry = lookup_page_table_entry (p); + gcc_assert (entry); + + /* Calculate the index of the object on the page; this is its bit + position in the in_use_p bitmap. */ + bit = OFFSET_TO_BIT (((const char *) p) - entry->page, entry->order); + word = bit / HOST_BITS_PER_LONG; + mask = (unsigned long) 1 << (bit % HOST_BITS_PER_LONG); + + return (entry->in_use_p[word] & mask) != 0; } /* Return the size of the gc-able object P. */ size_t -ggc_get_size (p) - const void *p; +ggc_get_size (const void *p) { page_entry *pe = lookup_page_table_entry (p); - return 1 << pe->order; + return OBJECT_SIZE (pe->order); +} + +/* Release the memory for object P. */ + +void +ggc_free (void *p) +{ + page_entry *pe = lookup_page_table_entry (p); + size_t order = pe->order; + size_t size = OBJECT_SIZE (order); + +#ifdef GATHER_STATISTICS + ggc_free_overhead (p); +#endif + + if (GGC_DEBUG_LEVEL >= 3) + fprintf (G.debug_file, + "Freeing object, actual size=%lu, at %p on %p\n", + (unsigned long) size, p, (void *) pe); + +#ifdef ENABLE_GC_CHECKING + /* Poison the data, to indicate the data is garbage. */ + VALGRIND_DISCARD (VALGRIND_MAKE_WRITABLE (p, size)); + memset (p, 0xa5, size); +#endif + /* Let valgrind know the object is free. */ + VALGRIND_DISCARD (VALGRIND_MAKE_NOACCESS (p, size)); + +#ifdef ENABLE_GC_ALWAYS_COLLECT + /* In the completely-anal-checking mode, we do *not* immediately free + the data, but instead verify that the data is *actually* not + reachable the next time we collect. */ + { + struct free_object *fo = XNEW (struct free_object); + fo->object = p; + fo->next = G.free_object_list; + G.free_object_list = fo; + } +#else + { + unsigned int bit_offset, word, bit; + + G.allocated -= size; + + /* Mark the object not-in-use. */ + bit_offset = OFFSET_TO_BIT (((const char *) p) - pe->page, order); + word = bit_offset / HOST_BITS_PER_LONG; + bit = bit_offset % HOST_BITS_PER_LONG; + pe->in_use_p[word] &= ~(1UL << bit); + + if (pe->num_free_objects++ == 0) + { + page_entry *p, *q; + + /* If the page is completely full, then it's supposed to + be after all pages that aren't. Since we've freed one + object from a page that was full, we need to move the + page to the head of the list. + + PE is the node we want to move. Q is the previous node + and P is the next node in the list. */ + q = pe->prev; + if (q && q->num_free_objects == 0) + { + p = pe->next; + + q->next = p; + + /* If PE was at the end of the list, then Q becomes the + new end of the list. If PE was not the end of the + list, then we need to update the PREV field for P. */ + if (!p) + G.page_tails[order] = q; + else + p->prev = q; + + /* Move PE to the head of the list. */ + pe->next = G.pages[order]; + pe->prev = NULL; + G.pages[order]->prev = pe; + G.pages[order] = pe; + } + + /* Reset the hint bit to point to the only free object. */ + pe->next_bit_hint = bit_offset; + } + } +#endif } -/* Initialize the ggc-mmap allocator. */ +/* Subroutine of init_ggc which computes the pair of numbers used to + perform division by OBJECT_SIZE (order) and fills in inverse_table[]. + + This algorithm is taken from Granlund and Montgomery's paper + "Division by Invariant Integers using Multiplication" + (Proc. SIGPLAN PLDI, 1994), section 9 (Exact division by + constants). */ + +static void +compute_inverse (unsigned order) +{ + size_t size, inv; + unsigned int e; + + size = OBJECT_SIZE (order); + e = 0; + while (size % 2 == 0) + { + e++; + size >>= 1; + } + inv = size; + while (inv * size != 1) + inv = inv * (2 - inv*size); + + DIV_MULT (order) = inv; + DIV_SHIFT (order) = e; +} + +/* Initialize the ggc-mmap allocator. */ void -init_ggc () +init_ggc (void) { + unsigned order; + G.pagesize = getpagesize(); G.lg_pagesize = exact_log2 (G.pagesize); -#if defined (HAVE_MMAP_ANYWHERE) && !defined(MAP_ANONYMOUS) +#ifdef HAVE_MMAP_DEV_ZERO G.dev_zero_fd = open ("/dev/zero", O_RDONLY); if (G.dev_zero_fd == -1) - abort (); + internal_error ("open /dev/zero: %m"); #endif #if 0 @@ -801,146 +1477,158 @@ init_ggc () G.debug_file = stdout; #endif - G.allocated_last_gc = GGC_MIN_LAST_ALLOCATED; - -#ifdef HAVE_MMAP_ANYWHERE +#ifdef USING_MMAP /* StunOS has an amazing off-by-one error for the first mmap allocation after fiddling with RLIMIT_STACK. The result, as hard as it is to believe, is an unaligned page allocation, which would cause us to hork badly if we tried to use it. */ { char *p = alloc_anon (NULL, G.pagesize); + struct page_entry *e; if ((size_t)p & (G.pagesize - 1)) { /* How losing. Discard this one and try another. If we still can't get something useful, give up. */ p = alloc_anon (NULL, G.pagesize); - if ((size_t)p & (G.pagesize - 1)) - abort (); + gcc_assert (!((size_t)p & (G.pagesize - 1))); } - munmap (p, G.pagesize); + + /* We have a good page, might as well hold onto it... */ + e = XCNEW (struct page_entry); + e->bytes = G.pagesize; + e->page = p; + e->next = G.free_pages; + G.free_pages = e; } #endif - empty_string = ggc_alloc_string ("", 0); - ggc_add_string_root (&empty_string, 1); + /* Initialize the object size table. */ + for (order = 0; order < HOST_BITS_PER_PTR; ++order) + object_size_table[order] = (size_t) 1 << order; + for (order = HOST_BITS_PER_PTR; order < NUM_ORDERS; ++order) + { + size_t s = extra_order_size_table[order - HOST_BITS_PER_PTR]; + + /* If S is not a multiple of the MAX_ALIGNMENT, then round it up + so that we're sure of getting aligned memory. */ + s = ROUND_UP (s, MAX_ALIGNMENT); + object_size_table[order] = s; + } + + /* Initialize the objects-per-page and inverse tables. */ + for (order = 0; order < NUM_ORDERS; ++order) + { + objects_per_page_table[order] = G.pagesize / OBJECT_SIZE (order); + if (objects_per_page_table[order] == 0) + objects_per_page_table[order] = 1; + compute_inverse (order); + } + + /* Reset the size_lookup array to put appropriately sized objects in + the special orders. All objects bigger than the previous power + of two, but no greater than the special size, should go in the + new order. */ + for (order = HOST_BITS_PER_PTR; order < NUM_ORDERS; ++order) + { + int o; + int i; + + i = OBJECT_SIZE (order); + if (i >= NUM_SIZE_LOOKUP) + continue; + + for (o = size_lookup[i]; o == size_lookup [i]; --i) + size_lookup[i] = order; + } + + G.depth_in_use = 0; + G.depth_max = 10; + G.depth = XNEWVEC (unsigned int, G.depth_max); + + G.by_depth_in_use = 0; + G.by_depth_max = INITIAL_PTE_COUNT; + G.by_depth = XNEWVEC (page_entry *, G.by_depth_max); + G.save_in_use = XNEWVEC (unsigned long *, G.by_depth_max); } -/* Increment the `GC context'. Objects allocated in an outer context - are never freed, eliminating the need to register their roots. */ +/* Start a new GGC zone. */ -void -ggc_push_context () +struct alloc_zone * +new_ggc_zone (const char *name ATTRIBUTE_UNUSED) { - ++G.context_depth; + return NULL; +} - /* Die on wrap. */ - if (G.context_depth == 0) - abort (); +/* Destroy a GGC zone. */ +void +destroy_ggc_zone (struct alloc_zone *zone ATTRIBUTE_UNUSED) +{ } /* Merge the SAVE_IN_USE_P and IN_USE_P arrays in P so that IN_USE_P reflects reality. Recalculate NUM_FREE_OBJECTS as well. */ static void -ggc_recalculate_in_use_p (p) - page_entry *p; +ggc_recalculate_in_use_p (page_entry *p) { unsigned int i; size_t num_objects; - /* Because the past-the-end bit in in_use_p is always set, we + /* Because the past-the-end bit in in_use_p is always set, we pretend there is one additional object. */ - num_objects = OBJECTS_PER_PAGE (p->order) + 1; + num_objects = OBJECTS_IN_PAGE (p) + 1; /* Reset the free object count. */ p->num_free_objects = num_objects; /* Combine the IN_USE_P and SAVE_IN_USE_P arrays. */ - for (i = 0; - i < DIV_ROUND_UP (BITMAP_SIZE (num_objects), - sizeof (*p->in_use_p)); + for (i = 0; + i < CEIL (BITMAP_SIZE (num_objects), + sizeof (*p->in_use_p)); ++i) { unsigned long j; /* Something is in use if it is marked, or if it was in use in a context further down the context stack. */ - p->in_use_p[i] |= p->save_in_use_p[i]; + p->in_use_p[i] |= save_in_use_p (p)[i]; /* Decrement the free object count for every object allocated. */ for (j = p->in_use_p[i]; j; j >>= 1) p->num_free_objects -= (j & 1); } - if (p->num_free_objects >= num_objects) - abort (); -} - -/* Decrement the `GC context'. All objects allocated since the - previous ggc_push_context are migrated to the outer context. */ - -void -ggc_pop_context () -{ - unsigned order, depth; - - depth = --G.context_depth; - - /* Any remaining pages in the popped context are lowered to the new - current context; i.e. objects allocated in the popped context and - left over are imported into the previous context. */ - for (order = 2; order < HOST_BITS_PER_PTR; order++) - { - page_entry *p; - - for (p = G.pages[order]; p != NULL; p = p->next) - { - if (p->context_depth > depth) - p->context_depth = depth; - - /* If this page is now in the topmost context, and we'd - saved its allocation state, restore it. */ - else if (p->context_depth == depth && p->save_in_use_p) - { - ggc_recalculate_in_use_p (p); - free (p->save_in_use_p); - p->save_in_use_p = 0; - } - } - } + gcc_assert (p->num_free_objects < num_objects); } /* Unmark all objects. */ -static inline void -clear_marks () +static void +clear_marks (void) { unsigned order; - for (order = 2; order < HOST_BITS_PER_PTR; order++) + for (order = 2; order < NUM_ORDERS; order++) { - size_t num_objects = OBJECTS_PER_PAGE (order); - size_t bitmap_size = BITMAP_SIZE (num_objects + 1); page_entry *p; for (p = G.pages[order]; p != NULL; p = p->next) { -#ifdef ENABLE_CHECKING + size_t num_objects = OBJECTS_IN_PAGE (p); + size_t bitmap_size = BITMAP_SIZE (num_objects + 1); + /* The data should be page-aligned. */ - if ((size_t) p->page & (G.pagesize - 1)) - abort (); -#endif + gcc_assert (!((size_t) p->page & (G.pagesize - 1))); /* Pages that aren't in the topmost context are not collected; nevertheless, we need their in-use bit vectors to store GC marks. So, back them up first. */ if (p->context_depth < G.context_depth) { - if (! p->save_in_use_p) - p->save_in_use_p = xmalloc (bitmap_size); - memcpy (p->save_in_use_p, p->in_use_p, bitmap_size); + if (! save_in_use_p (p)) + save_in_use_p (p) = xmalloc (bitmap_size); + memcpy (save_in_use_p (p), p->in_use_p, bitmap_size); } /* Reset reset the number of free objects and clear the @@ -949,7 +1637,7 @@ clear_marks () memset (p->in_use_p, 0, bitmap_size); /* Make sure the one-past-the-end bit is always set. */ - p->in_use_p[num_objects / HOST_BITS_PER_LONG] + p->in_use_p[num_objects / HOST_BITS_PER_LONG] = ((unsigned long) 1 << (num_objects % HOST_BITS_PER_LONG)); } } @@ -958,21 +1646,22 @@ clear_marks () /* Free all empty pages. Partially empty pages need no attention because the `mark' bit doubles as an `unused' bit. */ -static inline void -sweep_pages () +static void +sweep_pages (void) { unsigned order; - for (order = 2; order < HOST_BITS_PER_PTR; order++) + for (order = 2; order < NUM_ORDERS; order++) { /* The last page-entry to consider, regardless of entries placed at the end of the list. */ page_entry * const last = G.page_tails[order]; - size_t num_objects = OBJECTS_PER_PAGE (order); + size_t num_objects; + size_t live_objects; page_entry *p, *previous; int done; - + p = G.pages[order]; if (p == NULL) continue; @@ -985,18 +1674,33 @@ sweep_pages () /* Loop until all entries have been examined. */ done = (p == last); + num_objects = OBJECTS_IN_PAGE (p); + + /* Add all live objects on this page to the count of + allocated memory. */ + live_objects = num_objects - p->num_free_objects; + + G.allocated += OBJECT_SIZE (order) * live_objects; + /* Only objects on pages in the topmost context should get collected. */ if (p->context_depth < G.context_depth) ; /* Remove the page if it's empty. */ - else if (p->num_free_objects == num_objects) + else if (live_objects == 0) { + /* If P was the first page in the list, then NEXT + becomes the new first page in the list, otherwise + splice P out of the forward pointers. */ if (! previous) G.pages[order] = next; else previous->next = next; + + /* Splice P out of the back pointers too. */ + if (next) + next->prev = previous; /* Are we removing the last element? */ if (p == G.page_tails[order]) @@ -1013,6 +1717,7 @@ sweep_pages () { /* Move p to the end of the list. */ p->next = NULL; + p->prev = G.page_tails[order]; G.page_tails[order]->next = p; /* Update the tail pointer... */ @@ -1023,6 +1728,11 @@ sweep_pages () G.pages[order] = next; else previous->next = next; + + /* And update the backpointer in NEXT if necessary. */ + if (next) + next->prev = previous; + p = previous; } } @@ -1034,8 +1744,19 @@ sweep_pages () else if (p != G.pages[order]) { previous->next = p->next; + + /* Update the backchain in the next node if it exists. */ + if (p->next) + p->next->prev = previous; + + /* Move P to the head of the list. */ p->next = G.pages[order]; + p->prev = NULL; + G.pages[order]->prev = p; + + /* Update the head pointer. */ G.pages[order] = p; + /* Are we moving the last element? */ if (G.page_tails[order] == p) G.page_tails[order] = previous; @@ -1044,7 +1765,7 @@ sweep_pages () previous = p; p = next; - } + } while (! done); /* Now, restore the in_use_p vectors for any pages from contexts @@ -1055,22 +1776,22 @@ sweep_pages () } } -#ifdef GGC_POISON +#ifdef ENABLE_GC_CHECKING /* Clobber all free objects. */ -static inline void -poison_pages () +static void +poison_pages (void) { unsigned order; - for (order = 2; order < HOST_BITS_PER_PTR; order++) + for (order = 2; order < NUM_ORDERS; order++) { - size_t num_objects = OBJECTS_PER_PAGE (order); - size_t size = (size_t) 1 << order; + size_t size = OBJECT_SIZE (order); page_entry *p; for (p = G.pages[order]; p != NULL; p = p->next) { + size_t num_objects; size_t i; if (p->context_depth != G.context_depth) @@ -1080,114 +1801,525 @@ poison_pages () contexts. */ continue; + num_objects = OBJECTS_IN_PAGE (p); for (i = 0; i < num_objects; i++) { size_t word, bit; word = i / HOST_BITS_PER_LONG; bit = i % HOST_BITS_PER_LONG; if (((p->in_use_p[word] >> bit) & 1) == 0) - memset (p->page + i * size, 0xa5, size); + { + char *object = p->page + i * size; + + /* Keep poison-by-write when we expect to use Valgrind, + so the exact same memory semantics is kept, in case + there are memory errors. We override this request + below. */ + VALGRIND_DISCARD (VALGRIND_MAKE_WRITABLE (object, size)); + memset (object, 0xa5, size); + + /* Drop the handle to avoid handle leak. */ + VALGRIND_DISCARD (VALGRIND_MAKE_NOACCESS (object, size)); + } } } } } +#else +#define poison_pages() +#endif + +#ifdef ENABLE_GC_ALWAYS_COLLECT +/* Validate that the reportedly free objects actually are. */ + +static void +validate_free_objects (void) +{ + struct free_object *f, *next, *still_free = NULL; + + for (f = G.free_object_list; f ; f = next) + { + page_entry *pe = lookup_page_table_entry (f->object); + size_t bit, word; + + bit = OFFSET_TO_BIT ((char *)f->object - pe->page, pe->order); + word = bit / HOST_BITS_PER_LONG; + bit = bit % HOST_BITS_PER_LONG; + next = f->next; + + /* Make certain it isn't visible from any root. Notice that we + do this check before sweep_pages merges save_in_use_p. */ + gcc_assert (!(pe->in_use_p[word] & (1UL << bit))); + + /* If the object comes from an outer context, then retain the + free_object entry, so that we can verify that the address + isn't live on the stack in some outer context. */ + if (pe->context_depth != G.context_depth) + { + f->next = still_free; + still_free = f; + } + else + free (f); + } + + G.free_object_list = still_free; +} +#else +#define validate_free_objects() #endif /* Top level mark-and-sweep routine. */ void -ggc_collect () +ggc_collect (void) { /* Avoid frequent unnecessary work by skipping collection if the total allocations haven't expanded much since the last collection. */ -#ifndef GGC_ALWAYS_COLLECT - if (G.allocated < GGC_MIN_EXPAND_FOR_GC * G.allocated_last_gc) + float allocated_last_gc = + MAX (G.allocated_last_gc, (size_t)PARAM_VALUE (GGC_MIN_HEAPSIZE) * 1024); + + float min_expand = allocated_last_gc * PARAM_VALUE (GGC_MIN_EXPAND) / 100; + + if (G.allocated < allocated_last_gc + min_expand && !ggc_force_collect) return; -#endif timevar_push (TV_GC); if (!quiet_flag) fprintf (stderr, " {GC %luk -> ", (unsigned long) G.allocated / 1024); + if (GGC_DEBUG_LEVEL >= 2) + fprintf (G.debug_file, "BEGIN COLLECTING\n"); - /* Zero the total allocated bytes. We'll reaccumulate this while - marking. */ + /* Zero the total allocated bytes. This will be recalculated in the + sweep phase. */ G.allocated = 0; - /* Release the pages we freed the last time we collected, but didn't + /* Release the pages we freed the last time we collected, but didn't reuse in the interim. */ release_pages (); + /* Indicate that we've seen collections at this context depth. */ + G.context_depth_collections = ((unsigned long)1 << (G.context_depth + 1)) - 1; + clear_marks (); ggc_mark_roots (); - -#ifdef GGC_POISON - poison_pages (); +#ifdef GATHER_STATISTICS + ggc_prune_overhead_list (); #endif - + poison_pages (); + validate_free_objects (); sweep_pages (); G.allocated_last_gc = G.allocated; - if (G.allocated_last_gc < GGC_MIN_LAST_ALLOCATED) - G.allocated_last_gc = GGC_MIN_LAST_ALLOCATED; timevar_pop (TV_GC); if (!quiet_flag) fprintf (stderr, "%luk}", (unsigned long) G.allocated / 1024); + if (GGC_DEBUG_LEVEL >= 2) + fprintf (G.debug_file, "END COLLECTING\n"); } /* Print allocation statistics. */ +#define SCALE(x) ((unsigned long) ((x) < 1024*10 \ + ? (x) \ + : ((x) < 1024*1024*10 \ + ? (x) / 1024 \ + : (x) / (1024*1024)))) +#define STAT_LABEL(x) ((x) < 1024*10 ? ' ' : ((x) < 1024*1024*10 ? 'k' : 'M')) void -ggc_page_print_statistics () +ggc_print_statistics (void) { struct ggc_statistics stats; unsigned int i; + size_t total_overhead = 0; /* Clear the statistics. */ memset (&stats, 0, sizeof (stats)); - + /* Make sure collection will really occur. */ G.allocated_last_gc = 0; /* Collect and print the statistics common across collectors. */ - ggc_print_statistics (stderr, &stats); + ggc_print_common_statistics (stderr, &stats); /* Release free pages so that we will not count the bytes allocated there as part of the total allocated memory. */ release_pages (); - /* Collect some information about the various sizes of + /* Collect some information about the various sizes of allocation. */ - fprintf (stderr, "\n%-4s%-16s%-16s\n", "Log", "Allocated", "Used"); - for (i = 0; i < HOST_BITS_PER_PTR; ++i) + fprintf (stderr, + "Memory still allocated at the end of the compilation process\n"); + fprintf (stderr, "%-5s %10s %10s %10s\n", + "Size", "Allocated", "Used", "Overhead"); + for (i = 0; i < NUM_ORDERS; ++i) { page_entry *p; size_t allocated; size_t in_use; + size_t overhead; /* Skip empty entries. */ if (!G.pages[i]) continue; - allocated = in_use = 0; + overhead = allocated = in_use = 0; /* Figure out the total number of bytes allocated for objects of - this size, and how many of them are actually in use. */ + this size, and how many of them are actually in use. Also figure + out how much memory the page table is using. */ for (p = G.pages[i]; p; p = p->next) { allocated += p->bytes; - in_use += - (OBJECTS_PER_PAGE (i) - p->num_free_objects) * (1 << i); + in_use += + (OBJECTS_IN_PAGE (p) - p->num_free_objects) * OBJECT_SIZE (i); + + overhead += (sizeof (page_entry) - sizeof (long) + + BITMAP_SIZE (OBJECTS_IN_PAGE (p) + 1)); } - fprintf (stderr, "%-3d %-15lu %-15lu\n", i, - (unsigned long) allocated, (unsigned long) in_use); + fprintf (stderr, "%-5lu %10lu%c %10lu%c %10lu%c\n", + (unsigned long) OBJECT_SIZE (i), + SCALE (allocated), STAT_LABEL (allocated), + SCALE (in_use), STAT_LABEL (in_use), + SCALE (overhead), STAT_LABEL (overhead)); + total_overhead += overhead; + } + fprintf (stderr, "%-5s %10lu%c %10lu%c %10lu%c\n", "Total", + SCALE (G.bytes_mapped), STAT_LABEL (G.bytes_mapped), + SCALE (G.allocated), STAT_LABEL(G.allocated), + SCALE (total_overhead), STAT_LABEL (total_overhead)); + +#ifdef GATHER_STATISTICS + { + fprintf (stderr, "\nTotal allocations and overheads during the compilation process\n"); + + fprintf (stderr, "Total Overhead: %10lld\n", + G.stats.total_overhead); + fprintf (stderr, "Total Allocated: %10lld\n", + G.stats.total_allocated); + + fprintf (stderr, "Total Overhead under 32B: %10lld\n", + G.stats.total_overhead_under32); + fprintf (stderr, "Total Allocated under 32B: %10lld\n", + G.stats.total_allocated_under32); + fprintf (stderr, "Total Overhead under 64B: %10lld\n", + G.stats.total_overhead_under64); + fprintf (stderr, "Total Allocated under 64B: %10lld\n", + G.stats.total_allocated_under64); + fprintf (stderr, "Total Overhead under 128B: %10lld\n", + G.stats.total_overhead_under128); + fprintf (stderr, "Total Allocated under 128B: %10lld\n", + G.stats.total_allocated_under128); + + for (i = 0; i < NUM_ORDERS; i++) + if (G.stats.total_allocated_per_order[i]) + { + fprintf (stderr, "Total Overhead page size %7lu: %10lld\n", + (unsigned long) OBJECT_SIZE (i), + G.stats.total_overhead_per_order[i]); + fprintf (stderr, "Total Allocated page size %7lu: %10lld\n", + (unsigned long) OBJECT_SIZE (i), + G.stats.total_allocated_per_order[i]); + } + } +#endif +} + +struct ggc_pch_data +{ + struct ggc_pch_ondisk + { + unsigned totals[NUM_ORDERS]; + } d; + size_t base[NUM_ORDERS]; + size_t written[NUM_ORDERS]; +}; + +struct ggc_pch_data * +init_ggc_pch (void) +{ + return XCNEW (struct ggc_pch_data); +} + +void +ggc_pch_count_object (struct ggc_pch_data *d, void *x ATTRIBUTE_UNUSED, + size_t size, bool is_string ATTRIBUTE_UNUSED, + enum gt_types_enum type ATTRIBUTE_UNUSED) +{ + unsigned order; + + if (size < NUM_SIZE_LOOKUP) + order = size_lookup[size]; + else + { + order = 10; + while (size > OBJECT_SIZE (order)) + order++; + } + + d->d.totals[order]++; +} + +size_t +ggc_pch_total_size (struct ggc_pch_data *d) +{ + size_t a = 0; + unsigned i; + + for (i = 0; i < NUM_ORDERS; i++) + a += ROUND_UP (d->d.totals[i] * OBJECT_SIZE (i), G.pagesize); + return a; +} + +void +ggc_pch_this_base (struct ggc_pch_data *d, void *base) +{ + size_t a = (size_t) base; + unsigned i; + + for (i = 0; i < NUM_ORDERS; i++) + { + d->base[i] = a; + a += ROUND_UP (d->d.totals[i] * OBJECT_SIZE (i), G.pagesize); + } +} + + +char * +ggc_pch_alloc_object (struct ggc_pch_data *d, void *x ATTRIBUTE_UNUSED, + size_t size, bool is_string ATTRIBUTE_UNUSED, + enum gt_types_enum type ATTRIBUTE_UNUSED) +{ + unsigned order; + char *result; + + if (size < NUM_SIZE_LOOKUP) + order = size_lookup[size]; + else + { + order = 10; + while (size > OBJECT_SIZE (order)) + order++; } - /* Print out some global information. */ - fprintf (stderr, "\nTotal bytes marked: %lu\n", - (unsigned long) G.allocated); - fprintf (stderr, "Total bytes mapped: %lu\n", - (unsigned long) G.bytes_mapped); + result = (char *) d->base[order]; + d->base[order] += OBJECT_SIZE (order); + return result; +} + +void +ggc_pch_prepare_write (struct ggc_pch_data *d ATTRIBUTE_UNUSED, + FILE *f ATTRIBUTE_UNUSED) +{ + /* Nothing to do. */ +} + +void +ggc_pch_write_object (struct ggc_pch_data *d ATTRIBUTE_UNUSED, + FILE *f, void *x, void *newx ATTRIBUTE_UNUSED, + size_t size, bool is_string ATTRIBUTE_UNUSED) +{ + unsigned order; + static const char emptyBytes[256]; + + if (size < NUM_SIZE_LOOKUP) + order = size_lookup[size]; + else + { + order = 10; + while (size > OBJECT_SIZE (order)) + order++; + } + + if (fwrite (x, size, 1, f) != 1) + fatal_error ("can't write PCH file: %m"); + + /* If SIZE is not the same as OBJECT_SIZE(order), then we need to pad the + object out to OBJECT_SIZE(order). This happens for strings. */ + + if (size != OBJECT_SIZE (order)) + { + unsigned padding = OBJECT_SIZE(order) - size; + + /* To speed small writes, we use a nulled-out array that's larger + than most padding requests as the source for our null bytes. This + permits us to do the padding with fwrite() rather than fseek(), and + limits the chance the OS may try to flush any outstanding writes. */ + if (padding <= sizeof(emptyBytes)) + { + if (fwrite (emptyBytes, 1, padding, f) != padding) + fatal_error ("can't write PCH file"); + } + else + { + /* Larger than our buffer? Just default to fseek. */ + if (fseek (f, padding, SEEK_CUR) != 0) + fatal_error ("can't write PCH file"); + } + } + + d->written[order]++; + if (d->written[order] == d->d.totals[order] + && fseek (f, ROUND_UP_VALUE (d->d.totals[order] * OBJECT_SIZE (order), + G.pagesize), + SEEK_CUR) != 0) + fatal_error ("can't write PCH file: %m"); +} + +void +ggc_pch_finish (struct ggc_pch_data *d, FILE *f) +{ + if (fwrite (&d->d, sizeof (d->d), 1, f) != 1) + fatal_error ("can't write PCH file: %m"); + free (d); +} + +/* Move the PCH PTE entries just added to the end of by_depth, to the + front. */ + +static void +move_ptes_to_front (int count_old_page_tables, int count_new_page_tables) +{ + unsigned i; + + /* First, we swap the new entries to the front of the varrays. */ + page_entry **new_by_depth; + unsigned long **new_save_in_use; + + new_by_depth = XNEWVEC (page_entry *, G.by_depth_max); + new_save_in_use = XNEWVEC (unsigned long *, G.by_depth_max); + + memcpy (&new_by_depth[0], + &G.by_depth[count_old_page_tables], + count_new_page_tables * sizeof (void *)); + memcpy (&new_by_depth[count_new_page_tables], + &G.by_depth[0], + count_old_page_tables * sizeof (void *)); + memcpy (&new_save_in_use[0], + &G.save_in_use[count_old_page_tables], + count_new_page_tables * sizeof (void *)); + memcpy (&new_save_in_use[count_new_page_tables], + &G.save_in_use[0], + count_old_page_tables * sizeof (void *)); + + free (G.by_depth); + free (G.save_in_use); + + G.by_depth = new_by_depth; + G.save_in_use = new_save_in_use; + + /* Now update all the index_by_depth fields. */ + for (i = G.by_depth_in_use; i > 0; --i) + { + page_entry *p = G.by_depth[i-1]; + p->index_by_depth = i-1; + } + + /* And last, we update the depth pointers in G.depth. The first + entry is already 0, and context 0 entries always start at index + 0, so there is nothing to update in the first slot. We need a + second slot, only if we have old ptes, and if we do, they start + at index count_new_page_tables. */ + if (count_old_page_tables) + push_depth (count_new_page_tables); +} + +void +ggc_pch_read (FILE *f, void *addr) +{ + struct ggc_pch_ondisk d; + unsigned i; + char *offs = addr; + unsigned long count_old_page_tables; + unsigned long count_new_page_tables; + + count_old_page_tables = G.by_depth_in_use; + + /* We've just read in a PCH file. So, every object that used to be + allocated is now free. */ + clear_marks (); +#ifdef ENABLE_GC_CHECKING + poison_pages (); +#endif + /* Since we free all the allocated objects, the free list becomes + useless. Validate it now, which will also clear it. */ + validate_free_objects(); + + /* No object read from a PCH file should ever be freed. So, set the + context depth to 1, and set the depth of all the currently-allocated + pages to be 1 too. PCH pages will have depth 0. */ + gcc_assert (!G.context_depth); + G.context_depth = 1; + for (i = 0; i < NUM_ORDERS; i++) + { + page_entry *p; + for (p = G.pages[i]; p != NULL; p = p->next) + p->context_depth = G.context_depth; + } + + /* Allocate the appropriate page-table entries for the pages read from + the PCH file. */ + if (fread (&d, sizeof (d), 1, f) != 1) + fatal_error ("can't read PCH file: %m"); + + for (i = 0; i < NUM_ORDERS; i++) + { + struct page_entry *entry; + char *pte; + size_t bytes; + size_t num_objs; + size_t j; + + if (d.totals[i] == 0) + continue; + + bytes = ROUND_UP (d.totals[i] * OBJECT_SIZE (i), G.pagesize); + num_objs = bytes / OBJECT_SIZE (i); + entry = xcalloc (1, (sizeof (struct page_entry) + - sizeof (long) + + BITMAP_SIZE (num_objs + 1))); + entry->bytes = bytes; + entry->page = offs; + entry->context_depth = 0; + offs += bytes; + entry->num_free_objects = 0; + entry->order = i; + + for (j = 0; + j + HOST_BITS_PER_LONG <= num_objs + 1; + j += HOST_BITS_PER_LONG) + entry->in_use_p[j / HOST_BITS_PER_LONG] = -1; + for (; j < num_objs + 1; j++) + entry->in_use_p[j / HOST_BITS_PER_LONG] + |= 1L << (j % HOST_BITS_PER_LONG); + + for (pte = entry->page; + pte < entry->page + entry->bytes; + pte += G.pagesize) + set_page_table_entry (pte, entry); + + if (G.page_tails[i] != NULL) + G.page_tails[i]->next = entry; + else + G.pages[i] = entry; + G.page_tails[i] = entry; + + /* We start off by just adding all the new information to the + end of the varrays, later, we will move the new information + to the front of the varrays, as the PCH page tables are at + context 0. */ + push_by_depth (entry, 0); + } + + /* Now, we update the various data structures that speed page table + handling. */ + count_new_page_tables = G.by_depth_in_use - count_old_page_tables; + + move_ptes_to_front (count_old_page_tables, count_new_page_tables); + + /* Update the statistics. */ + G.allocated = G.allocated_last_gc = offs - (char *)addr; }