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

    技术2022-06-30  45

    5.12.5.2.2.2.1.3.11.        完成派生类 RECORD_TYPE 完成 vtable

    对于我们的模板具现的例子,它不包含虚函数及涉及虚拟基类( C++ 不允许模板拥有虚函数,但可以使用虚拟基类)。不过,多态的概念十分重要,在这里值得我们深入研究。因此下面的讨论以前一节的例 1 及例 2 展开。

    5159 行, CLASSTYPE_VFIELDS 是该类所有的包含的 vtable (主要及次要)的域。其 TREE_VALUE 是引入这个 vtable 域的类。对于从主要基类继承来的,或者由该类引入的 vtable 域,其 TREE_PURPOSE (由 VF_BINFO_VALUE 访问)是 NULL 。对于其他的 vtable 域(来自非主要基类),其 TREE_PURPOSE 是引入该 vtable 的基类的 BINFO 。对于这样的 vtable 域,设置了 TREE_ADDRESSABLE 标记。

     

    finish_struct_1 (continue)

     

    5129     /* Complete the rtl for any static member objects of the type we're

    5130       working on.  */

    5131     for (x = TYPE_FIELDS (t); x; x = TREE_CHAIN (x))

    5132       if (TREE_CODE (x) == VAR_DECL && TREE_STATIC (x)

    5133          && same_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (x)), t))

    5134         DECL_MODE (x) = TYPE_MODE (t);

    5135  

    5136     /* Done with FIELDS...now decide whether to sort these for

    5137       faster lookups later.

    5138  

    5139       We use a small number because most searches fail (succeeding

    5140       ultimately as the search bores through the inheritance

    5141       hierarchy), and we want this failure to occur quickly.  */

    5142  

    5143     n_fields = count_fields (TYPE_FIELDS (t));

    5144     if (n_fields > 7)

    5145     {

    5146       struct sorted_fields_type *field_vec = ggc_alloc (sizeof (struct sorted_fields_type)

    5147                                                 + n_fields * sizeof (tree));

    5148       field_vec->len = n_fields;

    5149       add_fields_to_record_type (TYPE_FIELDS (t), field_vec, 0);

    5150       qsort (field_vec->elts, n_fields, sizeof (tree),

    5151             field_decl_cmp);

    5152       if (! DECL_LANG_SPECIFIC (TYPE_MAIN_DECL (t)))

    5153         retrofit_lang_decl (TYPE_MAIN_DECL (t));

    5154       DECL_SORTED_FIELDS (TYPE_MAIN_DECL (t)) = field_vec;

    5155     }

    5156  

    5157     if (TYPE_HAS_CONSTRUCTOR (t))

    5158     {

    5159       tree vfields = CLASSTYPE_VFIELDS (t);

    5160  

    5161       for (vfields = CLASSTYPE_VFIELDS (t);

    5162           vfields; vfields = TREE_CHAIN (vfields))

    5163         /* Mark the fact that constructor for T could affect anybody

    5164           inheriting from T who wants to initialize vtables for

    5165           VFIELDS's type.  */

    5166         if (VF_BINFO_VALUE (vfields))

    5167           TREE_ADDRESSABLE (vfields) = 1;

    5168     }

    5169  

    5170     /* Make the rtl for any new vtables we have created, and unmark

    5171       the base types we marked.  */

    5172     finish_vtbls (t);

     

    对于包含 vtable 的类,编译器将扩展这个表来包括对于多态来说是必要的额外的项 ,并给出这个表的初始化方法。这是一个非常复杂的过程,并包含了精巧的设想。我们再次使用前一节中的例子。首先,让我们看一下 GCC 给出的真实输出。

    Example 1:

    Vtable for A

    A::_ZTV1A: 3u entries

    0     (int (*)(...))0              // vcall offset

    4     (int (*)(...))(& _ZTI1A)

    8     A ::f       // slot for A::f

    Vtable for B1

    B1::_ZTV2B1: 6u entries

    0     0u        // vbase offset

    4     0u        // vcall offset

    8     (int (*)(...))0

    12    (int (*)(...))(& _ZTI2B1)

    16    B1::_ZTcv0_n12_v0_n16_N2B11fEv         // slot for B1::A::f

    20    B1::f     // slot for B1::f

    Vtable for B2

    B2::_ZTV2B2: 6u entries

    0     0u        // vbase offset

    4     0u        // vcall offset

    8     (int (*)(...))0

    12    (int (*)(...))(& _ZTI2B2)

    16    B2::_ZTcv0_n12_v0_n16_N2B21fEv         // slot for B2::A::f

    20    B2::f     // slot for B2::f

    Vtable for C

    C::_ZTV1C: 12u entries

    0     0u        // vbase offset

    4     0u        // vcall offset

    8     (int (*)(...))0

    12    (int (*)(...))(& _ZTI1C)

    16    C ::_ZTcv0_n12_v0_n16_N1C1fEv     // slot for C::B1::A::f

    20    C ::f       // slot for C::f

    24    -4u

    28    -4u

    32    (int (*)(...))-0x000000004

    36    (int (*)(...))(& _ZTI1C)

    40    C ::_ZTcvn4_n12_v0_n16_N1C1fEv   // slot for C::B2::A::f

    44    C ::_ZTchn4_h4_N1C1fEv   // slot for C::B2::f

    在输出中“ B1::_ZTV2B1: 6u entries ”中,前缀“ _Z ”是 GCC 使用的普通前缀,而前缀“ TV ”则用于 vtable ,“ 2 ”是类名字的大小,而“ B1 ”则是类的名字,那么“ 6u ”显示了这个 vtable 的大小。

    而在“ B1::_ZTcv0_n12_v0_n16_N2B11fEv ”中,前缀“ “_ZTc ”用于 this 指针调整或 / 及结果指针调整 thunk ;“ v0_n12_ ”是 this 指针调整的信息,它遵循格式“ < 固定偏移量数( fixed offset number >_< 虚拟基类偏移数( virtual offset number >_ ”,而“ n12 ”代表“ -12 ”,其中“ n ”表示负数(这个值加到 vptr 的地址上,来得到包含真正调整值的 vtable 的项;而“ v0_n16_ ”是结果指针调整的信息,跟着的“ N2B11fE ”中,“ N…E ”意味着出现了嵌套名,而“ 2B11f ”就是嵌套名“ B1::f ”;接着,最后部分“ v ”表示该函数具有参数 void (没有参数)。

    接着的“ C::_ZTchn4_h4_N1C1fEv ”是一个协变( covariant thunk ,“ hn4_ ”具有形式“ h<fixed offset number>_ ”,而“ h4_ ”是嵌套的 thunk

    那么在“ _ZTI2B1 ”中,前缀“ TI ”代表 type-info ;在“ 2B1 ”中,“ 2 ”是“ B1 ”的长度,而“ B1 ”是该 type-info 对应的类。

    现在想一下为什么需要这样的安排的?考虑表达式“ p->func(); p 是一个指向派生类的基类指针,而 func 是在基类及派生类中都有定义的虚函数。作为成员查找的结果,应该找出在基类中定义的 func ,因为它在由 p 所引用的基类中查找,而不是在派生类中。不过,在运行时得到执行的函数应该是在派生类中定义的那个。虽然在运行时我们可以通过 vtable 使用定义在派生类中的虚函数,但是在调用时刻,在其实参列表中,那个隐含的“ this ”指针参数及返回值是为基类中的 func 所裁剪的,它不是我们所期望的派生类中的虚函数,并将产生不正确的代码,从而导致意想不到的运行时结果。因此 thunk 在此处将执行必须的调整,来为真正执行的虚函数设立正确的环境。

    下面的代码揭示了上面的 vtable 的构建,其中我们关注类 C 的处理。

     

    6762   static void

    6763   finish_vtbls (tree t)                                                                                   in class.c

    6764   {

    6765     tree list;

    6766     tree vbase;

    6767  

    6768     /* We lay out the primary and secondary vtables in one contiguous

    6769       vtable. The primary vtable is first, followed by the non-virtual

    6770       secondary vtables in inheritance graph order.  */

    6771     list = build_tree_list (TYPE_BINFO_VTABLE (t), NULL_TREE);

    6772     accumulate_vtbl_inits (TYPE_BINFO (t), TYPE_BINFO (t),

    6773                        TYPE_BINFO (t), t, list);

    6774    

    6775     /* Then come the virtual bases, also in inheritance graph order.  */

    6776     for (vbase = TYPE_BINFO (t); vbase; vbase = TREE_CHAIN (vbase))

    6777     {

    6778       if (!TREE_VIA_VIRTUAL (vbase))

    6779         continue ;

    6780       accumulate_vtbl_inits (vbase, vbase, TYPE_BINFO (t), t, list);

    6781     }

    6782  

    6783     if (TYPE_BINFO_VTABLE (t))

    6784       initialize_vtable (TYPE_BINFO (t), TREE_VALUE (list));

    6785   }

     

    如果是对类 C 的处理 那么上面的参数 t 就是 C RECORD_TYPE 节点;而 TYPE_BINFO_VTABLE(t) 则是 C vtable 对象。 6771 行, list 是一个 tree_list ,其 TREE_PURPOSE 域带的就是这个 vtable 对象,而 TREE_VALUE 域暂时是空的(下面的 dfs_accumulate_vtbl_inits 将填充它)。在下面的函数中,在 7167 行的 ctor_vtbl_p 被用于控制 vtable 组构造函数的创建(用在构建 VTT 的过程中),在我们的例子中,在这里,因为 rtti_binfo 是从 TYPE_BINFO (t ) 得到的, ctor_vtbl_p 0

    注意 accumulate_vtbl_inits 调用的次序,它确保被虚拟基类被最后处理。

     

    7159   static void

    7160   accumulate_vtbl_inits (tree binfo,                                                              in class.c

    7161                      tree orig_binfo,

    7162                      tree rtti_binfo,

    7163                      tree t,

    7164                      tree inits)

    7165   {

    7166     int i;

    7167     int ctor_vtbl_p = !same_type_p (BINFO_TYPE (rtti_binfo), t);

    7168  

    7169     my_friendly_assert (same_type_p (BINFO_TYPE (binfo),

    7170                      BINFO_TYPE (orig_binfo)),

    7171                      20000517);

    7172  

    7173     /* If it doesn't have a vptr, we don't do anything.  */

    7174     if (!TYPE_CONTAINS_VPTR_P (BINFO_TYPE (binfo)))

    7175       return ;

    7176    

    7177     /* If we're building a construction vtable, we're not interested in

    7178       subobjects that don't require construction vtables.  */

    7179     if (ctor_vtbl_p

    7180         && !TYPE_USES_VIRTUAL_BASECLASSES (BINFO_TYPE (binfo))

    7181         && !binfo_via_virtual (orig_binfo, BINFO_TYPE (rtti_binfo)))

    7182       return ;

    7183  

    7184     /* Build the initializers for the BINFO-in-T vtable.  */

    7185     TREE_VALUE (inits)

    7186       = chainon (TREE_VALUE (inits),

    7187                dfs_accumulate_vtbl_inits (binfo, orig_binfo,

    7188                                       rtti_binfo, t, inits));

    7189                    

    7190     /* Walk the BINFO and its bases. We walk in preorder so that as we

    7191       initialize each vtable we can figure out at what offset the

    7192       secondary vtable lies from the primary vtable. We can't use

    7193       dfs_walk here because we need to iterate through bases of BINFO

    7194       and RTTI_BINFO simultaneously.  */

    7195     for (i = 0; i < BINFO_N_BASETYPES (binfo); ++i)

    7196     {

    7197       tree base_binfo = BINFO_BASETYPE (binfo, i);

    7198        

    7199       /* Skip virtual bases.  */

    7200       if (TREE_VIA_VIRTUAL (base_binfo))

    7201         continue ;

    7202       accumulate_vtbl_inits (base_binfo,

    7203                          BINFO_BASETYPE (orig_binfo, i),

    7204                          rtti_binfo, t,

    7205                          inits);

    7206     }

    7207   }

     

    binfo orig_binfo 必须是来自同一个类型的 binfo 但它们不需要在布局方面也是相同的 (虽然在 这里它们是严格相同的 。在 7169 行的断言检查这个先决条件。

    对于类 C 下面 ctor_vtbl_p false 。如果断言 BINFO_NEW_VTABLE_MARKED 失败 它表明该(基)类没有准备 vtable 。记得在上面由 dfs_modify_vtables 进行的派生树遍历中 非虚拟的主要基类被跳过并且没有准备 vtables 因此 对于这些基类,断言 BINFO_NEW_VTABLE_MARKED 失败,从下面 7274 行返回。

    下面重新显示前一节的例子:

    class A { virtual A* f (); };

    class B1 : virtual public A { virtual B1* f (); };

    class B2 : virtual public A { virtual B2* f (); };

    class C: public B1, public B2 { virtual C* f (); };

    处理的次序将是 C B1 dfs_accumulate_vtbl_inits 跳过,然后 B2 ,接着 A

     

    7212   static tree

    7213   dfs_accumulate_vtbl_inits (tree binfo,                                                               in class.c

    7214                         tree orig_binfo,

    7215                         tree rtti_binfo,

    7216                         tree t,

    7217                         tree l)

    7218   {

    7219     tree inits = NULL_TREE;

    7220     tree vtbl = NULL_TREE;

    7221     int ctor_vtbl_p = !same_type_p (BINFO_TYPE (rtti_binfo), t);

    7222  

    7223     if (ctor_vtbl_p

    7224         && TREE_VIA_VIRTUAL (orig_binfo) && BINFO_PRIMARY_P (orig_binfo))

    7225     {

              …

    7272     }

    7273     else if (!BINFO_NEW_VTABLE_MARKED (orig_binfo))

    7274       return inits;

    7275  

    7276     if (!vtbl)

    7277     {

    7278       tree index;

    7279       int non_fn_entries;

    7280  

    7281       /* Compute the initializer for this vtable.  */

    7282       inits = build_vtbl_initializer (binfo, orig_binfo, t, rtti_binfo,

    7283                               &non_fn_entries);

     

    C vtable 对象与我们平时声明的对象差不多,都需要初始化。这个初始值由 build_vtbl_initializer 准备。下面的参数 non_fn_entries_p 用于记录 vtable 中额外的不用于虚函数的项。

     

    7338   static tree

    7339   build_vtbl_initializer (tree binfo,                                                                in class.c

    7340                      tree orig_binfo,

    7341                     tree t,

    7342                     tree rtti_binfo,

    7343                     int* non_fn_entries_p)

    7344   {

    7345     tree v, b;

    7346     tree vfun_inits;

    7347     tree vbase;

    7348     vtbl_init_data vid;

    7349  

    7350     /* Initialize VID.  */

    7351     memset (&vid, 0, sizeof (vid));

    7352     vid.binfo = binfo;

    7353     vid.derived = t;

    7354     vid.rtti_binfo = rtti_binfo;

    7355     vid.last_init = &vid.inits;

    7356     vid.primary_vtbl_p = (binfo == TYPE_BINFO (t));

    7357     vid.ctor_vtbl_p = !same_type_p (BINFO_TYPE (rtti_binfo), t);

    7358     vid.generate_vcall_entries = true;

    7359     /* The first vbase or vcall offset is at index -3 in the vtable.  */

    7360     vid.index = ssize_int (-3 * TARGET_VTABLE_DATA_ENTRY_DISTANCE);

    7361  

    7362     /* Add entries to the vtable for RTTI.  */

    7363     build_rtti_vtbl_entries (binfo, &vid);

    7364  

    7365     /* Create an array for keeping track of the functions we've

    7366       processed. When we see multiple functions with the same

    7367       signature, we share the vcall offsets.  */

    7368     VARRAY_TREE_INIT (vid.fns, 32, "fns");

    7369     /* Add the vcall and vbase offset entries.  */

    7370     build_vcall_and_vbase_vtbl_entries (binfo, &vid);

    7371     /* Clear BINFO_VTABLE_PATH_MARKED; it's set by

    7372       build_vbase_offset_vtbl_entries.  */

    7373     for (vbase = CLASSTYPE_VBASECLASSES (t);

    7374         vbase;

    7375         vbase = TREE_CHAIN (vbase))

    7376       BINFO_VTABLE_PATH_MARKED (TREE_VALUE (vbase)) = 0;

     

    上面类型 vtbl_init_data_s 的定义如下。

     

    65      typedef struct vtbl_init_data_s                                                                   in class.c

    66      {

    67        /* The base for which we're building initializers.  */

    68        tree binfo;

    69        /* The type of the most-derived type.  */

    70        tree derived;

    71        /* The binfo for the dynamic type. This will be TYPE_BINFO (derived),

    72          unless ctor_vtbl_p is true.  */

    73        tree rtti_binfo;

    74        /* The negative-index vtable initializers built up so far. These

    75          are in order from least negative index to most negative index.  */

    76        tree inits;

    77        /* The last (i.e., most negative) entry in INITS.  */

    78        tree* last_init;

    79        /* The binfo for the virtual base for which we're building

    80          vcall offset initializers.  */

    81        tree vbase;

    82        /* The functions in vbase for which we have already provided vcall

    83          offsets.  */

    84        varray_type fns;

    85        /* The vtable index of the next vcall or vbase offset.  */

    86        tree index;

    87        /* Nonzero if we are building the initializer for the primary

    88          vtable.  */

    89        int primary_vtbl_p;

    90        /* Nonzero if we are building the initializer for a construction

    91          vtable.  */

    92        int ctor_vtbl_p;

    93        /* True when adding vcall offset entries to the vtable. False when

    94          merely computing the indices.  */

    95        bool generate_vcall_entries;

    96      } vtbl_init_data ;

     

    7355 行, last_init 总是指向 inits 的最后一个节点。它将指向 vtable 初始值中的为负数的索引(这部分在程序员可见的部分之前,它对于程序员不可见)。

     

    7862   static void

    7863   build_rtti_vtbl_entries (tree binfo, vtbl_init_data * vid)                                  in class.c

    7864   {

    7865     tree b;

    7866     tree t;

    7867     tree basetype;

    7868     tree offset;

    7869     tree decl;

    7870     tree init;

    7871  

    7872     basetype = BINFO_TYPE (binfo);

    7873     t = BINFO_TYPE (vid->rtti_binfo);

    7874  

    7875     /* To find the complete object, we will first convert to our most

    7876       primary base, and then add the offset in the vtbl to that value.  */

    7877     b = binfo;

    7878     while (CLASSTYPE_HAS_PRIMARY_BASE_P (BINFO_TYPE (b))

    7879           && !BINFO_LOST_PRIMARY_P (b))

    7880     {

    7881       tree primary_base;

    7882  

    7883       primary_base = get_primary_binfo (b);

    7884       my_friendly_assert (BINFO_PRIMARY_BASE_OF (primary_base) == b, 20010127);

    7885       b = primary_base;

    7886     }

    7887     offset = size_diffop (BINFO_OFFSET (vid->rtti_binfo), BINFO_OFFSET (b));

    7888  

    7889     /* The second entry is the address of the typeinfo object.  */

    7890     if (flag_rtti )

    7891       decl = build_address (get_tinfo_decl (t));

    7892     else

    7893       decl = integer_zero_node;

    7894    

    7895     /* Convert the declaration to a type that can be stored in the

    7896       vtable.  */

    7897     init = build_nop (vfunc_ptr_type_node , decl);

    7898     *vid->last_init = build_tree_list (NULL_TREE, init);

    7899     vid->last_init = &TREE_CHAIN (*vid->last_init);

    7900  

    7901     /* Add the offset-to-top entry. It comes earlier in the vtable that

    7902       the the typeinfo entry. Convert the offset to look like a

    7903       function pointer, so that we can put it in the vtable.  */

    7904     init = build_nop (vfunc_ptr_type_node , offset);

    7905     *vid->last_init = build_tree_list (NULL_TREE, init);

    7906     vid->last_init = &TREE_CHAIN (*vid->last_init);

    7907   }

     

    vtable 的索引为 -1 的项中放着该类型的 tinfo (类型信息)对象。所有支持 tinfo 所必须的项在 init_rtti_processing 中初始化。这个 tinfo 将被声明为类型的静态数据成员。

    build_rtti_vtbl_entries 中,从由 binfo 相关的类型开始,沿着其主要基类路径深入,找出最底层的基类(注意到如果我们遇到了满足 BINFO_LOST_PRIMARY_P 的基类,我们正在深入非主要基类的那部分路径,必须停止)。从这个类到其最基本的主要基类间的偏移,将被记录在该类 vtable 的索引为 -2 的项中。在这里我们的例子中,这个偏移是从 C A

    返回 build_vtbl_initializer ,下面的函数被调用来处理虚拟基类对应的类型或包含虚拟基类的类型。

     

    7512   static void

    7513   build_vcall_and_vbase_vtbl_entries (tree binfo, vtbl_init_data * vid)               in class.c

    7514   {

    7515     tree b;

    7516  

    7517     /* If this is a derived class, we must first create entries

    7518       corresponding to the primary base class.  */

    7519     b = get_primary_binfo (binfo);

    7520     if (b)

    7521       build_vcall_and_vbase_vtbl_entries (b, vid);

    7522  

    7523     /* Add the vbase entries for this base.  */

    7524     build_vbase_offset_vtbl_entries (binfo, vid);

    7525     /* Add the vcall entries for this base.  */

    7526     build_vcall_offset_vtbl_entries (binfo, vid);

    7527   }

     

    注意到 build_vcall_and_vbase_vtbl_entries 自底向上执行,从 binfo 对应类型的最基本主要基类开始。在我们的例子中, build_vcall_and_vbase_vtbl_entries 直到遇到虚拟基类 A ,才不进入递归。因为基类 A 不包含虚拟基类, build_vbase_offset_vtbl_entries 不做任何事。而现在,我们在以下的调用栈中:

    build_vcall_and_vbase_vtbl_entries:       对应 C

            build_vcall_and_vbase_vtbl_entries:     对应 B1

                   build_vcall_and_vbase_vtbl_entries:     对应 A

                          build_vcall_offset_vtbl_entries:     对应 A

     

    7632   static void

    7633   build_vcall_offset_vtbl_entries (tree binfo, vtbl_init_data * vid)               in class.c

    7634   {

    7635     /* We only need these entries if this base is a virtual base. We

    7636       compute the indices -- but do not add to the vtable -- when

    7637       building the main vtable for a class.  */

    7638     if (TREE_VIA_VIRTUAL (binfo) || binfo == TYPE_BINFO (vid->derived))

    7639     {

    7640       /* We need a vcall offset for each of the virtual functions in this

    7641         vtable. For example:

    7642  

    7643           class A { virtual void f (); };

    7644          class B1 : virtual public A { virtual void f (); };

    7645          class B2 : virtual public A { virtual void f (); };

    7646          class C: public B1, public B2 { virtual void f (); };

    7647  

    7648          A C object has a primary base of B1, which has a primary base of A. A

    7649          C also has a secondary base of B2, which no longer has a primary base

    7650          of A. So the B2-in-C construction vtable needs a secondary vtable for

    7651          A, which will adjust the A* to a B2* to call f. We have no way of

    7652          knowing what (or even whether) this offset will be when we define B2,

    7653          so we store this "vcall offset" in the A sub-vtable and look it up in

    7654          a "virtual thunk" for B2::f.

    7655  

    7656           We need entries for all the functions in our primary vtable and

    7657          i n our non-virtual bases' secondary vtables.  */

    7658       vid->vbase = binfo;

    7659       /* If we are just computing the vcall indices -- but do not need

    7660          the actual entries -- not that.  */

    7661       if (!TREE_VIA_VIRTUAL (binfo))

    7662         vid->generate_vcall_entries = false;

    7663       /* Now, walk through the non-virtual bases, adding vcall offsets.  */

    7664       add_vcall_offset_vtbl_entries_r (binfo, vid);

    7665     }

    7666   }

     

    对于在 add_vcall_offset_vtbl_entries_r 中合格的候选者,在上面的 7658 行, vid vbase 域被设置为对应的 binfo ,这个 binfo 是我们正在构建 vcall 偏移的虚拟基类。在下面 7680 行的条件,滤除不是正在处理中的虚拟基类;而上面 7638 行条件,只放入虚拟基类及派生程度最高的类。

     

    7670   static void

    7671   add_vcall_offset_vtbl_entries_r (tree binfo, vtbl_init_data * vid)                    in class.c

    7672   {

    7673     int i;

    7674     tree primary_binfo;

    7675  

    7676     /* Don't walk into virtual bases -- except, of course, for the

    7677       virtual base for which we are building vcall offsets. Any

    7678       primary virtual base will have already had its offsets generated

    7679       through the recursion in build_vcall_and_vbase_vtbl_entries.  */

    7680     if (TREE_VIA_VIRTUAL (binfo) && vid->vbase != binfo)

    7681       return ;

    7682    

    7683     /* If BINFO has a primary base, process it first.  */

    7684     primary_binfo = get_primary_binfo (binfo);

    7685     if (primary_binfo)

    7686       add_vcall_offset_vtbl_entries_r (primary_binfo, vid);

    7687  

    7688     /* Add BINFO itself to the list.  */

    7689     add_vcall_offset_vtbl_entries_1 (binfo, vid);

    7690  

    7691     /* Scan the non-primary bases of BINFO.  */

    7692     for (i = 0; i < BINFO_N_BASETYPES (binfo); ++i)

    7693     {

    7694       tree base_binfo;

    7695        

    7696       base_binfo = BINFO_BASETYPE (binfo, i);

    7697       if (base_binfo != primary_binfo)

    7698         add_vcall_offset_vtbl_entries_r (base_binfo, vid);

    7699     }

    7700   }

     

    同样,在这里,如果 binfo 来自派生类,也要递归 add_vcall_offset_vtbl_entries_r ,直到遇到最底层的主要基类。注意这个主要基类仍然可以是派生的,不过其基类中不能有虚函数了。在上面,在派生类(注意只能是当前类,即 C ;或虚拟基类,但其基类不能有虚函数)的处理中,首先是主要基类,然后是自己,最后才是非主要基类。

     

    7704   static void

    7705   add_vcall_offset_vtbl_entries_1 (tree binfo, vtbl_init_data * vid)                   in class.c

    7706   {

    7707     /* Make entries for the rest of the virtuals.  */

    7708     if (abi_version_at_least (2))

    7709     {

    7710       tree orig_fn;

    7711  

    7712       /* The ABI requires that the methods be processed in declaration

    7713          order. G++ 3.2 used the order in the vtable.  */

    7714       for (orig_fn = TYPE_METHODS (BINFO_TYPE (binfo));

    7715            orig_fn;

    7716            orig_fn = TREE_CHAIN (orig_fn))

    7717         if (DECL_VINDEX (orig_fn))

    7718            add_vcall_offset (orig_fn, binfo, vid);

    7719     }

    7720     else

    7721     {

              …

    7785     }

    7786   }

     

    我们关注从 GCC 3.4 开始应用的 ABI 。在前一节我们已经看到在 finish_struct_1 5123 行,虚函数的 DECL_VINDEX 已经更新为 INTEGER_CST ,并且这个域对于非虚函数来说是空的。显然, add_vcall_offset 只处理虚函数,并且注意这个虚函数来自 binfo 所表示的类。

     

    7790   static void

    7791   add_vcall_offset (tree orig_fn, tree binfo, vtbl_init_data *vid)                             in class.c

    7792   {

    7793     size_t i;

    7794     tree vcall_offset;

    7795  

    7796     /* If there is already an entry for a function with the same

    7797       signature as FN, then we do not need a second vcall offset.

    7798       Check the list of functions already present in the derived

    7799       class vtable.  */

    7800     for (i = 0; i < VARRAY_ACTIVE_SIZE (vid->fns); ++i)

    7801     {

    7802       tree derived_entry;

    7803  

    7804       derived_entry = VARRAY_TREE (vid->fns, i);

    7805       if (same_signature_p (derived_entry, orig_fn)

    7806         /* We only use one vcall offset for virtual destructors,

    7807           even though there are two virtual table entries.  */

    7808          || (DECL_DESTRUCTOR_P (derived_entry)

    7809           && DECL_DESTRUCTOR_P (orig_fn)))

    7810         return ;

    7811     }

    7812  

    7813     /* If we are building these vcall offsets as part of building

    7814       the vtable for the most derived class, remember the vcall

    7815       offset.  */

    7816     if (vid->binfo == TYPE_BINFO (vid->derived))

    7817       CLASSTYPE_VCALL_INDICES (vid->derived)

    7818         = tree_cons (orig_fn, vid->index,

    7819                    CLASSTYPE_VCALL_INDICES (vid->derived));

    7820  

    7821     /* The next vcall offset will be found at a more negative

    7822       offset.  */

    7823     vid->index = size_binop (MINUS_EXPR, vid->index,

    7824                         ssize_int (TARGET_VTABLE_DATA_ENTRY_DISTANCE));

    7825  

    7826     /* Keep track of this function.  */

    7827     VARRAY_PUSH_TREE (vid->fns, orig_fn);

    7828  

    7829     if (vid->generate_vcall_entries)

    7830     {

    7831       tree base;

    7832       tree fn;

    7833  

    7834       /* Find the overriding function.  */

    7835       fn = find_final_overrider (vid->rtti_binfo, binfo, orig_fn);

    7836       if (fn == error_mark_node)

    7837         vcall_offset = build1 (NOP_EXPR, vtable_entry_type ,

    7838                           integer_zero_node);

    7839       else

    7840       {

    7841         base = TREE_VALUE (fn);

    7842  

    7843         /* The vbase we're working on is a primary base of

    7844           vid->binfo. But it might be a lost primary, so its

    7845           BINFO_OFFSET might be wrong, so we just use the

    7846           BINFO_OFFSET from vid->binfo.  */

    7847         vcall_offset = size_diffop (BINFO_OFFSET (base),

    7848                               BINFO_OFFSET (vid->binfo));

    7849         vcall_offset = fold (build1 (NOP_EXPR, vtable_entry_type ,

    7850                               vcall_offset));

    7851       }

    7852       /* Add the initializer to the vtable.  */

    7853       *vid->last_init = build_tree_list (NULL_TREE, vcall_offset);

    7854       vid->last_init = &TREE_CHAIN (*vid->last_init);

    7855     }

    7856   }

     

    一开始, vid->fns 是一个空的数组,这里它将被用来暂存虚函数,以确保每个函数只处理一次。 7818 行的 vid->index 记录了在当前类(即类 C )的 vtable 中的当前索引(注意这是负的索引);如果 7816 行条件满足,意味着 dfs_accumulate_vtbl_inits 正在处理类 C ,而 C 是第一个被该函数处理的。这正是我们现在的情形, CLASSTYPE_VCALL_INDICES 则记录了这些需要 vcall 偏移的函数的位置,在 C 作为虚拟基类时,它将被 get_vcall_index 基类在虚函数的 BV_VCALL_INDEX 中(参见前一节 update_vtable_entry_for_fn )。在 7827 行缓存这个虚函数。而 7829 行的 vid->generate_vcall_entries 对于虚拟基类是 true ,表示需要为 vcall 构建 vtable 中的项。那么在 7835 行, vid->rtti_binfo 是当前类的 binfo (类 C binfo ),因而这里的被 find_final_overrider 找出是最后重载的虚函数(对于我们的例子 fn C::f )。注意如果 fn error_mark_node ,表示 find_final_overrider 发现错误(如发现二义性),这里就把 vcall 偏移设为 0 。而在正常情况下, fn 是一个 tree_list 节点,其 value 域是定义它的(基)类。那么 7847 行的 vcall_offset 则是正在准备 vtable 项初始值的基类到最后重载该虚函数的基类间的偏移。对于我们例子现在的场景,两者都是 C

    那么从处理 A build_vcall_and_vbase_vtbl_entries 退出,我们回到处理 B1 的函数,其中 build_vcall_offset_vtbl_entries 不做任何事,因为 B1 不是虚拟基类。但因为他有虚拟基类 A ,它进入 build_vbase_offset_vtbl_entries 。现在的调用栈是:

    build_vcall_and_vbase_vtbl_entries:       对应 C

            build_vcall_and_vbase_vtbl_entries:     对应 B1

                   build_vbase_offset_vtbl_entries:   对应 B1

    在处理当前类期间, vid->derived 总是指向类 C 。而 7543 行的代码过滤出不从虚拟基类派生的类。

     

    7534   static void

    7535   build_vbase_offset_vtbl_entries (tree binfo, vtbl_init_data * vid)                    in class.c

    7536   {

    7537     tree vbase;

    7538     tree t;

    7539     tree non_primary_binfo;

    7540  

    7541     /* If there are no virtual baseclasses, then there is nothing to

    7542       do.  */

    7543     if (!TYPE_USES_VIRTUAL_BASECLASSES (BINFO_TYPE (binfo)))

    7544       return ;

    7545  

    7546     t = vid->derived;

    7547    

    7548     /* We might be a primary base class. Go up the inheritance hierarchy

    7549       until we find the most derived class of which we are a primary base:

    7550       it is the offset of that which we need to use.  */

    7551     non_primary_binfo = binfo;

    7552     while (BINFO_INHERITANCE_CHAIN (non_primary_binfo))

    7553     {

    7554       tree b;

    7555  

    7556       /* If we have reached a virtual base, then it must be a primary

    7557          base (possibly multi-level) of vid->binfo, or we wouldn't

    7558          have called build_vcall_and_vbase_vtbl_entries for it. But it

    7559          might be a lost primary, so just skip down to vid->binfo.  */

    7560       if (TREE_VIA_VIRTUAL (non_primary_binfo))

    7561       {

    7562          non_primary_binfo = vid->binfo;

    7563          break ;

    7564       }

    7565  

    7566       b = BINFO_INHERITANCE_CHAIN (non_primary_binfo);

    7567       if (get_primary_binfo (b) != non_primary_binfo)

    7568         break ;

    7569       non_primary_binfo = b;

    7570     }

    7571  

    7572     /* Go through the virtual bases, adding the offsets.  */

    7573     for (vbase = TYPE_BINFO (BINFO_TYPE (binfo));

    7574          vbase;

    7575          vbase = TREE_CHAIN (vbase))

    7576     {

    7577       tree b;

    7578       tree delta;

    7579        

    7580       if (!TREE_VIA_VIRTUAL (vbase))

    7581         continue ;

    7582  

    7583       /* Find the instance of this virtual base in the complete

    7584          object.  */

    7585       b = copied_binfo (vbase, binfo);

    7586  

    7587       /* If we've already got an offset for this virtual base, we

    7588          don't need another one.  */

    7589       if (BINFO_VTABLE_PATH_MARKED (b))

    7590         continue ;

    7591       BINFO_VTABLE_PATH_MARKED (b) = 1;

    7592  

    7593       /* Figure out where we can find this vbase offset.  */

    7594       delta = size_binop (MULT_EXPR,

    7595                        vid->index,

    7596                        convert (ssizetype,

    7597                                 TYPE_SIZE_UNIT (vtable_entry_type)));

    7598       if (vid->primary_vtbl_p)

    7599         BINFO_VPTR_FIELD (b) = delta;

    7600  

    7601       if (binfo != TYPE_BINFO (t))

    7602       {

    7603          /* The vbase offset had better be the same.  */

    7604          my_friendly_assert (tree_int_cst_equal (delta,

    7605                                          BINFO_VPTR_FIELD (vbase)),

    7606                          20030202);

    7607       }

    7608  

    7609       /* The next vbase will come at a more negative offset.  */

    7610       vid->index = size_binop (MINUS_EXPR, vid->index,

    7611                          ssize_int (TARGET_VTABLE_DATA_ENTRY_DISTANCE));

    7612  

    7613       /* The initializer is the delta from BINFO to this virtual base.

    7614          The vbase offsets go in reverse inheritance-graph order, and

    7615          we are walking in inheritance graph order so these end up in

    7616          the right order.  */

    7617       delta = size_diffop (BINFO_OFFSET (b), BINFO_OFFSET (non_primary_binfo));

    7618        

    7619       *vid->last_init

    7620             = build_tree_list (NULL_TREE,

    7621                            fold (build1 (NOP_EXPR,

    7622                                       vtable_entry_type ,

    7623                                       delta)));

    7624       vid->last_init = &TREE_CHAIN (*vid->last_init);

    7625     }

    7626   }

     

    在上面 7552 行的 WHILE 循环中,尝试找出最接近的,从 binfo 所对应的类派生,但不使用它作为主要基类的基类。注意如果没有这样的基类, WHILE 循环最后会给出当前类(当前类不是任何已知类的基类,也就不存在主要基类一说)。对于我们例子中的 B1 就是这样, non_primary_binfo 指向 C

    在这里的 7598 行, vid->primary_vtbl_p true ,如果我们正在为当前类( C )准备 vtable 项。虚拟基类的 BINFO_VPTR_FIELD 保存了一个 INTEGER_CST ,它给出了 vtable 的索引,在那里可以找到该虚拟基类的偏移。那么这里得到的偏移是 A à C 。这个偏移的意义在于,考虑以下语句:

    A *pa = new C;

    pa->f();

    运行时多态必须保证得到调用的是 C::f A à C 的偏移是必须知道的。这个偏移就是上面计算的这个。

    7601 行的条件及 7604 行的断言,是为了确保每个包含虚拟基类的类所产生的 vbase 偏移的索引是一致的。

     


    最新回复(0)