5.13.2. Output PCH file
Seeing something related to conversion, resumes our example in after-parsing stage. Return from expand_or_defer_fn we are back cp_parser_function_definition_after_declarator which returns the FUNCTION_DECL immediately, and which is returned in turn by cp_parser_function_definition_from_specifiers_and_declarator , then cp_parser_init_declarator , cp_parser_simple_declaration , cp_parser_block_declaration , cp_parser_declaration , then in cp_parser_declaration_seq_opt EOF breaks the WHILE loop at line 6224, and finally we return to the entry point of parser - cp_parser_translation_unit . In this function EOF is consumed at line 2336, and finish_translation_unit following restores the binding scope of global namespace.
Returning from cp_parser_translation_unit , now we are back in c_parser_file . Exitting it we go back to c_common_parse_file which is referred by parse_file of lang_hooks for C++. And see that c_parser_file is within a WHILE loop; if there is any other source file in the command line, the procedure we see in above will be repeated. That means if we use command line like: g++ -o all a.cc b.cc c.cc; the compiler will parse these three source files and put the result into a single tree of intermediate language. After all source files have been parsed, it comes to finish_file .
Arriving here, we must within the global namespace scope. Cheking at line 2540 promises it.
2527 void
2528 finish_file (void) in decl2.c
2529 {
2530 tree vars;
2531 bool reconsider;
2532 size_t i;
2533 location_t locus;
2534 unsigned ssdf_count = 0;
2535
2536 locus = input_location ;
2537 at_eof = 1;
2538
2539 /* Bad parse errors. Just forget about it. */
2540 if (! global_bindings_p () || current_class_type || decl_namespace_list )
2541 return ;
2542
2543 if (pch_file )
2544 c_common_write_pch ();
Below, cpp_write_pch_deps writes identifiers declared within files specified by this command line into PCH file designated by pch_outfile . And asm_file_startpos was set points to the end of asm_out_file in pch_init at time read in first source file; while in cpp_read_main_file at line 1242 in c_common_parse_file , asm_out_file is filled with content of included PCH files, and at line 177 asm_file_end points to the end of the file. So at line 178, h.asm_size will be the size of included PCH files. These content should be appended into the PCH file.
165 void
166 c_common_write_pch (void) in c-pch.c
167 {
168 char *buf;
169 long asm_file_end;
170 long written;
171 struct c_pch_header h;
172
173 (*debug_hooks ->handle_pch) (1);
174
175 cpp_write_pch_deps (parse_in , pch_outfile );
176
177 asm_file_end = ftell (asm_out_file );
178 h.asm_size = asm_file_end - asm_file_startpos ;
179
180 if (fwrite (&h, sizeof (h), 1, pch_outfile ) != 1)
181 fatal_error ("can't write %s: %m", pch_file );
182
183 buf = xmalloc (16384);
184 fflush (asm_out_file );
185
186 if (fseek (asm_out_file , asm_file_startpos , SEEK_SET) != 0)
187 fatal_error ("can't seek in %s: %m", asm_file_name );
188
189 for (written = asm_file_startpos ; written < asm_file_end; )
190 {
191 long size = asm_file_end - written;
192 if (size > 16384)
193 size = 16384;
194 if (fread (buf, size, 1, asm_out_file ) != 1)
195 fatal_error ("can't read %s: %m", asm_file_name );
196 if (fwrite (buf, size, 1, pch_outfile ) != 1)
197 fatal_error ("can't write %s: %m", pch_file );
198 written += size;
199 }
200 free (buf);
201 /* asm_out_file can be written afterwards, so must be flushed first. */
202 fflush (asm_out_file );
203
204 gt_pch_save (pch_outfile );
205 cpp_write_pch_state (parse_in , pch_outfile );
206
207 if (fseek (pch_outfile , 0, SEEK_SET) != 0
208 || fwrite (get_ident (), IDENT_LENGTH, 1, pch_outfile ) != 1)
209 fatal_error ("can't write %s: %m", pch_file );
210
211 fclose (pch_outfile );
212 }
Above at line 175, pch_outfile is the file handle opened for pch_file , which is also the same file handle written into in cpp_save_state . Now r->savedstate contains identifiers before us executing parsing for the files specified by command line.
299 int
300 cpp_write_pch_deps (cpp_reader *r, FILE *f) in cpppch.c
301 {
302 struct macrodef_struct z;
303 struct cpp_savedstate *const ss = r->savedstate;
304 unsigned char *definedstrs;
305 size_t i;
306
307 /* Collect the list of identifiers which have been seen and
308 weren't defined to anything previously. */
309 ss->hashsize = 0;
310 ss->n_defs = 0;
311 cpp_forall_identifiers (r, count_defs , ss);
312
313 ss->defs = xmalloc (ss->n_defs * sizeof (cpp_hashnode *));
314 ss->n_defs = 0;
315 cpp_forall_identifiers (r, write_defs , ss);
316
317 /* Sort the list, copy it into a buffer, and write it out. */
318 qsort (ss->defs, ss->n_defs, sizeof (cpp_hashnode *), &comp_hashnodes);
319 definedstrs = ss->definedstrs = xmalloc (ss->hashsize);
320 for (i = 0; i < ss->n_defs; ++i)
321 {
322 size_t len = NODE_LEN (ss->defs[i]);
323 memcpy (definedstrs, NODE_NAME (ss->defs[i]), len + 1);
324 definedstrs += len + 1;
325 }
326
327 memset (&z, 0, sizeof (z));
328 z.definition_length = ss->hashsize;
329 if (fwrite (&z, sizeof (z), 1, f) != 1
330 || fwrite (ss->definedstrs, ss->hashsize, 1, f) != 1)
331 {
332 cpp_errno (r, CPP_DL_ERROR, "while writing precompiled header");
333 return -1;
334 }
335 free (ss->definedstrs);
336
337 /* Free the saved state. */
338 free (ss);
339 r->savedstate = NULL;
340 return 0;
341 }
Now the cpp_reader after parsing all files specified in command line, in its hash_table contains identifiers declared within those files. Then routine count_defs transverses the hash_table to count how many identifiers need be added, and calculate the size of buffer to hold names by hashsize field. Note that in save_idents invoked within cpp_save_state , node of type NT_VOID is ignored; but not here.
209 static int
210 count_defs (cpp_reader *pfile ATTRIBUTE_UNUSED, cpp_hashnode *hn, void *ss_p)
211 {
212 struct cpp_savedstate *const ss = (struct cpp_savedstate *)ss_p;
213
214 switch (hn->type)
215 {
216 case NT_MACRO:
217 if (hn->flags & NODE_BUILTIN)
218 return 1;
219
220 /* else fall through. */
221
222 case NT_VOID:
223 {
224 struct cpp_string news;
225 void **slot;
226
227 news.len = NODE_LEN (hn);
228 news.text = NODE_NAME (hn);
229 slot = htab_find (ss->definedhash, &news);
230 if (slot == NULL)
231 {
232 ss->hashsize += NODE_LEN (hn) + 1;
233 ss->n_defs += 1;
234 }
235 }
236 return 1;
237
238 case NT_ASSERTION:
239 /* Not currently implemented. */
240 return 1;
241
242 default :
243 abort ();
244 }
245 }
After allocating necessary resource, write_defs then iterates the hash_table of cpp_reader again to refer to identifiers not seen in cpp_save_state . Then after sorting at line 318 above, these names are written into the PCH file.
248 static int
249 write_defs (cpp_reader *pfile ATTRIBUTE_UNUSED, cpp_hashnode *hn, void *ss_p)
250 {
251 struct cpp_savedstate *const ss = (struct cpp_savedstate *)ss_p;
252
253 switch (hn->type)
254 {
255 case NT_MACRO:
256 if (hn->flags & NODE_BUILTIN)
257 return 1;
258
259 /* else fall through. */
260
261 case NT_VOID:
262 {
263 struct cpp_string news;
264 void **slot;
265
266 news.len = NODE_LEN (hn);
267 news.text = NODE_NAME (hn);
268 slot = htab_find (ss->definedhash, &news);
269 if (slot == NULL)
270 {
271 ss->defs[ss->n_defs] = hn;
272 ss->n_defs += 1;
273 }
274 }
275 return 1;
276
277 case NT_ASSERTION:
278 /* Not currently implemented. */
279 return 1;
280
281 default :
282 abort ();
283 }
284 }
At the point of invoking gt_pch_save , the content of PCH file is shown in below figure. See that idn is the identifier that declared within source files by user.
Further compiler generates many controlling data during compilation, and which we have seen is managed by GC. These data describe the characteristics of the program and effect the output of code emission, so must be saved in the PCH file.
423 void
424 gt_pch_save (FILE *f) in ggc-common.c
425 {
426 const struct ggc_root_tab *const *rt;
427 const struct ggc_root_tab *rti;
428 size_t i;
429 struct traversal_state state;
430 char *this_object = NULL;
431 size_t this_object_size = 0;
432 struct mmap_info mmi;
433 size_t page_size = getpagesize();
434
435 gt_pch_save_stringpool ();
Then gt_pch_save_stringpool saves the content of ident_hash , remember ident_hash is pointed by hash_table of the cpp_reader . First the content is copied into data structure of spd .
232 void
233 gt_pch_save_stringpool (void) in stringpool.c
234 {
235 unsigned int i;
236
237 spd = ggc_alloc (sizeof (*spd ));
238 spd ->nslots = ident_hash ->nslots;
239 spd ->nelements = ident_hash ->nelements;
240 spd ->entries = ggc_alloc (sizeof (tree *) * spd ->nslots);
241 for (i = 0; i < spd ->nslots; i++)
242 if (ident_hash ->entries[i] != NULL)
243 spd->entries[i] = HT_IDENT_TO_GCC_IDENT (ident_hash ->entries[i]);
244 else
245 spd->entries[i] = NULL;
246
247 saved_ident_hash = ht_create (14);
248 saved_ident_hash ->alloc_node = alloc_node;
249 ht_forall (ident_hash , ht_copy_and_clear , saved_ident_hash );
250 }
To cache the content of ident_hash , spd has following structure. See that spd is within gt_ggc_r_gt_stringpool_h which is contained within gt_ggc_rtab . It will be written into the PCH file later and read out by files using the PCH file in gt_pch_restore_stringpool .
199 struct string_pool_data GTY(()) in stringpool.c
200 {
201 tree * GTY((length ("%h.nslots"))) entries;
202 unsigned int nslots;
203 unsigned int nelements;
204 };
205
206 static GTY(()) struct string_pool_data * spd;
Second, it moves the content of ident_hash into saved_ident_hash with below function (it is cached to avoid affecting below procedure, it will be restored before exitting gt_pch_save ).
208 static int
209 ht_copy_and_clear (cpp_reader *r ATTRIBUTE_UNUSED, hashnode hp, const void *ht2_p)
210 {
211 cpp_hashnode *h = CPP_HASHNODE (hp);
212 struct ht *ht2 = (struct ht *) ht2_p;
213
214 if (h->type != NT_VOID
215 && (h->flags & NODE_BUILTIN) == 0)
216 {
217 cpp_hashnode *h2 = CPP_HASHNODE (ht_lookup (ht2,
218 NODE_NAME (h),
219 NODE_LEN (h),
220 HT_ALLOC));
221 h2->type = h->type;
222 memcpy (&h2->value, &h->value, sizeof (h->value));
223
224 h->type = NT_VOID;
225 memset (&h->value, 0, sizeof (h->value));
226 }
227 return 1;
228 }
Remember that in the compiler, all GC managing objects are processed by GC tool to generate gtype-* files. And in gtype-c.h, several global arrays are generated to hold the reference of these controlled objects. There are gt_ggc_rtab , gt_ggc_deletable_rtab (holds hash tables for deleted objects), gt_ggc_cache_rtab (used for garbage collection purpose), gt_pch_cache_rtab (same content as gt_ggc_cache_rtab but with different handling methods), gt_pch_scalar_rtab .
gt_pch_save (continue)
437 saving_htab = htab_create (50000, saving_htab_hash, saving_htab_eq, free);
438
439 for (rt = gt_ggc_rtab ; *rt; rt++)
440 for (rti = *rt; rti->base != NULL; rti++)
441 for (i = 0; i < rti->nelt; i++)
442 (*rti->pchw)(*(void **)((char *)rti->base + rti->stride * i));
443
444 for (rt = gt_pch_cache_rtab ; *rt; rt++)
445 for (rti = *rt; rti->base != NULL; rti++)
446 for (i = 0; i < rti->nelt; i++)
447 (*rti->pchw)(*(void **)((char *)rti->base + rti->stride * i));
448
449 /* Prepare the objects for writing, determine addresses and such. */
450 state.f = f;
451 state.d = init_ggc_pch();
452 state.count = 0;
453 htab_traverse (saving_htab , call_count , &state);
The content of PCH file is the intermediate code (i.e., in tree form), which we have already seen contains lots of pointers, how to maintain these pointers in PCH file, so as to they can be restored by the readin?
First of all, objects managed by GC are all global ones, and they form a close set (that is no reference to object outside of the set, and no object is referred from outside). Thus if copy the content and address of these objects faithfully into PCH file, then in reading restores them one by one, the PCH file’s content can be restored theoretically. However, it’s not practical as at reading the PCH file, some of the addresses are most likely occupied, it is hard to guarantee all addresses are usable; further restores objects one by one is inefficent.
As the global objects managed by GC form a close set, with memory management supported by the target, it would be able to: arranging all objects as compact as possible, and selecting a start address unlikely be occupied (for target using virtual memory, it is highly possible of unoccupancy), then updating all pointers in objects with these new addresses (assuming the start address usable); at last writing this mapping information, and updated objects into PCH file; by this way it can ensure this PCH file readin later. It is what GCC now does.
So the first step is to collect all pointers’ content in objects (including obejcts’ address), which is done by function referred by pchw , which saves this information into saving_htab . Takes cgraph_nodes_queue of gt_ggc_r_gtype_desc_c of gt_ggc_rtab as example, its pchw points to gt_pch_nx_cgraph_node which is generated by GC tool.
1306 void
1307 gt_pch_nx_cgraph_node (void *x_p) in gtype-desc.c
1308 {
1309 struct cgraph_node * x = (struct cgraph_node *)x_p;
1310 struct cgraph_node * xlimit = x;
1311 while (gt_pch_note_object (xlimit, xlimit, gt_pch_p_11cgraph_node ))
1312 xlimit = ((*xlimit).next);
1313 if (x != xlimit)
1314 for (;;)
1315 {
1316 struct cgraph_node * const xprev = ((*x).previous);
1317 if (xprev == NULL) break ;
1318 x = xprev;
1319 (void) gt_pch_note_object (xprev, xprev, gt_pch_p_11cgraph_node );
1320 }
1321 while (x != xlimit)
1322 {
1323 gt_pch_n_9tree_node ((*x).decl);
1324 gt_pch_n_11cgraph_edge ((*x).callees);
1325 gt_pch_n_11cgraph_edge ((*x).callers);
1326 gt_pch_n_11cgraph_node ((*x).next);
1327 gt_pch_n_11cgraph_node ((*x).previous);
1328 gt_pch_n_11cgraph_node ((*x).origin);
1329 gt_pch_n_11cgraph_node ((*x).nested);
1330 gt_pch_n_11cgraph_node ((*x).next_nested);
1331 gt_pch_n_11cgraph_node ((*x).next_needed);
1332 x = ((*x).next);
1333 }
1334 }
The key function here is gt_pch_note_object , routines gt_pch_n_* above also invoke gt_pch_note_object to cache nodes they handle.
251 int
252 gt_pch_note_object (void *obj, void *note_ptr_cookie, in ggc-common.c
253 gt_note_pointers note_ptr_fn)
254 {
255 struct ptr_data **slot;
256
257 if (obj == NULL || obj == (void *) 1)
258 return 0;
259
260 slot = (struct ptr_data **)
261 htab_find_slot_with_hash (saving_htab , obj, POINTER_HASH (obj),
262 INSERT);
263 if (*slot != NULL)
264 {
265 if ((*slot)->note_ptr_fn != note_ptr_fn
266 || (*slot)->note_ptr_cookie != note_ptr_cookie)
267 abort ();
268 return 0;
269 }
270
271 *slot = xcalloc (sizeof (struct ptr_data), 1);
272 (*slot)->obj = obj;
273 (*slot)->note_ptr_fn = note_ptr_fn;
274 (*slot)->note_ptr_cookie = note_ptr_cookie;
275 if (note_ptr_fn == gt_pch_p_S)
276 (*slot)->size = strlen (obj) + 1;
277 else
278 (*slot)->size = ggc_get_size (obj);
279 return 1;
280 }
The content of saving_htab is of type ptr_data as below. Slot obj within the structure holds the address of the cached objects.
237 struct ptr_data in ggc-common.c
238 {
239 void *obj;
240 void *note_ptr_cookie;
241 gt_note_pointers note_ptr_fn;
242 gt_handle_reorder reorder_fn;
243 size_t size;
244 void *new_addr;
245 };
Then at line 453, htab_traverse iterates saving_htab and invokes call_count for every node within. See count field of state records the number of objects encountered.
328 static int
329 call_count (void **slot, void *state_p) in ggc-common.c
330 {
331 struct ptr_data *d = (struct ptr_data *)*slot;
332 struct traversal_state *state = (struct traversal_state *)state_p;
333
334 ggc_pch_count_object (state->d, d->obj, d->size, d->note_ptr_fn == gt_pch_p_S);
335 state->count++;
336 return 1;
337 }
Under Linux platform as target, ggc-page.c will be selected as the file offers memory management. Routine ggc_pch_count_object defined in the file helps to count the number of objects with specified size; order below indicates the size of 2 with power of order (it will be the size of memory allocated under page mechanism).
1941 void
1942 ggc_pch_count_object (struct ggc_pch_data *d, void *x ATTRIBUTE_UNUSED,
1943 size_t size, bool is_string ATTRIBUTE_UNUSED)
1944 {
1945 unsigned order;
1946
1947 if (size <= 256)
1948 order = size_lookup [size];
1949 else
1950 {
1951 order = 9;
1952 while (size > OBJECT_SIZE (order))
1953 order++;
1954 }
1955
1956 d->d.totals[order]++;
1957 }
Below it will select the new start address which will be followed by the read-in procedure.
gt_pch_save (continue)
455 mmi.size = ggc_pch_total_size (state.d);
456
457 /* Try to arrange things so that no relocation is necessary, but
458 don't try very hard. On most platforms, this will always work,
459 and on the rest it's a lot of work to do better.
460 (The extra work goes in HOST_HOOKS_GT_PCH_GET_ADDRESS and
461 HOST_HOOKS_GT_PCH_USE_ADDRESS.) */
462 mmi.preferred_base = host_hooks .gt_pch_get_address (mmi.size, fileno (f));
463
464 ggc_pch_this_base (state.d, mmi.preferred_base);
465
466 state.ptrs = xmalloc (state.count * sizeof (*state.ptrs));
467 state.ptrs_i = 0;
468 htab_traverse (saving_htab , call_alloc , &state);
469 qsort (state.ptrs, state.count, sizeof (*state.ptrs), compare_ptr_data);
470
471 /* Write out all the scalar variables. */
472 for (rt = gt_pch_scalar_rtab ; *rt; rt++)
473 for (rti = *rt; rti->base != NULL; rti++)
474 if (fwrite (rti->base, rti->stride, 1, f) != 1)
475 fatal_error ("can't write PCH file: %m");
First it needs know the total size of memory requested. The size is rounded up to the closest page boundary.
1959 size_t
1960 ggc_pch_total_size (struct ggc_pch_data *d) in ggc-page.c
1961 {
1962 size_t a = 0;
1963 unsigned i;
1964
1965 for (i = 0; i < NUM_ORDERS; i++)
1966 a += ROUND_UP (d->d.totals[i] * OBJECT_SIZE (i), G .pagesize);
1967 return a;
1968 }
Further the objects are handled with increasing size order, and all objects of the same size allocated will be placed tegother; base slot of below structure then gives the beginning address for these objects.
1925 struct ggc_pch_data in ggc-page.c
1926 {
1927 struct ggc_pch_ondisk
1928 {
1929 unsigned totals[NUM_ORDERS];
1930 } d;
1931 size_t base[NUM_ORDERS];
1932 size_t written[NUM_ORDERS];
1933 };
Under paging mechanism, all bases should also be rounded up to page boundary. See that every group of different size would have its own base.
1970 void
1971 ggc_pch_this_base (struct ggc_pch_data *d, void *base) in ggc-page.c
1972 {
1973 size_t a = (size_t) base;
1974 unsigned i;
1975
1976 for (i = 0; i < NUM_ORDERS; i++)
1977 {
1978 d->base[i] = a;
1979 a += ROUND_UP (d->d.totals[i] * OBJECT_SIZE (i), G .pagesize);
1980 }
1981 }
With above information, esepcially bases for objects, cached objects within saving_htab are relocated by call_alloc . Note that argument slot is the object comes from saving_htab .
339 static int
340 call_alloc (void **slot, void *state_p) in ggc-common.c
341 {
342 struct ptr_data *d = (struct ptr_data *)*slot;
343 struct traversal_state *state = (struct traversal_state *)state_p;
344
345 d->new_addr = ggc_pch_alloc_object (state->d, d->obj, d->size, d->note_ptr_fn == gt_pch_p_S);
346 state->ptrs[state->ptrs_i++] = d;
347 return 1;
348 }
See at line 345, new_addr holds the address after relocating the object, and in function below, base slot of d always refers to the next available address for coming object, it is a rehearsal for memory allocation not real resource allocated yet.
1984 char *
1985 ggc_pch_alloc_object (struct ggc_pch_data *d, void *x ATTRIBUTE_UNUSED, in ggc-page.c
1986 size_t size, bool is_string ATTRIBUTE_UNUSED)
1987 {
1988 unsigned order;
1989 char *result;
1990
1991 if (size <= 256)
1992 order = size_lookup [size];
1993 else
1994 {
1995 order = 9;
1996 while (size > OBJECT_SIZE (order))
1997 order++;
1998 }
1999
2000 result = (char *) d->base[order];
2001 d->base[order] += OBJECT_SIZE (order);
2002 return result;
2003 }
Now in state below, its ptrs array refers to objects and with new_addr slot of every entity records the remapped address for the specified object.
gt_pch_save (continue)
477 /* Write out all the global pointers, after translation. */
478 write_pch_globals (gt_ggc_rtab , &state);
479 write_pch_globals (gt_pch_cache_rtab , &state);
See write_pch_globals writes in the new address of the objects into PCH file.
381 static void
382 write_pch_globals (const struct ggc_root_tab * const *tab, in ggc-common.c
383 struct traversal_state *state)
384 {
385 const struct ggc_root_tab *const *rt;
386 const struct ggc_root_tab *rti;
387 size_t i;
388
389 for (rt = tab; *rt; rt++)
390 for (rti = *rt; rti->base != NULL; rti++)
391 for (i = 0; i < rti->nelt; i++)
392 {
393 void *ptr = *(void **)((char *)rti->base + rti->stride * i);
394 struct ptr_data *new_ptr;
395 if (ptr == NULL || ptr == (void *)1)
396 {
397 if (fwrite (&ptr, sizeof (void *), 1, state->f)
398 != 1)
399 fatal_error ("can't write PCH file: %m");
400 }
401 else
402 {
403 new_ptr = htab_find_with_hash (saving_htab , ptr,
404 POINTER_HASH (ptr));
405 if (fwrite (&new_ptr->new_addr, sizeof (void *), 1, state->f)
406 != 1)
407 fatal_error ("can't write PCH file: %m");
408 }
409 }
410 }
After that is the part needs be mapped correctly during readning in. As long as this part is mapped correctly, the addresses recorded above are meaningful. In the rest part, except structure of mmi, the left part must be laid at page boundary (fill 0 it between and mmi), so on target OS offering system call mmap, it can be mapped directly by mmap.
gt_pch_save (continue)
481 ggc_pch_prepare_write (state.d, state.f);
482
483 /* Pad the PCH file so that the mmapped area starts on a page boundary. */
484 {
485 long o;
486 o = ftell (state.f) + sizeof (mmi);
487 if (o == -1)
488 fatal_error ("can't get position in PCH file: %m");
489 mmi.offset = page_size - o % page_size;
490 if (mmi.offset == page_size)
491 mmi.offset = 0;
492 mmi.offset += o;
493 }
494 if (fwrite (&mmi, sizeof (mmi), 1, state.f) != 1)
495 fatal_error ("can't write PCH file: %m");
496 if (mmi.offset != 0
497 && fseek (state.f, mmi.offset, SEEK_SET) != 0)
498 fatal_error ("can't write padding to PCH file: %m");
499
500 /* Actually write out the objects. */
501 for (i = 0; i < state.count; i++)
502 {
503 if (this_object_size < state.ptrs[i]->size)
504 {
505 this_object_size = state.ptrs[i]->size;
506 this_object = xrealloc (this_object, this_object_size);
507 }
508 memcpy (this_object, state.ptrs[i]->obj, state.ptrs[i]->size);
509 if (state.ptrs[i]->reorder_fn != NULL)
510 state.ptrs[i]->reorder_fn (state.ptrs[i]->obj,
511 state.ptrs[i]->note_ptr_cookie,
512 relocate_ptrs, &state);
513 state.ptrs[i]->note_ptr_fn (state.ptrs[i]->obj,
514 state.ptrs[i]->note_ptr_cookie,
515 relocate_ptrs , &state);
516 ggc_pch_write_object (state.d, state.f, state.ptrs[i]->obj,
517 state.ptrs[i]->new_addr, state.ptrs[i]->size,
518 state.ptrs[i]->note_ptr_fn == gt_pch_p_S);
519 if (state.ptrs[i]->note_ptr_fn != gt_pch_p_S)
520 memcpy (state.ptrs[i]->obj, this_object, state.ptrs[i]->size);
521 }
522 ggc_pch_finish (state.d, state.f);
523 gt_pch_fixup_stringpool ();
524
525 free (state.ptrs);
526 htab_delete (saving_htab );
527 }
FOR loop at line 501 above, then writes the content of these GC managed objects into the file. Note that their content must be updated with the new addresses. Objects hold in array ptrs were allocated by gt_pch_note_object , which have empty reorder_fn but filled note_ptr_fn . Take cgraph_nodes_queue as example, its note_ptr_fn points to gt_pch_p_11cgraph_node .
2625 void
2626 gt_pch_p_11cgraph_node (void *this_obj ATTRIBUTE_UNUSED, in ggc-desc.c
2627 void *x_p,
2628 gt_pointer_operator op ATTRIBUTE_UNUSED,
2629 void *cookie ATTRIBUTE_UNUSED)
2630 {
2631 struct cgraph_node * const x ATTRIBUTE_UNUSED = (struct cgraph_node *)x_p;
2632 if ((void *)(x) == this_obj)
2633 op (&((*x).decl), cookie);
2634 if ((void *)(x) == this_obj)
2635 op (&((*x).callees), cookie);
2636 if ((void *)(x) == this_obj)
2637 op (&((*x).callers), cookie);
2638 if ((void *)(x) == this_obj)
2639 op (&((*x).next), cookie);
2640 if ((void *)(x) == this_obj)
2641 op (&((*x).previous), cookie);
2642 if ((void *)(x) == this_obj)
2643 op (&((*x).origin), cookie);
2644 if ((void *)(x) == this_obj)
2645 op (&((*x).nested), cookie);
2646 if ((void *)(x) == this_obj)
2647 op (&((*x).next_nested), cookie);
2648 if ((void *)(x) == this_obj)
2649 op (&((*x).next_needed), cookie);
2650 }
The function invokes function referred by op to update slots holding addresses within the object accordingly. Op ususally refers to relocate_ptrs .
363 static void
364 relocate_ptrs (void *ptr_p, void *state_p) in ggc-common.c
365 {
366 void **ptr = (void **)ptr_p;
367 struct traversal_state *state ATTRIBUTE_UNUSED
368 = (struct traversal_state *)state_p;
369 struct ptr_data *result;
370
371 if (*ptr == NULL || *ptr == (void *)1)
372 return ;
373
374 result = htab_find_with_hash (saving_htab , *ptr, POINTER_HASH (*ptr));
375 if (result == NULL)
376 abort ();
377 *ptr = result->new_addr;
378 }
At line 516, ggc_pch_write_object writes the updated content of objects into the file. Then ggc_pch_finish release d in state . And ident_hash is restored by gt_pch_fixup_stringpool .
252 void
253 gt_pch_fixup_stringpool (void) in stringpool.c
254 {
255 ht_forall (saved_ident_hash , ht_copy_and_clear , ident_hash );
256 ht_destroy (saved_ident_hash );
257 saved_ident_hash = 0;
258 }
The last part of the PCH file then is the macro’s definitions within its source file and the dependence information carried by –MT or –MQ option.
364 int
365 cpp_write_pch_state (cpp_reader *r, FILE *f) in cpppch.c
366 {
367 struct macrodef_struct z;
368
369 /* Write out the list of defined identifiers. */
370 cpp_forall_identifiers (r, write_macdef , f);
371 memset (&z, 0, sizeof (z));
372 if (fwrite (&z, sizeof (z), 1, f) != 1)
373 {
374 cpp_errno (r, CPP_DL_ERROR, "while writing precompiled header");
375 return -1;
376 }
377
378 if (!r->deps)
379 r->deps = deps_init ();
380
381 if (deps_save (r->deps, f) != 0)
382 {
383 cpp_errno (r, CPP_DL_ERROR, "while writing precompiled header");
384 return -1;
385 }
386
387 return 0;
388 }
Finally, the content of a PCH file is shown as below figure.
Figure 118 : Content of PCH file