4.1.3.1.2.1.2. 读入中间形式的PCH内容
之前,我们已经看到一些变量在声明中使用了GTY((…)),GC程序(垃圾回收器)将解析指定的文件(在指定列表外的文件中的GTY((…))是不被解析的,它们将导致编译错误——未定义的识别符),并将结果输出到加上gt-前缀的同名文件中。而该文件才作为编译编译器时的真正输入。因此,以下所涉及的变量才是编译器真正使用的,它们来自由GC工具处理的,具有GTY((…))修饰的变量。
GC搜集GTY((…))所修饰变量的属性。那些被用作自由缓存(free buffer)的变量被搜集入gt_ggc_deletable_rtab,这里在543行,这些自由缓存被清空。
531 void
532 gt_pch_restore (FILE *f) in ggc-common.c
533 {
534 const struct ggc_root_tab *const *rt;
535 const struct ggc_root_tab *rti;
536 size_t i;
537 struct mmap_info mmi;
538 int result;
539
540 /* Delete any deletable objects. This makes ggc_pch_read much
541 faster, as it can be sure that no GCable objects remain other
542 than the ones just read in. */
543 for (rt = gt_ggc_deletable_rtab; *rt; rt++)
544 for (rti = *rt; rti->base != NULL; rti++)
545 memset (rti->base, 0, rti->stride);
546
547 /* Read in all the scalar variables. */
548 for (rt = gt_pch_scalar_rtab; *rt; rt++)
549 for (rti = *rt; rti->base != NULL; rti++)
550 if (fread (rti->base, rti->stride, 1, f) != 1)
551 fatal_error ("can't read PCH file: %m");
552
553 /* Read in all the global pointers, in 6 easy loops. */
554 for (rt = gt_ggc_rtab; *rt; rt++)
555 for (rti = *rt; rti->base != NULL; rti++)
556 for (i = 0; i < rti->nelt; i++)
557 if (fread ((char *)rti->base + rti->stride * i,
558 sizeof (void *), 1, f) != 1)
559 fatal_error ("can't read PCH file: %m");
560
561 for (rt = gt_pch_cache_rtab; *rt; rt++)
562 for (rti = *rt; rti->base != NULL; rti++)
563 for (i = 0; i < rti->nelt; i++)
564 if (fread ((char *)rti->base + rti->stride * i,
565 sizeof (void *), 1, f) != 1)
566 fatal_error ("can't read PCH file: %m");
567
568 if (fread (&mmi, sizeof (mmi), 1, f) != 1)
569 fatal_error ("can't read PCH file: %m");
570
571 result = host_hooks.gt_pch_use_address (mmi.preferred_base, mmi.size,
572 fileno (f), mmi.offset);
573 if (result < 0)
574 fatal_error ("had to relocate PCH");
575 if (result == 0)
576 {
577 if (fseek (f, mmi.offset, SEEK_SET) != 0
578 || fread (mmi.preferred_base, mmi.size, 1, f) != 1)
579 fatal_error ("can't read PCH file: %m");
580 }
581 else if (fseek (f, mmi.offset + mmi.size, SEEK_SET) != 0)
582 fatal_error ("can't read PCH file: %m");
583
584 ggc_pch_read (f, mmi.preferred_base);
585
586 gt_pch_restore_stringpool ();
587 }
以上代码按部就班地读入这个PCH文件的中间代码部分,其步骤如下。
4.1.3.1.2.1.2.1. 纯量变量的内容
GC把指定文件中由GTY((…))修饰的纯量变量(scalar variable),搜集入数组gt_pch_scalar_rtab,在上面548行,这些变量被首先读入。
137 const struct ggc_root_tab * const gt_pch_scalar_rtab[] = { in gtype-c.h
138 gt_pch_rs_gtype_desc_c,
139 gt_pch_rs_gt_alias_h,
140 gt_pch_rs_gt_dbxout_h,
141 gt_pch_rs_gt_dwarf2out_h,
142 gt_pch_rs_gt_dwarf2asm_h,
143 gt_pch_rs_gt_emit_rtl_h,
144 gt_pch_rs_gt_except_h,
145 gt_pch_rs_gt_function_h,
146 gt_pch_rs_gt_langhooks_h,
147 gt_pch_rs_gt_sdbout_h,
148 gt_pch_rs_gt_tree_h,
149 gt_pch_rs_gt_varasm_h,
150 gt_pch_rs_gt_c_decl_h,
151 NULL
152 };
这个数组的元素具有如下类型ggc_root_tab,gt_pointer_walker是一个函数指针。
67 struct ggc_root_tab { in gtype-c.h
68 void *base;
69 size_t nelt;
70 size_t stride;
71 gt_pointer_walker cb;
72 gt_pointer_walker pchw;
73 };
如上所示,从相关的文件中抽取出来,这些元素构成了相应的数组。从它们的名字中,我们可以知道它们从何而来,例如,gt_pch_rs_gt_alias_h由alias.c文件产生。
纯量变量的内容包括为GC管理的全局数组的大小。例如gt_pch_rs_gtype_desc_c具有如下定义:
4532 const struct ggc_root_tab gt_pch_rs_gtype_desc_c[] = { in gtype-desc.c
4533 { &cgraph_varpool_n_nodes, 1, sizeof (cgraph_varpool_n_nodes), NULL, NULL },
4534 { &cgraph_max_uid, 1, sizeof (cgraph_max_uid), NULL, NULL },
4535 { &cgraph_n_nodes, 1, sizeof (cgraph_n_nodes), NULL, NULL },
4536 LAST_GGC_ROOT_TAB
4537 };
其中,cgraph_varpool_n_nodes保存了cgraph_varpool_nodes的长度,而cgraph_n_node则表示cgraph_nodes的长度。这2个数组则由下面的gt_ggc_rtab引用。.
4.1.3.1.2.1.2.2. 全局数组的内容
GC管理的全局数组由gt_ggc_rtab引用。这些对象同样按文件组织并由GC的工具产生。
55 const struct ggc_root_tab * const gt_ggc_rtab[] = { in gtype-c.h
56 gt_ggc_r_gt_coverage_h,
57 gt_ggc_r_gtype_desc_c,
58 gt_ggc_r_gt_alias_h,
59 gt_ggc_r_gt_cselib_h,
60 gt_ggc_r_gt_cgraph_h,
61 gt_ggc_r_gt_dbxout_h,
62 gt_ggc_r_gt_dwarf2out_h,
63 gt_ggc_r_gt_dwarf2asm_h,
64 gt_ggc_r_gt_dojump_h,
65 gt_ggc_r_gt_emit_rtl_h,
66 gt_ggc_r_gt_except_h,
67 gt_ggc_r_gt_explow_h,
68 gt_ggc_r_gt_expr_h,
69 gt_ggc_r_gt_fold_const_h,
70 gt_ggc_r_gt_function_h,
71 gt_ggc_r_gt_gcse_h,
72 gt_ggc_r_gt_integrate_h,
73 gt_ggc_r_gt_optabs_h,
74 gt_ggc_r_gt_ra_build_h,
75 gt_ggc_r_gt_regclass_h,
76 gt_ggc_r_gt_reg_stack_h,
77 gt_ggc_r_gt_cfglayout_h,
78 gt_ggc_r_gt_langhooks_h,
79 gt_ggc_r_gt_sdbout_h,
80 gt_ggc_r_gt_stor_layout_h,
81 gt_ggc_r_gt_stringpool_h,
82 gt_ggc_r_gt_tree_h,
83 gt_ggc_r_gt_varasm_h,
84 gt_ggc_r_gt_i386_h,
85 gt_ggc_r_gt_c_parse_h,
86 gt_ggc_r_gt_c_decl_h,
87 gt_ggc_r_gt_c_common_h,
88 gt_ggc_r_gt_c_pragma_h,
89 NULL
90 };
这个数组元素的一个例子显示如下。在下面的结构体中,gt_ggc_mx_rtx_def引用了访问GC所管理的数组元素内容的方法,而gt_pch_nx_rtx_def引用了把整个结构存入GC的saving_htab的方法。saving_htab随后被写入PCH文件。
25 const struct ggc_root_tab gt_ggc_r_gt_coverage_h[] = { in gt-coverage.h
26 {
27 &ctr_labels[0],
28 1 * (GCOV_COUNTERS),
29 sizeof (ctr_labels[0]),
30 >_ggc_mx_rtx_def,
31 >_pch_nx_rtx_def
32 },
33 LAST_GGC_ROOT_TAB
34 };
4.1.3.1.2.1.2.3. 哈希表
同样在GCC中,某些哈希表也是由GC管理。以下被gt_pch_cache_rtab引用到的哈希表包括:const_double_htab,reg_attrs_htab,mem_attrs_htab,const_int_htab(出现在gt_pch_rc_gt_emit_rtl_h中,而这个对象保存,到目前为止在RTL生成阶段,所产生的常量对象);size_htab(出现在gt_pch_rc_gt_fold_const_h中,这个对象保存到目前为止编译器所要求的INTEGER_CST);type_hash_table(出现在gt_pch_rc_gt_tree_h中,这个对象是声明类型的哈希表)。
118 const struct ggc_root_tab * const gt_pch_cache_rtab[] = { in gtype-c.h
119 gt_pch_rc_gt_emit_rtl_h,
120 gt_pch_rc_gt_fold_const_h,
121 gt_pch_rc_gt_tree_h,
122 NULL
123 };
4.1.3.1.2.1.2.4. 映射文件内容至虚存
除了上面这些在编译过程使用到的编译器内部变量,PCH文件本身的内容尚未读入。这个内容,在产生这个PCH文件时,是一棵中间形式的树。如何处理其中的指针是个大问题。在当前版本的GCC中,在写入PCH文件,是直接把这棵树映射入文件,并在如下mmap_info结构中记录有关映射的信息(关于映射文件,参考Linux有关内容,在此不详述)。
414 struct mmap_info in ggc-common.c
415 {
416 size_t offset;
417 size_t size;
418 void *preferred_base;
419 };
那么在读入PCH文件这部分内容时,相应地要将其映射入同一地址。这样做的原因很显然,因为PCH文件中同时也保存了其中的识别符,要在恢复的树中正确地访问到这些识别符,必须要严格地匹配这2边的地址。在gt_pch_restore的571行,gt_pch_use_address执行这个映射,在Linux平台上,这个钩子引用以下函数。
170 static int
171 linux_gt_pch_use_address (void *base, size_t size, int fd, size_t offset) in host-linux.c
172 {
173 void *addr;
174
175 /* We're called with size == 0 if we're not planning to load a PCH
176 file at all. This allows the hook to free any static space that
177 we might have allocated at link time. */
178 if (size == 0)
179 return -1;
180
181 /* Try to map the file with MAP_PRIVATE. */
182 addr = mmap (base, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, offset);
183
184 if (addr == base)
185 return 1;
186
187 if (addr != (void *) MAP_FAILED)
188 munmap (addr, size);
189
190 /* Try to make an anonymous private mmap at the desired location. */
191 addr = mmap (base, size, PROT_READ | PROT_WRITE,
192 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
193
194 if (addr != base)
195 {
196 if (addr != (void *) MAP_FAILED)
197 munmap (addr, size);
198 return -1;
199 }
200
201 if (lseek (fd, offset, SEEK_SET) == (off_t)-1)
202 return -1;
203
204 while (size)
205 {
206 ssize_t nbytes;
207
208 nbytes = read (fd, base, MIN (size, SSIZE_MAX));
209 if (nbytes <= 0)
210 return -1;
211 base = (char *) base + nbytes;
212 size -= nbytes;
213 }
214
215 return 1;
216 }
上面的mmap是Linux的系统调用,其细节参考Linux的手册。注意到如果文件不能映射至内存地址base,读入操作即失败。
回到gt_pch_restore,通过调用linux_gt_pch_use_address,在指定地址和大小的虚存已被保留,但物理内存尚未分配,现在584行,ggc_pch_read获取物理内存并读入文件内容。我们在以后看GC如何管理内存。
4.1.3.1.2.1.2.4.1. 恢复识别符
上面恢复了树形式的PCH文件内容,但树中的对象多为指针。其中所引用到的识别符虽然已经读入,但尚不能访问。它们必须出现在ident_hash中,编译器才能意识到其存在。
260 void
261 gt_pch_restore_stringpool (void) in stringpool.c
262 {
263 unsigned int i;
264
265 ident_hash->nslots = spd->nslots;
266 ident_hash->nelements = spd->nelements;
267 ident_hash->entries = xrealloc (ident_hash->entries,
268 sizeof (hashnode) * spd->nslots);
269 for (i = 0; i < spd->nslots; i++)
270 if (spd->entries[i] != NULL)
271 ident_hash->entries[i] = GCC_IDENT_TO_HT_IDENT (spd->entries[i]);
272 else
273 ident_hash->entries[i] = NULL;
274
275 spd = NULL;
276 }
其中spd在gt_ggc_r_gt_stringpool_h之中,gt_ggc_r_gt_stringpool_h则包含在gt_ggc_rtab内。
4.1.3.1.2.1.5. 恢复保存的宏及#pragma
预处理头文件对宏的使用有如下限制:在预处理头文件前定义的宏,要么与预处理头文件产生时的定义相同,要么不影响这个预处理头文件(这通常意味着这个宏不出现在该头文件中)。
这里首先恢复这些在预处理头文件前的宏,而PCH文件中的宏定义在后面才读入,以符合其出现的次序。
605 int
606 cpp_read_state (cpp_reader *r, const char *name, FILE *f, in cpppch.c
607 struct save_macro_data *data)
608 {
609 struct macrodef_struct m;
610 size_t defnlen = 256;
611 unsigned char *defn = xmalloc (defnlen);
612 struct lexer_state old_state;
613 struct save_macro_item *d;
614 size_t i, mac_count;
615 int saved_line = r->line;
616
617 /* Restore spec_nodes, which will be full of references to the old
618 hashtable entries and so will now be invalid. */
619 {
620 struct spec_nodes *s = &r->spec_nodes;
621 s->n_defined = cpp_lookup (r, DSC("defined"));
622 s->n_true = cpp_lookup (r, DSC("true"));
623 s->n_false = cpp_lookup (r, DSC("false"));
624 s->n__VA_ARGS__ = cpp_lookup (r, DSC("__VA_ARGS__"));
625 }
626
627 /* Run through the carefully-saved macros, insert them. */
628 d = data->macros;
629 mac_count = data->count;
630 while (d)
631 {
632 struct save_macro_item *nextd;
633 for (i = 0; i < mac_count; i++)
634 {
635 cpp_hashnode *h;
636
637 h = cpp_lookup (r, HT_STR (HT_NODE (&d->macs[i])),
638 HT_LEN (HT_NODE (&d->macs[i])));
639 h->type = d->macs[i].type;
640 h->flags = d->macs[i].flags;
641 h->value = d->macs[i].value;
642 free ((void *)HT_STR (HT_NODE (&d->macs[i])));
643 }
644 nextd = d->next;
645 free (d);
646 d = nextd;
647 mac_count = ARRAY_SIZE (d->macs);
648 }
649
650 _cpp_restore_pragma_names (r, data->saved_pragmas);
651
652 free (data);
类似的同样有#pragma指示。
1124 void
1125 _cpp_restore_pragma_names (cpp_reader *pfile, char **saved) in cpplib.c
1126 {
1127 (void) restore_registered_pragmas (pfile, pfile->pragmas, saved);
1128 free (saved);
1129 }
1107 static char **
1108 restore_registered_pragmas (cpp_reader *pfile, struct pragma_entry *pe,
1109 char **sd)
1110 {
1111 for (; pe != NULL; pe = pe->next)
1112 {
1113 if (pe->is_nspace)
1114 sd = restore_registered_pragmas (pfile, pe->u.space, sd);
1115 pe->pragma = cpp_lookup (pfile, U *sd, strlen (*sd));
1116 free (*sd);
1117 sd++;
1118 }
1119 return sd;
1120 }