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 偏移的索引是一致的。