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_DECLcp_parser_function_definition_from_specifiers_and_declarator返回,然后cp_parser_init_declaratorcp_parser_simple_declarationcp_parser_block_declarationcp_parser_declaration,然后在cp_parser_declaration_seq_optEOF突破了6224行的WHILE循环,最后我们回到了解析器的入口——cp_parser_translation_unit。在这个函数中,EOF2336行被“消化”了,接着finish_translation_unit恢复全局名字空间的绑定域。

cp_parser_translation_unit返回,现在我们回到c_parser_file。退出它,我们就回到c_common_parse_file——对于C++,它被lang_hooksparse_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_file1242行的cpp_read_main_file中,asm_out_file被填入被包含的PCH文件的内容,并且在177asm_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_identsNT_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_readerhash_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就是用户在源文件中声明的标识符。

t1

另外在编译过程中,编译器还产生许多控制数据,我们已经看到它们由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_hashcpp_readerhash_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具有如下的结构。看到spdgt_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_rtabgt_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_rtabgt_ggc_r_gtype_desc_ccgraph_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。看到statecount域记录了所遭遇的对象的个数。

 

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保存了重新安排后对象的地址,而在下面的函数中,dbase域指向下一个对象可用的地址,它是内存分配的应该彩排,没有真正分配内存。

 

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_hashgt_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文件的内容显示如下。

t2

118PCH文件内容