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

    技术2024-08-17  63

    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 就是用户在源文件中声明的标识符。

    另外在编译过程中,编译器还产生许多控制数据,我们已经看到它们由 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 文件的内容显示如下。

    118 PCH 文件内容

    最新回复(0)