当完成 B1 的处理时, build_vcall_and_vbase_vtbl_entries 返回到处理类 C 的最顶层的调用中。这一次, build_vbase_offset_vtbl_entries 不做任何事,因为 A 的部分已经被处理,设置了 BINFO_VTABLE_PATH_MARKED 标记。而在 build_vcall_offset_vtbl_entries 中,对于类 C , vid->generate_vcall_entries 被设置为 false ,而 vid->vbase 则设置为 C 的 binfo 。涉及的调用栈如下。
build_vcall_and_vbase_vtbl_entries: 对应 C
build_vcall_offset_vtbl_entries: 对应 C
add_vcall_offset_vtbl_entries_r: 对应 C
add_vcall_offset_vtbl_entries_r: 对应 B1
add_vcall_offset_vtbl_entries_r: 对应 A
add_vcall_offset_vtbl_entries_1: 对应 B1
add_vcall_offset: 对应 B1
add_vcall_offset_vtbl_entries_1: 对应 C
add_vcall_offset: 对应 C
add_vcall_offset_vtbl_entries_r: 对应 B2
add_vcall_offset_vtbl_entries_r: 对应 A
add_vcall_offset_vtbl_entries_1: 对应 B2
add_vcall_offset: 对应 B2
在上面的调用栈中, add_vcall_offset_vtbl_entries_r 首先递归,直到 C 的 A 。因为满足 7680 行条件,它不做任何事。在接下来的 add_vcall_offset 里, vid->fns 里现在缓存着前面处理过的虚函数,因此 7805 行的 same_signature_p 为所有 B1 的虚函数返回 true ,从 7810 行退出该函数。在从处理 B1 的 add_vcall_offset_vtbl_entries_r 返回后,为 C 调用 add_vcall_offset_vtbl_entries_1 。同样,在 add_vcall_offset 中, same_signature_p 为所有 C 的虚函数返回 true 。接着在 add_vcall_offset_vtbl_entries_r 的 7698 行,为 B2 (非主要基类)调用 add_vcall_offset_vtbl_entries_r ,它原来的主要基类 A 在递归时被 7680 行的条件滤除,同样的在 add_vcall_offset 中, same_signature_p 为所有 B2 的虚函数返回 true 。
当从为 C 调用的 build_vcall_and_vbase_vtbl_entries 退出时,我们将得到下面的 vtable 初始值及 CLASSTYPE_VCALL_INDICES 。
看到上图中 CLASSTYPE_VCALL_INDICES 的链表中, A::f 节点的 value 域是 -3 ,与 last_init 链所给出的 A::f 一致。
回到还在处理 C 的 build_vtbl_initializer 里。
build_vtbl_initializer (continue)
7378 /* If the target requires padding between data entries, add that now. */
7379 if (TARGET_VTABLE_DATA_ENTRY_DISTANCE > 1)
7380 {
…
7395 }
7396
7397 if (non_fn_entries_p)
7398 *non_fn_entries_p = list_length (vid.inits);
7399
7400 /* Go through all the ordinary virtual functions, building up
7401 initializers. */
7402 vfun_inits = NULL_TREE;
7403 for (v = BINFO_VIRTUALS (orig_binfo); v; v = TREE_CHAIN (v))
7404 {
7405 tree delta;
7406 tree vcall_index;
7407 tree fn, fn_original;
7408 tree init = NULL_TREE;
7409
7410 fn = BV_FN (v);
7411 fn_original = fn;
7412 if (DECL_THUNK_P (fn))
7413 {
7414 if (!DECL_NAME (fn))
7415 finish_thunk (fn);
7416 if (THUNK_ALIAS (fn))
7417 {
7418 fn = THUNK_ALIAS (fn);
7419 BV_FN (v) = fn;
7420 }
7421 fn_original = THUNK_TARGET (fn);
7422 }
7423
7424 /* If the only definition of this function signature along our
7425 primary base chain is from a lost primary, this vtable slot will
7426 never be used, so just zero it out. This is important to avoid
7427 requiring extra thunks which cannot be generated with the function.
7428
7429 We first check this in update_vtable_entry_for_fn, so we handle
7430 restored primary bases properly; we also need to do it here so we
7431 zero out unused slots in ctor vtables, rather than filling themff
7432 with erroneous values (though harmless, apart from relocation
7433 costs). */
7434 for (b = binfo; ; b = get_primary_binfo (b))
7435 {
7436 /* We found a defn before a lost primary; go ahead as normal. */
7437 if (look_for_overrides_here (BINFO_TYPE (b), fn_original))
7438 break ;
7439
7440 /* The nearest definition is from a lost primary; clear the
7441 slot. */
7442 if (BINFO_LOST_PRIMARY_P (b))
7443 {
7444 init = size_zero_node;
7445 break ;
7446 }
7447 }
7448
7449 if (! init)
7450 {
7451 /* Pull the offset for `this', and the function to call, out of
7452 the list. */
7453 delta = BV_DELTA (v);
7454 vcall_index = BV_VCALL_INDEX (v);
7455
7456 my_friendly_assert (TREE_CODE (delta) == INTEGER_CST, 19990727);
7457 my_friendly_assert (TREE_CODE (fn) == FUNCTION_DECL, 19990727);
7458
7459 /* You can't call an abstract virtual function; it's abstract.
7460 So, we replace these functions with __pure_virtual. */
7461 if (DECL_PURE_VIRTUAL_P (fn_original))
7462 fn = abort_fndecl;
7463 else if (!integer_zerop (delta) || vcall_index)
7464 {
7465 fn = make_thunk (fn, /*this_adjusting=*/ 1, delta, vcall_index);
7466 if (!DECL_NAME (fn))
7467 finish_thunk (fn);
7468 }
7469 /* Take the address of the function, considering it to be of an
7470 appropriate generic type. */
7471 init = build1 (ADDR_EXPR, vfunc_ptr_type_node , fn);
7472 /* The address of a function can't change. */
7473 TREE_CONSTANT (init) = 1;
7474 }
7475
7476 /* And add it to the chain of initializers. */
7477 if (TARGET_VTABLE_USES_DESCRIPTORS)
7478 {
…
7493 }
7494 else
7495 vfun_inits = tree_cons (NULL_TREE, init, vfun_inits);
7496 }
7497
7498 /* The initializers for virtual functions were built up in reverse
7499 order; straighten them out now. */
7450 vfun_inits = nreverse (vfun_inits);
7451
7452 /* The negative offset initializers are also in reverse order. */
7453 vid.inits = nreverse (vid.inits);
7454
7455 /* Chain the two together. */
7456 return chainon (vid.inits, vfun_inits);
7457 }
上面的 orig_binfo 来自当前类中,其 BINFO_VIRTUALS 在 update_vtable_entry_for_fn 中得到处理(见前一节)。 BINFO_VIRTUALS 中的虚函数可以是 thunk ,这些 thunk 是由 update_vtable_entry_for_fn 构建的,它们只是结果指针调整 thunk 。而这里构建的是 this 指针调整 thunk ,同时该 thunk 所需要的 BV_DELTA , BV_VCALL_INDEX (来自虚拟基类的 CLASSTYPE_VCALL_INDICES ,前面看到 C 也准备了这个,为未来做虚拟基类做打算)也是在 update_vtable_entry_for_fn 中准备的。
现在所有的 thunk 还是匿名的。这在前端中表示该 thunk 还未完成。
194 void
195 finish_thunk (tree thunk) in method.c
196 {
197 tree function, name;
198 tree fixed_offset = ssize_int (THUNK_FIXED_OFFSET (thunk));
199 tree virtual_offset = THUNK_VIRTUAL_OFFSET (thunk);
200
201 my_friendly_assert (!DECL_NAME (thunk) && DECL_THUNK_P (thunk), 20021127);
202 if (virtual_offset && DECL_RESULT_THUNK_P (thunk))
203 virtual_offset = BINFO_VPTR_FIELD (virtual_offset);
204 function = THUNK_TARGET (thunk);
205 name = mangle_thunk (function, DECL_THIS_THUNK_P (thunk),
206 fixed_offset, virtual_offset);
207
208 /* We can end up with declarations of (logically) different
209 covariant thunks, that do identical adjustments. The two thunks
210 will be adjusting between within different hierarchies, which
211 happen to have the same layout. We must nullify one of them to
212 refer to the other. */
213 if (DECL_RESULT_THUNK_P (thunk))
214 {
215 tree cov_probe;
216
217 for (cov_probe = DECL_THUNKS (function);
218 cov_probe; cov_probe = TREE_CHAIN (cov_probe))
219 if (DECL_NAME (cov_probe) == name)
220 {
221 my_friendly_assert (!DECL_THUNKS (thunk), 20031023);
222 THUNK_ALIAS (thunk) = (THUNK_ALIAS (cov_probe)
223 ? THUNK_ALIAS (cov_probe) : cov_probe);
224 break ;
225 }
226 }
227
228 DECL_NAME (thunk) = name;
229 SET_DECL_ASSEMBLER_NAME (thunk, name);
230 }
THUNK_VIRTUAL_OFFSET ,对于 this 指针调整 thunk ,保存了 vtable 的偏移,在 vtable 的该位置保存的是 vcall 偏移;而对于结果指针调整 thunk ,则是相关的虚拟基类的 binfo 。而 203 行的 BINFO_VPTR_FIELD 也是 vtable 的偏移,在那里可以找到这个虚拟基类的偏移量。因此对于结果指针调整 thunk ,需要 203 行的处理才能得到虚拟基类的偏移量。在 205 行, mangle_thunk 返回该 thunk 的修饰名(本节开头给出了 thunk 修饰名的例子)。在设置了 thunk 的名字后,它可以被视作已完成。因为 thunk 的名字是根据其作用来命名的,有可能会已经有了同名的 thunk ,这个时候把后面构建的 thunk 视为前一个的别名( alias ),它们共享同一个 thunk 函数。
dfs_accumulate_vtbl_inits (continue)
7285 /* Figure out the position to which the VPTR should point. */
7286 vtbl = TREE_PURPOSE (l);
7287 vtbl = build1 (ADDR_EXPR,
7288 vtbl_ptr_type_node ,
7289 vtbl);
7290 TREE_CONSTANT (vtbl) = 1;
7291 index = size_binop (PLUS_EXPR,
7292 size_int (non_fn_entries),
7293 size_int (list_length (TREE_VALUE (l))));
7294 index = size_binop (MULT_EXPR,
7295 TYPE_SIZE_UNIT (vtable_entry_type),
7296 index);
7297 vtbl = build (PLUS_EXPR, TREE_TYPE (vtbl), vtbl, index);
7298 TREE_CONSTANT (vtbl) = 1;
7299 }
7300
7301 if (ctor_vtbl_p)
7302 /* For a construction vtable, we can't overwrite BINFO_VTABLE.
7303 So, we make a TREE_LIST. Later, dfs_fixup_binfo_vtbls will
7304 straighten this out. */
7305 BINFO_VTABLE (binfo) = tree_cons (rtti_binfo, vtbl, BINFO_VTABLE (binfo));
7306 else if (BINFO_PRIMARY_P (binfo) && TREE_VIA_VIRTUAL (binfo))
7307 inits = NULL_TREE;
7308 else
7309 /* For an ordinary vtable, set BINFO_VTABLE. */
7310 BINFO_VTABLE (binfo) = vtbl;
7311
7312 return inits;
7313 }
回到 dfs_accumulate_vtbl_inits ,注意我们还在处理类 C 。上面的 l 是 finish_vtbls 中的 list ,在其节点的 TREE_PURPOSE 域是 TYPE_BINFO_VTABLE (保存在类型的 binfo 中的 vtable ,它是由 build_primary_vtable 构建的 VAR_DECL )。而一开始, l 中节点的 TREE_VALUE 域是 NULL (但是在 accumulate_vtbl_inits 的 7185 行,它把 dfs_accumulate_vtbl_inits 返回的链表串接起来)。注意到 non_fn_entrie 记录了负索引的个数。这样就能知道该类型 vtable 的可见项从那里开始。
当返回到 accumulate_vtbl_inits 时,在加长 finish_vtt 中的 list 之后,我们得到下图(记得在 create_vtable_ptr 中,每个虚函数都有一个 delta 是 0 的额外的节点)。
接着,在 accumulate_vtbl_inits 7195 行的 FOR 循环中开始处理 C 中的非虚拟基类。 B1 没有处理,因为在其 binfo 中标记 BINFO_NEW_VTABLE_MARKED 没有设置(参见 7273 行的 dfs_accumulate_vtbl_inits )。而对于 C 中的 B2 ,它准备了次要 vtable ,因此顺利通过 7273 行的测试。此时, ctor_vtbl_p 为 false , binfo 是类 B2 的 binfo , orig_binfo 是 C 中 B2 的 binfo , rtti_binfo 是 C 的 binfo ,它经历了与上面 C 类似的处理。然后当回到 finish_vtbls 时,在 C 中的 A 被 6780 行的 accumulate_vtbl_inits 处理,其中 ctor_vtbl_p 仍然是 false 。但这个 A 没有设置标记 BINFO_NEW_VTABLE_MARKED ,直接从 dfs_accumulate_vtbl_inits 返回。
那么在 6784 行调用 initialize_vtable 之前, list 就如下图所示。
( 点此打开 )
图 117 : vtable 的初始值
上图中没有出现 B1 及 A 的 vtable 部分,因为它们与 C 公用同一部分的 vtable 。
在下面的函数中,现在参数 inits 是由上图 list 的 value 域所指向的链表。这个链表保存了用于类 C 的 vtable 的初始值。注意到在 dfs_accumulate_vtbl_inits 的 7297 行的 vtbl 指向指定类 vtable 的开始点,它是 PLUS_EXPR ,对于 C 是 vtbl+4 ,对于基类 B2 是 vtbl+8 。
6789 static void
6790 initialize_vtable (tree binfo, tree inits) in class.c
6791 {
6792 tree decl;
6793
6794 layout_vtable_decl (binfo, list_length (inits));
6795 decl = get_vtbl_decl_for_binfo (binfo);
6796 initialize_array (decl, inits);
6797 dump_vtable (BINFO_TYPE (binfo), binfo, decl);
6798 }
一开始在 build_vtable 中, vtable 被构建为具有类型 vtbl_type_node ,这个类型是 vtable_entry_type 类型的维度为 0 的数组。现在该数组的大小已经确定了,我们可以通过 layout_vtable_decl 来构建确切的类型。
1778 static void
1779 layout_vtable_decl (tree binfo, int n) in class.c
1780 {
1781 tree atype;
1782 tree vtable;
1783
1784 atype = build_cplus_array_type (vtable_entry_type ,
1785 build_index_type (size_int (n - 1)));
1786 layout_type (atype);
1787
1788 /* We may have to grow the vtable. */
1789 vtable = get_vtbl_decl_for_binfo (binfo);
1790 if (!same_type_p (TREE_TYPE (vtable), atype))
1791 {
1792 TREE_TYPE (vtable) = atype;
1793 DECL_SIZE (vtable) = DECL_SIZE_UNIT (vtable) = NULL_TREE;
1794 layout_decl (vtable, 0);
1795 }
1796 }
因此在 6795 行,所获得的 vtable 的 VAR_DECL 已经有了正确定义的类型。并且它被传递作下面函数的参数 decl 。
6803 static void
6804 initialize_array (tree decl, tree inits) in class.c
6805 {
6806 tree context;
6807
6808 context = DECL_CONTEXT (decl);
6809 DECL_CONTEXT (decl) = NULL_TREE;
6810 DECL_INITIAL (decl) = build_constructor (NULL_TREE, inits);
6811 TREE_HAS_CONSTRUCTOR (DECL_INITIAL (decl)) = 1;
6812 cp_finish_decl (decl, DECL_INITIAL (decl), NULL_TREE, 0);
6813 DECL_CONTEXT (decl) = context;
6814 }
看到为这个 VAR_DECL 的初始化构建了 CONSTRUCTOR 节点。注意这个节点有空的 type 域(它作为“未知”来处理 ).
444 tree
445 build_constructor (tree type, tree vals) in tree.c
446 {
447 tree c = make_node (CONSTRUCTOR);
448 TREE_TYPE (c) = type;
449 CONSTRUCTOR_ELTS (c) = vals;
450
451 /* ??? May not be necessary. Mirrors what build does. */
452 if (vals)
453 {
454 TREE_SIDE_EFFECTS (c) = TREE_SIDE_EFFECTS (vals);
455 TREE_READONLY (c) = TREE_READONLY (vals);
456 TREE_CONSTANT (c) = TREE_CONSTANT (vals);
457 }
458 else
459 TREE_CONSTANT (c) = 0; /* safe side */
460
461 return c;
462 }
作为一个 VAR_DECL ,其处理的收尾工作由 cp_finish_decl 来完成。注意到我们现在仍然在处理“ SmallObject ”模板具现的另一个 cp_finish_decl 里。
4768 void
4769 cp_finish_decl (tree decl, tree init, tree asmspec_tree, int flags) in decl.c
4770 {
4771 tree type;
4772 tree ttype = NULL_TREE;
4773 tree cleanup;
4774 const char *asmspec = NULL;
4775 int was_readonly = 0;
4776 bool var_definition_p = false;
4777
4778 if (decl == error_mark_node)
4779 return ;
4780 else if (! decl)
4781 {
4782 if (init)
4783 error ("assignment (not initialization) in declaration");
4784 return ;
4785 }
4786
4787 my_friendly_assert (TREE_CODE (decl) != RESULT_DECL, 20030619);
4788
4789 /* Assume no cleanup is required. */
4790 cleanup = NULL_TREE;
4791
4792 /* If a name was specified, get the string. */
4793 if (global_scope_p (current_binding_level ))
4794 asmspec_tree = maybe_apply_renaming_pragma (decl, asmspec_tree);
4795 if (asmspec_tree)
4796 asmspec = TREE_STRING_POINTER (asmspec_tree);
4797
4798 if (init && TREE_CODE (init) == NAMESPACE_DECL)
4799 {
4800 error ("cannot initialize `%D' to namespace `%D'",
4802 decl, init);
4803 init = NULL_TREE;
4804 }
4805
4806 if (current_class_type
4807 && CP_DECL_CONTEXT (decl) == current_class_type
4808 && TYPE_BEING_DEFINED (current_class_type )
4809 && (DECL_INITIAL (decl) || init))
4810 DECL_INITIALIZED_IN_CLASS_P (decl) = 1;
4811
4812 if (TREE_CODE (decl) == VAR_DECL
4813 && DECL_CONTEXT (decl)
4814 && TREE_CODE (DECL_CONTEXT (decl)) == NAMESPACE_DECL
4815 && DECL_CONTEXT (decl) != current_namespace
4816 && init)
4817 {
4818 /* Leave the namespace of the object. */
4819 pop_decl_namespace ();
4820 }
4821
4822 type = TREE_TYPE (decl);
4823
4824 if (type == error_mark_node)
4825 goto finish_end0;
4826
4827 if (TYPE_HAS_MUTABLE_P (type))
4828 TREE_READONLY (decl) = 0;
4829
4830 if (processing_template_decl )
4831 {
…
4842 }
4843
4844 /* Parameters are handled by store_parm_decls, not cp_finish_decl. */
4845 my_friendly_assert (TREE_CODE (decl) != PARM_DECL, 19990828);
4846
4847 /* Take care of TYPE_DECLs up front. */
4848 if (TREE_CODE (decl) == TYPE_DECL)
4849 {
…
4868 }
4869
4870 if (TREE_CODE (decl) != FUNCTION_DECL)
4871 ttype = target_type (type);
4872
4873
4874 /* Currently, GNU C++ puts constants in text space, making them
4875 impossible to initialize. In the future, one would hope for
4876 an operating system which understood the difference between
4877 initialization and the running of a program. */
4878 if (! DECL_EXTERNAL (decl) && TREE_READONLY (decl))
4879 {
4880 was_readonly = 1;
4881 if (TYPE_NEEDS_CONSTRUCTING (type)
4882 || TREE_CODE (type) == REFERENCE_TYPE)
4883 TREE_READONLY (decl) = 0;
4884 }
4885
4886 if (TREE_CODE (decl) == VAR_DECL)
4887 {
4888 /* Only PODs can have thread-local storage. Other types may require
4889 various kinds of non-trivial initialization. */
4890 if (DECL_THREAD_LOCAL (decl) && !pod_type_p (TREE_TYPE (decl)))
4891 error ("`%D' cannot be thread-local because it has non-POD type `%T'",
4892 decl, TREE_TYPE (decl));
4893 /* Convert the initializer to the type of DECL, if we have not
4894 already initialized DECL. */
4895 if (!DECL_INITIALIZED_P (decl)
4896 /* If !DECL_EXTERNAL then DECL is being defined. In the
4897 case of a static data member initialized inside the
4898 class-specifier, there can be an initializer even if DECL
4899 is *not* defined. */
4900 && (!DECL_EXTERNAL (decl) || init))
4901 {
4902 init = check_initializer (decl, init, flags, &cleanup);
4903 /* Thread-local storage cannot be dynamically initialized. */
4904 if (DECL_THREAD_LOCAL (decl) && init)
4905 {
4906 error ("`%D' is thread-local and so cannot be dynamically "
4907 "initialized", decl);
4908 init = NULL_TREE;
4909 }
4910 if (DECL_EXTERNAL (decl) && init)
4911 {
4912 /* The static data member cannot be initialized by a
4913 non-constant when being declared. */
4914 error ("`%D' cannot be initialized by a non-constant expression"
4915 " when being declared", decl);
4916 DECL_INITIALIZED_IN_CLASS_P (decl) = 0;
4917 init = NULL_TREE;
4918 }
4919
4920 /* Handle:
4921
4922 [dcl.init]
4923
4924 The memory occupied by any object of static storage
4925 duration is zero-initialized at program startup before
4926 any other initialization takes place.
4927
4928 We cannot create an appropriate initializer until after
4929 the type of DECL is finalized. If DECL_INITIAL is set,
4930 then the DECL is statically initialized, and any
4931 necessary zero-initialization has already been performed. */
4932 if (TREE_STATIC (decl) && !DECL_INITIAL (decl))
4933 DECL_INITIAL (decl) = build_zero_init (TREE_TYPE (decl),
4934 /*nelts=*/ NULL_TREE,
4935 /*static_storage_p=*/ true);
4936 /* Remember that the initialization for this variable has
4937 taken place. */
4938 DECL_INITIALIZED_P (decl) = 1;
4939 /* This declaration is the definition of this variable,
4940 unless we are initializing a static data member within
4941 the class specifier. */
4942 if (!DECL_EXTERNAL (decl))
4943 var_definition_p = true;
4944 }
…
4951 }
在进入该函数之前 , 我们清除了 decl 的 DELC_CONTEXT 。 在 4794 行 , maybe_apply_renaming_pragma 出于兼容系统头文件的目的,仅用于 Solaris 及 Tru64 上的 UNIX 系统。