+/* Read the state of the compiler back in from F. */
+
+void
+gt_pch_restore (FILE *f)
+{
+ const struct ggc_root_tab *const *rt;
+ const struct ggc_root_tab *rti;
+ size_t i;
+ struct mmap_info mmi;
+ int result;
+
+ /* Delete any deletable objects. This makes ggc_pch_read much
+ faster, as it can be sure that no GCable objects remain other
+ than the ones just read in. */
+ for (rt = gt_ggc_deletable_rtab; *rt; rt++)
+ for (rti = *rt; rti->base != NULL; rti++)
+ memset (rti->base, 0, rti->stride);
+
+ /* Read in all the scalar variables. */
+ 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");
+
+ /* Read in all the global pointers, in 6 easy loops. */
+ for (rt = gt_ggc_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");
+
+ 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");
+
+ if (fread (&mmi, sizeof (mmi), 1, f) != 1)
+ 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 (result < 0)
+ fatal_error ("had to relocate PCH");
+ if (result == 0)
+ {
+ 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");
+ }
+ else if (fseek (f, mmi.offset + mmi.size, SEEK_SET) != 0)
+ fatal_error ("can%'t read PCH file: %m");
+
+ ggc_pch_read (f, mmi.preferred_base);
+
+ gt_pch_restore_stringpool ();
+}
+
+/* Default version of HOST_HOOKS_GT_PCH_GET_ADDRESS when mmap is not present.
+ Select no address whatsoever, and let gt_pch_save choose what it will with
+ malloc, presumably. */
+
+void *
+default_gt_pch_get_address (size_t size ATTRIBUTE_UNUSED,
+ int fd ATTRIBUTE_UNUSED)
+{
+ return NULL;
+}
+
+/* Default version of HOST_HOOKS_GT_PCH_USE_ADDRESS when mmap is not present.
+ Allocate SIZE bytes with malloc. Return 0 if the address we got is the
+ same as base, indicating that the memory has been allocated but needs to
+ be read in from the file. Return -1 if the address differs, to relocation
+ of the PCH file would be required. */
+
+int
+default_gt_pch_use_address (void *base, size_t size, int fd ATTRIBUTE_UNUSED,
+ size_t offset ATTRIBUTE_UNUSED)
+{
+ void *addr = xmalloc (size);
+ return (addr == base) - 1;
+}
+
+/* Default version of HOST_HOOKS_GT_PCH_GET_ADDRESS. Return the
+ alignment required for allocating virtual memory. Usually this is the
+ same as pagesize. */
+
+size_t
+default_gt_pch_alloc_granularity (void)
+{
+ return getpagesize();
+}
+
+#if HAVE_MMAP_FILE
+/* Default version of HOST_HOOKS_GT_PCH_GET_ADDRESS when mmap is present.
+ We temporarily allocate SIZE bytes, and let the kernel place the data
+ wherever it will. If it worked, that's our spot, if not we're likely
+ to be in trouble. */
+
+void *
+mmap_gt_pch_get_address (size_t size, int fd)
+{
+ void *ret;
+
+ ret = mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+ if (ret == (void *) MAP_FAILED)
+ ret = NULL;
+ else
+ 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
+ mapping the data at BASE, -1 if we couldn't.
+
+ This version assumes that the kernel honors the START operand of mmap
+ even without MAP_FIXED if START through START+SIZE are not currently
+ mapped with something. */
+
+int
+mmap_gt_pch_use_address (void *base, size_t size, int fd, size_t offset)
+{
+ void *addr;
+
+ /* We're called with size == 0 if we're not planning to load a PCH
+ file at all. This allows the hook to free any static space that
+ we might have allocated at link time. */
+ if (size == 0)
+ return -1;
+
+ 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)
+{
+#if defined(HAVE_GETRLIMIT)
+ struct rlimit rlim;
+# if defined (RLIMIT_AS)
+ /* RLIMIT_AS is what POSIX says is the limit on mmap. Presumably
+ any OS which has RLIMIT_AS also has a working mmap that GCC will use. */
+ if (getrlimit (RLIMIT_AS, &rlim) == 0
+ && rlim.rlim_cur != (rlim_t) RLIM_INFINITY
+ && rlim.rlim_cur < limit)
+ limit = rlim.rlim_cur;
+# elif defined (RLIMIT_DATA)
+ /* ... but some older OSs bound mmap based on RLIMIT_DATA, or we
+ might be on an OS that has a broken mmap. (Others don't bound
+ mmap at all, apparently.) */
+ if (getrlimit (RLIMIT_DATA, &rlim) == 0
+ && rlim.rlim_cur != (rlim_t) RLIM_INFINITY
+ && rlim.rlim_cur < limit
+ /* Darwin has this horribly bogus default setting of
+ RLIMIT_DATA, to 6144Kb. No-one notices because RLIMIT_DATA
+ appears to be ignored. Ignore such silliness. If a limit
+ this small was actually effective for mmap, GCC wouldn't even
+ start up. */
+ && rlim.rlim_cur >= 8 * 1024 * 1024)
+ limit = rlim.rlim_cur;
+# endif /* RLIMIT_AS or RLIMIT_DATA */
+#endif /* HAVE_GETRLIMIT */
+
+ return limit;
+}
+
+/* Heuristic to set a default for GGC_MIN_EXPAND. */
+static int
+ggc_min_expand_heuristic (void)
+{
+ double min_expand = physmem_total();
+
+ /* Adjust for rlimits. */
+ min_expand = ggc_rlimit_bound (min_expand);
+
+ /* The heuristic is a percentage equal to 30% + 70%*(RAM/1GB), yielding
+ a lower bound of 30% and an upper bound of 100% (when RAM >= 1GB). */
+ min_expand /= 1024*1024*1024;
+ min_expand *= 70;
+ min_expand = MIN (min_expand, 70);
+ min_expand += 30;
+
+ return min_expand;
+}
+
+/* Heuristic to set a default for GGC_MIN_HEAPSIZE. */
+static int
+ggc_min_heapsize_heuristic (void)
+{
+ double phys_kbytes = physmem_total();
+ double limit_kbytes = ggc_rlimit_bound (phys_kbytes * 2);
+
+ phys_kbytes /= 1024; /* Convert to Kbytes. */
+ limit_kbytes /= 1024;
+
+ /* The heuristic is RAM/8, with a lower bound of 4M and an upper
+ bound of 128M (when RAM >= 1GB). */
+ phys_kbytes /= 8;
+
+#if defined(HAVE_GETRLIMIT) && defined (RLIMIT_RSS)
+ /* 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;
+ if (getrlimit (RLIMIT_RSS, &rlim) == 0
+ && rlim.rlim_cur != (rlim_t) RLIM_INFINITY)
+ phys_kbytes = MIN (phys_kbytes, rlim.rlim_cur / 1024);
+ }
+# endif
+
+ /* Don't blindly run over our data limit; do GC at least when the
+ *next* GC would be within 20Mb of the limit or within a quarter of
+ 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 ());
+ phys_kbytes = MIN (phys_kbytes, limit_kbytes);
+
+ phys_kbytes = MAX (phys_kbytes, 4 * 1024);
+ phys_kbytes = MIN (phys_kbytes, 128 * 1024);
+
+ return phys_kbytes;
+}
+#endif
+
+void
+init_ggc_heuristics (void)
+{
+#if !defined ENABLE_GC_CHECKING && !defined ENABLE_GC_ALWAYS_COLLECT
+ set_default_param_value (GGC_MIN_EXPAND, ggc_min_expand_heuristic ());
+ set_default_param_value (GGC_MIN_HEAPSIZE, ggc_min_heapsize_heuristic ());
+#endif
+}
+
+#ifdef GATHER_STATISTICS
+
+/* Datastructure used to store per-call-site statistics. */
+struct loc_descriptor
+{
+ const char *file;
+ int line;
+ const char *function;
+ int times;
+ size_t allocated;
+ size_t overhead;
+ size_t freed;
+ size_t collected;
+};
+
+/* Hashtable used for statistics. */
+static htab_t loc_hash;
+
+/* Hash table helpers functions. */
+static hashval_t
+hash_descriptor (const void *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 *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);
+}
+
+/* Hashtable converting address of allocated field to loc descriptor. */
+static htab_t ptr_hash;
+struct ptr_hash_entry
+{
+ void *ptr;
+ struct loc_descriptor *loc;
+ size_t size;
+};
+
+/* Hash table helpers functions. */
+static hashval_t
+hash_ptr (const void *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 *const p = (const struct ptr_hash_entry *) p1;
+
+ return (p->ptr == p2);
+}
+
+/* Return descriptor for given call site, create new one if needed. */
+static struct loc_descriptor *
+loc_descriptor (const char *name, int line, const char *function)
+{
+ struct loc_descriptor loc;
+ struct loc_descriptor **slot;
+
+ loc.file = name;
+ loc.line = line;
+ loc.function = function;
+ if (!loc_hash)
+ loc_hash = htab_create (10, hash_descriptor, eq_descriptor, NULL);
+
+ slot = (struct loc_descriptor **) htab_find_slot (loc_hash, &loc, INSERT);
+ if (*slot)
+ return *slot;
+ *slot = XCNEW (struct loc_descriptor);
+ (*slot)->file = name;
+ (*slot)->line = line;
+ (*slot)->function = function;
+ return *slot;
+}
+
+/* Record ALLOCATED and OVERHEAD bytes to descriptor NAME:LINE (FUNCTION). */
+void
+ggc_record_overhead (size_t allocated, size_t overhead, void *ptr,
+ const char *name, int line, const char *function)
+{
+ struct loc_descriptor *loc = loc_descriptor (name, line, function);
+ struct ptr_hash_entry *p = XNEW (struct ptr_hash_entry);
+ PTR *slot;
+
+ p->ptr = ptr;
+ p->loc = loc;
+ p->size = allocated + overhead;
+ if (!ptr_hash)
+ ptr_hash = htab_create (10, hash_ptr, eq_ptr, NULL);
+ slot = htab_find_slot_with_hash (ptr_hash, ptr, htab_hash_pointer (ptr), INSERT);
+ gcc_assert (!*slot);
+ *slot = p;
+
+ loc->times++;
+ loc->allocated+=allocated;
+ loc->overhead+=overhead;
+}
+
+/* Helper function for prune_overhead_list. See if SLOT is still marked and
+ remove it from hashtable if it is not. */
+static int
+ggc_prune_ptr (void **slot, void *b ATTRIBUTE_UNUSED)
+{
+ struct ptr_hash_entry *p = (struct ptr_hash_entry *) *slot;
+ if (!ggc_marked_p (p->ptr))
+ {
+ p->loc->collected += p->size;
+ htab_clear_slot (ptr_hash, slot);
+ free (p);
+ }
+ return 1;
+}
+
+/* After live values has been marked, walk all recorded pointers and see if
+ they are still live. */
+void
+ggc_prune_overhead_list (void)
+{
+ htab_traverse (ptr_hash, ggc_prune_ptr, NULL);
+}
+
+/* Notice that the pointer has been freed. */
+void
+ggc_free_overhead (void *ptr)
+{
+ PTR *slot = htab_find_slot_with_hash (ptr_hash, ptr, htab_hash_pointer (ptr),
+ NO_INSERT);
+ 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);
+}
+
+/* Helper for qsort; sort descriptors by amount of memory consumed. */
+static int
+final_cmp_statistic (const void *loc1, const void *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));
+ return diff > 0 ? 1 : diff < 0 ? -1 : 0;
+}
+
+/* Helper for qsort; sort descriptors by amount of memory consumed. */
+static int
+cmp_statistic (const void *loc1, const void *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) -
+ (l2->allocated + l2->overhead - l2->freed - l2->collected));
+ if (diff)
+ return diff > 0 ? 1 : diff < 0 ? -1 : 0;
+ diff = ((long)(l1->allocated + l1->overhead - l1->freed) -
+ (l2->allocated + l2->overhead - l2->freed));
+ return diff > 0 ? 1 : diff < 0 ? -1 : 0;
+}
+
+/* Collect array of the descriptors from hashtable. */
+static struct loc_descriptor **loc_array;
+static int
+add_statistics (void **slot, void *b)
+{
+ int *n = (int *)b;
+ loc_array[*n] = (struct loc_descriptor *) *slot;
+ (*n)++;
+ return 1;
+}
+
+/* Dump per-site memory statistics. */
+#endif
+void
+dump_ggc_loc_statistics (bool final ATTRIBUTE_UNUSED)
+{
+#ifdef GATHER_STATISTICS
+ int nentries = 0;
+ char s[4096];
+ size_t collected = 0, freed = 0, allocated = 0, overhead = 0, times = 0;
+ int i;
+
+ ggc_force_collect = true;
+ ggc_collect ();
+
+ 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, "-------------------------------------------------------\n");
+ htab_traverse (loc_hash, add_statistics, &nentries);
+ qsort (loc_array, nentries, sizeof (*loc_array),
+ final ? final_cmp_statistic : cmp_statistic);
+ for (i = 0; i < nentries; i++)
+ {
+ struct loc_descriptor *d = loc_array[i];
+ allocated += d->allocated;
+ times += d->times;
+ freed += d->freed;
+ collected += d->collected;
+ overhead += d->overhead;
+ }
+ for (i = 0; i < nentries; i++)
+ {
+ struct loc_descriptor *d = loc_array[i];
+ if (d->allocated)
+ {
+ const char *s1 = d->file;
+ const char *s2;
+ while ((s2 = strstr (s1, "gcc/")))
+ s1 = s2 + 4;
+ sprintf (s, "%s:%i (%s)", s1, d->line, d->function);
+ s[48] = 0;
+ fprintf (stderr, "%-48s %10li:%4.1f%% %10li:%4.1f%% %10li:%4.1f%% %10li:%4.1f%% %10li\n", s,
+ (long)d->collected,
+ (d->collected) * 100.0 / collected,
+ (long)d->freed,
+ (d->freed) * 100.0 / freed,
+ (long)(d->allocated + d->overhead - d->freed - d->collected),
+ (d->allocated + d->overhead - d->freed - d->collected) * 100.0
+ / (allocated + overhead - freed - collected),
+ (long)d->overhead,
+ d->overhead * 100.0 / overhead,
+ (long)d->times);
+ }
+ }
+ fprintf (stderr, "%-48s %10ld %10ld %10ld %10ld %10ld\n",
+ "Total", (long)collected, (long)freed,
+ (long)(allocated + overhead - freed - collected), (long)overhead,
+ (long)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