X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fggc-common.c;h=e9c2f020668e95a8f1d912aaebfbe4ecf8fbd8ec;hb=06b9f7dcf9ff90ad68b7799ee8afe85374c1bf06;hp=55543dd7970ea3eb2d9dbff8ffcfc5f164c346a4;hpb=1f3233d13f58417984cb2239d328b65e8d172744;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/ggc-common.c b/gcc/ggc-common.c index 55543dd7970..e9c2f020668 100644 --- a/gcc/ggc-common.c +++ b/gcc/ggc-common.c @@ -23,58 +23,39 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "config.h" #include "system.h" -#include "rtl.h" -#include "tree.h" -#include "tm_p.h" +#include "coretypes.h" #include "hashtab.h" -#include "varray.h" #include "ggc.h" -#include "langhooks.h" +#include "toplev.h" + +#ifdef HAVE_MMAP_FILE +# include +#endif + +#ifdef ENABLE_VALGRIND_CHECKING +#include +#else +/* Avoid #ifdef:s when we can help it. */ +#define VALGRIND_DISCARD(x) +#endif /* Statistics about the allocation. */ static ggc_statistics *ggc_stats; -static void ggc_mark_rtx_children_1 PARAMS ((rtx)); +struct traversal_state; + static int ggc_htab_delete PARAMS ((void **, void *)); +static hashval_t saving_htab_hash PARAMS ((const PTR)); +static int saving_htab_eq PARAMS ((const PTR, const PTR)); +static int call_count PARAMS ((void **, void *)); +static int call_alloc PARAMS ((void **, void *)); +static int compare_ptr_data PARAMS ((const void *, const void *)); +static void relocate_ptrs PARAMS ((void *, void *)); +static void write_pch_globals PARAMS ((const struct ggc_root_tab * const *tab, + struct traversal_state *state)); /* Maintain global roots that are preserved during GC. */ -/* Global roots that are preserved during calls to gc. */ - -struct ggc_root -{ - struct ggc_root *next; - void *base; - int nelt; - int size; - void (*cb) PARAMS ((void *)); -}; - -static struct ggc_root *roots; - -/* Add BASE as a new garbage collection root. It is an array of - length NELT with each element SIZE bytes long. CB is a - function that will be called with a pointer to each element - of the array; it is the intention that CB call the appropriate - routine to mark gc-able memory for that element. */ - -void -ggc_add_root (base, nelt, size, cb) - void *base; - int nelt, size; - void (*cb) PARAMS ((void *)); -{ - struct ggc_root *x = (struct ggc_root *) xmalloc (sizeof (*x)); - - x->next = roots; - x->base = base; - x->nelt = nelt; - x->size = size; - x->cb = cb; - - roots = x; -} - /* Process a slot of an htab by deleting it if it has not been marked. */ static int @@ -97,13 +78,12 @@ ggc_htab_delete (slot, info) void ggc_mark_roots () { - struct ggc_root *x; const struct ggc_root_tab *const *rt; const struct ggc_root_tab *rti; const struct ggc_cache_tab *const *ct; const struct ggc_cache_tab *cti; size_t i; - + for (rt = gt_ggc_deletable_rtab; *rt; rt++) for (rti = *rt; rti->base != NULL; rti++) memset (rti->base, 0, rti->stride); @@ -113,159 +93,18 @@ ggc_mark_roots () for (i = 0; i < rti->nelt; i++) (*rti->cb)(*(void **)((char *)rti->base + rti->stride * i)); - for (x = roots; x != NULL; x = x->next) - { - char *elt = x->base; - int s = x->size, n = x->nelt; - void (*cb) PARAMS ((void *)) = x->cb; - int i; - - for (i = 0; i < n; ++i, elt += s) - (*cb)(elt); - } + ggc_mark_stringpool (); /* Now scan all hash tables that have objects which are to be deleted if they are not already marked. */ for (ct = gt_ggc_cache_rtab; *ct; ct++) for (cti = *ct; cti->base != NULL; cti++) - htab_traverse (*cti->base, ggc_htab_delete, (PTR) cti); -} - -/* R had not been previously marked, but has now been marked via - ggc_set_mark. Now recurse and process the children. */ - -void -ggc_mark_rtx_children (r) - rtx r; -{ - rtx i, last; - - /* Special case the instruction chain. This is a data structure whose - chain length is potentially unbounded, and which contain references - within the chain (e.g. label_ref and insn_list). If do nothing here, - we risk blowing the stack recursing through a long chain of insns. - - Combat this by marking all of the instructions in the chain before - marking the contents of those instructions. */ - - switch (GET_CODE (r)) - { - case INSN: - case JUMP_INSN: - case CALL_INSN: - case NOTE: - case CODE_LABEL: - case BARRIER: - for (i = NEXT_INSN (r); ; i = NEXT_INSN (i)) - if (! ggc_test_and_set_mark (i)) - break; - last = i; - - for (i = NEXT_INSN (r); i != last; i = NEXT_INSN (i)) - ggc_mark_rtx_children_1 (i); - - default: - break; - } - - ggc_mark_rtx_children_1 (r); -} - -static void -ggc_mark_rtx_children_1 (r) - rtx r; -{ - const char *fmt; - int i; - rtx next_rtx; - - do - { - enum rtx_code code = GET_CODE (r); - /* This gets set to a child rtx to eliminate tail recursion. */ - next_rtx = NULL; - - /* Collect statistics, if appropriate. */ - if (ggc_stats) + if (*cti->base) { - ++ggc_stats->num_rtxs[(int) code]; - ggc_stats->size_rtxs[(int) code] += ggc_get_size (r); + ggc_set_mark (*cti->base); + htab_traverse (*cti->base, ggc_htab_delete, (PTR) cti); + ggc_set_mark ((*cti->base)->entries); } - - /* ??? If (some of) these are really pass-dependent info, do we - have any right poking our noses in? */ - switch (code) - { - case MEM: - gt_ggc_m_mem_attrs (MEM_ATTRS (r)); - break; - case JUMP_INSN: - ggc_mark_rtx (JUMP_LABEL (r)); - break; - case CODE_LABEL: - ggc_mark_rtx (LABEL_REFS (r)); - break; - case LABEL_REF: - ggc_mark_rtx (LABEL_NEXTREF (r)); - ggc_mark_rtx (CONTAINING_INSN (r)); - break; - case ADDRESSOF: - ggc_mark_tree (ADDRESSOF_DECL (r)); - break; - case NOTE: - switch (NOTE_LINE_NUMBER (r)) - { - case NOTE_INSN_RANGE_BEG: - case NOTE_INSN_RANGE_END: - case NOTE_INSN_LIVE: - case NOTE_INSN_EXPECTED_VALUE: - ggc_mark_rtx (NOTE_RANGE_INFO (r)); - break; - - case NOTE_INSN_BLOCK_BEG: - case NOTE_INSN_BLOCK_END: - ggc_mark_tree (NOTE_BLOCK (r)); - break; - - default: - break; - } - break; - - default: - break; - } - - for (fmt = GET_RTX_FORMAT (GET_CODE (r)), i = 0; *fmt ; ++fmt, ++i) - { - rtx exp; - switch (*fmt) - { - case 'e': case 'u': - exp = XEXP (r, i); - if (ggc_test_and_set_mark (exp)) - { - if (next_rtx == NULL) - next_rtx = exp; - else - ggc_mark_rtx_children (exp); - } - break; - case 'V': case 'E': - gt_ggc_m_rtvec_def (XVEC (r, i)); - break; - } - } - } - while ((r = next_rtx) != NULL); -} - -/* Various adaptor functions. */ -void -gt_ggc_mx_rtx_def (x) - void *x; -{ - ggc_mark_rtx((rtx)x); } /* Allocate a block of memory, then clear it. */ @@ -292,10 +131,36 @@ ggc_realloc (x, size) old_size = ggc_get_size (x); if (size <= old_size) - return x; + { + /* Mark the unwanted memory as unaccessible. We also need to make + the "new" size accessible, since ggc_get_size returns the size of + the pool, not the size of the individually allocated object, the + size which was previously made accessible. Unfortunately, we + don't know that previously allocated size. Without that + knowledge we have to lose some initialization-tracking for the + old parts of the object. An alternative is to mark the whole + old_size as reachable, but that would lose tracking of writes + after the end of the object (by small offsets). Discard the + handle to avoid handle leak. */ + VALGRIND_DISCARD (VALGRIND_MAKE_NOACCESS ((char *) x + size, + old_size - size)); + VALGRIND_DISCARD (VALGRIND_MAKE_READABLE (x, size)); + return x; + } r = ggc_alloc (size); + + /* Since ggc_get_size returns the size of the pool, not the size of the + individually allocated object, we'd access parts of the old object + that were marked invalid with the memcpy below. We lose a bit of the + initialization-tracking since some of it may be uninitialized. */ + VALGRIND_DISCARD (VALGRIND_MAKE_READABLE (x, old_size)); + memcpy (r, x, old_size); + + /* The old object is not supposed to be used anymore. */ + VALGRIND_DISCARD (VALGRIND_MAKE_NOACCESS (x, old_size)); + return r; } @@ -307,6 +172,26 @@ ggc_calloc (s1, s2) return ggc_alloc_cleared (s1 * s2); } +/* These are for splay_tree_new_ggc. */ +PTR +ggc_splay_alloc (sz, nl) + int sz; + PTR nl; +{ + if (nl != NULL) + abort (); + return ggc_alloc (sz); +} + +void +ggc_splay_dont_free (x, nl) + PTR x ATTRIBUTE_UNUSED; + PTR nl; +{ + if (nl != NULL) + abort (); +} + /* Print statistics that are independent of the collector in use. */ #define SCALE(x) ((unsigned long) ((x) < 1024*10 \ ? (x) \ @@ -317,11 +202,9 @@ ggc_calloc (s1, s2) void ggc_print_common_statistics (stream, stats) - FILE *stream; + FILE *stream ATTRIBUTE_UNUSED; ggc_statistics *stats; { - int code; - /* Set the pointer so that during collection we will actually gather the statistics. */ ggc_stats = stats; @@ -329,58 +212,415 @@ ggc_print_common_statistics (stream, stats) /* Then do one collection to fill in the statistics. */ ggc_collect (); - /* Total the statistics. */ - for (code = 0; code < MAX_TREE_CODES; ++code) + /* At present, we don't really gather any interesting statistics. */ + + /* Don't gather statistics any more. */ + ggc_stats = NULL; +} + +/* Functions for saving and restoring GCable memory to disk. */ + +static htab_t saving_htab; + +struct ptr_data +{ + void *obj; + void *note_ptr_cookie; + gt_note_pointers note_ptr_fn; + gt_handle_reorder reorder_fn; + size_t size; + void *new_addr; +}; + +#define POINTER_HASH(x) (hashval_t)((long)x >> 3) + +/* Register an object in the hash table. */ + +int +gt_pch_note_object (obj, note_ptr_cookie, note_ptr_fn) + void *obj; + void *note_ptr_cookie; + gt_note_pointers note_ptr_fn; +{ + struct ptr_data **slot; + + if (obj == NULL || obj == (void *) 1) + return 0; + + slot = (struct ptr_data **) + htab_find_slot_with_hash (saving_htab, obj, POINTER_HASH (obj), + INSERT); + if (*slot != NULL) + { + if ((*slot)->note_ptr_fn != note_ptr_fn + || (*slot)->note_ptr_cookie != note_ptr_cookie) + abort (); + return 0; + } + + *slot = xcalloc (sizeof (struct ptr_data), 1); + (*slot)->obj = obj; + (*slot)->note_ptr_fn = note_ptr_fn; + (*slot)->note_ptr_cookie = note_ptr_cookie; + if (note_ptr_fn == gt_pch_p_S) + (*slot)->size = strlen (obj) + 1; + else + (*slot)->size = ggc_get_size (obj); + return 1; +} + +/* Register an object in the hash table. */ + +void +gt_pch_note_reorder (obj, note_ptr_cookie, reorder_fn) + void *obj; + void *note_ptr_cookie; + gt_handle_reorder reorder_fn; +{ + struct ptr_data *data; + + if (obj == NULL || obj == (void *) 1) + return; + + data = htab_find_with_hash (saving_htab, obj, POINTER_HASH (obj)); + if (data == NULL + || data->note_ptr_cookie != note_ptr_cookie) + abort (); + + data->reorder_fn = reorder_fn; +} + +/* Hash and equality functions for saving_htab, callbacks for htab_create. */ + +static hashval_t +saving_htab_hash (p) + const PTR p; +{ + return POINTER_HASH (((struct ptr_data *)p)->obj); +} + +static int +saving_htab_eq (p1, p2) + const PTR p1; + const PTR p2; +{ + return ((struct ptr_data *)p1)->obj == p2; +} + +/* Handy state for the traversal functions. */ + +struct traversal_state +{ + FILE *f; + struct ggc_pch_data *d; + size_t count; + struct ptr_data **ptrs; + size_t ptrs_i; +}; + +/* Callbacks for htab_traverse. */ + +static int +call_count (slot, state_p) + void **slot; + void *state_p; +{ + struct ptr_data *d = (struct ptr_data *)*slot; + struct traversal_state *state = (struct traversal_state *)state_p; + + ggc_pch_count_object (state->d, d->obj, d->size); + state->count++; + return 1; +} + +static int +call_alloc (slot, state_p) + void **slot; + void *state_p; +{ + struct ptr_data *d = (struct ptr_data *)*slot; + struct traversal_state *state = (struct traversal_state *)state_p; + + d->new_addr = ggc_pch_alloc_object (state->d, d->obj, d->size); + state->ptrs[state->ptrs_i++] = d; + return 1; +} + +/* Callback for qsort. */ + +static int +compare_ptr_data (p1_p, p2_p) + const void *p1_p; + const void *p2_p; +{ + struct ptr_data *p1 = *(struct ptr_data *const *)p1_p; + struct ptr_data *p2 = *(struct ptr_data *const *)p2_p; + return (((size_t)p1->new_addr > (size_t)p2->new_addr) + - ((size_t)p1->new_addr < (size_t)p2->new_addr)); +} + +/* Callbacks for note_ptr_fn. */ + +static void +relocate_ptrs (ptr_p, state_p) + void *ptr_p; + void *state_p; +{ + void **ptr = (void **)ptr_p; + struct traversal_state *state ATTRIBUTE_UNUSED + = (struct traversal_state *)state_p; + struct ptr_data *result; + + if (*ptr == NULL || *ptr == (void *)1) + return; + + result = htab_find_with_hash (saving_htab, *ptr, POINTER_HASH (*ptr)); + if (result == NULL) + abort (); + *ptr = result->new_addr; +} + +/* Write out, after relocation, the pointers in TAB. */ +static void +write_pch_globals (tab, state) + const struct ggc_root_tab * const *tab; + struct traversal_state *state; +{ + const struct ggc_root_tab *const *rt; + const struct ggc_root_tab *rti; + size_t i; + + for (rt = tab; *rt; rt++) + for (rti = *rt; rti->base != NULL; rti++) + for (i = 0; i < rti->nelt; i++) + { + void *ptr = *(void **)((char *)rti->base + rti->stride * i); + struct ptr_data *new_ptr; + if (ptr == NULL || ptr == (void *)1) + { + if (fwrite (&ptr, sizeof (void *), 1, state->f) + != 1) + fatal_io_error ("can't write PCH file"); + } + else + { + new_ptr = htab_find_with_hash (saving_htab, ptr, + POINTER_HASH (ptr)); + if (fwrite (&new_ptr->new_addr, sizeof (void *), 1, state->f) + != 1) + fatal_io_error ("can't write PCH file"); + } + } +} + +/* Hold the information we need to mmap the file back in. */ + +struct mmap_info +{ + size_t offset; + size_t size; + void *preferred_base; +}; + +/* Write out the state of the compiler to F. */ + +void +gt_pch_save (f) + FILE *f; +{ + const struct ggc_root_tab *const *rt; + const struct ggc_root_tab *rti; + size_t i; + struct traversal_state state; + char *this_object = NULL; + size_t this_object_size = 0; + struct mmap_info mmi; + size_t page_size = getpagesize(); + + gt_pch_save_stringpool (); + + saving_htab = htab_create (50000, saving_htab_hash, saving_htab_eq, free); + + for (rt = gt_ggc_rtab; *rt; rt++) + for (rti = *rt; rti->base != NULL; rti++) + for (i = 0; i < rti->nelt; i++) + (*rti->pchw)(*(void **)((char *)rti->base + rti->stride * i)); + + for (rt = gt_pch_cache_rtab; *rt; rt++) + for (rti = *rt; rti->base != NULL; rti++) + for (i = 0; i < rti->nelt; i++) + (*rti->pchw)(*(void **)((char *)rti->base + rti->stride * i)); + + /* Prepare the objects for writing, determine addresses and such. */ + state.f = f; + state.d = init_ggc_pch(); + state.count = 0; + htab_traverse (saving_htab, call_count, &state); + + mmi.size = ggc_pch_total_size (state.d); + + /* Try to arrange things so that no relocation is necessary, + but don't try very hard. On most platforms, this will always work, + and on the rest it's a lot of work to do better. */ +#if HAVE_MMAP_FILE + mmi.preferred_base = mmap (NULL, mmi.size, + PROT_READ | PROT_WRITE, MAP_PRIVATE, + fileno (state.f), 0); + if (mmi.preferred_base == (void *)-1) + mmi.preferred_base = NULL; + else + munmap (mmi.preferred_base, mmi.size); +#else /* HAVE_MMAP_FILE */ + mmi.preferred_base = NULL; +#endif /* HAVE_MMAP_FILE */ + + ggc_pch_this_base (state.d, mmi.preferred_base); + + state.ptrs = xmalloc (state.count * sizeof (*state.ptrs)); + state.ptrs_i = 0; + htab_traverse (saving_htab, call_alloc, &state); + qsort (state.ptrs, state.count, sizeof (*state.ptrs), compare_ptr_data); + + /* Write out all the scalar variables. */ + for (rt = gt_pch_scalar_rtab; *rt; rt++) + for (rti = *rt; rti->base != NULL; rti++) + if (fwrite (rti->base, rti->stride, 1, f) != 1) + fatal_io_error ("can't write PCH file"); + + /* Write out all the global pointers, after translation. */ + write_pch_globals (gt_ggc_rtab, &state); + write_pch_globals (gt_pch_cache_rtab, &state); + + ggc_pch_prepare_write (state.d, state.f); + + /* Pad the PCH file so that the mmaped area starts on a page boundary. */ + { + long o; + o = ftell (state.f) + sizeof (mmi); + if (o == -1) + fatal_io_error ("can't get position in PCH file"); + mmi.offset = page_size - o % page_size; + if (mmi.offset == page_size) + mmi.offset = 0; + mmi.offset += o; + } + if (fwrite (&mmi, sizeof (mmi), 1, state.f) != 1) + fatal_io_error ("can't write PCH file"); + if (mmi.offset != 0 + && fseek (state.f, mmi.offset, SEEK_SET) != 0) + fatal_io_error ("can't write padding to PCH file"); + + /* Actually write out the objects. */ + for (i = 0; i < state.count; i++) { - stats->total_num_trees += stats->num_trees[code]; - stats->total_size_trees += stats->size_trees[code]; + if (this_object_size < state.ptrs[i]->size) + { + this_object_size = state.ptrs[i]->size; + this_object = xrealloc (this_object, this_object_size); + } + memcpy (this_object, state.ptrs[i]->obj, state.ptrs[i]->size); + if (state.ptrs[i]->reorder_fn != NULL) + state.ptrs[i]->reorder_fn (state.ptrs[i]->obj, + state.ptrs[i]->note_ptr_cookie, + relocate_ptrs, &state); + state.ptrs[i]->note_ptr_fn (state.ptrs[i]->obj, + state.ptrs[i]->note_ptr_cookie, + relocate_ptrs, &state); + ggc_pch_write_object (state.d, state.f, state.ptrs[i]->obj, + state.ptrs[i]->new_addr, state.ptrs[i]->size); + if (state.ptrs[i]->note_ptr_fn != gt_pch_p_S) + memcpy (state.ptrs[i]->obj, this_object, state.ptrs[i]->size); } - for (code = 0; code < NUM_RTX_CODE; ++code) + ggc_pch_finish (state.d, state.f); + + free (state.ptrs); + htab_delete (saving_htab); +} + +/* Read the state of the compiler back in from F. */ + +void +gt_pch_restore (f) + FILE *f; +{ + const struct ggc_root_tab *const *rt; + const struct ggc_root_tab *rti; + size_t i; + struct mmap_info mmi; + void *addr; + + /* 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_io_error ("can't read PCH file"); + + /* 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_io_error ("can't read PCH file"); + + 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_io_error ("can't read PCH file"); + + if (fread (&mmi, sizeof (mmi), 1, f) != 1) + fatal_io_error ("can't read PCH file"); + +#if HAVE_MMAP_FILE + addr = mmap (mmi.preferred_base, mmi.size, + PROT_READ | PROT_WRITE, MAP_PRIVATE, + fileno (f), mmi.offset); +#else + addr = (void *)-1; +#endif + if (addr == (void *)-1) { - stats->total_num_rtxs += stats->num_rtxs[code]; - stats->total_size_rtxs += stats->size_rtxs[code]; + addr = xmalloc (mmi.size); + if (fseek (f, mmi.offset, SEEK_SET) != 0 + || fread (&mmi, mmi.size, 1, f) != 1) + fatal_io_error ("can't read PCH file"); } + else if (fseek (f, mmi.offset + mmi.size, SEEK_SET) != 0) + fatal_io_error ("can't read PCH file"); - /* Print the statistics for trees. */ - fprintf (stream, "\n%-17s%10s %16s %10s\n", "Tree", - "Number", "Bytes", "% Total"); - for (code = 0; code < MAX_TREE_CODES; ++code) - if (ggc_stats->num_trees[code]) - { - fprintf (stream, "%-17s%10u%16ld%c %10.3f\n", - tree_code_name[code], - ggc_stats->num_trees[code], - SCALE (ggc_stats->size_trees[code]), - LABEL (ggc_stats->size_trees[code]), - (100 * ((double) ggc_stats->size_trees[code]) - / ggc_stats->total_size_trees)); - } - fprintf (stream, - "%-17s%10u%16ld%c\n", "Total", - ggc_stats->total_num_trees, - SCALE (ggc_stats->total_size_trees), - LABEL (ggc_stats->total_size_trees)); - - /* Print the statistics for RTL. */ - fprintf (stream, "\n%-17s%10s %16s %10s\n", "RTX", - "Number", "Bytes", "% Total"); - for (code = 0; code < NUM_RTX_CODE; ++code) - if (ggc_stats->num_rtxs[code]) - { - fprintf (stream, "%-17s%10u%16ld%c %10.3f\n", - rtx_name[code], - ggc_stats->num_rtxs[code], - SCALE (ggc_stats->size_rtxs[code]), - LABEL (ggc_stats->size_rtxs[code]), - (100 * ((double) ggc_stats->size_rtxs[code]) - / ggc_stats->total_size_rtxs)); - } - fprintf (stream, - "%-17s%10u%16ld%c\n", "Total", - ggc_stats->total_num_rtxs, - SCALE (ggc_stats->total_size_rtxs), - LABEL (ggc_stats->total_size_rtxs)); + ggc_pch_read (f, addr); - /* Don't gather statistics any more. */ - ggc_stats = NULL; + 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 (); }