+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;
+ void *addr;
+ bool needs_read;
+
+ /* 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");
+
+ if (host_hooks.gt_pch_use_address (mmi.preferred_base, mmi.size))
+ {
+#if HAVE_MMAP_FILE
+ void *mmap_result;
+
+ mmap_result = mmap (mmi.preferred_base, mmi.size,
+ PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED,
+ fileno (f), mmi.offset);
+
+ /* The file might not be mmap-able. */
+ needs_read = mmap_result == (void *) MAP_FAILED;
+
+ /* Sanity check for broken MAP_FIXED. */
+ if (! needs_read && mmap_result != mmi.preferred_base)
+ abort ();
+#else
+ needs_read = true;
+#endif
+ addr = mmi.preferred_base;
+ }
+ else
+ {
+#if HAVE_MMAP_FILE
+ addr = mmap (mmi.preferred_base, mmi.size,
+ PROT_READ | PROT_WRITE, MAP_PRIVATE,
+ fileno (f), mmi.offset);
+
+#if HAVE_MINCORE
+ if (addr != mmi.preferred_base)
+ {
+ size_t page_size = getpagesize();
+ char one_byte;
+
+ if (addr != (void *) MAP_FAILED)
+ munmap (addr, mmi.size);
+
+ /* We really want to be mapped at mmi.preferred_base
+ so we're going to resort to MAP_FIXED. But before,
+ make sure that we can do so without destroying a
+ previously mapped area, by looping over all pages
+ that would be affected by the fixed mapping. */
+ errno = 0;
+
+ for (i = 0; i < mmi.size; i+= page_size)
+ if (mincore ((char *)mmi.preferred_base + i, page_size,
+ (void *)&one_byte) == -1
+ && errno == ENOMEM)
+ continue; /* The page is not mapped. */
+ else
+ break;
+
+ if (i >= mmi.size)
+ addr = mmap (mmi.preferred_base, mmi.size,
+ PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED,
+ fileno (f), mmi.offset);
+ }
+#endif /* HAVE_MINCORE */
+
+ needs_read = addr == (void *) MAP_FAILED;
+
+#else /* HAVE_MMAP_FILE */
+ needs_read = true;
+#endif /* HAVE_MMAP_FILE */
+ if (needs_read)
+ addr = xmalloc (mmi.size);
+ }
+
+ if (needs_read)
+ {
+ if (fseek (f, mmi.offset, SEEK_SET) != 0
+ || fread (&mmi, 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, addr);
+
+ if (addr != mmi.preferred_base)
+ {
+ for (rt = gt_ggc_rtab; *rt; rt++)
+ for (rti = *rt; rti->base != NULL; rti++)
+ for (i = 0; i < rti->nelt; i++)
+ {
+ char **ptr = (char **)((char *)rti->base + rti->stride * i);
+ if (*ptr != NULL)
+ *ptr += (size_t)addr - (size_t)mmi.preferred_base;
+ }
+
+ for (rt = gt_pch_cache_rtab; *rt; rt++)
+ for (rti = *rt; rti->base != NULL; rti++)
+ for (i = 0; i < rti->nelt; i++)
+ {
+ char **ptr = (char **)((char *)rti->base + rti->stride * i);
+ if (*ptr != NULL)
+ *ptr += (size_t)addr - (size_t)mmi.preferred_base;
+ }
+
+ sorry ("had to relocate PCH");
+ }
+
+ gt_pch_restore_stringpool ();
+}
+
+/* Modify the bound based on rlimits. Keep the smallest number found. */
+static double
+ggc_rlimit_bound (double limit)
+{
+#if defined(HAVE_GETRLIMIT)
+ struct rlimit rlim;
+# ifdef RLIMIT_RSS
+ if (getrlimit (RLIMIT_RSS, &rlim) == 0
+ && rlim.rlim_cur != (rlim_t) RLIM_INFINITY
+ && rlim.rlim_cur < limit)
+ limit = rlim.rlim_cur;
+# endif
+# ifdef RLIMIT_DATA
+ if (getrlimit (RLIMIT_DATA, &rlim) == 0
+ && rlim.rlim_cur != (rlim_t) RLIM_INFINITY
+ && rlim.rlim_cur < limit)
+ limit = rlim.rlim_cur;
+# endif
+# ifdef RLIMIT_AS
+ if (getrlimit (RLIMIT_AS, &rlim) == 0
+ && rlim.rlim_cur != (rlim_t) RLIM_INFINITY
+ && rlim.rlim_cur < limit)
+ limit = rlim.rlim_cur;
+# endif
+#endif /* HAVE_GETRLIMIT */
+
+ return limit;
+}
+
+/* Heuristic to set a default for GGC_MIN_EXPAND. */
+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. */
+int
+ggc_min_heapsize_heuristic (void)
+{
+ double min_heap_kbytes = physmem_total();
+
+ /* Adjust for rlimits. */
+ min_heap_kbytes = ggc_rlimit_bound (min_heap_kbytes);
+
+ min_heap_kbytes /= 1024; /* convert to Kbytes. */
+
+ /* The heuristic is RAM/8, with a lower bound of 4M and an upper
+ bound of 128M (when RAM >= 1GB). */
+ min_heap_kbytes /= 8;
+ min_heap_kbytes = MAX (min_heap_kbytes, 4 * 1024);
+ min_heap_kbytes = MIN (min_heap_kbytes, 128 * 1024);
+
+ return min_heap_kbytes;
+}
+
+void
+init_ggc_heuristics (void)
+{
+#ifndef ENABLE_GC_ALWAYS_COLLECT
+ set_param_value ("ggc-min-expand", ggc_min_expand_heuristic());
+ set_param_value ("ggc-min-heapsize", ggc_min_heapsize_heuristic());
+#endif