GCC-3.4.6源代码学习笔记(155)

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 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 ();

 

下面, 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      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    }

 

在上面的 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 *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    }

 

在解析了所有在命令行中指定的源文件后, cpp_reader 在其 hash_table 中包含了在这些文件中声明的标识符。那么函数 count_defs 遍历 hash_table 来计算还需要加入多少标识符,并通过域 hashsize 计算保存名字缓存的大小。注意到在中 cpp_save_state 调用的 save_idents NT_VOID 类型的节点被忽略;但这里没有。

 

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    }

 

在分配了所需的资源后, write_defs 也遍历 cpp_reader hash_table 来援引没有在 cpp_save_state 中出现的标识符。那么在上面 318 行的排序后,这些名字被写入这个 PCH 文件。

 

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    }

 

在调用 gt_pch_save 的这一点上, PCH 文件的内容如下图所示。看到 idn 就是用户在源文件中声明的标识符。

GCC-3.4.6源代码学习笔记(155)_第1张图片

另外在编译过程中,编译器还产生许多控制数据,我们已经看到它们由 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      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 ();

 

那么 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_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;

 

其次,通过下面的函数把 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 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);

 

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     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   }

 

这里关键的函数是 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 = (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    }

 

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 = (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    }

 

在以 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_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   }

 

下面将选定新的起始地址,它将为读入过程所使用。

 

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");

 

首先需要知道所需要的整个内存的大小。这个尺寸被取整到最近的页边界。

 

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     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   };

 

在分页机制中,所有的基址都应该被取整到页边界。看到每个不同大小的对象组都有自己的基址。

 

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      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    }

 

345 行, new_addr 保存了重新安排后对象的地址,而在下面的函数中, d base 域指向下一个对象可用的地址,它是内存分配的应该彩排,没有真正分配内存。

 

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   }

 

现在在下面的 state 里,其 ptrs 数组指向对象,并且对于每个实体, new_addr 域记录了指定对象的重新映射的地址。

 

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);

 

看到 write_pch_globals 把这些对象的新地址写入 PCH 文件。

 

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    }

 

在这之后就是在读入时需要进行正确映射的部分,只有下面的内容得到正确映射,上面所保存的地址才有意义。接下来的部分,除去 mmi 的结构,余下部分必须在页边界上对齐(在它和 mmi 之间填 0 ),这样在提供 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    }

 

那么 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_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   }

 

该函数调用了由 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      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    }

 

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 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    }

 

最后,一个 PCH 文件的内容显示如下。

GCC-3.4.6源代码学习笔记(155)_第2张图片

118 PCH 文件内容

你可能感兴趣的:(GCC-3.4.6源代码学习笔记(155))