analyzer: fix feasibility false +ve on jumps through function ptrs [PR107582]
[platform/upstream/gcc.git] / gcc / ggc-common.cc
1 /* Simple garbage collection for the GNU compiler.
2    Copyright (C) 1999-2022 Free Software Foundation, Inc.
3
4 This file is part of GCC.
5
6 GCC is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 3, or (at your option) any later
9 version.
10
11 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GCC; see the file COPYING3.  If not see
18 <http://www.gnu.org/licenses/>.  */
19
20 /* Generic garbage collection (GC) functions and data, not specific to
21    any particular GC implementation.  */
22
23 #include "config.h"
24 #define INCLUDE_MALLOC_H
25 #include "system.h"
26 #include "coretypes.h"
27 #include "timevar.h"
28 #include "diagnostic-core.h"
29 #include "ggc-internal.h"
30 #include "hosthooks.h"
31 #include "plugin.h"
32 #include "options.h"
33
34 /* When true, protect the contents of the identifier hash table.  */
35 bool ggc_protect_identifiers = true;
36
37 /* Statistics about the allocation.  */
38 static ggc_statistics *ggc_stats;
39
40 struct traversal_state;
41
42 static int compare_ptr_data (const void *, const void *);
43 static void relocate_ptrs (void *, void *, void *);
44 static void write_pch_globals (const struct ggc_root_tab * const *tab,
45                                struct traversal_state *state);
46
47 /* Maintain global roots that are preserved during GC.  */
48
49 /* This extra vector of dynamically registered root_tab-s is used by
50    ggc_mark_roots and gives the ability to dynamically add new GGC root
51    tables, for instance from some plugins; this vector is on the heap
52    since it is used by GGC internally.  */
53 typedef const struct ggc_root_tab *const_ggc_root_tab_t;
54 static vec<const_ggc_root_tab_t> extra_root_vec;
55
56 /* Dynamically register a new GGC root table RT. This is useful for
57    plugins. */
58
59 void
60 ggc_register_root_tab (const struct ggc_root_tab* rt)
61 {
62   if (rt)
63     extra_root_vec.safe_push (rt);
64 }
65
66 /* Mark all the roots in the table RT.  */
67
68 static void
69 ggc_mark_root_tab (const_ggc_root_tab_t rt)
70 {
71   size_t i;
72
73   for ( ; rt->base != NULL; rt++)
74     for (i = 0; i < rt->nelt; i++)
75       (*rt->cb) (*(void **) ((char *)rt->base + rt->stride * i));
76 }
77
78 /* Iterate through all registered roots and mark each element.  */
79
80 void
81 ggc_mark_roots (void)
82 {
83   const struct ggc_root_tab *const *rt;
84   const_ggc_root_tab_t rtp, rti;
85   size_t i;
86
87   for (rt = gt_ggc_deletable_rtab; *rt; rt++)
88     for (rti = *rt; rti->base != NULL; rti++)
89       memset (rti->base, 0, rti->stride);
90
91   for (rt = gt_ggc_rtab; *rt; rt++)
92     ggc_mark_root_tab (*rt);
93
94   FOR_EACH_VEC_ELT (extra_root_vec, i, rtp)
95     ggc_mark_root_tab (rtp);
96
97   if (ggc_protect_identifiers)
98     ggc_mark_stringpool ();
99
100   gt_clear_caches ();
101
102   if (! ggc_protect_identifiers)
103     ggc_purge_stringpool ();
104
105   /* Some plugins may call ggc_set_mark from here.  */
106   invoke_plugin_callbacks (PLUGIN_GGC_MARKING, NULL);
107 }
108
109 /* Allocate a block of memory, then clear it.  */
110 void *
111 ggc_internal_cleared_alloc (size_t size, void (*f)(void *), size_t s, size_t n
112                             MEM_STAT_DECL)
113 {
114   void *buf = ggc_internal_alloc (size, f, s, n PASS_MEM_STAT);
115   memset (buf, 0, size);
116   return buf;
117 }
118
119 /* Resize a block of memory, possibly re-allocating it.  */
120 void *
121 ggc_realloc (void *x, size_t size MEM_STAT_DECL)
122 {
123   void *r;
124   size_t old_size;
125
126   if (x == NULL)
127     return ggc_internal_alloc (size PASS_MEM_STAT);
128
129   old_size = ggc_get_size (x);
130
131   if (size <= old_size)
132     {
133       /* Mark the unwanted memory as unaccessible.  We also need to make
134          the "new" size accessible, since ggc_get_size returns the size of
135          the pool, not the size of the individually allocated object, the
136          size which was previously made accessible.  Unfortunately, we
137          don't know that previously allocated size.  Without that
138          knowledge we have to lose some initialization-tracking for the
139          old parts of the object.  An alternative is to mark the whole
140          old_size as reachable, but that would lose tracking of writes
141          after the end of the object (by small offsets).  Discard the
142          handle to avoid handle leak.  */
143       VALGRIND_DISCARD (VALGRIND_MAKE_MEM_NOACCESS ((char *) x + size,
144                                                     old_size - size));
145       VALGRIND_DISCARD (VALGRIND_MAKE_MEM_DEFINED (x, size));
146       return x;
147     }
148
149   r = ggc_internal_alloc (size PASS_MEM_STAT);
150
151   /* Since ggc_get_size returns the size of the pool, not the size of the
152      individually allocated object, we'd access parts of the old object
153      that were marked invalid with the memcpy below.  We lose a bit of the
154      initialization-tracking since some of it may be uninitialized.  */
155   VALGRIND_DISCARD (VALGRIND_MAKE_MEM_DEFINED (x, old_size));
156
157   memcpy (r, x, old_size);
158
159   /* The old object is not supposed to be used anymore.  */
160   ggc_free (x);
161
162   return r;
163 }
164
165 void *
166 ggc_cleared_alloc_htab_ignore_args (size_t c ATTRIBUTE_UNUSED,
167                                     size_t n ATTRIBUTE_UNUSED)
168 {
169   gcc_assert (c * n == sizeof (struct htab));
170   return ggc_cleared_alloc<htab> ();
171 }
172
173 /* TODO: once we actually use type information in GGC, create a new tag
174    gt_gcc_ptr_array and use it for pointer arrays.  */
175 void *
176 ggc_cleared_alloc_ptr_array_two_args (size_t c, size_t n)
177 {
178   gcc_assert (sizeof (void **) == n);
179   return ggc_cleared_vec_alloc<void **> (c);
180 }
181
182 /* These are for splay_tree_new_ggc.  */
183 void *
184 ggc_splay_alloc (int sz, void *nl)
185 {
186   gcc_assert (!nl);
187   return ggc_internal_alloc (sz);
188 }
189
190 void
191 ggc_splay_dont_free (void * x ATTRIBUTE_UNUSED, void *nl)
192 {
193   gcc_assert (!nl);
194 }
195
196 void
197 ggc_print_common_statistics (FILE *stream ATTRIBUTE_UNUSED,
198                              ggc_statistics *stats)
199 {
200   /* Set the pointer so that during collection we will actually gather
201      the statistics.  */
202   ggc_stats = stats;
203
204   /* Then do one collection to fill in the statistics.  */
205   ggc_collect ();
206
207   /* At present, we don't really gather any interesting statistics.  */
208
209   /* Don't gather statistics any more.  */
210   ggc_stats = NULL;
211 }
212 \f
213 /* Functions for saving and restoring GCable memory to disk.  */
214
215 struct ptr_data
216 {
217   void *obj;
218   void *note_ptr_cookie;
219   gt_note_pointers note_ptr_fn;
220   gt_handle_reorder reorder_fn;
221   size_t size;
222   void *new_addr;
223 };
224
225 #define POINTER_HASH(x) (hashval_t)((intptr_t)x >> 3)
226
227 /* Helper for hashing saving_htab.  */
228
229 struct saving_hasher : free_ptr_hash <ptr_data>
230 {
231   typedef void *compare_type;
232   static inline hashval_t hash (const ptr_data *);
233   static inline bool equal (const ptr_data *, const void *);
234 };
235
236 inline hashval_t
237 saving_hasher::hash (const ptr_data *p)
238 {
239   return POINTER_HASH (p->obj);
240 }
241
242 inline bool
243 saving_hasher::equal (const ptr_data *p1, const void *p2)
244 {
245   return p1->obj == p2;
246 }
247
248 static hash_table<saving_hasher> *saving_htab;
249 static vec<void *> callback_vec;
250 static vec<void *> reloc_addrs_vec;
251
252 /* Register an object in the hash table.  */
253
254 int
255 gt_pch_note_object (void *obj, void *note_ptr_cookie,
256                     gt_note_pointers note_ptr_fn,
257                     size_t length_override)
258 {
259   struct ptr_data **slot;
260
261   if (obj == NULL || obj == (void *) 1)
262     return 0;
263
264   slot = (struct ptr_data **)
265     saving_htab->find_slot_with_hash (obj, POINTER_HASH (obj), INSERT);
266   if (*slot != NULL)
267     {
268       gcc_assert ((*slot)->note_ptr_fn == note_ptr_fn
269                   && (*slot)->note_ptr_cookie == note_ptr_cookie);
270       return 0;
271     }
272
273   *slot = XCNEW (struct ptr_data);
274   (*slot)->obj = obj;
275   (*slot)->note_ptr_fn = note_ptr_fn;
276   (*slot)->note_ptr_cookie = note_ptr_cookie;
277   if (length_override != (size_t)-1)
278     (*slot)->size = length_override;
279   else if (note_ptr_fn == gt_pch_p_S)
280     (*slot)->size = strlen ((const char *)obj) + 1;
281   else
282     (*slot)->size = ggc_get_size (obj);
283   return 1;
284 }
285
286 /* Register address of a callback pointer.  */
287 void
288 gt_pch_note_callback (void *obj, void *base)
289 {
290   void *ptr;
291   memcpy (&ptr, obj, sizeof (void *));
292   if (ptr != NULL)
293     {
294       struct ptr_data *data
295         = (struct ptr_data *)
296           saving_htab->find_with_hash (base, POINTER_HASH (base));
297       gcc_assert (data);
298       callback_vec.safe_push ((char *) data->new_addr
299                               + ((char *) obj - (char *) base));
300     }
301 }
302
303 /* Register an object in the hash table.  */
304
305 void
306 gt_pch_note_reorder (void *obj, void *note_ptr_cookie,
307                      gt_handle_reorder reorder_fn)
308 {
309   struct ptr_data *data;
310
311   if (obj == NULL || obj == (void *) 1)
312     return;
313
314   data = (struct ptr_data *)
315     saving_htab->find_with_hash (obj, POINTER_HASH (obj));
316   gcc_assert (data && data->note_ptr_cookie == note_ptr_cookie);
317
318   data->reorder_fn = reorder_fn;
319 }
320
321 /* Handy state for the traversal functions.  */
322
323 struct traversal_state
324 {
325   FILE *f;
326   struct ggc_pch_data *d;
327   size_t count;
328   struct ptr_data **ptrs;
329   size_t ptrs_i;
330 };
331
332 /* Callbacks for htab_traverse.  */
333
334 int
335 ggc_call_count (ptr_data **slot, traversal_state *state)
336 {
337   struct ptr_data *d = *slot;
338
339   ggc_pch_count_object (state->d, d->obj, d->size,
340                         d->note_ptr_fn == gt_pch_p_S);
341   state->count++;
342   return 1;
343 }
344
345 int
346 ggc_call_alloc (ptr_data **slot, traversal_state *state)
347 {
348   struct ptr_data *d = *slot;
349
350   d->new_addr = ggc_pch_alloc_object (state->d, d->obj, d->size,
351                                       d->note_ptr_fn == gt_pch_p_S);
352   state->ptrs[state->ptrs_i++] = d;
353   return 1;
354 }
355
356 /* Callback for qsort.  */
357
358 static int
359 compare_ptr_data (const void *p1_p, const void *p2_p)
360 {
361   const struct ptr_data *const p1 = *(const struct ptr_data *const *)p1_p;
362   const struct ptr_data *const p2 = *(const struct ptr_data *const *)p2_p;
363   return (((size_t)p1->new_addr > (size_t)p2->new_addr)
364           - ((size_t)p1->new_addr < (size_t)p2->new_addr));
365 }
366
367 /* Callbacks for note_ptr_fn.  */
368
369 static void
370 relocate_ptrs (void *ptr_p, void *real_ptr_p, void *state_p)
371 {
372   void **ptr = (void **)ptr_p;
373   struct traversal_state *state
374     = (struct traversal_state *)state_p;
375   struct ptr_data *result;
376
377   if (*ptr == NULL || *ptr == (void *)1)
378     return;
379
380   result = (struct ptr_data *)
381     saving_htab->find_with_hash (*ptr, POINTER_HASH (*ptr));
382   gcc_assert (result);
383   *ptr = result->new_addr;
384   if (ptr_p == real_ptr_p)
385     return;
386   if (real_ptr_p == NULL)
387     real_ptr_p = ptr_p;
388   gcc_assert (real_ptr_p >= state->ptrs[state->ptrs_i]->obj
389               && ((char *) real_ptr_p + sizeof (void *)
390                   <= ((char *) state->ptrs[state->ptrs_i]->obj
391                       + state->ptrs[state->ptrs_i]->size)));
392   void *addr
393     = (void *) ((char *) state->ptrs[state->ptrs_i]->new_addr
394                 + ((char *) real_ptr_p
395                    - (char *) state->ptrs[state->ptrs_i]->obj));
396   reloc_addrs_vec.safe_push (addr);
397 }
398
399 /* Write out, after relocation, the pointers in TAB.  */
400 static void
401 write_pch_globals (const struct ggc_root_tab * const *tab,
402                    struct traversal_state *state)
403 {
404   const struct ggc_root_tab *const *rt;
405   const struct ggc_root_tab *rti;
406   size_t i;
407
408   for (rt = tab; *rt; rt++)
409     for (rti = *rt; rti->base != NULL; rti++)
410       for (i = 0; i < rti->nelt; i++)
411         {
412           void *ptr = *(void **)((char *)rti->base + rti->stride * i);
413           struct ptr_data *new_ptr;
414           if (ptr == NULL || ptr == (void *)1)
415             {
416               if (fwrite (&ptr, sizeof (void *), 1, state->f)
417                   != 1)
418                 fatal_error (input_location, "cannot write PCH file: %m");
419             }
420           else
421             {
422               new_ptr = (struct ptr_data *)
423                 saving_htab->find_with_hash (ptr, POINTER_HASH (ptr));
424               if (fwrite (&new_ptr->new_addr, sizeof (void *), 1, state->f)
425                   != 1)
426                 fatal_error (input_location, "cannot write PCH file: %m");
427             }
428         }
429 }
430
431 /* Callback for qsort.  */
432
433 static int
434 compare_ptr (const void *p1_p, const void *p2_p)
435 {
436   void *p1 = *(void *const *)p1_p;
437   void *p2 = *(void *const *)p2_p;
438   return (((uintptr_t)p1 > (uintptr_t)p2)
439           - ((uintptr_t)p1 < (uintptr_t)p2));
440 }
441
442 /* Decode one uleb128 from P, return first byte after it, store
443    decoded value into *VAL.  */
444
445 static unsigned char *
446 read_uleb128 (unsigned char *p, size_t *val)
447 {
448   unsigned int shift = 0;
449   unsigned char byte;
450   size_t result;
451
452   result = 0;
453   do
454     {
455       byte = *p++;
456       result |= ((size_t) byte & 0x7f) << shift;
457       shift += 7;
458     }
459   while (byte & 0x80);
460
461   *val = result;
462   return p;
463 }
464
465 /* Store VAL as uleb128 at P, return length in bytes.  */
466
467 static size_t
468 write_uleb128 (unsigned char *p, size_t val)
469 {
470   size_t len = 0;
471   do
472     {
473       unsigned char byte = (val & 0x7f);
474       val >>= 7;
475       if (val != 0)
476         /* More bytes to follow.  */
477         byte |= 0x80;
478
479       *p++ = byte;
480       ++len;
481     }
482   while (val != 0);
483   return len;
484 }
485
486 /* Hold the information we need to mmap the file back in.  */
487
488 struct mmap_info
489 {
490   size_t offset;
491   size_t size;
492   void *preferred_base;
493 };
494
495 /* Write out the state of the compiler to F.  */
496
497 void
498 gt_pch_save (FILE *f)
499 {
500   const struct ggc_root_tab *const *rt;
501   const struct ggc_root_tab *rti;
502   size_t i;
503   struct traversal_state state;
504   char *this_object = NULL;
505   size_t this_object_size = 0;
506   struct mmap_info mmi;
507   const size_t mmap_offset_alignment = host_hooks.gt_pch_alloc_granularity ();
508
509   gt_pch_save_stringpool ();
510
511   timevar_push (TV_PCH_PTR_REALLOC);
512   saving_htab = new hash_table<saving_hasher> (50000);
513
514   for (rt = gt_ggc_rtab; *rt; rt++)
515     for (rti = *rt; rti->base != NULL; rti++)
516       for (i = 0; i < rti->nelt; i++)
517         (*rti->pchw)(*(void **)((char *)rti->base + rti->stride * i));
518
519   /* Prepare the objects for writing, determine addresses and such.  */
520   state.f = f;
521   state.d = init_ggc_pch ();
522   state.count = 0;
523   saving_htab->traverse <traversal_state *, ggc_call_count> (&state);
524
525   mmi.size = ggc_pch_total_size (state.d);
526
527   /* Try to arrange things so that no relocation is necessary, but
528      don't try very hard.  On most platforms, this will always work,
529      and on the rest it's a lot of work to do better.
530      (The extra work goes in HOST_HOOKS_GT_PCH_GET_ADDRESS and
531      HOST_HOOKS_GT_PCH_USE_ADDRESS.)  */
532   mmi.preferred_base = host_hooks.gt_pch_get_address (mmi.size, fileno (f));
533   /* If the host cannot supply any suitable address for this, we are stuck.  */
534   if (mmi.preferred_base == NULL)
535     fatal_error (input_location,
536                  "cannot write PCH file: required memory segment unavailable");
537
538   ggc_pch_this_base (state.d, mmi.preferred_base);
539
540   state.ptrs = XNEWVEC (struct ptr_data *, state.count);
541   state.ptrs_i = 0;
542
543   saving_htab->traverse <traversal_state *, ggc_call_alloc> (&state);
544   timevar_pop (TV_PCH_PTR_REALLOC);
545
546   timevar_push (TV_PCH_PTR_SORT);
547   qsort (state.ptrs, state.count, sizeof (*state.ptrs), compare_ptr_data);
548   timevar_pop (TV_PCH_PTR_SORT);
549
550   /* Write out all the scalar variables.  */
551   for (rt = gt_pch_scalar_rtab; *rt; rt++)
552     for (rti = *rt; rti->base != NULL; rti++)
553       if (fwrite (rti->base, rti->stride, 1, f) != 1)
554         fatal_error (input_location, "cannot write PCH file: %m");
555
556   /* Write out all the global pointers, after translation.  */
557   write_pch_globals (gt_ggc_rtab, &state);
558
559   /* Pad the PCH file so that the mmapped area starts on an allocation
560      granularity (usually page) boundary.  */
561   {
562     long o;
563     o = ftell (state.f) + sizeof (mmi);
564     if (o == -1)
565       fatal_error (input_location, "cannot get position in PCH file: %m");
566     mmi.offset = mmap_offset_alignment - o % mmap_offset_alignment;
567     if (mmi.offset == mmap_offset_alignment)
568       mmi.offset = 0;
569     mmi.offset += o;
570   }
571   if (fwrite (&mmi, sizeof (mmi), 1, state.f) != 1)
572     fatal_error (input_location, "cannot write PCH file: %m");
573   if (mmi.offset != 0
574       && fseek (state.f, mmi.offset, SEEK_SET) != 0)
575     fatal_error (input_location, "cannot write padding to PCH file: %m");
576
577   ggc_pch_prepare_write (state.d, state.f);
578
579 #if defined ENABLE_VALGRIND_ANNOTATIONS && defined VALGRIND_GET_VBITS
580   vec<char> vbits = vNULL;
581 #endif
582
583   /* Actually write out the objects.  */
584   for (i = 0; i < state.count; i++)
585     {
586       state.ptrs_i = i;
587       if (this_object_size < state.ptrs[i]->size)
588         {
589           this_object_size = state.ptrs[i]->size;
590           this_object = XRESIZEVAR (char, this_object, this_object_size);
591         }
592 #if defined ENABLE_VALGRIND_ANNOTATIONS && defined VALGRIND_GET_VBITS
593       /* obj might contain uninitialized bytes, e.g. in the trailing
594          padding of the object.  Avoid warnings by making the memory
595          temporarily defined and then restoring previous state.  */
596       int get_vbits = 0;
597       size_t valid_size = state.ptrs[i]->size;
598       if (UNLIKELY (RUNNING_ON_VALGRIND))
599         {
600           if (vbits.length () < valid_size)
601             vbits.safe_grow (valid_size, true);
602           get_vbits = VALGRIND_GET_VBITS (state.ptrs[i]->obj,
603                                           vbits.address (), valid_size);
604           if (get_vbits == 3)
605             {
606               /* We assume that first part of obj is addressable, and
607                  the rest is unaddressable.  Find out where the boundary is
608                  using binary search.  */
609               size_t lo = 0, hi = valid_size;
610               while (hi > lo)
611                 {
612                   size_t mid = (lo + hi) / 2;
613                   get_vbits = VALGRIND_GET_VBITS ((char *) state.ptrs[i]->obj
614                                                   + mid, vbits.address (),
615                                                   1);
616                   if (get_vbits == 3)
617                     hi = mid;
618                   else if (get_vbits == 1)
619                     lo = mid + 1;
620                   else
621                     break;
622                 }
623               if (get_vbits == 1 || get_vbits == 3)
624                 {
625                   valid_size = lo;
626                   get_vbits = VALGRIND_GET_VBITS (state.ptrs[i]->obj,
627                                                   vbits.address (),
628                                                   valid_size);
629                 }
630             }
631           if (get_vbits == 1)
632             VALGRIND_DISCARD (VALGRIND_MAKE_MEM_DEFINED (state.ptrs[i]->obj,
633                                                          state.ptrs[i]->size));
634         }
635 #endif
636       memcpy (this_object, state.ptrs[i]->obj, state.ptrs[i]->size);
637       if (state.ptrs[i]->reorder_fn != NULL)
638         state.ptrs[i]->reorder_fn (state.ptrs[i]->obj,
639                                    state.ptrs[i]->note_ptr_cookie,
640                                    relocate_ptrs, &state);
641       state.ptrs[i]->note_ptr_fn (state.ptrs[i]->obj,
642                                   state.ptrs[i]->note_ptr_cookie,
643                                   relocate_ptrs, &state);
644       ggc_pch_write_object (state.d, state.f, state.ptrs[i]->obj,
645                             state.ptrs[i]->new_addr, state.ptrs[i]->size,
646                             state.ptrs[i]->note_ptr_fn == gt_pch_p_S);
647       if (state.ptrs[i]->note_ptr_fn != gt_pch_p_S)
648         memcpy (state.ptrs[i]->obj, this_object, state.ptrs[i]->size);
649 #if defined ENABLE_VALGRIND_ANNOTATIONS && defined VALGRIND_GET_VBITS
650       if (UNLIKELY (get_vbits == 1))
651         {
652           (void) VALGRIND_SET_VBITS (state.ptrs[i]->obj, vbits.address (),
653                                      valid_size);
654           if (valid_size != state.ptrs[i]->size)
655             VALGRIND_DISCARD (VALGRIND_MAKE_MEM_NOACCESS ((char *)
656                                                           state.ptrs[i]->obj
657                                                           + valid_size,
658                                                           state.ptrs[i]->size
659                                                           - valid_size));
660         }
661 #endif
662     }
663 #if defined ENABLE_VALGRIND_ANNOTATIONS && defined VALGRIND_GET_VBITS
664   vbits.release ();
665 #endif
666
667   reloc_addrs_vec.qsort (compare_ptr);
668
669   size_t reloc_addrs_size = 0;
670   void *last_addr = NULL;
671   unsigned char uleb128_buf[sizeof (size_t) * 2];
672   for (void *addr : reloc_addrs_vec)
673     {
674       gcc_assert ((uintptr_t) addr >= (uintptr_t) mmi.preferred_base
675                   && ((uintptr_t) addr + sizeof (void *)
676                       < (uintptr_t) mmi.preferred_base + mmi.size));
677       if (addr == last_addr)
678         continue;
679       if (last_addr == NULL)
680         last_addr = mmi.preferred_base;
681       size_t diff = (uintptr_t) addr - (uintptr_t) last_addr;
682       reloc_addrs_size += write_uleb128 (uleb128_buf, diff);
683       last_addr = addr;
684     }
685   if (fwrite (&reloc_addrs_size, sizeof (reloc_addrs_size), 1, f) != 1)
686     fatal_error (input_location, "cannot write PCH file: %m");
687   last_addr = NULL;
688   for (void *addr : reloc_addrs_vec)
689     {
690       if (addr == last_addr)
691         continue;
692       if (last_addr == NULL)
693         last_addr = mmi.preferred_base;
694       size_t diff = (uintptr_t) addr - (uintptr_t) last_addr;
695       reloc_addrs_size = write_uleb128 (uleb128_buf, diff);
696       if (fwrite (uleb128_buf, 1, reloc_addrs_size, f) != reloc_addrs_size)
697         fatal_error (input_location, "cannot write PCH file: %m");
698       last_addr = addr;
699     }
700
701   ggc_pch_finish (state.d, state.f);
702
703   gt_pch_fixup_stringpool ();
704
705   unsigned num_callbacks = callback_vec.length ();
706   void (*pch_save) (FILE *) = &gt_pch_save;
707   if (fwrite (&pch_save, sizeof (pch_save), 1, f) != 1
708       || fwrite (&num_callbacks, sizeof (num_callbacks), 1, f) != 1
709       || (num_callbacks
710           && fwrite (callback_vec.address (), sizeof (void *), num_callbacks,
711                      f) != num_callbacks))
712     fatal_error (input_location, "cannot write PCH file: %m");
713
714   XDELETE (state.ptrs);
715   XDELETE (this_object);
716   delete saving_htab;
717   saving_htab = NULL;
718   callback_vec.release ();
719   reloc_addrs_vec.release ();
720 }
721
722 /* Read the state of the compiler back in from F.  */
723
724 void
725 gt_pch_restore (FILE *f)
726 {
727   const struct ggc_root_tab *const *rt;
728   const struct ggc_root_tab *rti;
729   size_t i;
730   struct mmap_info mmi;
731   int result;
732
733   /* We are about to reload the line maps along with the rest of the PCH
734      data, which means that the (loaded) ones cannot be guaranteed to be
735      in any valid state for reporting diagnostics that happen during the
736      load.  Save the current table (and use it during the loading process
737      below).  */
738   class line_maps *save_line_table = line_table;
739
740   /* Delete any deletable objects.  This makes ggc_pch_read much
741      faster, as it can be sure that no GCable objects remain other
742      than the ones just read in.  */
743   for (rt = gt_ggc_deletable_rtab; *rt; rt++)
744     for (rti = *rt; rti->base != NULL; rti++)
745       memset (rti->base, 0, rti->stride);
746
747   /* Read in all the scalar variables.  */
748   for (rt = gt_pch_scalar_rtab; *rt; rt++)
749     for (rti = *rt; rti->base != NULL; rti++)
750       if (fread (rti->base, rti->stride, 1, f) != 1)
751         fatal_error (input_location, "cannot read PCH file: %m");
752
753   /* Read in all the global pointers, in 6 easy loops.  */
754   bool error_reading_pointers = false;
755   for (rt = gt_ggc_rtab; *rt; rt++)
756     for (rti = *rt; rti->base != NULL; rti++)
757       for (i = 0; i < rti->nelt; i++)
758         if (fread ((char *)rti->base + rti->stride * i,
759                    sizeof (void *), 1, f) != 1)
760           error_reading_pointers = true;
761
762   /* Stash the newly read-in line table pointer - it does not point to
763      anything meaningful yet, so swap the old one back in.  */
764   class line_maps *new_line_table = line_table;
765   line_table = save_line_table;
766   if (error_reading_pointers)
767     fatal_error (input_location, "cannot read PCH file: %m");
768
769   if (fread (&mmi, sizeof (mmi), 1, f) != 1)
770     fatal_error (input_location, "cannot read PCH file: %m");
771
772   void *orig_preferred_base = mmi.preferred_base;
773   result = host_hooks.gt_pch_use_address (mmi.preferred_base, mmi.size,
774                                           fileno (f), mmi.offset);
775
776   /* We could not mmap or otherwise allocate the required memory at the
777      address needed.  */
778   if (result < 0)
779     {
780       sorry_at (input_location, "PCH allocation failure");
781       /* There is no point in continuing from here, we will only end up
782          with a crashed (most likely hanging) compiler.  */
783       exit (-1);
784     }
785
786   /* (0) We allocated memory, but did not mmap the file, so we need to read
787      the data in manually.  (>0) Otherwise the mmap succeed for the address
788      we wanted.  */
789   if (result == 0)
790     {
791       if (fseek (f, mmi.offset, SEEK_SET) != 0
792           || fread (mmi.preferred_base, mmi.size, 1, f) != 1)
793         fatal_error (input_location, "cannot read PCH file: %m");
794     }
795   else if (fseek (f, mmi.offset + mmi.size, SEEK_SET) != 0)
796     fatal_error (input_location, "cannot read PCH file: %m");
797
798   size_t reloc_addrs_size;
799   if (fread (&reloc_addrs_size, sizeof (reloc_addrs_size), 1, f) != 1)
800     fatal_error (input_location, "cannot read PCH file: %m");
801
802   if (orig_preferred_base != mmi.preferred_base)
803     {
804       uintptr_t bias
805         = (uintptr_t) mmi.preferred_base - (uintptr_t) orig_preferred_base;
806
807       /* Adjust all the global pointers by bias.  */
808       line_table = new_line_table;
809       for (rt = gt_ggc_rtab; *rt; rt++)
810         for (rti = *rt; rti->base != NULL; rti++)
811       for (i = 0; i < rti->nelt; i++)
812         {
813           char *addr = (char *)rti->base + rti->stride * i;
814           char *p;
815           memcpy (&p, addr, sizeof (void *));
816           if ((uintptr_t) p >= (uintptr_t) orig_preferred_base
817               && (uintptr_t) p < (uintptr_t) orig_preferred_base + mmi.size)
818             {
819               p = (char *) ((uintptr_t) p + bias);
820               memcpy (addr, &p, sizeof (void *));
821             }
822         }
823       new_line_table = line_table;
824       line_table = save_line_table;
825
826       /* And adjust all the pointers in the image by bias too.  */
827       char *addr = (char *) mmi.preferred_base;
828       unsigned char uleb128_buf[4096], *uleb128_ptr = uleb128_buf;
829       while (reloc_addrs_size != 0)
830         {
831           size_t this_size
832             = MIN (reloc_addrs_size,
833                    (size_t) (4096 - (uleb128_ptr - uleb128_buf)));
834           if (fread (uleb128_ptr, 1, this_size, f) != this_size)
835             fatal_error (input_location, "cannot read PCH file: %m");
836           unsigned char *uleb128_end = uleb128_ptr + this_size;
837           if (this_size != reloc_addrs_size)
838             uleb128_end -= 2 * sizeof (size_t);
839           uleb128_ptr = uleb128_buf;
840           while (uleb128_ptr < uleb128_end)
841             {
842               size_t diff;
843               uleb128_ptr = read_uleb128 (uleb128_ptr, &diff);
844               addr = (char *) ((uintptr_t) addr + diff);
845
846               char *p;
847               memcpy (&p, addr, sizeof (void *));
848               gcc_assert ((uintptr_t) p >= (uintptr_t) orig_preferred_base
849                           && ((uintptr_t) p
850                               < (uintptr_t) orig_preferred_base + mmi.size));
851               p = (char *) ((uintptr_t) p + bias);
852               memcpy (addr, &p, sizeof (void *));
853             }
854           reloc_addrs_size -= this_size;
855           if (reloc_addrs_size == 0)
856             break;
857           this_size = uleb128_end + 2 * sizeof (size_t) - uleb128_ptr;
858           memcpy (uleb128_buf, uleb128_ptr, this_size);
859           uleb128_ptr = uleb128_buf + this_size;
860         }
861     }
862   else if (fseek (f, (mmi.offset + mmi.size + sizeof (reloc_addrs_size)
863                       + reloc_addrs_size), SEEK_SET) != 0)
864     fatal_error (input_location, "cannot read PCH file: %m");
865
866   ggc_pch_read (f, mmi.preferred_base);
867
868   void (*pch_save) (FILE *);
869   unsigned num_callbacks;
870   if (fread (&pch_save, sizeof (pch_save), 1, f) != 1
871       || fread (&num_callbacks, sizeof (num_callbacks), 1, f) != 1)
872     fatal_error (input_location, "cannot read PCH file: %m");
873   if (pch_save != &gt_pch_save)
874     {
875       uintptr_t binbias = (uintptr_t) &gt_pch_save - (uintptr_t) pch_save;
876       void **ptrs = XNEWVEC (void *, num_callbacks);
877       unsigned i;
878       uintptr_t bias
879         = (uintptr_t) mmi.preferred_base - (uintptr_t) orig_preferred_base;
880
881       if (fread (ptrs, sizeof (void *), num_callbacks, f) != num_callbacks)
882         fatal_error (input_location, "cannot read PCH file: %m");
883       for (i = 0; i < num_callbacks; ++i)
884         {
885           void *ptr = (void *) ((uintptr_t) ptrs[i] + bias);
886           memcpy (&pch_save, ptr, sizeof (pch_save));
887           pch_save = (void (*) (FILE *)) ((uintptr_t) pch_save + binbias);
888           memcpy (ptr, &pch_save, sizeof (pch_save));
889         }
890       XDELETE (ptrs);
891     }
892   else if (fseek (f, num_callbacks * sizeof (void *), SEEK_CUR) != 0)
893     fatal_error (input_location, "cannot read PCH file: %m");
894
895   gt_pch_restore_stringpool ();
896
897   /* Barring corruption of the PCH file, the restored line table should be
898      complete and usable.  */
899   line_table = new_line_table;
900 }
901
902 /* Default version of HOST_HOOKS_GT_PCH_GET_ADDRESS when mmap is not present.
903    Select no address whatsoever, and let gt_pch_save choose what it will with
904    malloc, presumably.  */
905
906 void *
907 default_gt_pch_get_address (size_t size ATTRIBUTE_UNUSED,
908                             int fd ATTRIBUTE_UNUSED)
909 {
910   return NULL;
911 }
912
913 /* Default version of HOST_HOOKS_GT_PCH_USE_ADDRESS when mmap is not present.
914    Allocate SIZE bytes with malloc.  Return 0 if the address we got is the
915    same as base, indicating that the memory has been allocated but needs to
916    be read in from the file.  Return -1 if the address differs, to relocation
917    of the PCH file would be required.  */
918
919 int
920 default_gt_pch_use_address (void *&base, size_t size, int fd ATTRIBUTE_UNUSED,
921                             size_t offset ATTRIBUTE_UNUSED)
922 {
923   void *addr = xmalloc (size);
924   return (addr == base) - 1;
925 }
926
927 /* Default version of HOST_HOOKS_GT_PCH_GET_ADDRESS.   Return the
928    alignment required for allocating virtual memory. Usually this is the
929    same as pagesize.  */
930
931 size_t
932 default_gt_pch_alloc_granularity (void)
933 {
934   return getpagesize ();
935 }
936
937 #if HAVE_MMAP_FILE
938 /* Default version of HOST_HOOKS_GT_PCH_GET_ADDRESS when mmap is present.
939    We temporarily allocate SIZE bytes, and let the kernel place the data
940    wherever it will.  If it worked, that's our spot, if not we're likely
941    to be in trouble.  */
942
943 void *
944 mmap_gt_pch_get_address (size_t size, int fd)
945 {
946   void *ret;
947
948   ret = mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
949   if (ret == (void *) MAP_FAILED)
950     ret = NULL;
951   else
952     munmap ((caddr_t) ret, size);
953
954   return ret;
955 }
956
957 /* Default version of HOST_HOOKS_GT_PCH_USE_ADDRESS when mmap is present.
958    Map SIZE bytes of FD+OFFSET at BASE.  Return 1 if we succeeded at
959    mapping the data at BASE, -1 if we couldn't.
960
961    This version assumes that the kernel honors the START operand of mmap
962    even without MAP_FIXED if START through START+SIZE are not currently
963    mapped with something.  */
964
965 int
966 mmap_gt_pch_use_address (void *&base, size_t size, int fd, size_t offset)
967 {
968   void *addr;
969
970   /* We're called with size == 0 if we're not planning to load a PCH
971      file at all.  This allows the hook to free any static space that
972      we might have allocated at link time.  */
973   if (size == 0)
974     return -1;
975
976   addr = mmap ((caddr_t) base, size, PROT_READ | PROT_WRITE, MAP_PRIVATE,
977                fd, offset);
978
979   return addr == base ? 1 : -1;
980 }
981 #endif /* HAVE_MMAP_FILE */
982
983 #if !defined ENABLE_GC_CHECKING && !defined ENABLE_GC_ALWAYS_COLLECT
984
985 /* Modify the bound based on rlimits.  */
986 static double
987 ggc_rlimit_bound (double limit)
988 {
989 #if defined(HAVE_GETRLIMIT)
990   struct rlimit rlim;
991 # if defined (RLIMIT_AS)
992   /* RLIMIT_AS is what POSIX says is the limit on mmap.  Presumably
993      any OS which has RLIMIT_AS also has a working mmap that GCC will use.  */
994   if (getrlimit (RLIMIT_AS, &rlim) == 0
995       && rlim.rlim_cur != (rlim_t) RLIM_INFINITY
996       && rlim.rlim_cur < limit)
997     limit = rlim.rlim_cur;
998 # elif defined (RLIMIT_DATA)
999   /* ... but some older OSs bound mmap based on RLIMIT_DATA, or we
1000      might be on an OS that has a broken mmap.  (Others don't bound
1001      mmap at all, apparently.)  */
1002   if (getrlimit (RLIMIT_DATA, &rlim) == 0
1003       && rlim.rlim_cur != (rlim_t) RLIM_INFINITY
1004       && rlim.rlim_cur < limit
1005       /* Darwin has this horribly bogus default setting of
1006          RLIMIT_DATA, to 6144Kb.  No-one notices because RLIMIT_DATA
1007          appears to be ignored.  Ignore such silliness.  If a limit
1008          this small was actually effective for mmap, GCC wouldn't even
1009          start up.  */
1010       && rlim.rlim_cur >= 8 * ONE_M)
1011     limit = rlim.rlim_cur;
1012 # endif /* RLIMIT_AS or RLIMIT_DATA */
1013 #endif /* HAVE_GETRLIMIT */
1014
1015   return limit;
1016 }
1017
1018 /* Heuristic to set a default for GGC_MIN_EXPAND.  */
1019 static int
1020 ggc_min_expand_heuristic (void)
1021 {
1022   double min_expand = physmem_total ();
1023
1024   /* Adjust for rlimits.  */
1025   min_expand = ggc_rlimit_bound (min_expand);
1026
1027   /* The heuristic is a percentage equal to 30% + 70%*(RAM/1GB), yielding
1028      a lower bound of 30% and an upper bound of 100% (when RAM >= 1GB).  */
1029   min_expand /= ONE_G;
1030   min_expand *= 70;
1031   min_expand = MIN (min_expand, 70);
1032   min_expand += 30;
1033
1034   return min_expand;
1035 }
1036
1037 /* Heuristic to set a default for GGC_MIN_HEAPSIZE.  */
1038 static int
1039 ggc_min_heapsize_heuristic (void)
1040 {
1041   double phys_kbytes = physmem_total ();
1042   double limit_kbytes = ggc_rlimit_bound (phys_kbytes * 2);
1043
1044   phys_kbytes /= ONE_K; /* Convert to Kbytes.  */
1045   limit_kbytes /= ONE_K;
1046
1047   /* The heuristic is RAM/8, with a lower bound of 4M and an upper
1048      bound of 128M (when RAM >= 1GB).  */
1049   phys_kbytes /= 8;
1050
1051 #if defined(HAVE_GETRLIMIT) && defined (RLIMIT_RSS)
1052   /* Try not to overrun the RSS limit while doing garbage collection.
1053      The RSS limit is only advisory, so no margin is subtracted.  */
1054  {
1055    struct rlimit rlim;
1056    if (getrlimit (RLIMIT_RSS, &rlim) == 0
1057        && rlim.rlim_cur != (rlim_t) RLIM_INFINITY)
1058      phys_kbytes = MIN (phys_kbytes, rlim.rlim_cur / ONE_K);
1059  }
1060 # endif
1061
1062   /* Don't blindly run over our data limit; do GC at least when the
1063      *next* GC would be within 20Mb of the limit or within a quarter of
1064      the limit, whichever is larger.  If GCC does hit the data limit,
1065      compilation will fail, so this tries to be conservative.  */
1066   limit_kbytes = MAX (0, limit_kbytes - MAX (limit_kbytes / 4, 20 * ONE_K));
1067   limit_kbytes = (limit_kbytes * 100) / (110 + ggc_min_expand_heuristic ());
1068   phys_kbytes = MIN (phys_kbytes, limit_kbytes);
1069
1070   phys_kbytes = MAX (phys_kbytes, 4 * ONE_K);
1071   phys_kbytes = MIN (phys_kbytes, 128 * ONE_K);
1072
1073   return phys_kbytes;
1074 }
1075 #endif
1076
1077 void
1078 init_ggc_heuristics (void)
1079 {
1080 #if !defined ENABLE_GC_CHECKING && !defined ENABLE_GC_ALWAYS_COLLECT
1081   param_ggc_min_expand = ggc_min_expand_heuristic ();
1082   param_ggc_min_heapsize = ggc_min_heapsize_heuristic ();
1083 #endif
1084 }
1085
1086 /* GGC memory usage.  */
1087 class ggc_usage: public mem_usage
1088 {
1089 public:
1090   /* Default constructor.  */
1091   ggc_usage (): m_freed (0), m_collected (0), m_overhead (0) {}
1092   /* Constructor.  */
1093   ggc_usage (size_t allocated, size_t times, size_t peak,
1094              size_t freed, size_t collected, size_t overhead)
1095     : mem_usage (allocated, times, peak),
1096     m_freed (freed), m_collected (collected), m_overhead (overhead) {}
1097
1098   /* Equality operator.  */
1099   inline bool
1100   operator== (const ggc_usage &second) const
1101   {
1102     return (get_balance () == second.get_balance ()
1103             && m_peak == second.m_peak
1104             && m_times == second.m_times);
1105   }
1106
1107   /* Comparison operator.  */
1108   inline bool
1109   operator< (const ggc_usage &second) const
1110   {
1111     if (*this == second)
1112       return false;
1113
1114     return (get_balance () == second.get_balance () ?
1115             (m_peak == second.m_peak ? m_times < second.m_times
1116              : m_peak < second.m_peak)
1117               : get_balance () < second.get_balance ());
1118   }
1119
1120   /* Register overhead of ALLOCATED and OVERHEAD bytes.  */
1121   inline void
1122   register_overhead (size_t allocated, size_t overhead)
1123   {
1124     m_allocated += allocated;
1125     m_overhead += overhead;
1126     m_times++;
1127   }
1128
1129   /* Release overhead of SIZE bytes.  */
1130   inline void
1131   release_overhead (size_t size)
1132   {
1133     m_freed += size;
1134   }
1135
1136   /* Sum the usage with SECOND usage.  */
1137   ggc_usage
1138   operator+ (const ggc_usage &second)
1139   {
1140     return ggc_usage (m_allocated + second.m_allocated,
1141                       m_times + second.m_times,
1142                       m_peak + second.m_peak,
1143                       m_freed + second.m_freed,
1144                       m_collected + second.m_collected,
1145                       m_overhead + second.m_overhead);
1146   }
1147
1148   /* Dump usage with PREFIX, where TOTAL is sum of all rows.  */
1149   inline void
1150   dump (const char *prefix, ggc_usage &total) const
1151   {
1152     size_t balance = get_balance ();
1153     fprintf (stderr,
1154              "%-48s " PRsa (9) ":%5.1f%%" PRsa (9) ":%5.1f%%"
1155              PRsa (9) ":%5.1f%%" PRsa (9) ":%5.1f%%" PRsa (9) "\n",
1156              prefix,
1157              SIZE_AMOUNT (balance), get_percent (balance, total.get_balance ()),
1158              SIZE_AMOUNT (m_collected),
1159              get_percent (m_collected, total.m_collected),
1160              SIZE_AMOUNT (m_freed), get_percent (m_freed, total.m_freed),
1161              SIZE_AMOUNT (m_overhead),
1162              get_percent (m_overhead, total.m_overhead),
1163              SIZE_AMOUNT (m_times));
1164   }
1165
1166   /* Dump usage coupled to LOC location, where TOTAL is sum of all rows.  */
1167   inline void
1168   dump (mem_location *loc, ggc_usage &total) const
1169   {
1170     char *location_string = loc->to_string ();
1171
1172     dump (location_string, total);
1173
1174     free (location_string);
1175   }
1176
1177   /* Dump footer.  */
1178   inline void
1179   dump_footer ()
1180   {
1181     dump ("Total", *this);
1182   }
1183
1184   /* Get balance which is GGC allocation leak.  */
1185   inline size_t
1186   get_balance () const
1187   {
1188     return m_allocated + m_overhead - m_collected - m_freed;
1189   }
1190
1191   typedef std::pair<mem_location *, ggc_usage *> mem_pair_t;
1192
1193   /* Compare wrapper used by qsort method.  */
1194   static int
1195   compare (const void *first, const void *second)
1196   {
1197     const mem_pair_t mem1 = *(const mem_pair_t *) first;
1198     const mem_pair_t mem2 = *(const mem_pair_t *) second;
1199
1200     size_t balance1 = mem1.second->get_balance ();
1201     size_t balance2 = mem2.second->get_balance ();
1202
1203     return balance1 == balance2 ? 0 : (balance1 < balance2 ? 1 : -1);
1204   }
1205
1206   /* Dump header with NAME.  */
1207   static inline void
1208   dump_header (const char *name)
1209   {
1210     fprintf (stderr, "%-48s %11s%17s%17s%16s%17s\n", name, "Leak", "Garbage",
1211              "Freed", "Overhead", "Times");
1212   }
1213
1214   /* Freed memory in bytes.  */
1215   size_t m_freed;
1216   /* Collected memory in bytes.  */
1217   size_t m_collected;
1218   /* Overhead memory in bytes.  */
1219   size_t m_overhead;
1220 };
1221
1222 /* GCC memory description.  */
1223 static mem_alloc_description<ggc_usage> ggc_mem_desc;
1224
1225 /* Dump per-site memory statistics.  */
1226
1227 void
1228 dump_ggc_loc_statistics ()
1229 {
1230   if (! GATHER_STATISTICS)
1231     return;
1232
1233   ggc_collect (GGC_COLLECT_FORCE);
1234
1235   ggc_mem_desc.dump (GGC_ORIGIN);
1236 }
1237
1238 /* Record ALLOCATED and OVERHEAD bytes to descriptor NAME:LINE (FUNCTION).  */
1239 void
1240 ggc_record_overhead (size_t allocated, size_t overhead, void *ptr MEM_STAT_DECL)
1241 {
1242   ggc_usage *usage = ggc_mem_desc.register_descriptor (ptr, GGC_ORIGIN, false
1243                                                        FINAL_PASS_MEM_STAT);
1244
1245   ggc_mem_desc.register_object_overhead (usage, allocated + overhead, ptr);
1246   usage->register_overhead (allocated, overhead);
1247 }
1248
1249 /* Notice that the pointer has been freed.  */
1250 void
1251 ggc_free_overhead (void *ptr)
1252 {
1253   ggc_mem_desc.release_object_overhead (ptr);
1254 }
1255
1256 /* After live values has been marked, walk all recorded pointers and see if
1257    they are still live.  */
1258 void
1259 ggc_prune_overhead_list (void)
1260 {
1261   typedef hash_map<const void *, std::pair<ggc_usage *, size_t > > map_t;
1262
1263   map_t::iterator it = ggc_mem_desc.m_reverse_object_map->begin ();
1264
1265   for (; it != ggc_mem_desc.m_reverse_object_map->end (); ++it)
1266     if (!ggc_marked_p ((*it).first))
1267       {
1268         (*it).second.first->m_collected += (*it).second.second;
1269         ggc_mem_desc.m_reverse_object_map->remove ((*it).first);
1270       }
1271 }
1272
1273 /* Print memory used by heap if this info is available.  */
1274
1275 void
1276 report_heap_memory_use ()
1277 {
1278 #if defined(HAVE_MALLINFO) || defined(HAVE_MALLINFO2)
1279 #ifdef HAVE_MALLINFO2
1280   #define MALLINFO_FN mallinfo2
1281 #else
1282   #define MALLINFO_FN mallinfo
1283 #endif
1284   if (!quiet_flag)
1285     fprintf (stderr, " {heap " PRsa (0) "}",
1286              SIZE_AMOUNT (MALLINFO_FN ().arena));
1287 #endif
1288 }