GCC-3.4.6源代码学习笔记(155)
来源:互联网 发布:淘宝新店如何找货源 编辑:程序博客网 时间:2024/05/23 13:16
5.13.2. 输出PCH文件
看过了转换相关的内容后,回到我们例子的解析后的阶段,从expand_or_defer_fn返回,我们回到cp_parser_function_definition_after_declarator,并立即返回FUNCTION_DECL,这个FUNCTION_DECL为cp_parser_function_definition_from_specifiers_and_declarator返回,然后cp_parser_init_declarator,cp_parser_simple_declaration,cp_parser_block_declaration,cp_parser_declaration,然后在cp_parser_declaration_seq_opt中EOF突破了6224行的WHILE循环,最后我们回到了解析器的入口——cp_parser_translation_unit。在这个函数中,EOF在2336行被“消化”了,接着finish_translation_unit恢复全局名字空间的绑定域。
从cp_parser_translation_unit返回,现在我们回到c_parser_file。退出它,我们就回到c_common_parse_file——对于C++,它被lang_hooks的parse_file所指向。并且看到c_parser_file是在一个WHILE循环里;如果在这个命令行中还有其他源文件,上面所看到的过程将再次重复。这意味如果我们使用形如:“g++ -o all a.cc b.cc c.cc;”的命令行,编译器将解析这3个源文件,并把结果合并到单个中间语言树。在所有的源文件被解析后,就来到finish_file。
来的此处,我们一定在全局名字空间中。2540行的检查保证了这一点。
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 parseerrors. 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 ();
下面,cpp_write_pch_deps把定义在命令行所指定的源文件中的标识符写入由pch_outfile指定的PCH文件里。而asm_file_startpos,在读入第一个源文件时,在pch_init中被设置成指向asm_out_file的末尾;而在c_common_parse_file的1242行的cpp_read_main_file中,asm_out_file被填入被包含的PCH文件的内容,并且在177行asm_file_end指向该文件的末尾。因此在178行,h.asm_size将是被包含的PCH文件的内容的大小。这些内容将被增加入这个PCH文件。
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 structc_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 canbe 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 }
在上面的175行,pch_outfile是为pch_file打开的文件句柄,它也是在cpp_save_state中写入使用的文件句柄。现在r->savedstate包含了在解析命令行指定源文件前,所出现的标识符。
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 *constss = r->savedstate;
304 unsigned char *definedstrs;
305 size_t i;
306
307 /* Collect the listof identifiers which have been seen and
308 weren't definedto 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, "whilewriting precompiled header");
333 return -1;
334 }
335 free (ss->definedstrs);
336
337 /* Free the savedstate. */
338 free (ss);
339 r->savedstate = NULL;
340 return 0;
341 }
在解析了所有在命令行中指定的源文件后,cpp_reader在其hash_table中包含了在这些文件中声明的标识符。那么函数count_defs遍历hash_table来计算还需要加入多少标识符,并通过域hashsize计算保存名字缓存的大小。注意到在中cpp_save_state调用的save_idents里NT_VOID类型的节点被忽略;但这里没有。
209 static int
210 count_defs (cpp_reader *pfileATTRIBUTE_UNUSED, cpp_hashnode *hn, void *ss_p)
211 {
212 structcpp_savedstate *const ss = (struct cpp_savedstate *)ss_p;
213
214 switch(hn->type)
215 {
216 caseNT_MACRO:
217 if (hn->flags & NODE_BUILTIN)
218 return 1;
219
220 /* else fall through. */
221
222 caseNT_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 caseNT_ASSERTION:
239 /* Not currently implemented. */
240 return 1;
241
242 default:
243 abort ();
244 }
245 }
在分配了所需的资源后,write_defs也遍历cpp_reader的hash_table来援引没有在cpp_save_state中出现的标识符。那么在上面318行的排序后,这些名字被写入这个PCH文件。
248 static int
249 write_defs (cpp_reader *pfileATTRIBUTE_UNUSED, cpp_hashnode *hn, void *ss_p)
250 {
251 structcpp_savedstate *const ss = (struct cpp_savedstate *)ss_p;
252
253 switch(hn->type)
254 {
255 caseNT_MACRO:
256 if (hn->flags & NODE_BUILTIN)
257 return 1;
258
259 /* else fall through. */
260
261 caseNT_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 caseNT_ASSERTION:
278 /* Notcurrently implemented. */
279 return 1;
280
281 default:
282 abort ();
283 }
284 }
在调用gt_pch_save的这一点上,PCH文件的内容如下图所示。看到idn就是用户在源文件中声明的标识符。
另外在编译过程中,编译器还产生许多控制数据,我们已经看到它们由GC管理。这些数据描述了程序的特性,并影响代码的生成,因此必须也保存在这个PCH文件里。
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 structtraversal_state state;
430 char *this_object = NULL;
431 size_t this_object_size = 0;
432 structmmap_info mmi;
433 size_t page_size = getpagesize();
434
435 gt_pch_save_stringpool();
那么gt_pch_save_stringpool保存了ident_hash的内容,记得ident_hash为cpp_reader的hash_table所指向。首先,这些内容被拷贝入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 }
为了缓存ident_hash的内容,spd具有如下的结构。看到spd在gt_ggc_r_gt_stringpool_h中,它进而包含在gt_ggc_rtab中。后面它也将被写入这个PCH文件,而通过gt_pch_restore_stringpool,被使用这个PCH文件的文件读入。
199 struct string_pool_dataGTY(()) 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;
其次,通过下面的函数把ident_hash的内容移入saved_ident_hash(把它缓存起来,以免对下面的过程产生影响,在退出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 }
记得在编译器中,所有GC管理的对象都由GC工具处理来产生gtype-*文件。而在gtype-c.h中,生成了几个全局数组来保存对这些对象的引用。它们是gt_ggc_rtab,gt_ggc_deletable_rtab(保存了被删除对象的哈希表),gt_ggc_cache_rtab(用于垃圾收集的目的),gt_pch_cache_rtab(与gt_ggc_cache_rtab的内容相同,但处理方法不一样—),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 theobjects 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);
PCH文件中的内容是中间形式的代码(也就是树节点的形式),我们已经看到这个中间形式中包含了大量的指针,如何在PCH文件中维护这些指针,使得在PCH文件的使用中能恢复这里的对象之间的关系?
首先,由GC管理的对象都是全局对象,而且它们构成了一个封闭的集合(也就是不会有对集合外对象的引用,也不会被集合外的对象所引用)。那么如果把这些对象的内容及地址一字不差地记录入PCH文件,然后读入时一个个恢复这些对象,理论上就能恢复PCH文件的内容。但事实上不可行,因为读入PCH文件时,内存中很可能已经有其他内容,很难保证需要的地址一定可用,而且一个个恢复对象效率也不高。
既然GC管理的是由全局对象构成的封闭集合,那么在目标系统所提供的内存管理机制的协助下,存在这种可能:把所有的对象尽可能紧凑地排列起来,并为以后的PCH读入选定一个不太可能被占用的起始地址(对于使用虚拟内存的系统,不占用的可能性很大),同时把对象内部的所有指针都更新为相应对象的新的地址(假定起始地址可用);最后把这个映射信息、更新后的对象内容写入PCH文件,就能保证后面对这个PCH文件的正确读入。这就是GCC现在的做法。
那么第一步就要收集对象中所有的指针的内容(包括对象自己的地址),这由pchw域引用的函数来完成,它把这些内容存入saving_htab。以gt_ggc_rtab中gt_ggc_r_gtype_desc_c的cgraph_nodes_queue为例,其pchw指向gt_pch_nx_cgraph_node,这个函数也是由GC工具产生。
1306 void
1307 gt_pch_nx_cgraph_node (void *x_p) in gtype-desc.c
1308 {
1309 structcgraph_node * x = (struct cgraph_node *)x_p;
1310 structcgraph_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 * constxprev = ((*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 }
这里关键的函数是gt_pch_note_object,上面的函数gt_pch_n_*也调用gt_pch_note_object来缓存它们所处理的节点。
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 = (structptr_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 }
saving_htab的内容具有下面的类型ptr_data。结构中的域obj保存了被缓存对象的地址。
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 };
那么在453行,htab_traverse遍历saving_htab并为其中的每个节点调用call_count。看到state的count域记录了所遭遇的对象的个数。
328 static int
329 call_count (void **slot, void *state_p) in ggc-common.c
330 {
331 struct ptr_data *d = (structptr_data *)*slot;
332 structtraversal_state *state = (structtraversal_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 }
在以Linux为目标平台时,ggc-page.c将被选中提供内存管理。定义在这个文件中的函数ggc_pch_count_object协助统计具有指定大小的对象的数目;下面的order表示以2为底order为指数的大小(它将是页面管理机制下所分配的内存大小)。
1941 void
1942 ggc_pch_count_object (struct ggc_pch_data *d, void *x ATTRIBUTE_UNUSED,
1943 size_t size, bool is_stringATTRIBUTE_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 }
下面将选定新的起始地址,它将为读入过程所使用。
gt_pch_save (continue)
455 mmi.size = ggc_pch_total_size(state.d);
456
457 /* Try to arrangethings so that no relocation is necessary, but
458 don't try veryhard. On most platforms, this will always work,
459 and on the restit's a lot of work to do better.
460 (The extra workgoes 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 allthe 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 PCHfile: %m");
首先需要知道所需要的整个内存的大小。这个尺寸被取整到最近的页边界。
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 }
另外对象被按尺寸已升序处理,所有具有相同分配大小的对象将被放在一起;下面结构的base域给出这些对象的起始地址。
1925 struct ggc_pch_data in ggc-page.c
1926 {
1927 structggc_pch_ondisk
1928 {
1929 unsigned totals[NUM_ORDERS];
1930 } d;
1931 size_t base[NUM_ORDERS];
1932 size_t written[NUM_ORDERS];
1933 };
在分页机制中,所有的基址都应该被取整到页边界。看到每个不同大小的对象组都有自己的基址。
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 }
以上面的信息,尤其是对象的基址,在saving_htab中被缓存的对象由call_alloc来重新安排。注意参数slot就是来自saving_htab的对象。
339 static int
340 call_alloc (void **slot, void *state_p) in ggc-common.c
341 {
342 structptr_data *d = (struct ptr_data *)*slot;
343 structtraversal_state *state = (structtraversal_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 }
在345行,new_addr保存了重新安排后对象的地址,而在下面的函数中,d的base域指向下一个对象可用的地址,它是内存分配的应该彩排,没有真正分配内存。
1984 char *
1985 ggc_pch_alloc_object (struct ggc_pch_data *d, void *xATTRIBUTE_UNUSED, in ggc-page.c
1986 size_t size, bool is_stringATTRIBUTE_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 }
现在在下面的state里,其ptrs数组指向对象,并且对于每个实体,new_addr域记录了指定对象的重新映射的地址。
gt_pch_save (continue)
477 /* Write out allthe global pointers, after translation. */
478 write_pch_globals(gt_ggc_rtab,&state);
479 write_pch_globals(gt_pch_cache_rtab,&state);
看到write_pch_globals把这些对象的新地址写入PCH文件。
381 static void
382 write_pch_globals (const struct ggc_root_tab * const *tab, in ggc-common.c
383 structtraversal_state *state)
384 {
385 const struct ggc_root_tab *const *rt;
386 const struct ggc_root_tab *rti;
387 size_ti;
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 structptr_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 PCHfile: %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 PCHfile: %m");
408 }
409 }
410 }
在这之后就是在读入时需要进行正确映射的部分,只有下面的内容得到正确映射,上面所保存的地址才有意义。接下来的部分,除去mmi的结构,余下部分必须在页边界上对齐(在它和mmi之间填0),这样在提供mmap系统调用的目标操作系统上就能直接映射这些内容。
gt_pch_save (continue)
481 ggc_pch_prepare_write (state.d, state.f);
482
483 /* Pad the PCH fileso 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 inPCH 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 toPCH file: %m");
499
500 /* Actually writeout 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 }
那么501行的FOR循环把内容更新后的GC管理对象写入文件。注意到它们的内容必须被更新为新地址。在数组ptrs中保存的对象由gt_pch_note_object分配而来,它们具有空的reorder_fn,不过填好了note_ptr_fn。以cgraph_nodes_queue为例,其note_ptr_fn指向gt_pch_p_11cgraph_node。
2625 void
2626 gt_pch_p_11cgraph_node (void *this_objATTRIBUTE_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 xATTRIBUTE_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 }
该函数调用了由op引用的函数来相应地更新对象中保存地址的域。通常op指向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 structtraversal_state *state ATTRIBUTE_UNUSED
368 = (structtraversal_state *)state_p;
369 structptr_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 }
在516行,ggc_pch_write_object把更新后的对象写入文件。然后ggc_pch_finish释放state中的d。并且ident_hash为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 }
PCH文件的最后部分是其源文件中的宏定义,及由–MT或–MQ选项所携带的依赖信息。
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 thelist 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, "whilewriting 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, "whilewriting precompiled header");
384 return -1;
385 }
386
387 return 0;
388 }
最后,一个PCH文件的内容显示如下。
图118:PCH文件内容
- GCC-3.4.6源代码学习笔记(155)
- GCC-3.4.6源代码学习笔记 (100)
- GCC-3.4.6源代码学习笔记 (101)
- GCC-3.4.6源代码学习笔记 (102)
- GCC-3.4.6源代码学习笔记 (103)
- GCC-3.4.6源代码学习笔记 (104)
- GCC-3.4.6源代码学习笔记 (105)
- GCC-3.4.6源代码学习笔记 (106)
- GCC-3.4.6源代码学习笔记(166)
- GCC-3.4.6源代码学习笔记
- GCC-3.4.6源代码学习笔记(6)
- GCC-3.4.6源代码学习笔记(1)
- GCC-3.4.6源代码学习笔记(2)
- GCC-3.4.6源代码学习笔记(3)
- GCC-3.4.6源代码学习笔记(4)
- GCC-3.4.6源代码学习笔记(5)
- GCC-3.4.6源代码学习笔记(7)
- GCC-3.4.6源代码学习笔记(8)
- Oracle 查询
- 春节愉快
- 手把手教你制作手机离线地图
- 指针的指针--------二级指针(做一下总结,免得忘了)
- 【每天读一点英文】gnuhpc注释版:The True Nobility
- GCC-3.4.6源代码学习笔记(155)
- Studying note of GCC-3.4.6 source (155)
- GCC-3.4.6源代码学习笔记(156)
- Studying note of GCC-3.4.6 source (156)
- 【每天读一点英文】gnuhpc:The World As I See It(节选)
- 分形艺术网贺岁图集很有新意的新年礼物!
- 做事
- java的几种引用类型
- sql server image type