]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
pch: Add support for relocation of the PCH data [PR71934]
authorJakub Jelinek <jakub@redhat.com>
Thu, 9 Dec 2021 14:40:15 +0000 (15:40 +0100)
committerJakub Jelinek <jakub@redhat.com>
Thu, 9 Dec 2021 14:40:15 +0000 (15:40 +0100)
The following patch adds support for relocation of the PCH blob on PCH
restore if we don't manage to get the preferred map slot for it.
The GTY stuff knows where all the pointers are, after all it relocates
it once during PCH save from the addresses where it was initially allocated
to addresses in the preferred map slot.
But, if we were to do it solely using GTY info upon PCH restore, we'd need
another set of GTY functions, which I think would make it less maintainable
and I think it would also be more costly at PCH restore time.  Those
functions would need to call something to add bias to pointers that haven't
been marked yet and make sure not to add bias to any pointer twice.

So, this patch instead builds a relocation table (sorted list of addresses
in the blob which needs relocation) at PCH save time, stores it in a very
compact form into the gch file and upon restore, adjusts pointers in GTY
roots (that is right away in the root structures) and the addresses in the
relocation table.
The cost on stdc++.gch/O2g.gch (previously 85MB large) is about 3% file size
growth, there are 2.5 million pointers that need relocation in the gch blob
and the relocation table uses uleb128 for address deltas and needs ~1.01 bytes
for one address that needs relocation, and about 20% compile time during
PCH save (I think it is mainly because of the need to qsort those 2.5
million pointers).  On PCH restore, if it doesn't need relocation (the usual
case), it is just an extra fread of sizeof (size_t) data and fseek
(in my tests real time on vanilla tree for #include <bits/stdc++.h> CU
was ~0.175s and with the patch but no relocation ~0.173s), while if it needs
relocation it took ~0.193s, i.e. 11.5% slower.
Without PCH that
 #include <bits/stdc++.h>
 int i;
testcase compiles with -O2 -g in ~1.199s, i.e. 6.2 times slower than PCH with
relocation and 6.9 times than PCH without relocation.

The discovery of the pointers in the blob that need relocation is done
in the relocate_ptrs hook which does the pointer relocation during PCH save.
Unfortunately, I had to make one change to the gengtype stuff due to the
nested_ptr feature of GTY, which some libcpp headers and stringpool.c use.
The relocate_ptrs hook had 2 arguments, pointer to the pointer and a cookie.
When relocate_ptrs is done, in most cases it is called solely on the
subfields of the current object, so e.g.
          if ((void *)(x) == this_obj)
            op (&((*x).u.fld[0].rt_rtx), cookie);
so relocate_ptrs can assert that ptr_p is within the
state->ptrs[state->ptrs_i]->obj ..
state->ptrs[state->ptrs_i]->obj+state->ptrs[state->ptrs_i]->size-sizeof(void*)
range and compute from that the address in the blob which will need
relocation (state->ptrs[state->ptrs_i]->new_addr is the new address
given to it and ptr_p-state->ptrs[state->ptrs_i]->obj is the relative
offset.  Unfortunately, for nested_ptr gengtype emits something like:
      {
        union tree_node * x0 =
          ((*x).val.node.node) ? HT_IDENT_TO_GCC_IDENT (HT_NODE (((*x).val.node.node))) : NULL;
        if ((void *)(x) == this_obj)
          op (&(x0), cookie);
        (*x).val.node.node = (x0) ? CPP_HASHNODE (GCC_IDENT_TO_HT_IDENT ((x0))) : NULL;
      }
so relocate_ptrs is called with an address of some temporary variable and
so doesn't know where the pointer will finally be.
So, I've added another argument to relocate_ptrs (and to
gt_pointer_operator).  For the most common case I pass NULL as the new middle
argument to that function, first one remains pointer to the pointer that
needs adjustment and last the cookie.  The NULL seems to be cheap to compute
and short in the gt*.[ch] files and stands for ptr_p is an address within
the this_obj's range, remember its address.  For the nested_ptr case, the
new middle argument contains actual address of the pointer that might need
to be relocated, so instead of the above
          op (&(x0), &((*x).val.node.node), cookie);
in there.  And finally, e.g. for the reorder case I need a way to tell
restore_ptrs to ignore a particular address for the relocation purposes
and only treat it the old way.  I've used for that the case when
the first and second arguments are equal.

In order to enable support for mapping PCH as fallback at different
addresses than the preferred ones, a small change is needed to the
host pch_use_address hooks.  One change I've done to all of them is
the change of the type of the first argument from void * to void *&,
such that the actual address can be told to the callers (or shall I
instead use void **?), but another change that still needs to be done
in them if they want the relocation is actually not fail if they couldn't
get a preferred address, but instead modify what the first argument
refers to.  I've done that only for host-linux.c and Iain is testing
similar change for host-darwin.c.  Didn't change hpux, netbsd, openbsd,
solaris, mingw32 or the fallbacks because I can't test those.

Tested also with the:
--- gcc/config/host-linux.c.jj  2021-12-06 22:22:42.007777367 +0100
+++ gcc/config/host-linux.c     2021-12-07 00:21:53.052674040 +0100
@@ -191,6 +191,8 @@ linux_gt_pch_use_address (void *&base, s
   if (size == 0)
     return -1;

+base = (char *) base + ((size + 8191) & (size_t) -4096);
+
   /* Try to map the file with MAP_PRIVATE.  */
   addr = mmap (base, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, offset);

hack which forces all PCH restores to be relocated.  An earlier version of the
patch has been also regrest with base = (char *) base + 16384; in that spot,
so both relocation to a non-overlapping spot and to an overlapping spot have
been tested.

2021-12-09  Jakub Jelinek  <jakub@redhat.com>

PR pch/71934
* coretypes.h (gt_pointer_operator): Use 3 pointer arguments instead
of two.
* gengtype.c (struct walk_type_data): Add in_nested_ptr argument.
(walk_type): Temporarily set d->in_nested_ptr around nested_ptr
handling.
(write_types_local_user_process_field): Pass a new middle pointer
to gt_pointer_operator op calls, if d->in_nested_ptr pass there
address of d->prev_val[2], otherwise NULL.
(write_types_local_process_field): Likewise.
* ggc-common.c (relocate_ptrs): Add real_ptr_p argument.  If equal
to ptr_p, do nothing, otherwise if NULL remember ptr_p's
or if non-NULL real_ptr_p's corresponding new address in
reloc_addrs_vec.
(reloc_addrs_vec): New variable.
(compare_ptr, read_uleb128, write_uleb128): New functions.
(gt_pch_save): When iterating over objects through relocate_ptrs,
save current i into state.ptrs_i.  Sort reloc_addrs_vec and emit
it as uleb128 of differences between pointer addresses into the
PCH file.
(gt_pch_restore): Allow restoring of PCH to a different address
than the preferred one, in that case adjust global pointers by bias
and also adjust by bias addresses read from the relocation table
as uleb128 differences.  Otherwise fseek over it.  Perform
gt_pch_restore_stringpool only after adjusting callbacks and for
callback adjustments also take into account the bias.
(default_gt_pch_use_address): Change type of first argument from
void * to void *&.
(mmap_gt_pch_use_address): Likewise.
* ggc-tests.c (gt_pch_nx): Pass NULL as new middle argument to op.
* hash-map.h (hash_map::pch_nx_helper): Likewise.
(gt_pch_nx): Likewise.
* hash-set.h (gt_pch_nx): Likewise.
* hash-table.h (gt_pch_nx): Likewise.
* hash-traits.h (ggc_remove::pch_nx): Likewise.
* hosthooks-def.h (default_gt_pch_use_address): Change type of first
argument from void * to void *&.
(mmap_gt_pch_use_address): Likewise.
* hosthooks.h (struct host_hooks): Change type of first argument of
gt_pch_use_address hook from void * to void *&.
* machmode.h (gt_pch_nx): Expect a callback with 3 pointers instead of
two in the middle argument.
* poly-int.h (gt_pch_nx): Likewise.
* stringpool.c (gt_pch_nx): Pass NULL as new middle argument to op.
* tree-cfg.c (gt_pch_nx): Likewise, except for LOCATION_BLOCK pass
the same &(block) twice.
* value-range.h (gt_pch_nx): Pass NULL as new middle argument to op.
* vec.h (gt_pch_nx): Likewise.
* wide-int.h (gt_pch_nx): Likewise.
* config/host-darwin.c (darwin_gt_pch_use_address): Change type of
first argument from void * to void *&.
* config/host-darwin.h (darwin_gt_pch_use_address): Likewise.
* config/host-hpux.c (hpux_gt_pch_use_address): Likewise.
* config/host-linux.c (linux_gt_pch_use_address): Likewise.  If
it couldn't succeed to mmap at the preferred location, set base
to the actual one.  Update addr in the manual reading loop instead of
base.
* config/host-netbsd.c (netbsd_gt_pch_use_address): Change type of
first argument from void * to void *&.
* config/host-openbsd.c (openbsd_gt_pch_use_address): Likewise.
* config/host-solaris.c (sol_gt_pch_use_address): Likewise.
* config/i386/host-mingw32.c (mingw32_gt_pch_use_address): Likewise.
* config/rs6000/rs6000-gen-builtins.c (write_init_file): Pass NULL
as new middle argument to op in the generated code.
* doc/gty.texi: Adjust samples for the addition of middle pointer
to gt_pointer_operator callback.
gcc/ada/
* gcc-interface/decl.c (gt_pch_nx): Pass NULL as new middle argument
to op.
gcc/c-family/
* c-pch.c (c_common_no_more_pch): Pass a temporary void * var
with NULL value instead of NULL to host_hooks.gt_pch_use_address.
gcc/c/
* c-decl.c (resort_field_decl_cmp): Pass the same pointer twice
to resort_data.new_value.
gcc/cp/
* module.cc (nop): Add another void * argument.
* name-lookup.c (resort_member_name_cmp): Pass the same pointer twice
to resort_data.new_value.

32 files changed:
gcc/ada/gcc-interface/decl.c
gcc/c-family/c-pch.c
gcc/c/c-decl.c
gcc/config/host-darwin.c
gcc/config/host-darwin.h
gcc/config/host-hpux.c
gcc/config/host-linux.c
gcc/config/host-netbsd.c
gcc/config/host-openbsd.c
gcc/config/host-solaris.c
gcc/config/i386/host-mingw32.c
gcc/config/rs6000/rs6000-gen-builtins.c
gcc/coretypes.h
gcc/cp/module.cc
gcc/cp/name-lookup.c
gcc/doc/gty.texi
gcc/gengtype.c
gcc/ggc-common.c
gcc/ggc-tests.c
gcc/hash-map.h
gcc/hash-set.h
gcc/hash-table.h
gcc/hash-traits.h
gcc/hosthooks-def.h
gcc/hosthooks.h
gcc/machmode.h
gcc/poly-int.h
gcc/stringpool.c
gcc/tree-cfg.c
gcc/value-range.h
gcc/vec.h
gcc/wide-int.h

index 93b6eb5bb4520697c9a8be8501dad084dfe5ff2e..af6b6bd14d422eacc4e50280d809601ff6d2174e 100644 (file)
@@ -171,7 +171,7 @@ gt_pch_nx (Entity_Id &)
 void
 gt_pch_nx (Entity_Id *x, gt_pointer_operator op, void *cookie)
 {
-  op (x, cookie);
+  op (x, NULL, cookie);
 }
 
 struct dummy_type_hasher : ggc_cache_ptr_hash<tree_entity_vec_map>
index 2cafa1387bbea96e2f2d770d7255fe7232427b8b..73a846d95412c9c29a82f55de5aefe48992841d0 100644 (file)
@@ -375,7 +375,8 @@ c_common_no_more_pch (void)
   if (cpp_get_callbacks (parse_in)->valid_pch)
     {
       cpp_get_callbacks (parse_in)->valid_pch = NULL;
-      host_hooks.gt_pch_use_address (NULL, 0, -1, 0);
+      void *addr = NULL;
+      host_hooks.gt_pch_use_address (addr, 0, -1, 0);
     }
 }
 
index 3e28a038095ecc495ed67d400f53d1b059ceec92..9e45798ca4fd8cb89b317f24885fa1c8d6abe8d9 100644 (file)
@@ -9040,8 +9040,8 @@ resort_field_decl_cmp (const void *x_p, const void *y_p)
   {
     tree d1 = DECL_NAME (*x);
     tree d2 = DECL_NAME (*y);
-    resort_data.new_value (&d1, resort_data.cookie);
-    resort_data.new_value (&d2, resort_data.cookie);
+    resort_data.new_value (&d1, &d1, resort_data.cookie);
+    resort_data.new_value (&d2, &d2, resort_data.cookie);
     if (d1 < d2)
       return -1;
   }
index 559b919f2ee0dca81da0c1a05aea91a0120be161..d3c2858c9975e29c9792443fdd3152e2589d29c3 100644 (file)
@@ -136,7 +136,7 @@ darwin_gt_pch_get_address (size_t sz, int fd)
    fail with -1.  */
 
 int
-darwin_gt_pch_use_address (void *addr, size_t sz, int fd, size_t off)
+darwin_gt_pch_use_address (void *&addr, size_t sz, int fd, size_t off)
 {
   void *mapped_addr;
 
index 4acae9cf341db9c271128f8cd020c3b65f761d6d..f3a477e883732ab6aa42299adad2bd4362c9c918 100644 (file)
@@ -18,7 +18,7 @@
    <http://www.gnu.org/licenses/>.  */
 
 extern void * darwin_gt_pch_get_address (size_t sz, int fd);
-extern int darwin_gt_pch_use_address (void *addr, size_t sz, int fd, 
+extern int darwin_gt_pch_use_address (void *&addr, size_t sz, int fd, 
                                      size_t off);
 
 #undef HOST_HOOKS_GT_PCH_GET_ADDRESS
index 6009810d7321d461181b1e986d5bc72e5f0709eb..796f501ebfb661872adc07511d7d513f47a2bfe7 100644 (file)
@@ -24,7 +24,7 @@
 #include "hosthooks-def.h"
 
 static void *hpux_gt_pch_get_address (size_t, int);
-static int hpux_gt_pch_use_address (void *, size_t, int, size_t);
+static int hpux_gt_pch_use_address (void *&, size_t, int, size_t);
 
 #undef HOST_HOOKS_GT_PCH_GET_ADDRESS
 #define HOST_HOOKS_GT_PCH_GET_ADDRESS hpux_gt_pch_get_address
@@ -78,7 +78,7 @@ hpux_gt_pch_get_address (size_t size, int fd)
    little else we can do given the current PCH implementation.  */
 
 static int
-hpux_gt_pch_use_address (void *base, size_t size, int fd, size_t offset)
+hpux_gt_pch_use_address (void *&base, size_t size, int fd, size_t offset)
 {
   void *addr;
 
index 34945f1401944c7f36b331101ba3cc602c9dff40..0481ecff3f20a02269c79f12ba9b9a6b6df28861 100644 (file)
@@ -181,7 +181,7 @@ linux_gt_pch_get_address (size_t size, int fd)
    little else we can do given the current PCH implementation.  */
 
 static int
-linux_gt_pch_use_address (void *base, size_t size, int fd, size_t offset)
+linux_gt_pch_use_address (void *&base, size_t size, int fd, size_t offset)
 {
   void *addr;
 
@@ -204,24 +204,22 @@ linux_gt_pch_use_address (void *base, size_t size, int fd, size_t offset)
   addr = mmap (base, size, PROT_READ | PROT_WRITE,
               MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
 
-  if (addr != base)
-    {
-      if (addr != (void *) MAP_FAILED)
-        munmap (addr, size);
-      return -1;
-    }
+  if (addr == (void *) MAP_FAILED)
+    return -1;
 
   if (lseek (fd, offset, SEEK_SET) == (off_t)-1)
     return -1;
 
+  base = addr;
+
   while (size)
     {
       ssize_t nbytes;
 
-      nbytes = read (fd, base, MIN (size, (size_t)-1 >> 1));
+      nbytes = read (fd, addr, MIN (size, (size_t)-1 >> 1));
       if (nbytes <= 0)
         return -1;
-      base = (char *) base + nbytes;
+      addr = (char *) addr + nbytes;
       size -= nbytes;
     }
 
index 818ecb2d0cbe45c13942f4e3bffb84c2a053f9f5..32f86c83bb2b578cf5682e94c0065b45ad514858 100644 (file)
@@ -66,7 +66,7 @@ netbsd_gt_pch_get_address (size_t size, int fd)
    mapping the data at BASE, -1 if we couldn't.  */
 
 static int
-netbsd_gt_pch_use_address (void *base, size_t size, int fd, size_t offset)
+netbsd_gt_pch_use_address (void *&base, size_t size, int fd, size_t offset)
 {
   void *addr;
 
index c5f8944a62c15d1ac89e93ba99342e8e13c6a75b..f7b90432b55407f86ec380ecf1167026e39d875e 100644 (file)
@@ -66,7 +66,7 @@ openbsd_gt_pch_get_address (size_t size, int fd)
    mapping the data at BASE, -1 if we couldn't.  */
 
 static int
-openbsd_gt_pch_use_address (void *base, size_t size, int fd, size_t offset)
+openbsd_gt_pch_use_address (void *&base, size_t size, int fd, size_t offset)
 {
   void *addr;
 
index 556e9cf649cf48b53a09e1f8205c0c994108dc7f..b08cf513ec5be75c32c6479c1d3a15c5595ba3fa 100644 (file)
@@ -105,7 +105,7 @@ sol_gt_pch_get_address (size_t size, int fd)
    mapping the data at BASE, -1 if we couldn't.  */
 
 static int
-sol_gt_pch_use_address (void *base, size_t size, int fd, size_t offset)
+sol_gt_pch_use_address (void *&base, size_t size, int fd, size_t offset)
 {
   void *addr;
 
index 360a280b23ae90dd4c12161b275d06b99361c57a..a182563bf27e865dce3977dcab777d376da7d3f7 100644 (file)
@@ -32,7 +32,7 @@
 #include <stdlib.h>
 
 static void * mingw32_gt_pch_get_address (size_t, int);
-static int mingw32_gt_pch_use_address (void *, size_t, int, size_t);
+static int mingw32_gt_pch_use_address (void *&, size_t, int, size_t);
 static size_t mingw32_gt_pch_alloc_granularity (void);
 
 #undef HOST_HOOKS_GT_PCH_GET_ADDRESS
@@ -118,7 +118,7 @@ mingw32_gt_pch_get_address (size_t size, int)
    if the memory is allocated but the data not loaded, return 1 if done.  */
 
 static int
-mingw32_gt_pch_use_address (void *addr, size_t size, int fd,
+mingw32_gt_pch_use_address (void *&addr, size_t size, int fd,
                            size_t offset)
 {
   void * mmap_addr;
index d2e9c4ce5476660d2a7bc4455eb53238dda1327a..34ab70f7609daf8c55348527ef487dbf35ddb429 100644 (file)
@@ -2886,7 +2886,7 @@ write_init_file (void)
           "void gt_pch_nx (bifdata *bd, gt_pointer_operator op, "
           "void *cookie)\n");
   fprintf (init_file,
-          "{\n  op(&(bd->fntype), cookie);\n}\n\n");
+          "{\n  op(&(bd->fntype), NULL, cookie);\n}\n\n");
   fprintf (init_file,
           "void gt_ggc_mx (ovlddata *od)\n");
   fprintf (init_file,
@@ -2899,7 +2899,7 @@ write_init_file (void)
           "void gt_pch_nx (ovlddata *od, gt_pointer_operator op, "
           "void *cookie)\n");
   fprintf (init_file,
-          "{\n  op(&(od->fntype), cookie);\n}\n");
+          "{\n  op(&(od->fntype), NULL, cookie);\n}\n");
 
   return 1;
 }
index b4f530d57ac0a90522478ea0c693d55ca852eabe..5d0fc1d915db0ffa0907ed2e3d9932a36d7cda55 100644 (file)
@@ -442,8 +442,10 @@ enum optimize_size_level
 };
 
 /* Support for user-provided GGC and PCH markers.  The first parameter
-   is a pointer to a pointer, the second a cookie.  */
-typedef void (*gt_pointer_operator) (void *, void *);
+   is a pointer to a pointer, the second either NULL if the pointer to
+   pointer points into a GC object or the actual pointer address if
+   the first argument points to a temporary and the third a cookie.  */
+typedef void (*gt_pointer_operator) (void *, void *, void *);
 
 #if !defined (HAVE_UCHAR)
 typedef unsigned char uchar;
index 71d0fab411f6fb3fc976f8e31b42fe6d0d055761..9266055cd92472734cfaaddfb86eed4b73723fe1 100644 (file)
@@ -11750,7 +11750,7 @@ trees_out::mark_class_def (tree defn)
 /* Nop sorting, needed for resorting the member vec.  */
 
 static void
-nop (void *, void *)
+nop (void *, void *, void *)
 {
 }
 
index 080692899a81d73ae879b366a90e1b1acef5db8e..229ba45d901dac37c9392bec88f7293d84b7a75d 100644 (file)
@@ -2123,8 +2123,8 @@ resort_member_name_cmp (const void *a_p, const void *b_p)
   tree name_a = OVL_NAME (a);
   tree name_b = OVL_NAME (b);
 
-  resort_data.new_value (&name_a, resort_data.cookie);
-  resort_data.new_value (&name_b, resort_data.cookie);
+  resort_data.new_value (&name_a, &name_a, resort_data.cookie);
+  resort_data.new_value (&name_b, &name_b, resort_data.cookie);
 
   gcc_checking_assert (name_a != name_b);
 
index ca2c8404894a40542ace58cd5df5ebc2a81bb18b..5a84d9a48db67ab275f3a6c79398e8acb566ce28 100644 (file)
@@ -483,7 +483,7 @@ void gt_pch_nx (my_struct *p)
 void gt_pch_nx (my_struct *p, gt_pointer_operator op, void *cookie)
 @{
   /* For every field 'fld', call the given pointer operator.  */
-  op (&(tp->fld), cookie);
+  op (&(tp->fld), NULL, cookie);
 @}
 @end smallexample
 
@@ -536,7 +536,7 @@ void gt_pch_nx (TP<T *> *tp, gt_pointer_operator op, void *cookie)
 @{
   /* For every field 'fld' of 'tp' with type 'T *', call the given
      pointer operator.  */
-  op (&(tp->fld), cookie);
+  op (&(tp->fld), NULL, cookie);
 @}
 
 template<typename T>
index b9daaa43689c29422c5592b7ddd21a43e740db7e..db218a7bce764093bc6bdeab487a10f7ca2ed3d7 100644 (file)
@@ -2491,6 +2491,7 @@ struct walk_type_data
   int loopcounter;
   bool in_ptr_field;
   bool have_this_obj;
+  bool in_nested_ptr;
 };
 
 
@@ -2807,6 +2808,7 @@ walk_type (type_p t, struct walk_type_data *d)
            if (nested_ptr_d)
              {
                const char *oldprevval2 = d->prev_val[2];
+               bool old_in_nested_ptr = d->in_nested_ptr;
 
                if (!union_or_struct_p (nested_ptr_d->type))
                  {
@@ -2817,6 +2819,7 @@ walk_type (type_p t, struct walk_type_data *d)
                  }
 
                d->prev_val[2] = d->val;
+               d->in_nested_ptr = true;
                oprintf (d->of, "%*s{\n", d->indent, "");
                d->indent += 2;
                d->val = xasprintf ("x%d", d->counter++);
@@ -2846,6 +2849,7 @@ walk_type (type_p t, struct walk_type_data *d)
                oprintf (d->of, "%*s}\n", d->indent, "");
                d->val = d->prev_val[2];
                d->prev_val[2] = oldprevval2;
+               d->in_nested_ptr = old_in_nested_ptr;
              }
            else
              d->process_field (t->u.p, d);
@@ -3828,12 +3832,17 @@ write_types_local_user_process_field (type_p f, const struct walk_type_data *d)
     case TYPE_UNION:
     case TYPE_LANG_STRUCT:
     case TYPE_STRING:
-      oprintf (d->of, "%*s  op (&(%s), cookie);\n", d->indent, "", d->val);
+      if (d->in_nested_ptr)
+       oprintf (d->of, "%*s  op (&(%s), &(%s), cookie);\n",
+                d->indent, "", d->val, d->prev_val[2]);
+      oprintf (d->of, "%*s  op (&(%s), NULL, cookie);\n",
+              d->indent, "", d->val);
       break;
 
     case TYPE_USER_STRUCT:
       if (d->in_ptr_field)
-       oprintf (d->of, "%*s  op (&(%s), cookie);\n", d->indent, "", d->val);
+       oprintf (d->of, "%*s  op (&(%s), NULL, cookie);\n",
+                d->indent, "", d->val);
       else
        oprintf (d->of, "%*s  gt_pch_nx (&(%s), op, cookie);\n",
                 d->indent, "", d->val);
@@ -3911,14 +3920,20 @@ write_types_local_process_field (type_p f, const struct walk_type_data *d)
     case TYPE_STRING:
       oprintf (d->of, "%*sif ((void *)(%s) == this_obj)\n", d->indent, "",
               d->prev_val[3]);
-      oprintf (d->of, "%*s  op (&(%s), cookie);\n", d->indent, "", d->val);
+      if (d->in_nested_ptr)
+       oprintf (d->of, "%*s  op (&(%s), &(%s), cookie);\n",
+                d->indent, "", d->val, d->prev_val[2]);
+      else
+       oprintf (d->of, "%*s  op (&(%s), NULL, cookie);\n",
+                d->indent, "", d->val);
       break;
 
     case TYPE_USER_STRUCT:
       oprintf (d->of, "%*sif ((void *)(%s) == this_obj)\n", d->indent, "",
               d->prev_val[3]);
       if (d->in_ptr_field)
-       oprintf (d->of, "%*s  op (&(%s), cookie);\n", d->indent, "", d->val);
+       oprintf (d->of, "%*s  op (&(%s), NULL, cookie);\n",
+                d->indent, "", d->val);
       else
        oprintf (d->of, "%*s  gt_pch_nx (&(%s), op, cookie);\n",
                 d->indent, "", d->val);
index 7c998e9547311023e128cf7107e07025d6181cc9..4fe9ff104a0a8f93d3dec279c3d92ec0aa48b4bf 100644 (file)
@@ -40,7 +40,7 @@ static ggc_statistics *ggc_stats;
 struct traversal_state;
 
 static int compare_ptr_data (const void *, const void *);
-static void relocate_ptrs (void *, void *);
+static void relocate_ptrs (void *, void *, void *);
 static void write_pch_globals (const struct ggc_root_tab * const *tab,
                               struct traversal_state *state);
 
@@ -247,6 +247,7 @@ saving_hasher::equal (const ptr_data *p1, const void *p2)
 
 static hash_table<saving_hasher> *saving_htab;
 static vec<void *> callback_vec;
+static vec<void *> reloc_addrs_vec;
 
 /* Register an object in the hash table.  */
 
@@ -363,10 +364,10 @@ compare_ptr_data (const void *p1_p, const void *p2_p)
 /* Callbacks for note_ptr_fn.  */
 
 static void
-relocate_ptrs (void *ptr_p, void *state_p)
+relocate_ptrs (void *ptr_p, void *real_ptr_p, void *state_p)
 {
   void **ptr = (void **)ptr_p;
-  struct traversal_state *state ATTRIBUTE_UNUSED
+  struct traversal_state *state
     = (struct traversal_state *)state_p;
   struct ptr_data *result;
 
@@ -377,6 +378,19 @@ relocate_ptrs (void *ptr_p, void *state_p)
     saving_htab->find_with_hash (*ptr, POINTER_HASH (*ptr));
   gcc_assert (result);
   *ptr = result->new_addr;
+  if (ptr_p == real_ptr_p)
+    return;
+  if (real_ptr_p == NULL)
+    real_ptr_p = ptr_p;
+  gcc_assert (real_ptr_p >= state->ptrs[state->ptrs_i]->obj
+             && ((char *) real_ptr_p + sizeof (void *)
+                 <= ((char *) state->ptrs[state->ptrs_i]->obj
+                     + state->ptrs[state->ptrs_i]->size)));
+  void *addr
+    = (void *) ((char *) state->ptrs[state->ptrs_i]->new_addr
+               + ((char *) real_ptr_p
+                  - (char *) state->ptrs[state->ptrs_i]->obj));
+  reloc_addrs_vec.safe_push (addr);
 }
 
 /* Write out, after relocation, the pointers in TAB.  */
@@ -411,6 +425,61 @@ write_pch_globals (const struct ggc_root_tab * const *tab,
        }
 }
 
+/* Callback for qsort.  */
+
+static int
+compare_ptr (const void *p1_p, const void *p2_p)
+{
+  void *p1 = *(void *const *)p1_p;
+  void *p2 = *(void *const *)p2_p;
+  return (((uintptr_t)p1 > (uintptr_t)p2)
+         - ((uintptr_t)p1 < (uintptr_t)p2));
+}
+
+/* Decode one uleb128 from P, return first byte after it, store
+   decoded value into *VAL.  */
+
+static unsigned char *
+read_uleb128 (unsigned char *p, size_t *val)
+{
+  unsigned int shift = 0;
+  unsigned char byte;
+  size_t result;
+
+  result = 0;
+  do
+    {
+      byte = *p++;
+      result |= ((size_t) byte & 0x7f) << shift;
+      shift += 7;
+    }
+  while (byte & 0x80);
+
+  *val = result;
+  return p;
+}
+
+/* Store VAL as uleb128 at P, return length in bytes.  */
+
+static size_t
+write_uleb128 (unsigned char *p, size_t val)
+{
+  size_t len = 0;
+  do
+    {
+      unsigned char byte = (val & 0x7f);
+      val >>= 7;
+      if (val != 0)
+       /* More bytes to follow.  */
+       byte |= 0x80;
+
+      *p++ = byte;
+      ++len;
+    }
+  while (val != 0);
+  return len;
+}
+
 /* Hold the information we need to mmap the file back in.  */
 
 struct mmap_info
@@ -511,6 +580,7 @@ gt_pch_save (FILE *f)
   /* Actually write out the objects.  */
   for (i = 0; i < state.count; i++)
     {
+      state.ptrs_i = i;
       if (this_object_size < state.ptrs[i]->size)
        {
          this_object_size = state.ptrs[i]->size;
@@ -591,7 +661,42 @@ gt_pch_save (FILE *f)
   vbits.release ();
 #endif
 
+  reloc_addrs_vec.qsort (compare_ptr);
+
+  size_t reloc_addrs_size = 0;
+  void *last_addr = NULL;
+  unsigned char uleb128_buf[sizeof (size_t) * 2];
+  for (void *addr : reloc_addrs_vec)
+    {
+      gcc_assert ((uintptr_t) addr >= (uintptr_t) mmi.preferred_base
+                 && ((uintptr_t) addr + sizeof (void *)
+                     < (uintptr_t) mmi.preferred_base + mmi.size));
+      if (addr == last_addr)
+       continue;
+      if (last_addr == NULL)
+       last_addr = mmi.preferred_base;
+      size_t diff = (uintptr_t) addr - (uintptr_t) last_addr;
+      reloc_addrs_size += write_uleb128 (uleb128_buf, diff);
+      last_addr = addr;
+    }
+  if (fwrite (&reloc_addrs_size, sizeof (reloc_addrs_size), 1, f) != 1)
+    fatal_error (input_location, "cannot write PCH file: %m");
+  last_addr = NULL;
+  for (void *addr : reloc_addrs_vec)
+    {
+      if (addr == last_addr)
+       continue;
+      if (last_addr == NULL)
+       last_addr = mmi.preferred_base;
+      size_t diff = (uintptr_t) addr - (uintptr_t) last_addr;
+      reloc_addrs_size = write_uleb128 (uleb128_buf, diff);
+      if (fwrite (uleb128_buf, 1, reloc_addrs_size, f) != reloc_addrs_size)
+       fatal_error (input_location, "cannot write PCH file: %m");
+      last_addr = addr;
+    }
+
   ggc_pch_finish (state.d, state.f);
+
   gt_pch_fixup_stringpool ();
 
   unsigned num_callbacks = callback_vec.length ();
@@ -608,6 +713,7 @@ gt_pch_save (FILE *f)
   delete saving_htab;
   saving_htab = NULL;
   callback_vec.release ();
+  reloc_addrs_vec.release ();
 }
 
 /* Read the state of the compiler back in from F.  */
@@ -660,6 +766,7 @@ gt_pch_restore (FILE *f)
   if (fread (&mmi, sizeof (mmi), 1, f) != 1)
     fatal_error (input_location, "cannot read PCH file: %m");
 
+  void *orig_preferred_base = mmi.preferred_base;
   result = host_hooks.gt_pch_use_address (mmi.preferred_base, mmi.size,
                                          fileno (f), mmi.offset);
 
@@ -667,7 +774,7 @@ gt_pch_restore (FILE *f)
      address needed.  */
   if (result < 0)
     {
-      sorry_at (input_location, "PCH relocation is not yet supported");
+      sorry_at (input_location, "PCH allocation failure");
       /* There is no point in continuing from here, we will only end up
         with a crashed (most likely hanging) compiler.  */
       exit (-1);
@@ -685,9 +792,75 @@ gt_pch_restore (FILE *f)
   else if (fseek (f, mmi.offset + mmi.size, SEEK_SET) != 0)
     fatal_error (input_location, "cannot read PCH file: %m");
 
-  ggc_pch_read (f, mmi.preferred_base);
+  size_t reloc_addrs_size;
+  if (fread (&reloc_addrs_size, sizeof (reloc_addrs_size), 1, f) != 1)
+    fatal_error (input_location, "cannot read PCH file: %m");
 
-  gt_pch_restore_stringpool ();
+  if (orig_preferred_base != mmi.preferred_base)
+    {
+      uintptr_t bias
+       = (uintptr_t) mmi.preferred_base - (uintptr_t) orig_preferred_base;
+
+      /* Adjust all the global pointers by bias.  */
+      line_table = new_line_table;
+      for (rt = gt_ggc_rtab; *rt; rt++)
+       for (rti = *rt; rti->base != NULL; rti++)
+      for (i = 0; i < rti->nelt; i++)
+       {
+         char *addr = (char *)rti->base + rti->stride * i;
+         char *p;
+         memcpy (&p, addr, sizeof (void *));
+         if ((uintptr_t) p >= (uintptr_t) orig_preferred_base
+             && (uintptr_t) p < (uintptr_t) orig_preferred_base + mmi.size)
+           {
+             p = (char *) ((uintptr_t) p + bias);
+             memcpy (addr, &p, sizeof (void *));
+           }
+       }
+      new_line_table = line_table;
+      line_table = save_line_table;
+
+      /* And adjust all the pointers in the image by bias too.  */
+      char *addr = (char *) mmi.preferred_base;
+      unsigned char uleb128_buf[4096], *uleb128_ptr = uleb128_buf;
+      while (reloc_addrs_size != 0)
+       {
+         size_t this_size
+           = MIN (reloc_addrs_size,
+                  (size_t) (4096 - (uleb128_ptr - uleb128_buf)));
+         if (fread (uleb128_ptr, 1, this_size, f) != this_size)
+           fatal_error (input_location, "cannot read PCH file: %m");
+         unsigned char *uleb128_end = uleb128_ptr + this_size;
+         if (this_size != reloc_addrs_size)
+           uleb128_end -= 2 * sizeof (size_t);
+         uleb128_ptr = uleb128_buf;
+         while (uleb128_ptr < uleb128_end)
+           {
+             size_t diff;
+             uleb128_ptr = read_uleb128 (uleb128_ptr, &diff);
+             addr = (char *) ((uintptr_t) addr + diff);
+
+             char *p;
+             memcpy (&p, addr, sizeof (void *));
+             gcc_assert ((uintptr_t) p >= (uintptr_t) orig_preferred_base
+                         && ((uintptr_t) p
+                             < (uintptr_t) orig_preferred_base + mmi.size));
+             p = (char *) ((uintptr_t) p + bias);
+             memcpy (addr, &p, sizeof (void *));
+           }
+         reloc_addrs_size -= this_size;
+         if (reloc_addrs_size == 0)
+           break;
+         this_size = uleb128_end + 2 * sizeof (size_t) - uleb128_ptr;
+         memcpy (uleb128_buf, uleb128_ptr, this_size);
+         uleb128_ptr = uleb128_buf + this_size;
+       }
+    }
+  else if (fseek (f, (mmi.offset + mmi.size + sizeof (reloc_addrs_size)
+                     + reloc_addrs_size), SEEK_SET) != 0)
+    fatal_error (input_location, "cannot read PCH file: %m");
+
+  ggc_pch_read (f, mmi.preferred_base);
 
   void (*pch_save) (FILE *);
   unsigned num_callbacks;
@@ -696,23 +869,28 @@ gt_pch_restore (FILE *f)
     fatal_error (input_location, "cannot read PCH file: %m");
   if (pch_save != &gt_pch_save)
     {
-      uintptr_t bias = (uintptr_t) &gt_pch_save - (uintptr_t) pch_save;
+      uintptr_t binbias = (uintptr_t) &gt_pch_save - (uintptr_t) pch_save;
       void **ptrs = XNEWVEC (void *, num_callbacks);
       unsigned i;
+      uintptr_t bias
+       = (uintptr_t) mmi.preferred_base - (uintptr_t) orig_preferred_base;
 
       if (fread (ptrs, sizeof (void *), num_callbacks, f) != num_callbacks)
        fatal_error (input_location, "cannot read PCH file: %m");
       for (i = 0; i < num_callbacks; ++i)
        {
-         memcpy (&pch_save, ptrs[i], sizeof (pch_save));
-         pch_save = (void (*) (FILE *)) ((uintptr_t) pch_save + bias);
-         memcpy (ptrs[i], &pch_save, sizeof (pch_save));
+         void *ptr = (void *) ((uintptr_t) ptrs[i] + bias);
+         memcpy (&pch_save, ptr, sizeof (pch_save));
+         pch_save = (void (*) (FILE *)) ((uintptr_t) pch_save + binbias);
+         memcpy (ptr, &pch_save, sizeof (pch_save));
        }
       XDELETE (ptrs);
     }
   else if (fseek (f, num_callbacks * sizeof (void *), SEEK_CUR) != 0)
     fatal_error (input_location, "cannot read PCH file: %m");
 
+  gt_pch_restore_stringpool ();
+
   /* Barring corruption of the PCH file, the restored line table should be
      complete and usable.  */
   line_table = new_line_table;
@@ -736,7 +914,7 @@ default_gt_pch_get_address (size_t size ATTRIBUTE_UNUSED,
    of the PCH file would be required.  */
 
 int
-default_gt_pch_use_address (void *base, size_t size, int fd ATTRIBUTE_UNUSED,
+default_gt_pch_use_address (void *&base, size_t size, int fd ATTRIBUTE_UNUSED,
                            size_t offset ATTRIBUTE_UNUSED)
 {
   void *addr = xmalloc (size);
@@ -782,7 +960,7 @@ mmap_gt_pch_get_address (size_t size, int fd)
    mapped with something.  */
 
 int
-mmap_gt_pch_use_address (void *base, size_t size, int fd, size_t offset)
+mmap_gt_pch_use_address (void *&base, size_t size, int fd, size_t offset)
 {
   void *addr;
 
index e83f701986368e7b8e9ccb512203e56366edb233..cd8621a6baab0a8d0ada3b81ab2f7c9af2090e42 100644 (file)
@@ -415,7 +415,7 @@ gt_pch_nx (user_struct *p)
 static void
 gt_pch_nx (user_struct *p, gt_pointer_operator op, void *cookie)
 {
-  op (&(p->m_ptr), cookie);
+  op (&(p->m_ptr), NULL, cookie);
 }
 
 /* Verify that GTY((user)) works.  */
index c4fe26cf5d103257b4a0718f77287491fd376c0a..e495b62a1176f9095b574a73be7f852557753358 100644 (file)
@@ -111,7 +111,7 @@ class GTY((user)) hash_map
       static void
       pch_nx_helper (T *&x, gt_pointer_operator op, void *cookie)
        {
-         op (&x, cookie);
+         op (&x, NULL, cookie);
        }
 
     /* The overloads below should match those in ggc.h.  */
@@ -336,7 +336,7 @@ template<typename K, typename V, typename H>
 static inline void
 gt_pch_nx (hash_map<K, V, H> *h, gt_pointer_operator op, void *cookie)
 {
-  op (&h->m_table.m_entries, cookie);
+  op (&h->m_table.m_entries, NULL, cookie);
 }
 
 enum hm_alloc { hm_heap = false, hm_ggc = true };
index 85c31efd412bad83d796fa586a5e71a807abc034..60b3fcbf0c5a6538efcbffa4ec40fb02c7303479 100644 (file)
@@ -206,7 +206,7 @@ template<typename K, typename H>
 static inline void
 gt_pch_nx (hash_set<K, false, H> *h, gt_pointer_operator op, void *cookie)
 {
-  op (&h->m_table.m_entries, cookie);
+  op (&h->m_table.m_entries, NULL, cookie);
 }
 
 #endif
index ff415c7250bcaf365261afa7c538f084ed4b65de..00b31d8b679143d72394809be0d144c0ea166a6e 100644 (file)
@@ -1206,7 +1206,7 @@ template<typename D>
 static inline void
 gt_pch_nx (hash_table<D> *h, gt_pointer_operator op, void *cookie)
 {
-  op (&h->m_entries, cookie);
+  op (&h->m_entries, NULL, cookie);
 }
 
 template<typename H>
index 57c81bfd34e5fadc2d58d055af289dd3638c065b..6f0373ec27f7adcc2bfc56e7e64a65b43d109501 100644 (file)
@@ -254,7 +254,7 @@ struct ggc_remove
   static void
   pch_nx (T &p, gt_pointer_operator op, void *cookie)
   {
-    op (&p, cookie);
+    op (&p, NULL, cookie);
   }
 };
 
index a87b6d3e5801d34aa0fd1c64fba1f7b244a440e6..8f50932274b51d6991a115796d3e2eb10c47cc7c 100644 (file)
@@ -35,10 +35,10 @@ along with GCC; see the file COPYING3.  If not see
   default_gt_pch_alloc_granularity
 
 extern void* default_gt_pch_get_address (size_t, int);
-extern int default_gt_pch_use_address (void *, size_t, int, size_t);
+extern int default_gt_pch_use_address (void *&, size_t, int, size_t);
 extern size_t default_gt_pch_alloc_granularity (void);
 extern void* mmap_gt_pch_get_address (size_t, int);
-extern int mmap_gt_pch_use_address (void *, size_t, int, size_t);
+extern int mmap_gt_pch_use_address (void *&, size_t, int, size_t);
 
 /* The structure is defined in hosthooks.h.  */
 #define HOST_HOOKS_INITIALIZER {               \
index 42ed3dcebf61d353d6e589d5adb36bd20f4e8233..8241a533905ae3b1acab3403ba4d2cc4285fdc23 100644 (file)
@@ -30,10 +30,12 @@ struct host_hooks
   void * (*gt_pch_get_address) (size_t size, int fd);
 
   /* ADDR is an address returned by gt_pch_get_address.  Attempt to allocate
-     SIZE bytes at the same address and load it with the data from FD at
-     OFFSET.  Return -1 if we couldn't allocate memory at ADDR, return 0
-     if the memory is allocated but the data not loaded, return 1 if done.  */
-  int (*gt_pch_use_address) (void *addr, size_t size, int fd, size_t offset);
+     SIZE bytes at the same address (preferrably) or some other address
+     and load it with the data from FD at OFFSET.  Return -1 if we couldn't
+     allocate memory, otherwise update ADDR to the actual address where it got
+     allocated, return 0 if the memory is allocated but the data not loaded,
+     return 1 if done.  */
+  int (*gt_pch_use_address) (void *&addr, size_t size, int fd, size_t offset);
 
   /*  Return the alignment required for allocating virtual memory. Usually
       this is the same as pagesize.  */
index 158351350de09c8cf26f77f6690b07d408ab895d..2e5bafd0f9675622bcdfce0b4bcb5b3aa149d166 100644 (file)
@@ -1199,7 +1199,7 @@ gt_pch_nx (pod_mode<T> *)
 
 template<typename T>
 void
-gt_pch_nx (pod_mode<T> *, void (*) (void *, void *), void *)
+gt_pch_nx (pod_mode<T> *, void (*) (void *, void *, void *), void *)
 {
 }
 
index 94e7b701f64ef9b986ac20db23eface010762aa1..60a38c3ea6172280053f6f89f09937e1d7e2f648 100644 (file)
@@ -2717,7 +2717,7 @@ gt_pch_nx (poly_int_pod<N, C> *)
 
 template<unsigned int N, typename C>
 void
-gt_pch_nx (poly_int_pod<N, C> *, void (*) (void *, void *), void *)
+gt_pch_nx (poly_int_pod<N, C> *, void (*) (void *, void *, void *), void *)
 {
 }
 
index 2f21466af245d282e6cc9d4a10e1b7495209f29b..cf7f87db7c510133db3e216042b77fcc36a6f0c6 100644 (file)
@@ -225,7 +225,7 @@ gt_pch_nx (unsigned char& x ATTRIBUTE_UNUSED)
 void
 gt_pch_nx (unsigned char *x, gt_pointer_operator op, void *cookie)
 {
-  op (x, cookie);
+  op (x, NULL, cookie);
 }
 \f
 /* Handle saving and restoring the string pool for PCH.  */
index ebbd894ae0312e23b9bce68051c9101c7a7c3867..672e384ef09ce638484c121037dee19c75b6e4ad 100644 (file)
@@ -9946,13 +9946,13 @@ void
 gt_pch_nx (edge_def *e, gt_pointer_operator op, void *cookie)
 {
   tree block = LOCATION_BLOCK (e->goto_locus);
-  op (&(e->src), cookie);
-  op (&(e->dest), cookie);
+  op (&(e->src), NULL, cookie);
+  op (&(e->dest), NULL, cookie);
   if (current_ir_type () == IR_GIMPLE)
-    op (&(e->insns.g), cookie);
+    op (&(e->insns.g), NULL, cookie);
   else
-    op (&(e->insns.r), cookie);
-  op (&(block), cookie);
+    op (&(e->insns.r), NULL, cookie);
+  op (&(block), &(block), cookie);
 }
 
 #if CHECKING_P
index 32200ff890f18b2c3bd657d2a40fe0a02061275e..8cc8cca26cf0c1efa7a83cefd344283f89870338 100644 (file)
@@ -365,8 +365,8 @@ gt_pch_nx (irange *x, gt_pointer_operator op, void *cookie)
 {
   for (unsigned i = 0; i < x->m_num_ranges; ++i)
     {
-      op (&x->m_base[i * 2], cookie);
-      op (&x->m_base[i * 2 + 1], cookie);
+      op (&x->m_base[i * 2], NULL, cookie);
+      op (&x->m_base[i * 2 + 1], NULL, cookie);
     }
 }
 
index b3f47b1f65b1a7ed74c6d5c1b58e948fbf22a1e2..d83a19a7f25570aee1720ebabec9071883e70ceb 100644 (file)
--- a/gcc/vec.h
+++ b/gcc/vec.h
@@ -1388,7 +1388,7 @@ void
 gt_pch_nx (vec<T *, A, vl_embed> *v, gt_pointer_operator op, void *cookie)
 {
   for (unsigned i = 0; i < v->length (); i++)
-    op (&((*v)[i]), cookie);
+    op (&((*v)[i]), NULL, cookie);
 }
 
 template<typename T, typename A>
index 19bf65d999c891ed154cf1838f45b38315d26d7b..d03a17412536599c8836afc82573a5f2b616c604 100644 (file)
@@ -3338,7 +3338,7 @@ gt_pch_nx (generic_wide_int <T> *)
 
 template<typename T>
 void
-gt_pch_nx (generic_wide_int <T> *, void (*) (void *, void *), void *)
+gt_pch_nx (generic_wide_int <T> *, void (*) (void *, void *, void *), void *)
 {
 }
 
@@ -3356,7 +3356,7 @@ gt_pch_nx (trailing_wide_ints <N> *)
 
 template<int N>
 void
-gt_pch_nx (trailing_wide_ints <N> *, void (*) (void *, void *), void *)
+gt_pch_nx (trailing_wide_ints <N> *, void (*) (void *, void *, void *), void *)
 {
 }